/* ========================================== * JGraphT : a free Java graph-theory library * ========================================== * * Project Info: http://jgrapht.sourceforge.net/ * Project Creator: Barak Naveh (http://sourceforge.net/users/barak_naveh) * * (C) Copyright 2003-2008, by Barak Naveh and Contributors. * * This program and the accompanying materials are dual-licensed under * either * * (a) the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation, or (at your option) any * later version. * * or (per the licensee's choosing) * * (b) the terms of the Eclipse Public License v1.0 as published by * the Eclipse Foundation. */ /* ------------------------- * JohnsonSimpleCycles.java * ------------------------- * (C) Copyright 2013, by Nikolay Ognyanov * * Original Author: Nikolay Ognyanov. * Contributor(s) : * * $Id$ * * Changes * ------- * 06-Sep-2013 : Initial revision (NO); */ package org.jgrapht.alg.cycle; import java.util.*; import org.jgrapht.*; import org.jgrapht.graph.*; /** * Find all simple cycles of a directed graph using the Johnson's algorithm. * * <p/>See:<br/> * D.B.Johnson, Finding all the elementary circuits of a directed graph, SIAM J. * Comput., 4 (1975), pp. 77-84. * * @param <V> the vertex type. * @param <E> the edge type. * * @author Nikolay Ognyanov */ public class JohnsonSimpleCycles<V, E> implements DirectedSimpleCycles<V, E> { // The graph. private DirectedGraph<V, E> graph; // The main state of the algorithm. private List<List<V>> cycles = null; private V [] iToV = null; private Map<V, Integer> vToI = null; private Set<V> blocked = null; private Map<V, Set<V>> bSets = null; private ArrayDeque<V> stack = null; // The state of the embedded Tarjan SCC algorithm. private List<Set<V>> SCCs = null; private int index = 0; private Map<V, Integer> vIndex = null; private Map<V, Integer> vLowlink = null; private ArrayDeque<V> path = null; private Set<V> pathSet = null; /** * Create a simple cycle finder with an unspecified graph. */ public JohnsonSimpleCycles() { } /** * Create a simple cycle finder for the specified graph. * * @param graph - the DirectedGraph in which to find cycles. * * @throws IllegalArgumentException if the graph argument is <code> * null</code>. */ public JohnsonSimpleCycles(DirectedGraph<V, E> graph) { if (graph == null) { throw new IllegalArgumentException("Null graph argument."); } this.graph = graph; } /** * {@inheritDoc} */ @Override public DirectedGraph<V, E> getGraph() { return graph; } /** * {@inheritDoc} */ @Override public void setGraph(DirectedGraph<V, E> graph) { if (graph == null) { throw new IllegalArgumentException("Null graph argument."); } this.graph = graph; } /** * {@inheritDoc} */ @Override public List<List<V>> findSimpleCycles() { if (graph == null) { throw new IllegalArgumentException("Null graph."); } initState(); int startIndex = 0; int size = graph.vertexSet().size(); while (startIndex < size) { Object [] minSCCGResult = findMinSCSG(startIndex); if (minSCCGResult[0] != null) { startIndex = (Integer) minSCCGResult[1]; @SuppressWarnings("unchecked") DirectedGraph<V, E> scg = (DirectedGraph<V, E>) minSCCGResult[0]; V startV = toV(startIndex); for (E e : scg.outgoingEdgesOf(startV)) { V v = graph.getEdgeTarget(e); blocked.remove(v); getBSet(v).clear(); } findCyclesInSCG(startIndex, startIndex, scg); startIndex++; } else { break; } } List<List<V>> result = cycles; clearState(); return result; } private Object [] findMinSCSG(int startIndex) { // Per Johnson : "adjacency structure of strong // component K with least vertex in subgraph of // G induced by {s, s+ 1, n}". // Or in contemporary terms: the strongly connected // component of the subgraph induced by {v1,...,vn} // which contains the minimum (among those SCCs) // vertex index. We return that index together with // the graph. initMinSCGState(); Object [] result = new Object[2]; List<Set<V>> SCCs = findSCCS(startIndex); // find the SCC with the minimum index int minIndexFound = Integer.MAX_VALUE; Set<V> minSCC = null; for (Set<V> scc : SCCs) { for (V v : scc) { int t = toI(v); if (t < minIndexFound) { minIndexFound = t; minSCC = scc; } } } if (minSCC == null) { return result; } // build a graph for the SCC found @SuppressWarnings("unchecked") DirectedGraph<V, E> resultGraph = new DefaultDirectedGraph<V, E>( new ClassBasedEdgeFactory<V, E>( (Class<? extends E>) DefaultEdge.class)); for (V v : minSCC) { resultGraph.addVertex(v); } for (V v : minSCC) { for (V w : minSCC) { if (graph.containsEdge(v, w)) { resultGraph.addEdge(v, w); } } } // It is ugly to return results in an array // of Object but the idea is to restrict // dependencies to JgraphT only and there is // no utility pair container in JgraphT. result[0] = resultGraph; result[1] = minIndexFound; clearMinSCCState(); return result; } private List<Set<V>> findSCCS(int startIndex) { // Find SCCs in the subgraph induced // by vertices startIndex and beyond. // A call to StrongConnectivityInspector // would be too expensive because of the // need to materialize the subgraph. // So - do a local search by the Tarjan's // algorithm and pretend that vertices // with an index smaller than startIndex // do not exist. for (V v : graph.vertexSet()) { int vI = toI(v); if (vI < startIndex) { continue; } if (!vIndex.containsKey(v)) { getSCCs(startIndex, vI); } } List<Set<V>> result = SCCs; SCCs = null; return result; } private void getSCCs(int startIndex, int vertexIndex) { V vertex = toV(vertexIndex); vIndex.put(vertex, index); vLowlink.put(vertex, index); index++; path.push(vertex); pathSet.add(vertex); Set<E> edges = graph.outgoingEdgesOf(vertex); for (E e : edges) { V successor = graph.getEdgeTarget(e); int successorIndex = toI(successor); if (successorIndex < startIndex) { continue; } if (!vIndex.containsKey(successor)) { getSCCs(startIndex, successorIndex); vLowlink.put( vertex, Math.min(vLowlink.get(vertex), vLowlink.get(successor))); } else if (pathSet.contains(successor)) { vLowlink.put( vertex, Math.min(vLowlink.get(vertex), vIndex.get(successor))); } } if (vLowlink.get(vertex).equals(vIndex.get(vertex))) { Set<V> result = new HashSet<V>(); V temp = null; do { temp = path.pop(); pathSet.remove(temp); result.add(temp); } while (!vertex.equals(temp)); if (result.size() == 1) { V v = result.iterator().next(); if (graph.containsEdge(vertex, v)) { SCCs.add(result); } } else { SCCs.add(result); } } } private boolean findCyclesInSCG( int startIndex, int vertexIndex, DirectedGraph<V, E> scg) { // Find cycles in a strongly connected graph // per Johnson. boolean foundCycle = false; V vertex = toV(vertexIndex); stack.push(vertex); blocked.add(vertex); for (E e : scg.outgoingEdgesOf(vertex)) { V successor = scg.getEdgeTarget(e); int successorIndex = toI(successor); if (successorIndex == startIndex) { List<V> cycle = new ArrayList<V>(); cycle.addAll(stack); cycles.add(cycle); foundCycle = true; } else if (!blocked.contains(successor)) { boolean gotCycle = findCyclesInSCG(startIndex, successorIndex, scg); foundCycle = foundCycle || gotCycle; } } if (foundCycle) { unblock(vertex); } else { for (E ew : scg.outgoingEdgesOf(vertex)) { V w = scg.getEdgeTarget(ew); Set<V> bSet = getBSet(w); bSet.add(vertex); } } stack.pop(); return foundCycle; } private void unblock(V vertex) { blocked.remove(vertex); Set<V> bSet = getBSet(vertex); while (bSet.size() > 0) { V w = bSet.iterator().next(); bSet.remove(w); if (blocked.contains(w)) { unblock(w); } } } @SuppressWarnings("unchecked") private void initState() { cycles = new LinkedList<List<V>>(); iToV = (V []) graph.vertexSet().toArray(); vToI = new HashMap<V, Integer>(); blocked = new HashSet<V>(); bSets = new HashMap<V, Set<V>>(); stack = new ArrayDeque<V>(); for (int i = 0; i < iToV.length; i++) { vToI.put(iToV[i], i); } } private void clearState() { cycles = null; iToV = null; vToI = null; blocked = null; bSets = null; stack = null; } private void initMinSCGState() { index = 0; SCCs = new ArrayList<Set<V>>(); vIndex = new HashMap<V, Integer>(); vLowlink = new HashMap<V, Integer>(); path = new ArrayDeque<V>(); pathSet = new HashSet<V>(); } private void clearMinSCCState() { index = 0; SCCs = null; vIndex = null; vLowlink = null; path = null; pathSet = null; } private Integer toI(V vertex) { return vToI.get(vertex); } private V toV(Integer i) { return iToV[i]; } private Set<V> getBSet(V v) { // B sets typically not all needed, // so instantiate lazily. Set<V> result = bSets.get(v); if (result == null) { result = new HashSet<V>(); bSets.put(v, result); } return result; } } // End JohnsonSimpleCycles.java