org.jgrapht.alg.shortestpath.DijkstraClosestFirstIterator Maven / Gradle / Ivy
/*
* (C) Copyright 2003-2023, by John V Sichi, Dimitrios Michail 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.shortestpath;
import org.jgrapht.*;
import org.jgrapht.alg.interfaces.ShortestPathAlgorithm.*;
import org.jgrapht.alg.util.*;
import org.jheaps.*;
import org.jheaps.tree.*;
import java.util.*;
import java.util.function.*;
/**
* A light-weight version of the closest-first iterator for a directed or undirected graphs. For
* this iterator to work correctly the graph must not be modified during iteration. Currently there
* are no means to ensure that, nor to fail-fast. The results of such modifications are undefined.
*
*
* The metric for closest here is the weighted path length from a start vertex, i.e.
* Graph.getEdgeWeight(Edge) is summed to calculate path length. Negative edge weights will result
* in an IllegalArgumentException. Optionally, path length may be bounded by a finite radius. This
* iterator can use a custom heap implementation.
*
*
* NOTE: This is an internal iterator for use in shortest paths algorithms. For an iterator that is
* suitable to return to the users see {@link org.jgrapht.traverse.ClosestFirstIterator}. This
* implementation is faster since it does not support graph traversal listeners nor disconnected
* components.
*
* @param the graph vertex type
* @param the graph edge type
* @author John V. Sichi
* @author Dimitrios Michail
*/
class DijkstraClosestFirstIterator
implements Iterator
{
private final Graph graph;
private final V source;
private final double radius;
private final Map>> seen;
private AddressableHeap> heap;
/**
* Creates a new iterator for the specified graph. Iteration will start at the specified start
* vertex and will be limited to the connected component that includes that vertex. This
* iterator will use pairing heap as a default heap implementation.
*
* @param graph the graph to be iterated.
* @param source the source vertex
*/
public DijkstraClosestFirstIterator(Graph graph, V source)
{
this(graph, source, Double.POSITIVE_INFINITY, PairingHeap::new);
}
/**
* Creates a new radius-bounded iterator for the specified graph. Iteration will start at the
* specified start vertex and will be limited to the subset of the connected component which
* includes that vertex and is reachable via paths of weighted length less than or equal to the
* specified radius. This iterator will use pairing heap as a default heap implementation.
*
* @param graph the graph
* @param source the source vertex
* @param radius limit on weighted path length, or Double.POSITIVE_INFINITY for unbounded search
*/
public DijkstraClosestFirstIterator(Graph graph, V source, double radius)
{
this(graph, source, radius, PairingHeap::new);
}
/**
* Creates a new iterator for the specified graph. Iteration will start at the specified start
* vertex and will be limited to the connected component that includes that vertex. This
* iterator will use heap supplied by the {@code heapSupplier}
*
* @param graph the graph to be iterated.
* @param source the source vertex
* @param heapSupplier supplier of the preferable heap implementation
*/
public DijkstraClosestFirstIterator(
Graph graph, V source, Supplier>> heapSupplier)
{
this(graph, source, Double.POSITIVE_INFINITY, heapSupplier);
}
/**
* Creates a new radius-bounded iterator for the specified graph. Iteration will start at the
* specified start vertex and will be limited to the subset of the connected component which
* includes that vertex and is reachable via paths of weighted length less than or equal to the
* specified radius. This iterator will use the heap supplied by {@code heapSupplier}
*
* @param graph the graph
* @param source the source vertex
* @param radius limit on weighted path length, or Double.POSITIVE_INFINITY for unbounded search
* @param heapSupplier supplier of the preferable heap implementation
*/
public DijkstraClosestFirstIterator(
Graph graph, V source, double radius,
Supplier>> heapSupplier)
{
this.graph = Objects.requireNonNull(graph, "Graph cannot be null");
this.source = Objects.requireNonNull(source, "Source vertex cannot be null");
Objects.requireNonNull(heapSupplier, "Heap supplier cannot be null");
if (radius < 0.0) {
throw new IllegalArgumentException("Radius must be non-negative");
}
this.radius = radius;
this.seen = new HashMap<>();
this.heap = heapSupplier.get();
// initialize with source vertex
updateDistance(source, null, 0d);
}
/**
* {@inheritDoc}
*/
@Override
public boolean hasNext()
{
if (heap.isEmpty()) {
return false;
}
AddressableHeap.Handle> vNode = heap.findMin();
double vDistance = vNode.getKey();
if (radius < vDistance) {
heap.clear();
return false;
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
public V next()
{
if (!hasNext()) {
throw new NoSuchElementException();
}
// settle next node
AddressableHeap.Handle> vNode = heap.deleteMin();
V v = vNode.getValue().getFirst();
double vDistance = vNode.getKey();
// relax edges
for (E e : graph.outgoingEdgesOf(v)) {
V u = Graphs.getOppositeVertex(graph, e, v);
double eWeight = graph.getEdgeWeight(e);
if (eWeight < 0.0) {
throw new IllegalArgumentException("Negative edge weight not allowed");
}
updateDistance(u, e, vDistance + eWeight);
}
return v;
}
/**
* Return the paths computed by this iterator. Only the paths to vertices which are already
* returned by the iterator will be shortest paths. Additional paths to vertices which are not
* yet returned (settled) by the iterator might be included with the following properties: the
* distance will be an upper bound on the actual shortest path and the distance will be inside
* the radius of the search.
*
* @return the single source paths
*/
public SingleSourcePaths getPaths()
{
return new TreeSingleSourcePathsImpl<>(graph, source, getDistanceAndPredecessorMap());
}
/**
* Return all paths using the traditional representation of the shortest path tree, which stores
* for each vertex (a) the distance of the path from the source vertex and (b) the last edge
* used to reach the vertex from the source vertex.
*
* Only the paths to vertices which are already returned by the iterator will be shortest paths.
* Additional paths to vertices which are not yet returned (settled) by the iterator might be
* included with the following properties: the distance will be an upper bound on the actual
* shortest path and the distance will be inside the radius of the search.
*
* @return a distance and predecessor map
*/
public Map> getDistanceAndPredecessorMap()
{
Map> distanceAndPredecessorMap = new HashMap<>();
for (AddressableHeap.Handle> vNode : seen.values()) {
double vDistance = vNode.getKey();
if (radius < vDistance) {
continue;
}
V v = vNode.getValue().getFirst();
distanceAndPredecessorMap.put(v, Pair.of(vDistance, vNode.getValue().getSecond()));
}
return distanceAndPredecessorMap;
}
private void updateDistance(V v, E e, double distance)
{
AddressableHeap.Handle> node = seen.get(v);
if (node == null) {
node = heap.insert(distance, Pair.of(v, e));
seen.put(v, node);
} else if (distance < node.getKey()) {
node.decreaseKey(distance);
node.setValue(Pair.of(node.getValue().getFirst(), e));
}
}
}