Complete node splitting algorithm. Add test case for SCC finder.

This commit is contained in:
Alexey Andreev 2015-03-01 23:19:46 +03:00
parent 77b42e677a
commit f106afb034
3 changed files with 113 additions and 34 deletions

View File

@ -19,7 +19,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
*
* @author Alexey Andreev
@ -111,9 +110,7 @@ public final class GraphUtils {
*/
public static int[][] findStronglyConnectedComponents(Graph graph, int[] start, GraphNodeFilter filter) {
List<int[]> components = new ArrayList<>();
boolean[] done = new boolean[graph.size()];
int[] visitIndex = new int[graph.size()];
Arrays.fill(visitIndex, -1);
int[] headerIndex = new int[graph.size()];
int lastIndex = 0;
IntegerStack stack = new IntegerStack(graph.size());
@ -124,36 +121,32 @@ public final class GraphUtils {
IntegerArray currentComponent = new IntegerArray(1);
while (!stack.isEmpty()) {
int node = stack.pop();
if (visitIndex[node] == 0) {
if (done[node]) {
currentComponent.add(node);
int hdr = node;
for (int successor : graph.outgoingEdges(node)) {
if (!filter.match(successor)) {
continue;
}
if (!done[successor]) {
hdr = Math.min(hdr, visitIndex[successor]);
} else {
hdr = Math.min(hdr, headerIndex[successor]);
}
}
if (hdr == node) {
components.add(currentComponent.getAll());
currentComponent.clear();
}
headerIndex[node] = hdr;
} else {
done[node] = true;
}
} else {
visitIndex[node] = ++lastIndex;
stack.push(node);
if (visitIndex[node] > 0) {
currentComponent.add(node);
int hdr = visitIndex[node];
for (int successor : graph.outgoingEdges(node)) {
if (!filter.match(successor)) {
continue;
}
stack.push(node);
if (headerIndex[successor] == 0) {
hdr = Math.min(hdr, visitIndex[successor]);
} else {
hdr = Math.min(hdr, headerIndex[successor]);
}
}
if (hdr == visitIndex[node]) {
components.add(currentComponent.getAll());
currentComponent.clear();
}
headerIndex[node] = hdr;
} else {
visitIndex[node] = ++lastIndex;
stack.push(node);
for (int successor : graph.outgoingEdges(node)) {
if (!filter.match(successor) || visitIndex[successor] > 0) {
continue;
}
stack.push(successor);
}
}
}
@ -243,7 +236,6 @@ public final class GraphUtils {
return domFrontiers;
}
private static int[] makeSet(IntegerArray array) {
int[] items = array.getAll();
int[] set = new int[items.length];

View File

@ -55,7 +55,7 @@ public class IrreducibleGraphConverter {
irreducible = true;
}
} else if (djGraph.isBackJoin(node, pred)) {
djGraph.collapse(reachUnder(djGraph, pred));
djGraph.collapse(reachUnder(djGraph, pred, node));
}
}
}
@ -68,9 +68,20 @@ public class IrreducibleGraphConverter {
}
}
private int[] reachUnder(DJGraph djGraph, int top) {
// TODO: implement
return null;
private int[] reachUnder(DJGraph djGraph, int back, int header) {
IntSet naturalLoop = IntOpenHashSet.from(header);
IntegerStack stack = new IntegerStack(djGraph.getGraph().size());
stack.push(back);
while (!stack.isEmpty()) {
int node = stack.pop();
if (!naturalLoop.add(node)) {
continue;
}
for (int pred : djGraph.getGraph().incomingEdges(node)) {
stack.push(pred);
}
}
return naturalLoop.toArray();
}
private void handleStronglyConnectedComponent(DJGraph djGraph, int[] scc, int[] nodeMap) {
@ -126,6 +137,9 @@ public class IrreducibleGraphConverter {
// Split
splitStronglyConnectedComponent(domainNodes, sharedDom, scc, nodeMap);
// Collapse
djGraph.collapse(scc);
}
private void splitStronglyConnectedComponent(IntSet domain, int sharedDom, int[] scc, int[] nodeMap) {

View File

@ -0,0 +1,73 @@
/*
* Copyright 2015 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.common;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.Comparator;
import org.junit.Test;
/**
*
* @author Alexey Andreev
*/
public class GraphTest {
@Test
public void stronglyConnectedComponentsCalculated() {
GraphBuilder builder = new GraphBuilder();
builder.addEdge(0, 1);
builder.addEdge(1, 2);
builder.addEdge(2, 3);
builder.addEdge(2, 4);
builder.addEdge(3, 5);
builder.addEdge(4, 5);
builder.addEdge(5, 6);
builder.addEdge(6, 1);
builder.addEdge(6, 7);
builder.addEdge(7, 8);
builder.addEdge(7, 9);
builder.addEdge(8, 1);
builder.addEdge(9, 10);
builder.addEdge(10, 11);
builder.addEdge(11, 12);
builder.addEdge(12, 11);
builder.addEdge(12, 13);
Graph graph = builder.build();
int[][] sccs = GraphUtils.findStronglyConnectedComponents(graph, new int[] { 0 }, new GraphNodeFilter() {
@Override public boolean match(int node) {
return true;
}
});
for (int i = 0; i < sccs.length; ++i) {
Arrays.sort(sccs[i]);
}
Arrays.sort(sccs, new Comparator<int[]>() {
@Override public int compare(int[] o1, int[] o2) {
return Integer.compare(o1[0], o2[0]);
}
});
assertThat(sccs[0], is(new int[] { 0 }));
assertThat(sccs[1], is(new int[] { 1, 2, 3, 4, 5, 6, 7, 8 }));
assertThat(sccs[2], is(new int[] { 9 }));
assertThat(sccs[3], is(new int[] { 10 }));
assertThat(sccs[4], is(new int[] { 11, 12 }));
assertThat(sccs[5], is(new int[] { 13 }));
}
}