org.jgrapht.alg.AllDirectedPaths Maven / Gradle / Ivy
/*
* (C) Copyright 2015-2016, by Vera-Licona Research Group and Contributors.
*
* JGraphT : a free Java graph-theory library
*
* 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.
*/
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.
*
* @param the graph vertex type
* @param the graph edge type
*
* @author Andrew Gainer-Dewar
* @since Feb, 2016
*/
public class AllDirectedPaths
{
private final DirectedGraph graph;
/**
* Create a new instance
*
* @param graph the input 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)
* @return all paths from the source vertex to the target vertex
*/
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 Collections.emptyList();
}
// Decorate the edges with the minimum path lengths through them
Map edgeMinDistancesFromTargets =
edgeMinDistancesBackwards(targetVertices, maxPathLength);
// Generate all the paths
return generatePaths(
sourceVertices, targetVertices, simplePathsOnly, maxPathLength,
edgeMinDistancesFromTargets);
}
/**
* 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) {
if (targetVertices.contains(source)) {
completePaths.add(new GraphWalk<>(graph, source, source, new ArrayList<>(), 0));
}
for (E edge : graph.outgoingEdgesOf(source)) {
assert graph.getEdgeSource(edge).equals(source);
if (targetVertices.contains(graph.getEdgeTarget(edge))) {
completePaths.add(makePath(Collections.singletonList(edge)));
}
if (edgeMinDistancesFromTargets.containsKey(edge)) {
List path = Collections.singletonList(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 GraphWalk<>(graph, source, target, edges, weight);
}
}
// End AllDirectedPaths.java
© 2015 - 2025 Weber Informatics LLC | Privacy Policy