Skip to content

Commit

Permalink
Add parameter for FW negative cycle detection
Browse files Browse the repository at this point in the history
In b1f9e1e ("Implemented heuristics for Floyd-Warshall", Sat Jul 14
12:16:16 2018 +0300), some heuristics were added to improve the
performance of the Floyd-Warshall algorithm.  However, as an unintended
consequence, the FW algorithm could no longer be used to detect negative
cycles.

Add a parameter to the algorithm constructor, maintaining the existing
behavior, which enables users to detect negative cycle detection.
Furthermore, add a method to check for negative cycles.

This resolves jgrapht#1096.

Signed-off-by: Kenny Ballou <kb@devnulllabs.io>
  • Loading branch information
kennyballou committed Jun 28, 2021
1 parent 5d5554c commit fb191c3
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,28 @@ public class FloydWarshallShortestPaths<V, E>
private Object[][] backtrace = null;
private Object[][] lastHopMatrix = null;

// Should the algorithm attempt to find negative cycles
private final boolean detectNegativeCycles;

/**
* Create a new instance of the Floyd-Warshall all-pairs shortest path algorithm.
*
* @param graph the input graph
*/
public FloydWarshallShortestPaths(Graph<V, E> graph)
{
this(graph, false);
}

/**
* Create a new instance of the Floyd-Warshall all-pairs shortest path algorithm.
*
* Optionally, detect negative cycles by disabling iteration optimizations.
*
* @param graph the input graph
* @param detectNegativeCycles optionally detect negative cycles
*/
public FloydWarshallShortestPaths(Graph<V, E> graph, boolean detectNegativeCycles)
{
super(graph);

Expand Down Expand Up @@ -99,6 +115,7 @@ public FloydWarshallShortestPaths(Graph<V, E> graph)
}
this.minDegreeOne = minDegreeOne;
this.minDegreeTwo = minDegreeTwo;
this.detectNegativeCycles = detectNegativeCycles;
}

/**
Expand Down Expand Up @@ -241,6 +258,24 @@ public V getLastHop(V a, V b)
}
}

/**
* Return whether the graph contains a negative cycle.
* This runs in linear time by checking the self-loops of the adjacency matrix.
* @return true if a negative cycle is detected, otherwise false
*
*/
public boolean containsNegativeCycle()
{
lazyCalculateMatrix();
int n = vertices.size();
for (int i = 0; i < n; i++) {
if (d[i][i] < 0.0) {
return true;
}
}
return false;
}

/**
* Calculates the matrix of all shortest paths, but does not populate the last hops matrix.
*/
Expand Down Expand Up @@ -305,11 +340,11 @@ private void lazyCalculateMatrix()
// run fw alg
for (int k = minDegreeTwo; k < n; k++) {
for (int i = minDegreeOne; i < n; i++) {
if (i == k) {
if (!detectNegativeCycles && i == k) {
continue;
}
for (int j = minDegreeOne; j < n; j++) {
if (i == j || j == k) {
if (!detectNegativeCycles && (i == j || j == k)) {
continue;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,4 +162,18 @@ public void testWeightedEdges()
assertEquals(fw.getLastHop("a", "b"), vertexPath.get(vertexPath.size() - 2));
assertNull(fw.getPath("b", "a"));
}

@Test
public void testNegativeCycleDetection()
{
DefaultDirectedWeightedGraph<String, DefaultWeightedEdge> negativeCycle =
new DefaultDirectedWeightedGraph<>(DefaultWeightedEdge.class);
negativeCycle.addVertex("a");
negativeCycle.addVertex("b");
negativeCycle.setEdgeWeight(negativeCycle.addEdge("a", "b"), 0.0);
negativeCycle.setEdgeWeight(negativeCycle.addEdge("b", "a"), -1.0);
FloydWarshallShortestPaths<String, DefaultWeightedEdge> fw =
new FloydWarshallShortestPaths<>(negativeCycle, true);
assertTrue(fw.containsNegativeCycle());
}
}

0 comments on commit fb191c3

Please sign in to comment.