package soottocfg.cfg.util; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Queue; import java.util.Set; import org.jgrapht.DirectedGraph; import org.jgrapht.Graphs; import org.jgrapht.graph.DefaultDirectedGraph; import com.google.common.base.Preconditions; import com.google.common.base.Verify; public class GraphUtil { /** * Get the unique source of 'g'. * @param g Graph with a unique source * @return The source of 'g', or an exception if the source is not unique. */ public static <V> V getSource(DirectedGraph<V, ?> g) { V source = null; for (V b : g.vertexSet()) { if (g.inDegreeOf(b)==0) { Verify.verify(source==null, String.format("More than one source: %n\t%s%n\t%s", source, b)); source = b; } } return source; } /** * Get the unique sink of 'g'. * @param g Graph with a unique sink * @return The source of 'g', or an exception if the sink is not unique. */ public static <V> V getSink(DirectedGraph<V, ?> g) { V sink = null; for (V b : g.vertexSet()) { if (g.outDegreeOf(b)==0) { Verify.verify(sink==null); sink = b; } } return sink; } /** * Returns the set of all sinks in 'g' * @param g Directed graph. * @return Set of all sinks in 'g'. */ public static <V> Set<V> getSinks(DirectedGraph<V, ?> g) { Set<V> sinks = new HashSet<V>(); for (V b : g.vertexSet()) { if (g.outDegreeOf(b)==0) { sinks.add(b); } } return sinks; } /** * Returns the set of all sinks in 'g' * @param g Directed graph. * @return Set of all sinks in 'g'. */ public static <V> Set<V> getSources(DirectedGraph<V, ?> g) { Set<V> sources = new HashSet<V>(); for (V b : g.vertexSet()) { if (g.inDegreeOf(b)==0) { sources.add(b); } } return sources; } /** * Generates a subgraph containing all complete paths in 'orig' that pass through 'from'. * This assumes that 'orig' has one unique source and one unique 'sink'. The resulting * subgraph is a shallow copy of 'orig'. That is, it uses the same objects as vertices * as 'orig' * @param orig The original graph (with unique source and sink). * @param from The vertex for which we want to have a subgraph. * @return A subgraph containing all complete paths in 'orig' that contain 'from'. */ public static <V,E> DirectedGraph<V, E> computeSubgraphThroughVertex(DirectedGraph<V, E> orig, V from) { DirectedGraph<V, E> subgraph = new DefaultDirectedGraph<V, E>(orig.getEdgeFactory()); subgraph.addVertex(from); /* * Add all vertices to the subgraph that can reach 'from'. */ Queue<V> todo = new LinkedList<V>(); todo.add(from); while (!todo.isEmpty()) { V current = todo.poll(); for (V pre : Graphs.predecessorListOf(orig, current)) { if (!subgraph.containsVertex(pre) && !todo.contains(pre)) { subgraph.addVertex(pre); todo.add(pre); } if (!subgraph.containsEdge(pre, current)) { subgraph.addEdge(pre, current); } } } /* * add all vertices to the subgraph that are reachable from 'from'. */ todo.add(from); while (!todo.isEmpty()) { V current = todo.poll(); for (V suc : Graphs.successorListOf(orig, current)) { if (!subgraph.containsVertex(suc) && !todo.contains(suc)) { subgraph.addVertex(suc); todo.add(suc); } if (!subgraph.containsEdge(current, suc)) { subgraph.addEdge(current, suc); } } } return subgraph; } /** * Generates a subgraph containing all paths in 'orig' from the source to 'from'. * This assumes that 'orig' has one unique source and one unique 'sink'. The resulting * subgraph is a shallow copy of 'orig'. That is, it uses the same objects as vertices * as 'orig' * @param orig The original graph (with unique source and sink). * @param from The vertex for which we want to have a subgraph. * @return A subgraph containing all paths in 'orig' from source to'from'. */ public static <V,E> DirectedGraph<V, E> computeSubgraphToVertex(DirectedGraph<V, E> orig, V from) { DirectedGraph<V, E> subgraph = new DefaultDirectedGraph<V, E>(orig.getEdgeFactory()); subgraph.addVertex(from); /* * Add all vertices to the subgraph that can reach 'from'. */ Queue<V> todo = new LinkedList<V>(); todo.add(from); while (!todo.isEmpty()) { V current = todo.poll(); for (V pre : Graphs.predecessorListOf(orig, current)) { if (!subgraph.containsVertex(pre) && !todo.contains(pre)) { subgraph.addVertex(pre); todo.add(pre); } if (!subgraph.containsEdge(pre, current)) { subgraph.addEdge(pre, current); } } } return subgraph; } /** TODO: inefficient! * Returns the set of all vertices between from and to, including from and to. * Doesn't check if 'to' is reachable from 'from' * @param orig * @param from * @param to * @return */ public static <V> Set<V> getVerticesBetween(DirectedGraph<V, ?> graph, V from, V to) { Set<V> res = new HashSet<V>(); if (from.equals(to)) { res.add(from); return res; } else { res.addAll(getForwardReachableVertices(graph, from)); res.retainAll(getBackwardReachableVertices(graph, to)); } return res; } public static <V> Set<V> getForwardReachableVertices(DirectedGraph<V, ?> graph, V from) { BfsIterator<V> iter = new BfsIterator<V>(graph, from); return new HashSet<V>(iter.getElements()); } public static <V> Set<V> getBackwardReachableVertices(DirectedGraph<V, ?> graph, V from) { Queue<V> todo = new LinkedList<V>(); Set<V> visited = new HashSet<V>(); todo.add(from); while (!todo.isEmpty()) { V current = todo.poll(); visited.add(current); for (V suc : Graphs.predecessorListOf(graph, current)) { if (!todo.contains(suc) && !visited.contains(suc)) { todo.add(suc); } } } return visited; } /** * TODO can be done more efficiently * @param graph * @param from * @param to * @return */ public static <V> boolean pathExists(DirectedGraph<V, ?> graph, V from, V to) { Preconditions.checkArgument(graph.containsVertex(from)); Preconditions.checkArgument(graph.containsVertex(to)); BfsIterator<V> iter = new BfsIterator<V>(graph, from); while (iter.hasNext()) { if (iter.next().equals(to)) { return true; } } return false; } public static <V> V findCommonSuccessor(DirectedGraph<V, ?> graph, V a, V b) { BfsIterator<V> aIter = new BfsIterator<V>(graph, a); List<V> bReachable = (new BfsIterator<V>(graph, b)).getElements(); while (aIter.hasNext()) { V current = aIter.next(); if (bReachable.contains(current)) { return current; } } return null; } public static <V,E> boolean isReducibleGraph(DirectedGraph<V, E> graph, V source) { CategorizeEdges<V, E> ce = new CategorizeEdges<V, E>(graph, source); return ce.getCrossEdges().isEmpty(); } public static <V,E> boolean isIrreducibleGraphAndHasLoops(DirectedGraph<V, E> graph, V source) { CategorizeEdges<V, E> ce = new CategorizeEdges<V, E>(graph, source); return !ce.getCrossEdges().isEmpty() && !ce.getBackwardEdges().isEmpty(); } }