All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jgrapht.alg.cycle.HawickJamesSimpleCycles Maven / Gradle / Ivy

/*
 * (C) Copyright 2014-2021, by Luiz Kill and Contributors.
 *
 * JGraphT : a free Java graph-theory library
 *
 * See the CONTRIBUTORS.md file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0, or the
 * GNU Lesser General Public License v2.1 or later
 * which is available at
 * http://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR LGPL-2.1-or-later
 */
package org.jgrapht.alg.cycle;

import org.jgrapht.*;

import java.util.*;

import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.toList;

/**
 * Find all simple cycles of a directed graph using the algorithm described by Hawick and James.
 *
 * 

* See:
* K. A. Hawick, H. A. James. Enumerating Circuits and Loops in Graphs with Self-Arcs and * Multiple-Arcs. Computational Science Technical Note CSTN-013, 2008 * * @param the vertex type. * @param the edge type. * * @author Luiz Kill */ public class HawickJamesSimpleCycles implements DirectedSimpleCycles { private Graph graph; private int nVertices = 0; private long nCycles = 0; private List> cycles = null; // The main state of the algorithm private Integer start = 0; private List[] aK = null; private List[] b = null; private boolean[] blocked = null; private ArrayDeque stack = null; // Indexing the vertices private V[] iToV = null; private Map vToI = null; private int pathLimit = 0; private boolean hasLimit = false; private Runnable operation; /** * Create a simple cycle finder with an unspecified graph. */ public HawickJamesSimpleCycles() { } /** * 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 * null. */ public HawickJamesSimpleCycles(Graph graph) throws IllegalArgumentException { this.graph = GraphTests.requireDirected(graph, "Graph must be directed"); } @SuppressWarnings("unchecked") private void initState() { nCycles = 0; nVertices = graph.vertexSet().size(); blocked = new boolean[nVertices]; stack = new ArrayDeque<>(nVertices); b = new ArrayList[nVertices]; for (int i = 0; i < nVertices; i++) { b[i] = new ArrayList<>(); } iToV = (V[]) graph.vertexSet().toArray(); vToI = new HashMap<>(); for (int i = 0; i < iToV.length; i++) { vToI.put(iToV[i], i); } aK = buildAdjacencyList(); stack.clear(); } @SuppressWarnings("unchecked") private List[] buildAdjacencyList() { @SuppressWarnings("rawtypes") List[] listAk = new ArrayList[nVertices]; for (int j = 0; j < nVertices; j++) { V v = iToV[j]; List s = Graphs.successorListOf(graph, v); listAk[j] = new ArrayList(s.size()); for (V value : s) { listAk[j].add(vToI.get(value)); } } return listAk; } private void clearState() { aK = null; nVertices = 0; blocked = null; stack = null; iToV = null; vToI = null; b = null; operation = () -> { }; } private boolean circuit(Integer v, int steps) { boolean f = false; stack.push(v); blocked[v] = true; for (Integer w : aK[v]) { if (w < start) { continue; } if (Objects.equals(w, start)) { operation.run(); f = true; } else if (!blocked[w]) { if (limitReached(steps) || circuit(w, steps + 1)) { f = true; } } } if (f) { unblock(v); } else { for (Integer w : aK[v]) { if (w < start) { continue; } if (!b[w].contains(v)) { b[w].add(v); } } } stack.pop(); return f; } private void unblock(Integer u) { blocked[u] = false; for (int wPos = 0; wPos < b[u].size(); wPos++) { Integer w = b[u].get(wPos); int sizeBeforeRemove = b[u].size(); b[u].removeAll(singletonList(w)); wPos -= (sizeBeforeRemove - b[u].size()); if (blocked[w]) { unblock(w); } } } /** * Get the graph * * @return graph */ public Graph getGraph() { return graph; } /** * Set the graph * * @param graph graph */ public void setGraph(Graph graph) { this.graph = GraphTests.requireDirected(graph, "Graph must be directed"); } /** * {@inheritDoc} */ @Override public List> findSimpleCycles() throws IllegalArgumentException { if (graph == null) { throw new IllegalArgumentException("Null graph."); } initState(); cycles = new ArrayList<>(); operation = () -> { List cycle = stack.stream().map(v -> iToV[v]).collect(toList()); Collections.reverse(cycle); cycles.add(cycle); }; analyzeCircuits(); List> result = cycles; clearState(); return result; } /** * Print to the standard output all simple cycles without building a list to keep them, thus * avoiding high memory consumption when investigating large and much connected graphs. */ public void printSimpleCycles() { if (graph == null) { throw new IllegalArgumentException("Null graph."); } initState(); operation = () -> { stack.stream().map(i -> iToV[i].toString() + " ").forEach(System.out::print); System.out.println(); }; analyzeCircuits(); clearState(); } /** * Count the number of simple cycles. It can count up to Long.MAX cycles in a graph. * * @return the number of simple cycles */ public long countSimpleCycles() { if (graph == null) { throw new IllegalArgumentException("Null graph."); } initState(); nCycles = 0; operation = () -> nCycles++; analyzeCircuits(); clearState(); return nCycles; } private void analyzeCircuits() { for (int i = 0; i < nVertices; i++) { for (int j = 0; j < nVertices; j++) { blocked[j] = false; b[j].clear(); } start = vToI.get(iToV[i]); circuit(start, 0); } } /** * Limits the maximum number of edges in a cycle. * * @param pathLimit maximum paths. */ public void setPathLimit(int pathLimit) { this.pathLimit = pathLimit - 1; this.hasLimit = true; } /** * This is the default behaviour of the algorithm. It will keep looking as long as there are * paths available. */ public void clearPathLimit() { this.hasLimit = false; } private boolean limitReached(int steps) { return hasLimit && steps >= pathLimit; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy