Add unit tests for irreducible graph splitter

This commit is contained in:
Alexey Andreev 2015-03-05 18:32:36 +04:00
parent 04677d0103
commit e2aababde8
2 changed files with 82 additions and 40 deletions

View File

@ -31,37 +31,8 @@ public final class GraphUtils {
private GraphUtils() { private GraphUtils() {
} }
public static Graph invert(Graph graph) { public static int[] findBackEdges(Graph graph) {
int sz = graph.size(); int sz = graph.size();
GraphBuilder result = new GraphBuilder();
int[] sourceEdges = new int[sz];
for (int node = 0; node < sz; ++node) {
int sourceCount = graph.copyIncomingEdges(node, sourceEdges);
for (int i = 0; i < sourceCount; ++i) {
int source = sourceEdges[i];
result.addEdge(node, source);
}
}
return result.build();
}
public static Graph close(Graph graph) {
GraphBuilder result = new GraphBuilder();
for (int node = 0; node < graph.size(); ++node) {
int[] next = graph.outgoingEdges(node);
for (int target : next) {
result.addEdge(node, target);
}
if (next.length == 0) {
result.addEdge(node, graph.size());
}
}
return result.build();
}
public static Graph removeLoops(Graph graph) {
int sz = graph.size();
GraphBuilder result = new GraphBuilder();
int[] stack = new int[sz * 2]; int[] stack = new int[sz * 2];
int stackSize = 0; int stackSize = 0;
byte[] state = new byte[sz]; byte[] state = new byte[sz];
@ -70,6 +41,7 @@ public final class GraphUtils {
stack[stackSize++] = i; stack[stackSize++] = i;
} }
} }
IntegerArray result = new IntegerArray(2);
while (stackSize > 0) { while (stackSize > 0) {
int node = stack[--stackSize]; int node = stack[--stackSize];
switch (state[node]) { switch (state[node]) {
@ -79,11 +51,11 @@ public final class GraphUtils {
for (int next : graph.outgoingEdges(node)) { for (int next : graph.outgoingEdges(node)) {
switch (state[next]) { switch (state[next]) {
case NONE: case NONE:
result.addEdge(node, next);
stack[stackSize++] = next; stack[stackSize++] = next;
break; break;
case VISITED: case VISITING:
result.addEdge(node, next); result.add(node);
result.add(next);
break; break;
} }
} }
@ -93,22 +65,25 @@ public final class GraphUtils {
break; break;
} }
} }
return result.build(); return result.getAll();
} }
public static int edgeCount(Graph graph) { public static boolean isIrreducible(Graph graph) {
int cnt = 0; DominatorTree dom = buildDominatorTree(graph);
int sz = graph.size(); int[] backEdges = findBackEdges(graph);
for (int node = 0; node < sz; ++node) { for (int i = 0; i < backEdges.length; i += 2) {
cnt += graph.outgoingEdgesCount(node); if (!dom.dominates(backEdges[i + 1], backEdges[i])) {
return true;
}
} }
return cnt; return false;
} }
/* /*
* Tarjan's algorithm * Tarjan's algorithm
*/ */
public static int[][] findStronglyConnectedComponents(Graph graph, int[] start, GraphNodeFilter filter) { public static int[][] findStronglyConnectedComponents(Graph graph, int[] start, GraphNodeFilter filter) {
// TODO: can show incorrect behaviour sometimes
List<int[]> components = new ArrayList<>(); List<int[]> components = new ArrayList<>();
int[] visitIndex = new int[graph.size()]; int[] visitIndex = new int[graph.size()];
int[] headerIndex = new int[graph.size()]; int[] headerIndex = new int[graph.size()];

View File

@ -17,6 +17,8 @@ package org.teavm.common;
import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import com.carrotsearch.hppc.IntOpenHashSet;
import com.carrotsearch.hppc.IntSet;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator; import java.util.Comparator;
import org.junit.Test; import org.junit.Test;
@ -86,6 +88,71 @@ public class GraphTest {
assertThat(sccs[0], is(new int[] { 1, 2, 3, 4, 5 })); assertThat(sccs[0], is(new int[] { 1, 2, 3, 4, 5 }));
} }
@Test
public void irreducibleGraphSplit() {
GraphBuilder builder = new GraphBuilder();
builder.addEdge(0, 1);
builder.addEdge(0, 2);
builder.addEdge(0, 3);
builder.addEdge(1, 2);
builder.addEdge(2, 1);
builder.addEdge(3, 2);
builder.addEdge(2, 4);
builder.addEdge(4, 5);
builder.addEdge(4, 1);
builder.addEdge(5, 3);
Graph graph = builder.build();
DefaultGraphSplittingBackend backend = new DefaultGraphSplittingBackend(graph);
int[] weights = { 1, 4, 1, 10, 1, 1 };
GraphUtils.splitIrreducibleGraph(graph, weights, backend);
Graph result = backend.getGraph();
assertTrue("Should be irreducible", GraphUtils.isIrreducible(graph));
assertFalse("Should be reducible", GraphUtils.isIrreducible(result));
assertTrue("Should be equialent", isEquialent(backend, graph));
}
@Test
public void irreducibleGraphSplit2() {
GraphBuilder builder = new GraphBuilder();
builder.addEdge(0, 1);
builder.addEdge(0, 2);
builder.addEdge(1, 2);
builder.addEdge(2, 1);
Graph graph = builder.build();
DefaultGraphSplittingBackend backend = new DefaultGraphSplittingBackend(graph);
int[] weights = new int[graph.size()];
Arrays.fill(weights, 1);
GraphUtils.splitIrreducibleGraph(graph, weights, backend);
Graph result = backend.getGraph();
assertTrue("Should be irreducible", GraphUtils.isIrreducible(graph));
assertFalse("Should be reducible", GraphUtils.isIrreducible(result));
assertTrue("Should be equialent", isEquialent(backend, graph));
}
private boolean isEquialent(DefaultGraphSplittingBackend backend, Graph proto) {
Graph graph = backend.getGraph();
for (int node = 0; node < graph.size(); ++node) {
int nodeProto = backend.prototype(node);
IntSet succProto = new IntOpenHashSet();
for (int succ : graph.outgoingEdges(node)) {
succProto.add(backend.prototype(succ));
}
if (succProto.size() != proto.outgoingEdgesCount(nodeProto)) {
return false;
}
for (int succ : proto.outgoingEdges(nodeProto)) {
if (!succProto.contains(succ)) {
return false;
}
}
}
return true;
}
private GraphNodeFilter filter = new GraphNodeFilter() { private GraphNodeFilter filter = new GraphNodeFilter() {
@Override public boolean match(int node) { @Override public boolean match(int node) {
return true; return true;