Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Floyd Warshall Shortest Paths cannot detect negative cycles #1096

Open
kennyballou opened this issue Jun 24, 2021 · 4 comments · May be fixed by #1097
Open

Floyd Warshall Shortest Paths cannot detect negative cycles #1096

kennyballou opened this issue Jun 24, 2021 · 4 comments · May be fixed by #1097

Comments

@kennyballou
Copy link

Issue

I may be missing something, but the heuristics introduced from #563, specifically number 4, introduce an assumption that I don't believe was there before, and therefore, causes a bug with a specific use-case of the FloydWarshall Algorithm. The underlying idea behind number 4 is that immediate neighbors will not affect the weights of the current shortest path, so it's safe to skip them. However, this only applies if there is not a negative weight cycle. Using the FW algorithm to detect negative cycles no longer works with these improvements.

I'm not sure what the right solution is, maybe I need to use a different algorithm for detecting negative cycles. For now, I've replicated the algorithm sans the number 4 improvements, which works for me. But I would much rather use something from jgrapht if I can, instead of copying and making mistakes.

Perhaps it's sufficient to update the docs for FW to say that it will not detect negative cycles?

Steps to reproduce (small coding example)

Graph<Local, DefaultEdge> g = new DefaultDirectedWeightedGraph<>(DefaultEdge.class);
this.graph.vertexSet().forEach(v -> g.addVertex(v));
this.graph.vertexSet().forEach(v1 -> {
        this.graph.vertexSet().forEach(v2 -> {
                DefaultEdge e;
                if (v1.equals(v2)) {
                    e = g.addEdge(v1, v2);
                    g.setEdgeWeight(e, 0.0);
                } else {
                    e = g.addEdge(v1, v2);
                    g.setEdgeWeight(e, Double.POSITIVE_INFINITY);
                }
            });
    });
this.graph.edgeSet().forEach(c -> {
        Local s = this.graph.getEdgeSource(c);
        Local t = this.graph.getEdgeTarget(c);
        DefaultEdge e = g.getEdge(s, t);
        g.setEdgeWeight(e, c.bound());
    });
FloydWarshallShortestPaths<Local, DefaultEdge> sps = new FloydWarshallShortestPaths<>(g);
for (Local l : g.vertexSet()) {
    if (sps.getPathWeight(l, l) < 0.0) {
        return false;
    }
}
return true;

this.graph is the graph I use for representing a weakly relational domain (static analysis). I translate the graph into a edge weighted graph, copying the constraint bounds as edge weights. From there, I attempt to compute all shortest paths using the FW algorithm.

Expected behaviour

The FW algorithm should be able to detect negative weight cycles, however, this is not happening.

Other information

I have used this minimal example for testing negative cycle detection.

[[0, -1],
 [0, 0]]
@kennyballou
Copy link
Author

Re using a different algorithm: I can use the BellmanFordShortestPath algorithm to detect negative weight cycles instead of attempting to roll my own FW version.

@jsichi
Copy link
Member

jsichi commented Jun 27, 2021

Hmm, it seems like an important use case. Taking a boolean parameter and using it to control the checks (I'm guessing a short-circuit in the if statements might compile quite efficiently to either or both variants after warmup) could work.

@kennyballou
Copy link
Author

Yeah, that should work well. The JIT compiler should start skipping the checks after an iteration or too. I was worried the checks within the loop would be too slow and that it would be more time efficient to duplicate the function for negative cycle detection, however, given that the JIT compilation step, this is likely unnecessary.

If it's all the same, I would like to try fixing this and submitting a PR for it.

@jsichi
Copy link
Member

jsichi commented Jun 28, 2021

Yes, please submit a PR, thanks!

kennyballou added a commit to kennyballou/jgrapht that referenced this issue Jun 28, 2021
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>
@kennyballou kennyballou linked a pull request Jun 28, 2021 that will close this issue
7 tasks
kennyballou added a commit to kennyballou/jgrapht that referenced this issue Jun 30, 2021
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants