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

org.jgrapht.alg.AllDirectedPaths Maven / Gradle / Ivy

/* ==========================================
 * 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-2012, 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.
 */

/* -------------------------
 * AllDirectedPaths.java
 * -------------------------
 * (C) Copyright 2015-2015, Vera-Licona Research Group and Contributors.
 *
 * Original Author:  Andrew Gainer-Dewar, Ph.D. (Vera-Licona Research Group)
 * Contributor(s):
 *
 * Changes
 * -------
 * Feb-2016 : Initial version;
 *
 */
package org.jgrapht.alg;

import java.util.*;

import org.jgrapht.*;
import org.jgrapht.graph.*;


/**
 * A Dijkstra-like algorithm to find all paths between two sets of nodes in a
 * directed graph, with options to search only simple paths and to limit the
 * path length.
 *
 * @author Andrew Gainer-Dewar
 * @since Feb, 2016
 */

public class AllDirectedPaths
{
    private final DirectedGraph graph;

    public AllDirectedPaths(DirectedGraph graph)
    {
        if (graph == null) {
            throw new IllegalArgumentException("Graph cannot be null!");
        }

        this.graph = graph;
    }

    /**
     * Calculate (and return) all paths from the source vertex to the target
     * vertex.
     *
     * @param sourceVertex the source vertex
     * @param targetVertex the target vertex
     * @param simplePathsOnly if true, only search simple
     * (non-self-intersecting) paths
     * @param maxPathLength maximum number of edges to allow in a path (if null,
     * all paths are considered, which may be very slow due to potentially huge
     * output)
     */
    public List> getAllPaths(
        V sourceVertex,
        V targetVertex,
        boolean simplePathsOnly,
        Integer maxPathLength)
    {
        return getAllPaths(
            Collections.singleton(sourceVertex),
            Collections.singleton(targetVertex),
            simplePathsOnly,
            maxPathLength);
    }

    /**
     * Calculate (and return) all paths from the source vertices to the target
     * vertices.
     *
     * @param sourceVertices the source vertices
     * @param targetVertices the target vertices
     * @param simplePathsOnly if true, only search simple
     * (non-self-intersecting) paths
     * @param maxPathLength maximum number of edges to allow in a path (if null,
     * all paths are considered, which may be very slow due to potentially huge
     * output)
     *
     * @return list of all paths from the sources to the targets containing no
     * more than maxPathLength edges
     */
    public List> getAllPaths(
        Set sourceVertices,
        Set targetVertices,
        boolean simplePathsOnly,
        Integer maxPathLength)
    {
        if ((maxPathLength != null) && (maxPathLength < 0)) {
            throw new IllegalArgumentException(
                "maxPathLength must be non-negative if defined");
        }

        if (!simplePathsOnly && (maxPathLength == null)) {
            throw new IllegalArgumentException(
                "If search is not restricted to simple paths, a maximum path length must be set to avoid infinite cycles");
        }

        if ((sourceVertices.isEmpty()) || (targetVertices.isEmpty())) {
            return new ArrayList();
        }

        // Decorate the edges with the minimum path lengths through them
        Map edgeMinDistancesFromTargets =
            edgeMinDistancesBackwards(targetVertices, maxPathLength);

        // Generate all the paths
        List> allPaths =
            generatePaths(
                sourceVertices,
                targetVertices,
                simplePathsOnly,
                maxPathLength,
                edgeMinDistancesFromTargets);

        return allPaths;
    }

    /**
     * Compute the minimum number of edges in a path to the targets through each
     * edge, so long as it is not greater than a bound.
     *
     * @param targetVertices the target vertices
     * @param maxPathLength maximum number of edges to allow in a path (if null,
     * all edges will be considered, which may be expensive)
     *
     * @return the minimum number of edges in a path from each edge to the
     * targets, encoded in a Map
     */
    private Map edgeMinDistancesBackwards(
        Set targetVertices,
        Integer maxPathLength)
    {
        /*
         * We walk backwards through the network from the target
         * vertices, marking edges and vertices with their minimum
         * distances as we go.
         */
        Map edgeMinDistances = new HashMap();
        Map vertexMinDistances = new HashMap();
        Queue verticesToProcess = new LinkedList();

        // Input sanity checking
        if (maxPathLength != null) {
            if (maxPathLength < 0) {
                throw new IllegalArgumentException(
                    "maxPathLength must be non-negative if defined");
            }
            if (maxPathLength == 0) {
                return edgeMinDistances;
            }
        }

        // Bootstrap the process with the target vertices
        for (V target : targetVertices) {
            vertexMinDistances.put(target, 0);
            verticesToProcess.add(target);
        }

        // Work through the node queue. When it's empty, we're done!
        for (V vertex; (vertex = verticesToProcess.poll()) != null;) {
            assert vertexMinDistances.containsKey(vertex);

            Integer childDistance = vertexMinDistances.get(vertex) + 1;

            // Check whether the incoming edges of this node are correctly
            // decorated
            for (E edge : graph.incomingEdgesOf(vertex)) {
                // Mark the edge if needed
                if (!edgeMinDistances.containsKey(edge)
                    || (edgeMinDistances.get(edge) > childDistance))
                {
                    edgeMinDistances.put(edge, childDistance);
                }

                // Mark the edge's source vertex if needed
                V edgeSource = graph.getEdgeSource(edge);
                if (!vertexMinDistances.containsKey(edgeSource)
                    || (vertexMinDistances.get(edgeSource) > childDistance))
                {
                    vertexMinDistances.put(edgeSource, childDistance);

                    if ((maxPathLength == null)
                        || (childDistance < maxPathLength))
                    {
                        verticesToProcess.add(edgeSource);
                    }
                }
            }
        }

        assert verticesToProcess.isEmpty();
        return edgeMinDistances;
    }

    /**
     * Generate all paths from the sources to the targets, using pre-computed
     * minimum distances.
     *
     * @param sourceVertices the source vertices
     * @param targetVertices the target vertices
     * @param maxPathLength maximum number of edges to allow in a path
     * @param simplePathsOnly if true, only search simple
     * (non-self-intersecting) paths (if null, all edges will be considered,
     * which may be expensive)
     * @param edgeMinDistancesFromTargets the minimum number of edges in a path
     * to a target through each edge, as computed by {@code
     * edgeMinDistancesBackwards}.
     *
     * @return a List of all GraphPaths from the sources to the targets
     * satisfying the given constraints
     */
    private List> generatePaths(
        Set sourceVertices,
        Set targetVertices,
        boolean simplePathsOnly,
        Integer maxPathLength,
        Map edgeMinDistancesFromTargets)
    {
        /*
         * We walk forwards through the network from the source
         * vertices, exploring all outgoing edges whose minimum
         * distances is small enough.
         */
        List> completePaths = new ArrayList();
        Deque> incompletePaths = new LinkedList();

        // Input sanity checking
        if (maxPathLength != null) {
            if (maxPathLength < 0) {
                throw new IllegalArgumentException(
                    "maxPathLength must be non-negative if defined");
            }
            if (maxPathLength == 0) {
                return completePaths;
            }
        }

        // Bootstrap the search with the source vertices
        for (V source : sourceVertices) {
            for (E edge : graph.outgoingEdgesOf(source)) {
                assert graph.getEdgeSource(edge).equals(source);

                if (edgeMinDistancesFromTargets.containsKey(edge)) {
                    List path = Arrays.asList(edge);
                    incompletePaths.add(path);
                }
            }
        }

        // Walk through the queue of incomplete paths
        for (
            List incompletePath;
            (incompletePath = incompletePaths.poll()) != null;)
        {
            Integer lengthSoFar = incompletePath.size();
            assert (maxPathLength == null) || (lengthSoFar < maxPathLength);

            E leafEdge = incompletePath.get(lengthSoFar - 1);
            V leafNode = graph.getEdgeTarget(leafEdge);

            Set pathVertices = new HashSet();
            for (E pathEdge : incompletePath) {
                pathVertices.add(graph.getEdgeSource(pathEdge));
                pathVertices.add(graph.getEdgeTarget(pathEdge));
            }

            for (E outEdge : graph.outgoingEdgesOf(leafNode)) {
                // Proceed if the outgoing edge is marked and the mark
                // is sufficiently small
                if (edgeMinDistancesFromTargets.containsKey(outEdge)
                    && ((maxPathLength == null)
                        || ((edgeMinDistancesFromTargets.get(outEdge)
                                + lengthSoFar) <= maxPathLength)))
                {
                    List newPath = new ArrayList(incompletePath);
                    newPath.add(outEdge);

                    // If requested, make sure this path isn't self-intersecting
                    if (simplePathsOnly
                        && pathVertices.contains(
                            graph.getEdgeTarget(outEdge)))
                    {
                        continue;
                    }

                    // If this path reaches a target, add it to completePaths
                    if (targetVertices.contains(graph.getEdgeTarget(outEdge))) {
                        GraphPath completePath = makePath(newPath);
                        assert sourceVertices.contains(
                            completePath.getStartVertex());
                        assert targetVertices.contains(
                            completePath.getEndVertex());
                        assert (maxPathLength == null)
                            || (completePath.getWeight() <= maxPathLength);
                        completePaths.add(completePath);
                    }

                    // If this path is short enough, consider further
                    // extensions of it
                    if ((maxPathLength == null)
                        || (newPath.size() < maxPathLength))
                    {
                        incompletePaths.addFirst(newPath); // We use
                                                           // incompletePaths in
                                                           // FIFO mode to avoid
                                                           // memory blowup
                    }
                }
            }
        }

        assert incompletePaths.isEmpty();
        return completePaths;
    }

    /**
     * Transform an ordered list of edges into a GraphPath
     *
     * @param edges the edges
     *
     * @return the corresponding GraphPath
     */
    private GraphPath makePath(List edges)
    {
        V source = graph.getEdgeSource(edges.get(0));
        V target = graph.getEdgeTarget(edges.get(edges.size() - 1));
        double weight = edges.size();
        return new GraphPathImpl(graph, source, target, edges, weight);
    }
}

// End AllDirectedPaths.java




© 2015 - 2025 Weber Informatics LLC | Privacy Policy