Showing
7 changed files
with
353 additions
and
48 deletions
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +import java.util.ArrayList; | ||
4 | +import java.util.Collections; | ||
5 | +import java.util.HashMap; | ||
6 | +import java.util.HashSet; | ||
7 | +import java.util.List; | ||
8 | +import java.util.Map; | ||
9 | +import java.util.Set; | ||
10 | + | ||
11 | +/** | ||
12 | + * Tarjan algorithm for searching a graph and producing results describing | ||
13 | + * the graph SCC (strongly-connected components). | ||
14 | + */ | ||
15 | +public class TarjanGraphSearch<V extends Vertex, E extends Edge<V>> | ||
16 | + implements GraphSearch<V, E> { | ||
17 | + | ||
18 | + /** | ||
19 | + * {@inheritDoc} | ||
20 | + * <p/> | ||
21 | + * This implementation produces results augmented with information on | ||
22 | + * SCCs within the graph. | ||
23 | + * <p/> | ||
24 | + * To prevent traversal of an edge, the {@link EdgeWeight#weight} should | ||
25 | + * return a negative value as an edge weight. | ||
26 | + */ | ||
27 | + @Override | ||
28 | + public SCCResult<V, E> search(Graph<V, E> graph, EdgeWeight<V, E> weight) { | ||
29 | + SCCResult<V, E> result = new SCCResult<>(graph); | ||
30 | + for (V vertex : graph.getVertexes()) { | ||
31 | + VertexData data = result.data(vertex); | ||
32 | + if (data == null) { | ||
33 | + connect(graph, vertex, weight, result); | ||
34 | + } | ||
35 | + } | ||
36 | + return result.build(); | ||
37 | + } | ||
38 | + | ||
39 | + /** | ||
40 | + * Scans the specified graph, using recursion, and produces SCC results. | ||
41 | + * | ||
42 | + * @param graph graph to search | ||
43 | + * @param vertex current vertex to scan and connect | ||
44 | + * @param weight optional edge weight | ||
45 | + * @param result graph search result | ||
46 | + * @return augmentation vertexData for the current vertex | ||
47 | + */ | ||
48 | + private VertexData<V> connect(Graph<V, E> graph, V vertex, | ||
49 | + EdgeWeight<V, E> weight, | ||
50 | + SCCResult<V, E> result) { | ||
51 | + VertexData<V> data = result.addData(vertex); | ||
52 | + | ||
53 | + // Scan through all egress edges of the current vertex. | ||
54 | + for (E edge : graph.getEdgesFrom(vertex)) { | ||
55 | + V nextVertex = edge.dst(); | ||
56 | + | ||
57 | + // If edge weight is negative, skip it. | ||
58 | + if (weight != null && weight.weight(edge) < 0) { | ||
59 | + continue; | ||
60 | + } | ||
61 | + | ||
62 | + // Attempt to get the augmentation vertexData for the next vertex. | ||
63 | + VertexData<V> nextData = result.data(nextVertex); | ||
64 | + if (nextData == null) { | ||
65 | + // Next vertex has not been visited yet, so do this now. | ||
66 | + nextData = connect(graph, nextVertex, weight, result); | ||
67 | + data.lowLink = Math.min(data.lowLink, nextData.lowLink); | ||
68 | + | ||
69 | + } else if (result.visited(nextData)) { | ||
70 | + // Next vertex has been visited, which means it is in the | ||
71 | + // same cluster as the current vertex. | ||
72 | + data.lowLink = Math.min(data.lowLink, nextData.index); | ||
73 | + } | ||
74 | + } | ||
75 | + | ||
76 | + if (data.lowLink == data.index) { | ||
77 | + result.addCluster(data); | ||
78 | + } | ||
79 | + return data; | ||
80 | + } | ||
81 | + | ||
82 | + /** | ||
83 | + * Graph search result augmented with SCC vertexData. | ||
84 | + */ | ||
85 | + public static final class SCCResult<V extends Vertex, E extends Edge<V>> | ||
86 | + implements Result { | ||
87 | + | ||
88 | + private final Graph<V, E> graph; | ||
89 | + private List<Set<V>> clusterVertexes = new ArrayList<>(); | ||
90 | + private List<Set<E>> clusterEdges = new ArrayList<>(); | ||
91 | + | ||
92 | + private int index = 0; | ||
93 | + private final Map<V, VertexData<V>> vertexData = new HashMap<>(); | ||
94 | + private final List<VertexData<V>> visited = new ArrayList<>(); | ||
95 | + | ||
96 | + private SCCResult(Graph<V, E> graph) { | ||
97 | + this.graph = graph; | ||
98 | + } | ||
99 | + | ||
100 | + /** | ||
101 | + * Returns the number of SCC clusters in the graph. | ||
102 | + * | ||
103 | + * @return number of clusters | ||
104 | + */ | ||
105 | + public int clusterCount() { | ||
106 | + return clusterEdges.size(); | ||
107 | + } | ||
108 | + | ||
109 | + /** | ||
110 | + * Returns the list of strongly connected vertex clusters. | ||
111 | + * | ||
112 | + * @return list of strongly connected vertex sets | ||
113 | + */ | ||
114 | + public List<Set<V>> clusterVertexes() { | ||
115 | + return clusterVertexes; | ||
116 | + } | ||
117 | + | ||
118 | + /** | ||
119 | + * Returns the list of edges linking strongly connected vertex clusters. | ||
120 | + * | ||
121 | + * @return list of strongly connected edge sets | ||
122 | + */ | ||
123 | + public List<Set<E>> clusterEdges() { | ||
124 | + return clusterEdges; | ||
125 | + } | ||
126 | + | ||
127 | + // Gets the augmentation vertexData for the specified vertex | ||
128 | + private VertexData<V> data(V vertex) { | ||
129 | + return vertexData.get(vertex); | ||
130 | + } | ||
131 | + | ||
132 | + // Adds augmentation vertexData for the specified vertex | ||
133 | + private VertexData<V> addData(V vertex) { | ||
134 | + VertexData<V> d = new VertexData<>(vertex, index); | ||
135 | + vertexData.put(vertex, d); | ||
136 | + visited.add(0, d); | ||
137 | + index++; | ||
138 | + return d; | ||
139 | + } | ||
140 | + | ||
141 | + // Indicates whether the given vertex has been visited | ||
142 | + private boolean visited(VertexData data) { | ||
143 | + return visited.contains(data); | ||
144 | + } | ||
145 | + | ||
146 | + // Adds a new cluster for the specified vertex | ||
147 | + private void addCluster(VertexData data) { | ||
148 | + Set<V> vertexes = findClusterVertices(data); | ||
149 | + clusterVertexes.add(vertexes); | ||
150 | + clusterEdges.add(findClusterEdges(vertexes)); | ||
151 | + } | ||
152 | + | ||
153 | + private Set<V> findClusterVertices(VertexData data) { | ||
154 | + VertexData<V> nextVertexData; | ||
155 | + Set<V> vertexes = new HashSet<>(); | ||
156 | + do { | ||
157 | + nextVertexData = visited.remove(0); | ||
158 | + vertexes.add(nextVertexData.vertex); | ||
159 | + } while (data != nextVertexData); | ||
160 | + return Collections.unmodifiableSet(vertexes); | ||
161 | + } | ||
162 | + | ||
163 | + private Set<E> findClusterEdges(Set<V> vertexes) { | ||
164 | + Set<E> edges = new HashSet<>(); | ||
165 | + for (V vertex : vertexes) { | ||
166 | + for (E edge : graph.getEdgesFrom(vertex)) { | ||
167 | + if (vertexes.contains((edge.dst()))) { | ||
168 | + edges.add(edge); | ||
169 | + } | ||
170 | + } | ||
171 | + } | ||
172 | + return Collections.unmodifiableSet(edges); | ||
173 | + } | ||
174 | + | ||
175 | + public SCCResult<V, E> build() { | ||
176 | + clusterVertexes = Collections.unmodifiableList(clusterVertexes); | ||
177 | + clusterEdges = Collections.unmodifiableList(clusterEdges); | ||
178 | + return this; | ||
179 | + } | ||
180 | + } | ||
181 | + | ||
182 | + // Augments the vertex to assist in determining SCC clusters. | ||
183 | + private static final class VertexData<V extends Vertex> { | ||
184 | + final V vertex; | ||
185 | + int index; | ||
186 | + int lowLink; | ||
187 | + | ||
188 | + private VertexData(V vertex, int index) { | ||
189 | + this.vertex = vertex; | ||
190 | + this.index = index; | ||
191 | + this.lowLink = index; | ||
192 | + } | ||
193 | + } | ||
194 | + | ||
195 | +} |
... | @@ -31,17 +31,17 @@ public class BellmanFordGraphSearchTest extends BreadthFirstSearchTest { | ... | @@ -31,17 +31,17 @@ public class BellmanFordGraphSearchTest extends BreadthFirstSearchTest { |
31 | 31 | ||
32 | @Test | 32 | @Test |
33 | public void searchGraphWithNegativeCycles() { | 33 | public void searchGraphWithNegativeCycles() { |
34 | - Set<TestVertex> vertexes = new HashSet<>(vertices()); | 34 | + Set<TestVertex> vertexes = new HashSet<>(vertexes()); |
35 | vertexes.add(Z); | 35 | vertexes.add(Z); |
36 | 36 | ||
37 | Set<TestEdge> edges = new HashSet<>(edges()); | 37 | Set<TestEdge> edges = new HashSet<>(edges()); |
38 | edges.add(new TestEdge(G, Z, 1.0)); | 38 | edges.add(new TestEdge(G, Z, 1.0)); |
39 | edges.add(new TestEdge(Z, G, -2.0)); | 39 | edges.add(new TestEdge(Z, G, -2.0)); |
40 | 40 | ||
41 | - g = new AdjacencyListsGraph<>(vertexes, edges); | 41 | + graph = new AdjacencyListsGraph<>(vertexes, edges); |
42 | 42 | ||
43 | GraphPathSearch<TestVertex, TestEdge> search = graphSearch(); | 43 | GraphPathSearch<TestVertex, TestEdge> search = graphSearch(); |
44 | - Set<Path<TestVertex, TestEdge>> paths = search.search(g, A, H, weight).paths(); | 44 | + Set<Path<TestVertex, TestEdge>> paths = search.search(graph, A, H, weight).paths(); |
45 | assertEquals("incorrect paths count", 1, paths.size()); | 45 | assertEquals("incorrect paths count", 1, paths.size()); |
46 | 46 | ||
47 | Path p = paths.iterator().next(); | 47 | Path p = paths.iterator().next(); |
... | @@ -50,10 +50,10 @@ public class BellmanFordGraphSearchTest extends BreadthFirstSearchTest { | ... | @@ -50,10 +50,10 @@ public class BellmanFordGraphSearchTest extends BreadthFirstSearchTest { |
50 | assertEquals("incorrect path length", 5, p.edges().size()); | 50 | assertEquals("incorrect path length", 5, p.edges().size()); |
51 | assertEquals("incorrect path cost", 5.0, p.cost(), 0.1); | 51 | assertEquals("incorrect path cost", 5.0, p.cost(), 0.1); |
52 | 52 | ||
53 | - paths = search.search(g, A, G, weight).paths(); | 53 | + paths = search.search(graph, A, G, weight).paths(); |
54 | assertEquals("incorrect paths count", 0, paths.size()); | 54 | assertEquals("incorrect paths count", 0, paths.size()); |
55 | 55 | ||
56 | - paths = search.search(g, A, null, weight).paths(); | 56 | + paths = search.search(graph, A, null, weight).paths(); |
57 | printPaths(paths); | 57 | printPaths(paths); |
58 | assertEquals("incorrect paths count", 6, paths.size()); | 58 | assertEquals("incorrect paths count", 6, paths.size()); |
59 | } | 59 | } | ... | ... |
... | @@ -29,10 +29,10 @@ public class BreadthFirstSearchTest extends AbstractGraphPathSearchTest { | ... | @@ -29,10 +29,10 @@ public class BreadthFirstSearchTest extends AbstractGraphPathSearchTest { |
29 | 29 | ||
30 | // Executes the default test | 30 | // Executes the default test |
31 | protected void executeDefaultTest(int pathCount, int pathLength, double pathCost) { | 31 | protected void executeDefaultTest(int pathCount, int pathLength, double pathCost) { |
32 | - g = new AdjacencyListsGraph<>(vertices(), edges()); | 32 | + graph = new AdjacencyListsGraph<>(vertexes(), edges()); |
33 | 33 | ||
34 | GraphPathSearch<TestVertex, TestEdge> search = graphSearch(); | 34 | GraphPathSearch<TestVertex, TestEdge> search = graphSearch(); |
35 | - Set<Path<TestVertex, TestEdge>> paths = search.search(g, A, H, weight).paths(); | 35 | + Set<Path<TestVertex, TestEdge>> paths = search.search(graph, A, H, weight).paths(); |
36 | assertEquals("incorrect paths count", 1, paths.size()); | 36 | assertEquals("incorrect paths count", 1, paths.size()); |
37 | 37 | ||
38 | Path p = paths.iterator().next(); | 38 | Path p = paths.iterator().next(); |
... | @@ -41,7 +41,7 @@ public class BreadthFirstSearchTest extends AbstractGraphPathSearchTest { | ... | @@ -41,7 +41,7 @@ public class BreadthFirstSearchTest extends AbstractGraphPathSearchTest { |
41 | assertEquals("incorrect path length", pathLength, p.edges().size()); | 41 | assertEquals("incorrect path length", pathLength, p.edges().size()); |
42 | assertEquals("incorrect path cost", pathCost, p.cost(), 0.1); | 42 | assertEquals("incorrect path cost", pathCost, p.cost(), 0.1); |
43 | 43 | ||
44 | - paths = search.search(g, A, null, weight).paths(); | 44 | + paths = search.search(graph, A, null, weight).paths(); |
45 | printPaths(paths); | 45 | printPaths(paths); |
46 | assertEquals("incorrect paths count", pathCount, paths.size()); | 46 | assertEquals("incorrect paths count", pathCount, paths.size()); |
47 | } | 47 | } | ... | ... |
... | @@ -33,11 +33,11 @@ public class DepthFirstSearchTest extends AbstractGraphPathSearchTest { | ... | @@ -33,11 +33,11 @@ public class DepthFirstSearchTest extends AbstractGraphPathSearchTest { |
33 | 33 | ||
34 | protected void executeDefaultTest(int minLength, int maxLength, | 34 | protected void executeDefaultTest(int minLength, int maxLength, |
35 | double minCost, double maxCost) { | 35 | double minCost, double maxCost) { |
36 | - g = new AdjacencyListsGraph<>(vertices(), edges()); | 36 | + graph = new AdjacencyListsGraph<>(vertexes(), edges()); |
37 | DepthFirstSearch<TestVertex, TestEdge> search = graphSearch(); | 37 | DepthFirstSearch<TestVertex, TestEdge> search = graphSearch(); |
38 | 38 | ||
39 | DepthFirstSearch<TestVertex, TestEdge>.SpanningTreeResult result = | 39 | DepthFirstSearch<TestVertex, TestEdge>.SpanningTreeResult result = |
40 | - search.search(g, A, H, weight); | 40 | + search.search(graph, A, H, weight); |
41 | Set<Path<TestVertex, TestEdge>> paths = result.paths(); | 41 | Set<Path<TestVertex, TestEdge>> paths = result.paths(); |
42 | assertEquals("incorrect path count", 1, paths.size()); | 42 | assertEquals("incorrect path count", 1, paths.size()); |
43 | 43 | ||
... | @@ -57,12 +57,12 @@ public class DepthFirstSearchTest extends AbstractGraphPathSearchTest { | ... | @@ -57,12 +57,12 @@ public class DepthFirstSearchTest extends AbstractGraphPathSearchTest { |
57 | } | 57 | } |
58 | 58 | ||
59 | public void executeBroadSearch() { | 59 | public void executeBroadSearch() { |
60 | - g = new AdjacencyListsGraph<>(vertices(), edges()); | 60 | + graph = new AdjacencyListsGraph<>(vertexes(), edges()); |
61 | DepthFirstSearch<TestVertex, TestEdge> search = graphSearch(); | 61 | DepthFirstSearch<TestVertex, TestEdge> search = graphSearch(); |
62 | 62 | ||
63 | // Perform narrow path search to a specific destination. | 63 | // Perform narrow path search to a specific destination. |
64 | DepthFirstSearch<TestVertex, TestEdge>.SpanningTreeResult result = | 64 | DepthFirstSearch<TestVertex, TestEdge>.SpanningTreeResult result = |
65 | - search.search(g, A, null, weight); | 65 | + search.search(graph, A, null, weight); |
66 | assertEquals("incorrect paths count", 7, result.paths().size()); | 66 | assertEquals("incorrect paths count", 7, result.paths().size()); |
67 | 67 | ||
68 | int[] types = new int[]{0, 0, 0, 0}; | 68 | int[] types = new int[]{0, 0, 0, 0}; | ... | ... |
... | @@ -32,22 +32,22 @@ public class DijkstraGraphSearchTest extends BreadthFirstSearchTest { | ... | @@ -32,22 +32,22 @@ public class DijkstraGraphSearchTest extends BreadthFirstSearchTest { |
32 | 32 | ||
33 | @Test | 33 | @Test |
34 | public void noPath() { | 34 | public void noPath() { |
35 | - g = new AdjacencyListsGraph<>(of(A, B, C, D), | 35 | + graph = new AdjacencyListsGraph<>(of(A, B, C, D), |
36 | - of(new TestEdge(A, B, 1), | 36 | + of(new TestEdge(A, B, 1), |
37 | - new TestEdge(B, A, 1), | 37 | + new TestEdge(B, A, 1), |
38 | - new TestEdge(C, D, 1), | 38 | + new TestEdge(C, D, 1), |
39 | - new TestEdge(D, C, 1))); | 39 | + new TestEdge(D, C, 1))); |
40 | GraphPathSearch<TestVertex, TestEdge> gs = graphSearch(); | 40 | GraphPathSearch<TestVertex, TestEdge> gs = graphSearch(); |
41 | - Set<Path<TestVertex, TestEdge>> paths = gs.search(g, A, B, weight).paths(); | 41 | + Set<Path<TestVertex, TestEdge>> paths = gs.search(graph, A, B, weight).paths(); |
42 | printPaths(paths); | 42 | printPaths(paths); |
43 | assertEquals("incorrect paths count", 1, paths.size()); | 43 | assertEquals("incorrect paths count", 1, paths.size()); |
44 | assertEquals("incorrect path cost", 1.0, paths.iterator().next().cost(), 0.1); | 44 | assertEquals("incorrect path cost", 1.0, paths.iterator().next().cost(), 0.1); |
45 | 45 | ||
46 | - paths = gs.search(g, A, D, weight).paths(); | 46 | + paths = gs.search(graph, A, D, weight).paths(); |
47 | printPaths(paths); | 47 | printPaths(paths); |
48 | assertEquals("incorrect paths count", 0, paths.size()); | 48 | assertEquals("incorrect paths count", 0, paths.size()); |
49 | 49 | ||
50 | - paths = gs.search(g, A, null, weight).paths(); | 50 | + paths = gs.search(graph, A, null, weight).paths(); |
51 | printPaths(paths); | 51 | printPaths(paths); |
52 | assertEquals("incorrect paths count", 1, paths.size()); | 52 | assertEquals("incorrect paths count", 1, paths.size()); |
53 | assertEquals("incorrect path cost", 1.0, paths.iterator().next().cost(), 0.1); | 53 | assertEquals("incorrect path cost", 1.0, paths.iterator().next().cost(), 0.1); |
... | @@ -55,40 +55,40 @@ public class DijkstraGraphSearchTest extends BreadthFirstSearchTest { | ... | @@ -55,40 +55,40 @@ public class DijkstraGraphSearchTest extends BreadthFirstSearchTest { |
55 | 55 | ||
56 | @Test | 56 | @Test |
57 | public void simpleMultiplePath() { | 57 | public void simpleMultiplePath() { |
58 | - g = new AdjacencyListsGraph<>(of(A, B, C, D), | 58 | + graph = new AdjacencyListsGraph<>(of(A, B, C, D), |
59 | - of(new TestEdge(A, B, 1), | 59 | + of(new TestEdge(A, B, 1), |
60 | - new TestEdge(A, C, 1), | 60 | + new TestEdge(A, C, 1), |
61 | - new TestEdge(B, D, 1), | 61 | + new TestEdge(B, D, 1), |
62 | - new TestEdge(C, D, 1))); | 62 | + new TestEdge(C, D, 1))); |
63 | - executeSearch(graphSearch(), g, A, D, weight, 2, 2.0); | 63 | + executeSearch(graphSearch(), graph, A, D, weight, 2, 2.0); |
64 | } | 64 | } |
65 | 65 | ||
66 | @Test | 66 | @Test |
67 | public void denseMultiplePath() { | 67 | public void denseMultiplePath() { |
68 | - g = new AdjacencyListsGraph<>(of(A, B, C, D, E, F, G), | 68 | + graph = new AdjacencyListsGraph<>(of(A, B, C, D, E, F, G), |
69 | - of(new TestEdge(A, B, 1), | 69 | + of(new TestEdge(A, B, 1), |
70 | - new TestEdge(A, C, 1), | 70 | + new TestEdge(A, C, 1), |
71 | - new TestEdge(B, D, 1), | 71 | + new TestEdge(B, D, 1), |
72 | - new TestEdge(C, D, 1), | 72 | + new TestEdge(C, D, 1), |
73 | - new TestEdge(D, E, 1), | 73 | + new TestEdge(D, E, 1), |
74 | - new TestEdge(D, F, 1), | 74 | + new TestEdge(D, F, 1), |
75 | - new TestEdge(E, G, 1), | 75 | + new TestEdge(E, G, 1), |
76 | - new TestEdge(F, G, 1), | 76 | + new TestEdge(F, G, 1), |
77 | - new TestEdge(A, G, 4))); | 77 | + new TestEdge(A, G, 4))); |
78 | - executeSearch(graphSearch(), g, A, G, weight, 5, 4.0); | 78 | + executeSearch(graphSearch(), graph, A, G, weight, 5, 4.0); |
79 | } | 79 | } |
80 | 80 | ||
81 | @Test | 81 | @Test |
82 | public void dualEdgeMultiplePath() { | 82 | public void dualEdgeMultiplePath() { |
83 | - g = new AdjacencyListsGraph<>(of(A, B, C, D, E, F, G, H), | 83 | + graph = new AdjacencyListsGraph<>(of(A, B, C, D, E, F, G, H), |
84 | - of(new TestEdge(A, B, 1), new TestEdge(A, C, 3), | 84 | + of(new TestEdge(A, B, 1), new TestEdge(A, C, 3), |
85 | - new TestEdge(B, D, 2), new TestEdge(B, C, 1), | 85 | + new TestEdge(B, D, 2), new TestEdge(B, C, 1), |
86 | - new TestEdge(B, E, 4), new TestEdge(C, E, 1), | 86 | + new TestEdge(B, E, 4), new TestEdge(C, E, 1), |
87 | - new TestEdge(D, H, 5), new TestEdge(D, E, 1), | 87 | + new TestEdge(D, H, 5), new TestEdge(D, E, 1), |
88 | - new TestEdge(E, F, 1), new TestEdge(F, D, 1), | 88 | + new TestEdge(E, F, 1), new TestEdge(F, D, 1), |
89 | - new TestEdge(F, G, 1), new TestEdge(F, H, 1), | 89 | + new TestEdge(F, G, 1), new TestEdge(F, H, 1), |
90 | - new TestEdge(A, E, 3), new TestEdge(B, D, 1))); | 90 | + new TestEdge(A, E, 3), new TestEdge(B, D, 1))); |
91 | - executeSearch(graphSearch(), g, A, E, weight, 3, 3.0); | 91 | + executeSearch(graphSearch(), graph, A, E, weight, 3, 3.0); |
92 | } | 92 | } |
93 | 93 | ||
94 | } | 94 | } | ... | ... |
... | @@ -19,7 +19,7 @@ public class GraphTest { | ... | @@ -19,7 +19,7 @@ public class GraphTest { |
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 | static final TestVertex Z = new TestVertex("Z"); |
21 | 21 | ||
22 | - protected Graph<TestVertex, TestEdge> g; | 22 | + protected Graph<TestVertex, TestEdge> graph; |
23 | 23 | ||
24 | protected EdgeWeight<TestVertex, TestEdge> weight = | 24 | protected EdgeWeight<TestVertex, TestEdge> weight = |
25 | new EdgeWeight<TestVertex, TestEdge>() { | 25 | new EdgeWeight<TestVertex, TestEdge>() { |
... | @@ -35,7 +35,7 @@ public class GraphTest { | ... | @@ -35,7 +35,7 @@ public class GraphTest { |
35 | } | 35 | } |
36 | } | 36 | } |
37 | 37 | ||
38 | - protected Set<TestVertex> vertices() { | 38 | + protected Set<TestVertex> vertexes() { |
39 | return of(A, B, C, D, E, F, G, H); | 39 | return of(A, B, C, D, E, F, G, H); |
40 | } | 40 | } |
41 | 41 | ... | ... |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +import org.junit.Test; | ||
4 | + | ||
5 | +import static com.google.common.collect.ImmutableSet.of; | ||
6 | +import static org.junit.Assert.assertEquals; | ||
7 | +import static org.onlab.graph.TarjanGraphSearch.SCCResult; | ||
8 | + | ||
9 | +/** | ||
10 | + * Tarjan graph search tests. | ||
11 | + */ | ||
12 | +public class TarjanGraphSearchTest extends GraphTest { | ||
13 | + | ||
14 | + private void validate(SCCResult<TestVertex, TestEdge> result, int cc) { | ||
15 | + System.out.println("Cluster count: " + result.clusterVertexes().size()); | ||
16 | + System.out.println("Clusters: " + result.clusterVertexes()); | ||
17 | + assertEquals("incorrect cluster count", cc, result.clusterCount()); | ||
18 | + } | ||
19 | + | ||
20 | + private void validate(SCCResult<TestVertex, TestEdge> result, | ||
21 | + int i, int vc, int ec) { | ||
22 | + assertEquals("incorrect cluster count", vc, result.clusterVertexes().get(i).size()); | ||
23 | + assertEquals("incorrect edge count", ec, result.clusterEdges().get(i).size()); | ||
24 | + } | ||
25 | + | ||
26 | + @Test | ||
27 | + public void basic() { | ||
28 | + graph = new AdjacencyListsGraph<>(vertexes(), edges()); | ||
29 | + TarjanGraphSearch<TestVertex, TestEdge> gs = new TarjanGraphSearch<>(); | ||
30 | + SCCResult<TestVertex, TestEdge> result = gs.search(graph, null); | ||
31 | + validate(result, 6); | ||
32 | + } | ||
33 | + | ||
34 | + @Test | ||
35 | + public void singleCluster() { | ||
36 | + graph = new AdjacencyListsGraph<>(vertexes(), | ||
37 | + of(new TestEdge(A, B, 1), | ||
38 | + new TestEdge(B, C, 1), | ||
39 | + new TestEdge(C, D, 1), | ||
40 | + new TestEdge(D, E, 1), | ||
41 | + new TestEdge(E, F, 1), | ||
42 | + new TestEdge(F, G, 1), | ||
43 | + new TestEdge(G, H, 1), | ||
44 | + new TestEdge(H, A, 1))); | ||
45 | + | ||
46 | + TarjanGraphSearch<TestVertex, TestEdge> gs = new TarjanGraphSearch<>(); | ||
47 | + SCCResult<TestVertex, TestEdge> result = gs.search(graph, null); | ||
48 | + validate(result, 1); | ||
49 | + validate(result, 0, 8, 8); | ||
50 | + } | ||
51 | + | ||
52 | + @Test | ||
53 | + public void twoUnconnectedCluster() { | ||
54 | + graph = new AdjacencyListsGraph<>(vertexes(), | ||
55 | + of(new TestEdge(A, B, 1), | ||
56 | + new TestEdge(B, C, 1), | ||
57 | + new TestEdge(C, D, 1), | ||
58 | + new TestEdge(D, A, 1), | ||
59 | + new TestEdge(E, F, 1), | ||
60 | + new TestEdge(F, G, 1), | ||
61 | + new TestEdge(G, H, 1), | ||
62 | + new TestEdge(H, E, 1))); | ||
63 | + TarjanGraphSearch<TestVertex, TestEdge> gs = new TarjanGraphSearch<>(); | ||
64 | + SCCResult<TestVertex, TestEdge> result = gs.search(graph, null); | ||
65 | + validate(result, 2); | ||
66 | + validate(result, 0, 4, 4); | ||
67 | + validate(result, 1, 4, 4); | ||
68 | + } | ||
69 | + | ||
70 | + @Test | ||
71 | + public void twoWeaklyConnectedClusters() { | ||
72 | + graph = new AdjacencyListsGraph<>(vertexes(), | ||
73 | + of(new TestEdge(A, B, 1), | ||
74 | + new TestEdge(B, C, 1), | ||
75 | + new TestEdge(C, D, 1), | ||
76 | + new TestEdge(D, A, 1), | ||
77 | + new TestEdge(E, F, 1), | ||
78 | + new TestEdge(F, G, 1), | ||
79 | + new TestEdge(G, H, 1), | ||
80 | + new TestEdge(H, E, 1), | ||
81 | + new TestEdge(B, E, 1))); | ||
82 | + TarjanGraphSearch<TestVertex, TestEdge> gs = new TarjanGraphSearch<>(); | ||
83 | + SCCResult<TestVertex, TestEdge> result = gs.search(graph, null); | ||
84 | + validate(result, 2); | ||
85 | + validate(result, 0, 4, 4); | ||
86 | + validate(result, 1, 4, 4); | ||
87 | + } | ||
88 | + | ||
89 | + @Test | ||
90 | + public void twoClustersConnectedWithIgnoredEdges() { | ||
91 | + graph = new AdjacencyListsGraph<>(vertexes(), | ||
92 | + of(new TestEdge(A, B, 1), | ||
93 | + new TestEdge(B, C, 1), | ||
94 | + new TestEdge(C, D, 1), | ||
95 | + new TestEdge(D, A, 1), | ||
96 | + new TestEdge(E, F, 1), | ||
97 | + new TestEdge(F, G, 1), | ||
98 | + new TestEdge(G, H, 1), | ||
99 | + new TestEdge(H, E, 1), | ||
100 | + new TestEdge(B, E, -1), | ||
101 | + new TestEdge(E, B, -1))); | ||
102 | + | ||
103 | + TarjanGraphSearch<TestVertex, TestEdge> gs = new TarjanGraphSearch<>(); | ||
104 | + SCCResult<TestVertex, TestEdge> result = gs.search(graph, weight); | ||
105 | + validate(result, 2); | ||
106 | + validate(result, 0, 4, 4); | ||
107 | + validate(result, 1, 4, 4); | ||
108 | + } | ||
109 | + | ||
110 | +} |
-
Please register or login to post a comment