Showing
5 changed files
with
118 additions
and
10 deletions
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +/** | ||
4 | + * Bellman-Ford graph search algorithm for locating shortest-paths in | ||
5 | + * directed graphs that may contain negative cycles. | ||
6 | + */ | ||
7 | +public class BellmanFordGraphSearch<V extends Vertex, E extends Edge<V>> | ||
8 | + extends AbstractGraphPathSearch<V, E> { | ||
9 | + | ||
10 | + @Override | ||
11 | + public Result<V, E> search(Graph<V, E> graph, V src, V dst, | ||
12 | + EdgeWeight<V, E> weight) { | ||
13 | + checkArguments(graph, src, dst); | ||
14 | + | ||
15 | + // Prepare the graph search result. | ||
16 | + DefaultResult result = new DefaultResult(src, dst); | ||
17 | + | ||
18 | + // The source vertex has cost 0, of course. | ||
19 | + result.updateVertex(src, null, 0.0, true); | ||
20 | + | ||
21 | + int max = graph.getVertexes().size() - 1; | ||
22 | + for (int i = 0; i < max; i++) { | ||
23 | + // Relax, if possible, all egress edges of the current vertex. | ||
24 | + for (E edge : graph.getEdges()) { | ||
25 | + if (result.hasCost(edge.src())) { | ||
26 | + result.relaxEdge(edge, result.cost(edge.src()), weight); | ||
27 | + } | ||
28 | + } | ||
29 | + } | ||
30 | + | ||
31 | + // Remove any vertexes reached by traversing edges with negative weights. | ||
32 | + for (E edge : graph.getEdges()) { | ||
33 | + if (result.hasCost(edge.src())) { | ||
34 | + if (result.relaxEdge(edge, result.cost(edge.src()), weight)) { | ||
35 | + result.removeVertex(edge.dst()); | ||
36 | + } | ||
37 | + } | ||
38 | + } | ||
39 | + | ||
40 | + // Finally, but the paths on the search result and return. | ||
41 | + result.buildPaths(); | ||
42 | + return result; | ||
43 | + } | ||
44 | + | ||
45 | +} |
... | @@ -10,7 +10,8 @@ public class BreadthFirstSearch<V extends Vertex, E extends Edge<V>> | ... | @@ -10,7 +10,8 @@ public class BreadthFirstSearch<V extends Vertex, E extends Edge<V>> |
10 | extends AbstractGraphPathSearch<V, E> { | 10 | extends AbstractGraphPathSearch<V, E> { |
11 | 11 | ||
12 | @Override | 12 | @Override |
13 | - public Result<V, E> search(Graph<V, E> graph, V src, V dst, EdgeWeight<V, E> ew) { | 13 | + public Result<V, E> search(Graph<V, E> graph, V src, V dst, |
14 | + EdgeWeight<V, E> weight) { | ||
14 | checkArguments(graph, src, dst); | 15 | checkArguments(graph, src, dst); |
15 | 16 | ||
16 | // Prepare the graph result. | 17 | // Prepare the graph result. |
... | @@ -18,7 +19,7 @@ public class BreadthFirstSearch<V extends Vertex, E extends Edge<V>> | ... | @@ -18,7 +19,7 @@ public class BreadthFirstSearch<V extends Vertex, E extends Edge<V>> |
18 | 19 | ||
19 | // Setup the starting frontier with the source as the sole vertex. | 20 | // Setup the starting frontier with the source as the sole vertex. |
20 | Set<V> frontier = new HashSet<>(); | 21 | Set<V> frontier = new HashSet<>(); |
21 | - result.costs.put(src, 0.0); | 22 | + result.updateVertex(src, null, 0.0, true); |
22 | frontier.add(src); | 23 | frontier.add(src); |
23 | 24 | ||
24 | boolean reachedEnd = false; | 25 | boolean reachedEnd = false; |
... | @@ -35,9 +36,8 @@ public class BreadthFirstSearch<V extends Vertex, E extends Edge<V>> | ... | @@ -35,9 +36,8 @@ public class BreadthFirstSearch<V extends Vertex, E extends Edge<V>> |
35 | V nextVertex = edge.dst(); | 36 | V nextVertex = edge.dst(); |
36 | if (!result.hasCost(nextVertex)) { | 37 | if (!result.hasCost(nextVertex)) { |
37 | // If this vertex has not been visited yet, update it. | 38 | // If this vertex has not been visited yet, update it. |
38 | - result.updateVertex(nextVertex, edge, | 39 | + double newCost = cost + (weight == null ? 1.0 : weight.weight(edge)); |
39 | - cost + (ew == null ? 1.0 : ew.weight(edge)), | 40 | + result.updateVertex(nextVertex, edge, newCost, true); |
40 | - true); | ||
41 | // If we have reached our intended destination, bail. | 41 | // If we have reached our intended destination, bail. |
42 | if (nextVertex.equals(dst)) { | 42 | if (nextVertex.equals(dst)) { |
43 | reachedEnd = true; | 43 | reachedEnd = true; | ... | ... |
... | @@ -12,8 +12,9 @@ public class DijkstraGraphSearch<V extends Vertex, E extends Edge<V>> | ... | @@ -12,8 +12,9 @@ public class DijkstraGraphSearch<V extends Vertex, E extends Edge<V>> |
12 | extends AbstractGraphPathSearch<V, E> { | 12 | extends AbstractGraphPathSearch<V, E> { |
13 | 13 | ||
14 | @Override | 14 | @Override |
15 | - public Result<V, E> search(Graph<V, E> g, V src, V dst, EdgeWeight<V, E> ew) { | 15 | + public Result<V, E> search(Graph<V, E> graph, V src, V dst, |
16 | - checkArguments(g, src, dst); | 16 | + EdgeWeight<V, E> weight) { |
17 | + checkArguments(graph, src, dst); | ||
17 | 18 | ||
18 | // Use the default result to remember cumulative costs and parent | 19 | // Use the default result to remember cumulative costs and parent |
19 | // edges to each each respective vertex. | 20 | // edges to each each respective vertex. |
... | @@ -25,7 +26,7 @@ public class DijkstraGraphSearch<V extends Vertex, E extends Edge<V>> | ... | @@ -25,7 +26,7 @@ public class DijkstraGraphSearch<V extends Vertex, E extends Edge<V>> |
25 | // Use the min priority queue to progressively find each nearest | 26 | // Use the min priority queue to progressively find each nearest |
26 | // vertex until we reach the desired destination, if one was given, | 27 | // vertex until we reach the desired destination, if one was given, |
27 | // or until we reach all possible destinations. | 28 | // or until we reach all possible destinations. |
28 | - Heap<V> minQueue = createMinQueue(g.getVertexes(), | 29 | + Heap<V> minQueue = createMinQueue(graph.getVertexes(), |
29 | new PathCostComparator(result)); | 30 | new PathCostComparator(result)); |
30 | while (!minQueue.isEmpty()) { | 31 | while (!minQueue.isEmpty()) { |
31 | // Get the nearest vertex | 32 | // Get the nearest vertex |
... | @@ -38,8 +39,8 @@ public class DijkstraGraphSearch<V extends Vertex, E extends Edge<V>> | ... | @@ -38,8 +39,8 @@ public class DijkstraGraphSearch<V extends Vertex, E extends Edge<V>> |
38 | double cost = result.cost(nearest); | 39 | double cost = result.cost(nearest); |
39 | if (cost < Double.MAX_VALUE) { | 40 | if (cost < Double.MAX_VALUE) { |
40 | // If the vertex is reachable, relax all its egress edges. | 41 | // If the vertex is reachable, relax all its egress edges. |
41 | - for (E e : g.getEdgesFrom(nearest)) { | 42 | + for (E e : graph.getEdgesFrom(nearest)) { |
42 | - result.relaxEdge(e, cost, ew); | 43 | + result.relaxEdge(e, cost, weight); |
43 | } | 44 | } |
44 | } | 45 | } |
45 | 46 | ... | ... |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +import org.junit.Test; | ||
4 | + | ||
5 | +import java.util.HashSet; | ||
6 | +import java.util.Set; | ||
7 | + | ||
8 | +import static org.junit.Assert.assertEquals; | ||
9 | + | ||
10 | +/** | ||
11 | + * Test of the Bellman-Ford algorithm. | ||
12 | + */ | ||
13 | +public class BellmanFordGraphSearchTest extends BreadthFirstSearchTest { | ||
14 | + | ||
15 | + @Override | ||
16 | + protected AbstractGraphPathSearch<TestVertex, TestEdge> graphSearch() { | ||
17 | + return new BellmanFordGraphSearch<>(); | ||
18 | + } | ||
19 | + | ||
20 | + @Test | ||
21 | + @Override | ||
22 | + public void defaultGraphTest() { | ||
23 | + executeDefaultTest(7, 5, 5.0); | ||
24 | + } | ||
25 | + | ||
26 | + @Test | ||
27 | + public void defaultHopCountWeight() { | ||
28 | + weight = null; | ||
29 | + executeDefaultTest(10, 3, 3.0); | ||
30 | + } | ||
31 | + | ||
32 | + @Test | ||
33 | + public void searchGraphWithNegativeCycles() { | ||
34 | + Set<TestVertex> vertexes = new HashSet<>(vertices()); | ||
35 | + vertexes.add(Z); | ||
36 | + | ||
37 | + Set<TestEdge> edges = new HashSet<>(edges()); | ||
38 | + edges.add(new TestEdge(G, Z, 1.0)); | ||
39 | + edges.add(new TestEdge(Z, G, -2.0)); | ||
40 | + | ||
41 | + g = new AdjacencyListsGraph<>(vertexes, edges); | ||
42 | + | ||
43 | + GraphPathSearch<TestVertex, TestEdge> search = graphSearch(); | ||
44 | + Set<Path<TestVertex, TestEdge>> paths = search.search(g, A, H, weight).paths(); | ||
45 | + assertEquals("incorrect paths count", 1, paths.size()); | ||
46 | + | ||
47 | + Path p = paths.iterator().next(); | ||
48 | + assertEquals("incorrect src", A, p.src()); | ||
49 | + assertEquals("incorrect dst", H, p.dst()); | ||
50 | + assertEquals("incorrect path length", 5, p.edges().size()); | ||
51 | + assertEquals("incorrect path cost", 5.0, p.cost(), 0.1); | ||
52 | + | ||
53 | + paths = search.search(g, A, G, weight).paths(); | ||
54 | + assertEquals("incorrect paths count", 0, paths.size()); | ||
55 | + | ||
56 | + paths = search.search(g, A, null, weight).paths(); | ||
57 | + printPaths(paths); | ||
58 | + assertEquals("incorrect paths count", 6, paths.size()); | ||
59 | + } | ||
60 | + | ||
61 | +} |
... | @@ -17,6 +17,7 @@ public class GraphTest { | ... | @@ -17,6 +17,7 @@ public class GraphTest { |
17 | static final TestVertex F = new TestVertex("F"); | 17 | static final TestVertex F = new TestVertex("F"); |
18 | static final TestVertex G = new TestVertex("G"); | 18 | static final TestVertex G = new TestVertex("G"); |
19 | static final TestVertex H = new TestVertex("H"); | 19 | static final TestVertex H = new TestVertex("H"); |
20 | + static final TestVertex Z = new TestVertex("Z"); | ||
20 | 21 | ||
21 | protected Graph<TestVertex, TestEdge> g; | 22 | protected Graph<TestVertex, TestEdge> g; |
22 | 23 | ... | ... |
-
Please register or login to post a comment