mirror of
https://github.com/opennetworkinglab/onos.git
synced 2025-10-17 02:11:38 +02:00
Added Tarjan SCC computation algorithm and associated tests.
This commit is contained in:
parent
cbff93954e
commit
0633d68dec
195
utils/misc/src/main/java/org/onlab/graph/TarjanGraphSearch.java
Normal file
195
utils/misc/src/main/java/org/onlab/graph/TarjanGraphSearch.java
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
package org.onlab.graph;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tarjan algorithm for searching a graph and producing results describing
|
||||||
|
* the graph SCC (strongly-connected components).
|
||||||
|
*/
|
||||||
|
public class TarjanGraphSearch<V extends Vertex, E extends Edge<V>>
|
||||||
|
implements GraphSearch<V, E> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
* <p/>
|
||||||
|
* This implementation produces results augmented with information on
|
||||||
|
* SCCs within the graph.
|
||||||
|
* <p/>
|
||||||
|
* To prevent traversal of an edge, the {@link EdgeWeight#weight} should
|
||||||
|
* return a negative value as an edge weight.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public SCCResult<V, E> search(Graph<V, E> graph, EdgeWeight<V, E> weight) {
|
||||||
|
SCCResult<V, E> result = new SCCResult<>(graph);
|
||||||
|
for (V vertex : graph.getVertexes()) {
|
||||||
|
VertexData data = result.data(vertex);
|
||||||
|
if (data == null) {
|
||||||
|
connect(graph, vertex, weight, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scans the specified graph, using recursion, and produces SCC results.
|
||||||
|
*
|
||||||
|
* @param graph graph to search
|
||||||
|
* @param vertex current vertex to scan and connect
|
||||||
|
* @param weight optional edge weight
|
||||||
|
* @param result graph search result
|
||||||
|
* @return augmentation vertexData for the current vertex
|
||||||
|
*/
|
||||||
|
private VertexData<V> connect(Graph<V, E> graph, V vertex,
|
||||||
|
EdgeWeight<V, E> weight,
|
||||||
|
SCCResult<V, E> result) {
|
||||||
|
VertexData<V> data = result.addData(vertex);
|
||||||
|
|
||||||
|
// Scan through all egress edges of the current vertex.
|
||||||
|
for (E edge : graph.getEdgesFrom(vertex)) {
|
||||||
|
V nextVertex = edge.dst();
|
||||||
|
|
||||||
|
// If edge weight is negative, skip it.
|
||||||
|
if (weight != null && weight.weight(edge) < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to get the augmentation vertexData for the next vertex.
|
||||||
|
VertexData<V> nextData = result.data(nextVertex);
|
||||||
|
if (nextData == null) {
|
||||||
|
// Next vertex has not been visited yet, so do this now.
|
||||||
|
nextData = connect(graph, nextVertex, weight, result);
|
||||||
|
data.lowLink = Math.min(data.lowLink, nextData.lowLink);
|
||||||
|
|
||||||
|
} else if (result.visited(nextData)) {
|
||||||
|
// Next vertex has been visited, which means it is in the
|
||||||
|
// same cluster as the current vertex.
|
||||||
|
data.lowLink = Math.min(data.lowLink, nextData.index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.lowLink == data.index) {
|
||||||
|
result.addCluster(data);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Graph search result augmented with SCC vertexData.
|
||||||
|
*/
|
||||||
|
public static final class SCCResult<V extends Vertex, E extends Edge<V>>
|
||||||
|
implements Result {
|
||||||
|
|
||||||
|
private final Graph<V, E> graph;
|
||||||
|
private List<Set<V>> clusterVertexes = new ArrayList<>();
|
||||||
|
private List<Set<E>> clusterEdges = new ArrayList<>();
|
||||||
|
|
||||||
|
private int index = 0;
|
||||||
|
private final Map<V, VertexData<V>> vertexData = new HashMap<>();
|
||||||
|
private final List<VertexData<V>> visited = new ArrayList<>();
|
||||||
|
|
||||||
|
private SCCResult(Graph<V, E> graph) {
|
||||||
|
this.graph = graph;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of SCC clusters in the graph.
|
||||||
|
*
|
||||||
|
* @return number of clusters
|
||||||
|
*/
|
||||||
|
public int clusterCount() {
|
||||||
|
return clusterEdges.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of strongly connected vertex clusters.
|
||||||
|
*
|
||||||
|
* @return list of strongly connected vertex sets
|
||||||
|
*/
|
||||||
|
public List<Set<V>> clusterVertexes() {
|
||||||
|
return clusterVertexes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of edges linking strongly connected vertex clusters.
|
||||||
|
*
|
||||||
|
* @return list of strongly connected edge sets
|
||||||
|
*/
|
||||||
|
public List<Set<E>> clusterEdges() {
|
||||||
|
return clusterEdges;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets the augmentation vertexData for the specified vertex
|
||||||
|
private VertexData<V> data(V vertex) {
|
||||||
|
return vertexData.get(vertex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds augmentation vertexData for the specified vertex
|
||||||
|
private VertexData<V> addData(V vertex) {
|
||||||
|
VertexData<V> d = new VertexData<>(vertex, index);
|
||||||
|
vertexData.put(vertex, d);
|
||||||
|
visited.add(0, d);
|
||||||
|
index++;
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Indicates whether the given vertex has been visited
|
||||||
|
private boolean visited(VertexData data) {
|
||||||
|
return visited.contains(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds a new cluster for the specified vertex
|
||||||
|
private void addCluster(VertexData data) {
|
||||||
|
Set<V> vertexes = findClusterVertices(data);
|
||||||
|
clusterVertexes.add(vertexes);
|
||||||
|
clusterEdges.add(findClusterEdges(vertexes));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<V> findClusterVertices(VertexData data) {
|
||||||
|
VertexData<V> nextVertexData;
|
||||||
|
Set<V> vertexes = new HashSet<>();
|
||||||
|
do {
|
||||||
|
nextVertexData = visited.remove(0);
|
||||||
|
vertexes.add(nextVertexData.vertex);
|
||||||
|
} while (data != nextVertexData);
|
||||||
|
return Collections.unmodifiableSet(vertexes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<E> findClusterEdges(Set<V> vertexes) {
|
||||||
|
Set<E> edges = new HashSet<>();
|
||||||
|
for (V vertex : vertexes) {
|
||||||
|
for (E edge : graph.getEdgesFrom(vertex)) {
|
||||||
|
if (vertexes.contains((edge.dst()))) {
|
||||||
|
edges.add(edge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableSet(edges);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SCCResult<V, E> build() {
|
||||||
|
clusterVertexes = Collections.unmodifiableList(clusterVertexes);
|
||||||
|
clusterEdges = Collections.unmodifiableList(clusterEdges);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Augments the vertex to assist in determining SCC clusters.
|
||||||
|
private static final class VertexData<V extends Vertex> {
|
||||||
|
final V vertex;
|
||||||
|
int index;
|
||||||
|
int lowLink;
|
||||||
|
|
||||||
|
private VertexData(V vertex, int index) {
|
||||||
|
this.vertex = vertex;
|
||||||
|
this.index = index;
|
||||||
|
this.lowLink = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -31,17 +31,17 @@ public class BellmanFordGraphSearchTest extends BreadthFirstSearchTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void searchGraphWithNegativeCycles() {
|
public void searchGraphWithNegativeCycles() {
|
||||||
Set<TestVertex> vertexes = new HashSet<>(vertices());
|
Set<TestVertex> vertexes = new HashSet<>(vertexes());
|
||||||
vertexes.add(Z);
|
vertexes.add(Z);
|
||||||
|
|
||||||
Set<TestEdge> edges = new HashSet<>(edges());
|
Set<TestEdge> edges = new HashSet<>(edges());
|
||||||
edges.add(new TestEdge(G, Z, 1.0));
|
edges.add(new TestEdge(G, Z, 1.0));
|
||||||
edges.add(new TestEdge(Z, G, -2.0));
|
edges.add(new TestEdge(Z, G, -2.0));
|
||||||
|
|
||||||
g = new AdjacencyListsGraph<>(vertexes, edges);
|
graph = new AdjacencyListsGraph<>(vertexes, edges);
|
||||||
|
|
||||||
GraphPathSearch<TestVertex, TestEdge> search = graphSearch();
|
GraphPathSearch<TestVertex, TestEdge> search = graphSearch();
|
||||||
Set<Path<TestVertex, TestEdge>> paths = search.search(g, A, H, weight).paths();
|
Set<Path<TestVertex, TestEdge>> paths = search.search(graph, A, H, weight).paths();
|
||||||
assertEquals("incorrect paths count", 1, paths.size());
|
assertEquals("incorrect paths count", 1, paths.size());
|
||||||
|
|
||||||
Path p = paths.iterator().next();
|
Path p = paths.iterator().next();
|
||||||
@ -50,10 +50,10 @@ public class BellmanFordGraphSearchTest extends BreadthFirstSearchTest {
|
|||||||
assertEquals("incorrect path length", 5, p.edges().size());
|
assertEquals("incorrect path length", 5, p.edges().size());
|
||||||
assertEquals("incorrect path cost", 5.0, p.cost(), 0.1);
|
assertEquals("incorrect path cost", 5.0, p.cost(), 0.1);
|
||||||
|
|
||||||
paths = search.search(g, A, G, weight).paths();
|
paths = search.search(graph, A, G, weight).paths();
|
||||||
assertEquals("incorrect paths count", 0, paths.size());
|
assertEquals("incorrect paths count", 0, paths.size());
|
||||||
|
|
||||||
paths = search.search(g, A, null, weight).paths();
|
paths = search.search(graph, A, null, weight).paths();
|
||||||
printPaths(paths);
|
printPaths(paths);
|
||||||
assertEquals("incorrect paths count", 6, paths.size());
|
assertEquals("incorrect paths count", 6, paths.size());
|
||||||
}
|
}
|
||||||
|
@ -29,10 +29,10 @@ public class BreadthFirstSearchTest extends AbstractGraphPathSearchTest {
|
|||||||
|
|
||||||
// Executes the default test
|
// Executes the default test
|
||||||
protected void executeDefaultTest(int pathCount, int pathLength, double pathCost) {
|
protected void executeDefaultTest(int pathCount, int pathLength, double pathCost) {
|
||||||
g = new AdjacencyListsGraph<>(vertices(), edges());
|
graph = new AdjacencyListsGraph<>(vertexes(), edges());
|
||||||
|
|
||||||
GraphPathSearch<TestVertex, TestEdge> search = graphSearch();
|
GraphPathSearch<TestVertex, TestEdge> search = graphSearch();
|
||||||
Set<Path<TestVertex, TestEdge>> paths = search.search(g, A, H, weight).paths();
|
Set<Path<TestVertex, TestEdge>> paths = search.search(graph, A, H, weight).paths();
|
||||||
assertEquals("incorrect paths count", 1, paths.size());
|
assertEquals("incorrect paths count", 1, paths.size());
|
||||||
|
|
||||||
Path p = paths.iterator().next();
|
Path p = paths.iterator().next();
|
||||||
@ -41,7 +41,7 @@ public class BreadthFirstSearchTest extends AbstractGraphPathSearchTest {
|
|||||||
assertEquals("incorrect path length", pathLength, p.edges().size());
|
assertEquals("incorrect path length", pathLength, p.edges().size());
|
||||||
assertEquals("incorrect path cost", pathCost, p.cost(), 0.1);
|
assertEquals("incorrect path cost", pathCost, p.cost(), 0.1);
|
||||||
|
|
||||||
paths = search.search(g, A, null, weight).paths();
|
paths = search.search(graph, A, null, weight).paths();
|
||||||
printPaths(paths);
|
printPaths(paths);
|
||||||
assertEquals("incorrect paths count", pathCount, paths.size());
|
assertEquals("incorrect paths count", pathCount, paths.size());
|
||||||
}
|
}
|
||||||
|
@ -33,11 +33,11 @@ public class DepthFirstSearchTest extends AbstractGraphPathSearchTest {
|
|||||||
|
|
||||||
protected void executeDefaultTest(int minLength, int maxLength,
|
protected void executeDefaultTest(int minLength, int maxLength,
|
||||||
double minCost, double maxCost) {
|
double minCost, double maxCost) {
|
||||||
g = new AdjacencyListsGraph<>(vertices(), edges());
|
graph = new AdjacencyListsGraph<>(vertexes(), edges());
|
||||||
DepthFirstSearch<TestVertex, TestEdge> search = graphSearch();
|
DepthFirstSearch<TestVertex, TestEdge> search = graphSearch();
|
||||||
|
|
||||||
DepthFirstSearch<TestVertex, TestEdge>.SpanningTreeResult result =
|
DepthFirstSearch<TestVertex, TestEdge>.SpanningTreeResult result =
|
||||||
search.search(g, A, H, weight);
|
search.search(graph, A, H, weight);
|
||||||
Set<Path<TestVertex, TestEdge>> paths = result.paths();
|
Set<Path<TestVertex, TestEdge>> paths = result.paths();
|
||||||
assertEquals("incorrect path count", 1, paths.size());
|
assertEquals("incorrect path count", 1, paths.size());
|
||||||
|
|
||||||
@ -57,12 +57,12 @@ public class DepthFirstSearchTest extends AbstractGraphPathSearchTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void executeBroadSearch() {
|
public void executeBroadSearch() {
|
||||||
g = new AdjacencyListsGraph<>(vertices(), edges());
|
graph = new AdjacencyListsGraph<>(vertexes(), edges());
|
||||||
DepthFirstSearch<TestVertex, TestEdge> search = graphSearch();
|
DepthFirstSearch<TestVertex, TestEdge> search = graphSearch();
|
||||||
|
|
||||||
// Perform narrow path search to a specific destination.
|
// Perform narrow path search to a specific destination.
|
||||||
DepthFirstSearch<TestVertex, TestEdge>.SpanningTreeResult result =
|
DepthFirstSearch<TestVertex, TestEdge>.SpanningTreeResult result =
|
||||||
search.search(g, A, null, weight);
|
search.search(graph, A, null, weight);
|
||||||
assertEquals("incorrect paths count", 7, result.paths().size());
|
assertEquals("incorrect paths count", 7, result.paths().size());
|
||||||
|
|
||||||
int[] types = new int[]{0, 0, 0, 0};
|
int[] types = new int[]{0, 0, 0, 0};
|
||||||
|
@ -32,22 +32,22 @@ public class DijkstraGraphSearchTest extends BreadthFirstSearchTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void noPath() {
|
public void noPath() {
|
||||||
g = new AdjacencyListsGraph<>(of(A, B, C, D),
|
graph = new AdjacencyListsGraph<>(of(A, B, C, D),
|
||||||
of(new TestEdge(A, B, 1),
|
of(new TestEdge(A, B, 1),
|
||||||
new TestEdge(B, A, 1),
|
new TestEdge(B, A, 1),
|
||||||
new TestEdge(C, D, 1),
|
new TestEdge(C, D, 1),
|
||||||
new TestEdge(D, C, 1)));
|
new TestEdge(D, C, 1)));
|
||||||
GraphPathSearch<TestVertex, TestEdge> gs = graphSearch();
|
GraphPathSearch<TestVertex, TestEdge> gs = graphSearch();
|
||||||
Set<Path<TestVertex, TestEdge>> paths = gs.search(g, A, B, weight).paths();
|
Set<Path<TestVertex, TestEdge>> paths = gs.search(graph, A, B, weight).paths();
|
||||||
printPaths(paths);
|
printPaths(paths);
|
||||||
assertEquals("incorrect paths count", 1, paths.size());
|
assertEquals("incorrect paths count", 1, paths.size());
|
||||||
assertEquals("incorrect path cost", 1.0, paths.iterator().next().cost(), 0.1);
|
assertEquals("incorrect path cost", 1.0, paths.iterator().next().cost(), 0.1);
|
||||||
|
|
||||||
paths = gs.search(g, A, D, weight).paths();
|
paths = gs.search(graph, A, D, weight).paths();
|
||||||
printPaths(paths);
|
printPaths(paths);
|
||||||
assertEquals("incorrect paths count", 0, paths.size());
|
assertEquals("incorrect paths count", 0, paths.size());
|
||||||
|
|
||||||
paths = gs.search(g, A, null, weight).paths();
|
paths = gs.search(graph, A, null, weight).paths();
|
||||||
printPaths(paths);
|
printPaths(paths);
|
||||||
assertEquals("incorrect paths count", 1, paths.size());
|
assertEquals("incorrect paths count", 1, paths.size());
|
||||||
assertEquals("incorrect path cost", 1.0, paths.iterator().next().cost(), 0.1);
|
assertEquals("incorrect path cost", 1.0, paths.iterator().next().cost(), 0.1);
|
||||||
@ -55,40 +55,40 @@ public class DijkstraGraphSearchTest extends BreadthFirstSearchTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void simpleMultiplePath() {
|
public void simpleMultiplePath() {
|
||||||
g = new AdjacencyListsGraph<>(of(A, B, C, D),
|
graph = new AdjacencyListsGraph<>(of(A, B, C, D),
|
||||||
of(new TestEdge(A, B, 1),
|
of(new TestEdge(A, B, 1),
|
||||||
new TestEdge(A, C, 1),
|
new TestEdge(A, C, 1),
|
||||||
new TestEdge(B, D, 1),
|
new TestEdge(B, D, 1),
|
||||||
new TestEdge(C, D, 1)));
|
new TestEdge(C, D, 1)));
|
||||||
executeSearch(graphSearch(), g, A, D, weight, 2, 2.0);
|
executeSearch(graphSearch(), graph, A, D, weight, 2, 2.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void denseMultiplePath() {
|
public void denseMultiplePath() {
|
||||||
g = new AdjacencyListsGraph<>(of(A, B, C, D, E, F, G),
|
graph = new AdjacencyListsGraph<>(of(A, B, C, D, E, F, G),
|
||||||
of(new TestEdge(A, B, 1),
|
of(new TestEdge(A, B, 1),
|
||||||
new TestEdge(A, C, 1),
|
new TestEdge(A, C, 1),
|
||||||
new TestEdge(B, D, 1),
|
new TestEdge(B, D, 1),
|
||||||
new TestEdge(C, D, 1),
|
new TestEdge(C, D, 1),
|
||||||
new TestEdge(D, E, 1),
|
new TestEdge(D, E, 1),
|
||||||
new TestEdge(D, F, 1),
|
new TestEdge(D, F, 1),
|
||||||
new TestEdge(E, G, 1),
|
new TestEdge(E, G, 1),
|
||||||
new TestEdge(F, G, 1),
|
new TestEdge(F, G, 1),
|
||||||
new TestEdge(A, G, 4)));
|
new TestEdge(A, G, 4)));
|
||||||
executeSearch(graphSearch(), g, A, G, weight, 5, 4.0);
|
executeSearch(graphSearch(), graph, A, G, weight, 5, 4.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void dualEdgeMultiplePath() {
|
public void dualEdgeMultiplePath() {
|
||||||
g = new AdjacencyListsGraph<>(of(A, B, C, D, E, F, G, H),
|
graph = new AdjacencyListsGraph<>(of(A, B, C, D, E, F, G, H),
|
||||||
of(new TestEdge(A, B, 1), new TestEdge(A, C, 3),
|
of(new TestEdge(A, B, 1), new TestEdge(A, C, 3),
|
||||||
new TestEdge(B, D, 2), new TestEdge(B, C, 1),
|
new TestEdge(B, D, 2), new TestEdge(B, C, 1),
|
||||||
new TestEdge(B, E, 4), new TestEdge(C, E, 1),
|
new TestEdge(B, E, 4), new TestEdge(C, E, 1),
|
||||||
new TestEdge(D, H, 5), new TestEdge(D, E, 1),
|
new TestEdge(D, H, 5), new TestEdge(D, E, 1),
|
||||||
new TestEdge(E, F, 1), new TestEdge(F, D, 1),
|
new TestEdge(E, F, 1), new TestEdge(F, D, 1),
|
||||||
new TestEdge(F, G, 1), new TestEdge(F, H, 1),
|
new TestEdge(F, G, 1), new TestEdge(F, H, 1),
|
||||||
new TestEdge(A, E, 3), new TestEdge(B, D, 1)));
|
new TestEdge(A, E, 3), new TestEdge(B, D, 1)));
|
||||||
executeSearch(graphSearch(), g, A, E, weight, 3, 3.0);
|
executeSearch(graphSearch(), graph, A, E, weight, 3, 3.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ public class GraphTest {
|
|||||||
static final TestVertex H = new TestVertex("H");
|
static final TestVertex H = new TestVertex("H");
|
||||||
static final TestVertex Z = new TestVertex("Z");
|
static final TestVertex Z = new TestVertex("Z");
|
||||||
|
|
||||||
protected Graph<TestVertex, TestEdge> g;
|
protected Graph<TestVertex, TestEdge> graph;
|
||||||
|
|
||||||
protected EdgeWeight<TestVertex, TestEdge> weight =
|
protected EdgeWeight<TestVertex, TestEdge> weight =
|
||||||
new EdgeWeight<TestVertex, TestEdge>() {
|
new EdgeWeight<TestVertex, TestEdge>() {
|
||||||
@ -35,7 +35,7 @@ public class GraphTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Set<TestVertex> vertices() {
|
protected Set<TestVertex> vertexes() {
|
||||||
return of(A, B, C, D, E, F, G, H);
|
return of(A, B, C, D, E, F, G, H);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,110 @@
|
|||||||
|
package org.onlab.graph;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static com.google.common.collect.ImmutableSet.of;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.onlab.graph.TarjanGraphSearch.SCCResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tarjan graph search tests.
|
||||||
|
*/
|
||||||
|
public class TarjanGraphSearchTest extends GraphTest {
|
||||||
|
|
||||||
|
private void validate(SCCResult<TestVertex, TestEdge> result, int cc) {
|
||||||
|
System.out.println("Cluster count: " + result.clusterVertexes().size());
|
||||||
|
System.out.println("Clusters: " + result.clusterVertexes());
|
||||||
|
assertEquals("incorrect cluster count", cc, result.clusterCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validate(SCCResult<TestVertex, TestEdge> result,
|
||||||
|
int i, int vc, int ec) {
|
||||||
|
assertEquals("incorrect cluster count", vc, result.clusterVertexes().get(i).size());
|
||||||
|
assertEquals("incorrect edge count", ec, result.clusterEdges().get(i).size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void basic() {
|
||||||
|
graph = new AdjacencyListsGraph<>(vertexes(), edges());
|
||||||
|
TarjanGraphSearch<TestVertex, TestEdge> gs = new TarjanGraphSearch<>();
|
||||||
|
SCCResult<TestVertex, TestEdge> result = gs.search(graph, null);
|
||||||
|
validate(result, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void singleCluster() {
|
||||||
|
graph = new AdjacencyListsGraph<>(vertexes(),
|
||||||
|
of(new TestEdge(A, B, 1),
|
||||||
|
new TestEdge(B, C, 1),
|
||||||
|
new TestEdge(C, D, 1),
|
||||||
|
new TestEdge(D, E, 1),
|
||||||
|
new TestEdge(E, F, 1),
|
||||||
|
new TestEdge(F, G, 1),
|
||||||
|
new TestEdge(G, H, 1),
|
||||||
|
new TestEdge(H, A, 1)));
|
||||||
|
|
||||||
|
TarjanGraphSearch<TestVertex, TestEdge> gs = new TarjanGraphSearch<>();
|
||||||
|
SCCResult<TestVertex, TestEdge> result = gs.search(graph, null);
|
||||||
|
validate(result, 1);
|
||||||
|
validate(result, 0, 8, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void twoUnconnectedCluster() {
|
||||||
|
graph = new AdjacencyListsGraph<>(vertexes(),
|
||||||
|
of(new TestEdge(A, B, 1),
|
||||||
|
new TestEdge(B, C, 1),
|
||||||
|
new TestEdge(C, D, 1),
|
||||||
|
new TestEdge(D, A, 1),
|
||||||
|
new TestEdge(E, F, 1),
|
||||||
|
new TestEdge(F, G, 1),
|
||||||
|
new TestEdge(G, H, 1),
|
||||||
|
new TestEdge(H, E, 1)));
|
||||||
|
TarjanGraphSearch<TestVertex, TestEdge> gs = new TarjanGraphSearch<>();
|
||||||
|
SCCResult<TestVertex, TestEdge> result = gs.search(graph, null);
|
||||||
|
validate(result, 2);
|
||||||
|
validate(result, 0, 4, 4);
|
||||||
|
validate(result, 1, 4, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void twoWeaklyConnectedClusters() {
|
||||||
|
graph = new AdjacencyListsGraph<>(vertexes(),
|
||||||
|
of(new TestEdge(A, B, 1),
|
||||||
|
new TestEdge(B, C, 1),
|
||||||
|
new TestEdge(C, D, 1),
|
||||||
|
new TestEdge(D, A, 1),
|
||||||
|
new TestEdge(E, F, 1),
|
||||||
|
new TestEdge(F, G, 1),
|
||||||
|
new TestEdge(G, H, 1),
|
||||||
|
new TestEdge(H, E, 1),
|
||||||
|
new TestEdge(B, E, 1)));
|
||||||
|
TarjanGraphSearch<TestVertex, TestEdge> gs = new TarjanGraphSearch<>();
|
||||||
|
SCCResult<TestVertex, TestEdge> result = gs.search(graph, null);
|
||||||
|
validate(result, 2);
|
||||||
|
validate(result, 0, 4, 4);
|
||||||
|
validate(result, 1, 4, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void twoClustersConnectedWithIgnoredEdges() {
|
||||||
|
graph = new AdjacencyListsGraph<>(vertexes(),
|
||||||
|
of(new TestEdge(A, B, 1),
|
||||||
|
new TestEdge(B, C, 1),
|
||||||
|
new TestEdge(C, D, 1),
|
||||||
|
new TestEdge(D, A, 1),
|
||||||
|
new TestEdge(E, F, 1),
|
||||||
|
new TestEdge(F, G, 1),
|
||||||
|
new TestEdge(G, H, 1),
|
||||||
|
new TestEdge(H, E, 1),
|
||||||
|
new TestEdge(B, E, -1),
|
||||||
|
new TestEdge(E, B, -1)));
|
||||||
|
|
||||||
|
TarjanGraphSearch<TestVertex, TestEdge> gs = new TarjanGraphSearch<>();
|
||||||
|
SCCResult<TestVertex, TestEdge> result = gs.search(graph, weight);
|
||||||
|
validate(result, 2);
|
||||||
|
validate(result, 0, 4, 4);
|
||||||
|
validate(result, 1, 4, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user