/* * Copyright 2012 Odysseus Software GmbH * * 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 de.odysseus.ithaka.digraph; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Stack; /** * This class provides some common digraph utilities. */ public class Digraphs { /** * Get an unmodifiable empty digraph. * @return empty digraph */ public static <V,E> DoubledDigraph<V,E> emptyDigraph() { return new EmptyDigraph<V,E>(); } /** * Wraps the given digraph to make it unmodifiable. Whenever a method * is called on the resulting digraph that could modify the underlying * digraph, an exception is thrown. * @param <V> vertex type * @param <E> edge type * @param digraph * @return unmodifiable digraph equivalent to the given digraph */ public static <V,E> Digraph<V,E> unmodifiableDigraph(Digraph<V,E> digraph) { return new UnmodifiableDigraph<V, E>(digraph); } /** * Topologically sort vertices of an acyclic directed graph (DAG). * This method will produce an ordering of vertices, such that all * edges go from left right. * If the input graph is not a DAG, the algorithm will still perform, * but in the resulting list there will be edges from vertices to * vertices prior in the list. * @param <V> vertex type * @param digraph input graph * @param descending let edges go from right to left if <code>true</code> * @return list of vertices toplologically ordered. */ public static <V> List<V> topsort(Digraph<V,?> digraph, boolean descending) { List<V> finished = new ArrayList<V>(); Set<V> discovered = new HashSet<V>(digraph.getVertexCount()); for (V vertex : digraph.vertices()) { if (!discovered.contains(vertex)) { dfs(digraph, vertex, discovered, finished); } } if (!descending) { Collections.reverse(finished); } return finished; } /** * Compute the set of vertices reachable from the given source in the given digraph. * @param <V> vertex type * @param digraph * @param source source vertex * @return the set of vertices reachable from <code>source</code> */ public static <V> Set<V> closure(Digraph<V,?> digraph, V source) { Set<V> closure = new HashSet<V>(); dfs(digraph, source, closure, closure); return closure; } /** * Answer <code>true</code> if the given digraph is acyclic (DAG). * Per definition, the empty graph and single vertex digraphs are acyclic. * @param <V> vertex type * @param digraph * @return <code>true</code> iff the given digraph is acyclic */ public static <V> boolean isAcyclic(Digraph<V,?> digraph) { int n = digraph.getVertexCount(); if (n < 2) { return true; // no self loop } if (digraph.getEdgeCount() > (n*(n-1))/2) { return false; } return Digraphs.<V>scc(digraph).size() == n; } /** * Test if the given digraphs are equivalent. * This is the case if and the same vertices are connected by an edge. * <ol> * <li>both digraphs contain the same vertices</li> * <li>the same pairs of vertices are connected by an edge in both digraphs </li> * <li>optionally, this method may require that the corresponding edges are equal.</li> * </ol> * @param <V> vertex type * @param first first digraph. * @param second second digraph. * @param compareEdges if <code>true</code>, compare edges using <code>equals()</code>. * @return <code>true</code> iff the two digraphs are equivalent according to the above description. */ public static <V> boolean isEquivalent(Digraph<V,?> first, Digraph<V,?> second, boolean compareEdges) { if (first == second) { return true; } if (first.getEdgeCount() != second.getEdgeCount() || first.getVertexCount() != second.getVertexCount()) { return false; } for (V source : first.vertices()) { if (!second.contains(source)) { return false; } for (V target : first.targets(source)) { if (!second.contains(source, target)) { return false; } if (compareEdges) { Object edge1 = first.get(source, target); Object edge2 = second.get(source, target); if (edge1 != edge2 && (edge1 == null || edge2 == null || !edge1.equals(edge2))) { return false; } } } } return true; } /** * Answer <code>true</code> if the given digraph is strongly connected. * Per definition, the empty graph and single vertex digraphs are strongly connected. * * @param <V> vertex type * @param digraph * @return <code>true</code> iff the given digraph is strongly connected */ public static <V> boolean isStronglyConnected(Digraph<V,?> digraph) { int n = digraph.getVertexCount(); if (n < 2) { return true; } return Digraphs.<V>scc(digraph).size() == 1; } /** * Answer <code>true</code> if there is a path from the given source to the given target * in the supplied graph. If source is equal to target, answer <code>true</code>. * @param <V> vertex type * @param digraph * @param source source vertex * @param target target vertex * @return <code>true</code> iff there's a path from <code>source</code> to <code>target</code> in <code>digraph</code> */ public static <V> boolean isReachable(Digraph<V,?> digraph, V source, V target) { return digraph.contains(source, target) || Digraphs.<V>closure(digraph, source).contains(target); } /** * Perform a depth first search. * * @param <V> vertex type * @param digraph * @param source dfs start vertex * @param discovered set of vertices already discovered during search * @param finished collection of vertices visited during search */ public static <V> void dfs(Digraph<V,?> digraph, V source, Set<? super V> discovered, Collection<? super V> finished) { if (discovered.add(source)) { for (V target : digraph.targets(source)) { dfs(digraph, target, discovered, finished); } finished.add(source); } } /** * Perform an undirected depth first search. * * @param <V> vertex type * @param digraph * @param source dfs start vertex * @param discovered set of vertices already discovered during search * @param finished collection of vertices visited during search */ public static <V> void dfs2(Digraph<V,?> digraph, V source, Set<? super V> discovered, Collection<? super V> finished) { dfs2(digraph, digraph.reverse(), source, discovered, finished); } private static <V> void dfs2(Digraph<V,?> forward, Digraph<V,?> backward, V source, Set<? super V> discovered, Collection<? super V> finished) { if (discovered.add(source)) { for (V target : forward.targets(source)) { dfs2(forward, backward, target, discovered, finished); } for (V target : backward.targets(source)) { dfs2(forward, backward, target, discovered, finished); } finished.add(source); } } /** * Compute strongly connected components. * @param <V> * @param digraph * @return strongly connected components */ public static <V> List<Set<V>> scc(Digraph<V,?> digraph) { List<Set<V>> components = new ArrayList<Set<V>>(); Digraph<V,?> reverse = digraph.reverse(); // dfs on this graph Stack<V> stack = new Stack<V>(); Set<V> discovered = new HashSet<V>(); for (V vertex : digraph.vertices()) { dfs(digraph, vertex, discovered, stack); } // dfs on reverse graph discovered = new HashSet<V>(); while (!stack.isEmpty()) { V vertex = stack.pop(); if (!discovered.contains(vertex)) { Set<V> component = new HashSet<V>(); dfs(reverse, vertex, discovered, component); components.add(component); } } return components; } /** * Compute weakly connected components. * @param <V> * @param digraph * @return weakly connected components */ public static <V> List<Set<V>> wcc(Digraph<V,?> digraph) { List<Set<V>> components = new ArrayList<Set<V>>(); Digraph<V,?> reverse = digraph.reverse(); // dfs on both graphs Set<V> discovered = new HashSet<V>(); for (V vertex : digraph.vertices()) { if (!discovered.contains(vertex)) { Set<V> component = new HashSet<V>(); dfs2(digraph, reverse, vertex, discovered, component); components.add(component); } } return components; } /** * Compute the reverse graph. * @param <V> vertex type * @param <E> edge type * @param <G> result type * @param digraph input digraph * @param factory factory used to create result graph * @return the reverse digraph */ public static <V,E,G extends Digraph<V, E>> G reverse(Digraph<V,E> digraph, DigraphFactory<? extends G> factory) { G reverse = factory.create(); for (V source : digraph.vertices()) { reverse.add(source); for (V target : digraph.targets(source)) { reverse.put(target, source, digraph.get(source, target)); } } return reverse; } /** * Copy a digraph. * @param digraph graph to copy * @param factory factory used to create copy * @return a copy of the given digraph */ public static <V, E, G extends Digraph<V, E>> G copy(Digraph<V, E> digraph, DigraphFactory<? extends G> factory) { G result = factory.create(); for (V source : digraph.vertices()) { result.add(source); for (V target : digraph.targets(source)) { result.put(source, target, digraph.get(source, target)); } } return result; } /** * Create subgraph induced by the specified vertices. * @param <V> vertex type * @param <E> edge type * @param <G> subgraph type * @param digraph * @param vertices * @param factory * @return subgraph of the supplied digraph containing the specified vertices. */ public static <V,E,G extends Digraph<V,E>> G subgraph( Digraph<V,E> digraph, Set<V> vertices, DigraphFactory<? extends G> factory) { G subgraph = factory.create(); for (V v : vertices) { if (digraph.contains(v)) { subgraph.add(v); for (V w : digraph.targets(v)) { if (vertices.contains(w)) { subgraph.put(v, w, digraph.get(v, w)); } } } } return subgraph; } /** * Create partition graph from a given vertex decomposition. * @param <G> the type of the component graphs * @param <P> the type of the result graph * @param <C> the edge type of the result graph * @param factory1 used to create the partition graph * @param factory2 used to create the subgraphs * @param cumulator used to cumulate edges between subgraphs * @return a digraph of subgraphs of this digraph */ public static <V,E,G extends Digraph<V,E>,P extends Digraph<G,C>, C> P partition( Digraph<V,E> digraph, Iterable<Set<V>> sets, DigraphFactory<? extends P> factory1, DigraphFactory<? extends G> factory2, EdgeCumulator<? super G,C,? super E> cumulator) { P partition = factory1.create(); // map vertices to their component graph Map<V,G> vertex2subgraph = new HashMap<V,G>(); for (Set<V> set : sets) { G subgraph = subgraph(digraph, set, factory2); for (V v : subgraph.vertices()) { assert digraph.contains(v) && !vertex2subgraph.containsKey(v); vertex2subgraph.put(v, subgraph); } partition.add(subgraph); } assert vertex2subgraph.size() == digraph.getVertexCount(); // build component graph for (V v : digraph.vertices()) { G source = vertex2subgraph.get(v); for (V w : digraph.targets(v)) { G target = vertex2subgraph.get(w); if (source != target) { C cumulatedEdge = null; if (cumulator != null) { E subgraphEdge = digraph.get(v, w); C componentEdge = partition.get(source, target); cumulatedEdge = cumulator.add(source, target, componentEdge, subgraphEdge); } partition.put(source, target, cumulatedEdge); } } } return partition; } }