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

org.jgrapht.traverse.ClosestFirstIterator Maven / Gradle / Ivy

/*
 * (C) Copyright 2003-2021, by John V Sichi 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.traverse;

import org.jgrapht.*;
import org.jheaps.*;
import org.jheaps.tree.*;

import java.util.*;
import java.util.function.*;

/**
 * A closest-first iterator for a directed or undirected graph. 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. A * custom heap implementation can be specified during the construction time. Pairing heap is used by * default. *

* * @param the graph vertex type * @param the graph edge type * @author John V. Sichi */ public class ClosestFirstIterator extends CrossComponentIterator>> { /** * Priority queue of fringe vertices. */ private AddressableHeap> heap; /** * Maximum distance to search. */ private double radius = Double.POSITIVE_INFINITY; private boolean initialized = false; /** * Creates a new closest-first 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. If the specified start vertex is null, iteration will start at an * arbitrary vertex and will not be limited, that is, will be able to traverse all the graph. * * @param g the graph to be iterated. * @param startVertex the vertex iteration to be started. */ public ClosestFirstIterator(Graph g, V startVertex) { this(g, startVertex, Double.POSITIVE_INFINITY); } /** * Creates a new closest-first iterator for the specified graph. Iteration will start at the * specified start vertices and will be limited to the subset of the graph reachable from those * vertices. Iteration order is based on minimum distance from any of the start vertices, * regardless of the order in which the start vertices are supplied. Because of this, the entire * traversal is treated as if it were over a single connected component with respect to events * fired. * * @param g the graph to be iterated. * @param startVertices the vertices iteration to be started. */ public ClosestFirstIterator(Graph g, Iterable startVertices) { this(g, startVertices, Double.POSITIVE_INFINITY); } /** * Creates a new radius-bounded closest-first 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. The specified start vertex may not be * null. * * @param g the graph to be iterated. * @param startVertex the vertex iteration to be started. * @param radius limit on weighted path length, or Double.POSITIVE_INFINITY for unbounded * search. */ public ClosestFirstIterator(Graph g, V startVertex, double radius) { this( g, startVertex == null ? null : Collections.singletonList(startVertex), radius, PairingHeap::new); } /** * Creates a new radius-bounded closest-first 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. The specified start vertex may not be * null. This algorithm will use the heap supplied by the {@code heapSupplier}. * * @param g the graph to be iterated. * @param startVertex the vertex iteration to be started. * @param radius limit on weighted path length, or Double.POSITIVE_INFINITY for unbounded * search. * @param heapSupplier supplier of the preferable heap implementation */ public ClosestFirstIterator( Graph g, V startVertex, double radius, Supplier>> heapSupplier) { this( g, startVertex == null ? null : Collections.singletonList(startVertex), radius, heapSupplier); } /** * Creates a new radius-bounded closest-first iterator for the specified graph. Iteration will * start at the specified start vertices and will be limited to the subset of the graph * reachable from those vertices via paths of weighted length less than or equal to the * specified radius. The specified collection of start vertices may not be null. * Iteration order is based on minimum distance from any of the start vertices, regardless of * the order in which the start vertices are supplied. Because of this, the entire traversal is * treated as if it were over a single connected component with respect to events fired. * * @param g the graph to be iterated. * @param startVertices the vertices iteration to be started. * @param radius limit on weighted path length, or Double.POSITIVE_INFINITY for unbounded * search. */ public ClosestFirstIterator(Graph g, Iterable startVertices, double radius) { this(g, startVertices, radius, PairingHeap::new); } /** * Creates a new radius-bounded closest-first iterator for the specified graph. Iteration will * start at the specified start vertices and will be limited to the subset of the graph * reachable from those vertices via paths of weighted length less than or equal to the * specified radius. The specified collection of start vertices may not be null. * Iteration order is based on minimum distance from any of the start vertices, regardless of * the order in which the start vertices are supplied. Because of this, the entire traversal is * treated as if it were over a single connected component with respect to events fired. This * algorithm will use the heap supplied by the {@code heapSupplier}. * * @param g the graph to be iterated. * @param startVertices the vertices iteration to be started. * @param radius limit on weighted path length, or Double.POSITIVE_INFINITY for unbounded * search. * @param heapSupplier supplier of the preferable heap implementation */ public ClosestFirstIterator( Graph g, Iterable startVertices, double radius, Supplier>> heapSupplier) { super(g, startVertices); this.radius = radius; Objects.requireNonNull(heapSupplier, "Heap supplier cannot be null"); this.heap = heapSupplier.get(); checkRadiusTraversal(isCrossComponentTraversal()); initialized = true; if (!crossComponentTraversal) { // prime the heap by processing the first start vertex hasNext(); Iterator iter = startVertices.iterator(); if (iter.hasNext()) { // discard the first since we already primed it above iter.next(); // prime the heap with the rest of the start vertices so that // we can process them all simultaneously while (iter.hasNext()) { V v = iter.next(); encounterVertex(v, null); } } } } // override AbstractGraphIterator @Override public void setCrossComponentTraversal(boolean crossComponentTraversal) { if (initialized) { checkRadiusTraversal(crossComponentTraversal); } super.setCrossComponentTraversal(crossComponentTraversal); } /** * Get the weighted length of the shortest path known to the given vertex. If the vertex has * already been visited, then it is truly the shortest path length; otherwise, it is the best * known upper bound. * * @param vertex vertex being sought from start vertex * @return weighted length of shortest path known, or Double.POSITIVE_INFINITY if no path found * yet */ public double getShortestPathLength(V vertex) { AddressableHeap.Handle> node = getSeenData(vertex); if (node == null) { return Double.POSITIVE_INFINITY; } return node.getKey(); } /** * Get the spanning tree edge reaching a vertex which has been seen already in this traversal. * This edge is the last link in the shortest known path between the start vertex and the * requested vertex. If the vertex has already been visited, then it is truly the minimum * spanning tree edge; otherwise, it is the best candidate seen so far. * * @param vertex the spanned vertex. * @return the spanning tree edge, or null if the vertex either has not been seen yet or is a * start vertex. */ public E getSpanningTreeEdge(V vertex) { AddressableHeap.Handle> node = getSeenData(vertex); if (node == null) { return null; } return node.getValue().spanningTreeEdge; } /** * @see CrossComponentIterator#isConnectedComponentExhausted() */ @Override protected boolean isConnectedComponentExhausted() { if (heap.size() == 0) { return true; } else { if (heap.findMin().getKey() > radius) { heap.clear(); return true; } else { return false; } } } /** * @see CrossComponentIterator#encounterVertex(Object, Object) */ @Override protected void encounterVertex(V vertex, E edge) { double shortestPathLength; if (edge == null) { shortestPathLength = 0; } else { shortestPathLength = calculatePathLength(vertex, edge); } AddressableHeap.Handle> handle = heap.insert(shortestPathLength, new QueueEntry<>(vertex, edge)); putSeenData(vertex, handle); } /** * Override superclass. When we see a vertex again, we need to see if the new edge provides a * shorter path than the old edge. * * @param vertex the vertex re-encountered * @param edge the edge via which the vertex was re-encountered */ @Override protected void encounterVertexAgain(V vertex, E edge) { AddressableHeap.Handle> node = getSeenData(vertex); if (node.getValue().frozen) { // no improvement for this vertex possible return; } double candidatePathLength = calculatePathLength(vertex, edge); if (candidatePathLength < node.getKey()) { node.getValue().spanningTreeEdge = edge; node.decreaseKey(candidatePathLength); } } /** * @see CrossComponentIterator#provideNextVertex() */ @Override protected V provideNextVertex() { AddressableHeap.Handle> node = heap.deleteMin(); node.getValue().frozen = true; return node.getValue().vertex; } private void assertNonNegativeEdge(E edge) { if (getGraph().getEdgeWeight(edge) < 0) { throw new IllegalArgumentException("negative edge weights not allowed"); } } /** * Determine weighted path length to a vertex via an edge, using the path length for the * opposite vertex. * * @param vertex the vertex for which to calculate the path length. * @param edge the edge via which the path is being extended. * @return calculated path length. */ private double calculatePathLength(V vertex, E edge) { assertNonNegativeEdge(edge); V otherVertex = Graphs.getOppositeVertex(getGraph(), edge, vertex); AddressableHeap.Handle> otherEntry = getSeenData(otherVertex); return otherEntry.getKey() + getGraph().getEdgeWeight(edge); } private void checkRadiusTraversal(boolean crossComponentTraversal) { if (crossComponentTraversal && (radius != Double.POSITIVE_INFINITY)) { throw new IllegalArgumentException( "radius may not be specified for cross-component traversal"); } } /** * Private data to associate with each entry in the priority queue. */ static class QueueEntry { /** * The vertex reached. */ V vertex; /** * Best spanning tree edge to vertex seen so far. */ E spanningTreeEdge; /** * True once spanningTreeEdge is guaranteed to be the true minimum. */ boolean frozen; QueueEntry(V vertex, E spanningTreeEdge) { this.vertex = vertex; this.spanningTreeEdge = spanningTreeEdge; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy