com.salesforce.jgrapht.alg.AStarShortestPath Maven / Gradle / Ivy
Show all versions of AptSpringProcessor Show documentation
/*
* (C) Copyright 2015-2017, by Joris Kinable, Jon Robison, Thomas Breitbart 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 com.salesforce.jgrapht.alg;
import java.util.*;
import com.salesforce.jgrapht.*;
import com.salesforce.jgrapht.alg.interfaces.*;
import com.salesforce.jgrapht.graph.*;
import com.salesforce.jgrapht.util.*;
/**
* An implementation of A* shortest path
* algorithm. A* shortest path algorithm
* german Wiki . This class works for Directed and Undirected graphs, as well as Multi-Graphs
* and Mixed-Graphs. It's ok if the graph changes in between invocations of the
* {@link #getShortestPath(Object, Object, AStarAdmissibleHeuristic)} getShortestPath} method; no
* new instance of this class has to be created. The heuristic is implemented using a FibonacciHeap
* data structure to maintain the set of open nodes. However, there still exist several approaches
* in literature to improve the performance of this heuristic which one could consider to implement.
* Another issue to take into consideration is the following: given two candidate nodes, i, j to
* expand, where f(i)=f(j), g(i)>g(j), h(i)<g(j), f(i)=g(i)+h(i), g(i) is the actual distance
* from the source node to i, h(i) is the estimated distance from i to the target node. Usually a
* depth-first search is desired, so ideally we would expand node i first. Using the FibonacciHeap,
* this is not necessarily the case though. This could be improved in a later version.
*
*
* Note: This implementation works with both consistent and inconsistent admissible heuristics. For
* details on consistency, refer to the description of the method
* {@link #isConsistentHeuristic(AStarAdmissibleHeuristic)}. However, this class is not
* optimized for inconsistent heuristics. Several opportunities to improve both worst case and
* average runtime complexities for A* with inconsistent heuristics described in literature can be
* used to improve this implementation!
*
* @param the graph vertex type
* @param the graph edge type
*
* @author Joris Kinable
* @author Jon Robison
* @author Thomas Breitbart
* @since Aug, 2015
*
* @deprecated in favor of {@link com.salesforce.jgrapht.alg.shortestpath.AStarShortestPath}
*/
@Deprecated
public class AStarShortestPath
{
private final Graph graph;
// List of open nodes
protected FibonacciHeap openList;
protected Map> vertexToHeapNodeMap;
// List of closed nodes
protected Set closedList;
// Mapping of nodes to their g-scores (g(x)).
protected Map gScoreMap;
// Predecessor map: mapping of a node to an edge that leads to its
// predecessor on its shortest path towards the targetVertex
protected Map cameFrom;
// Reference to the admissible heuristic
protected AStarAdmissibleHeuristic admissibleHeuristic;
// Counter which keeps track of the number of expanded nodes
protected int numberOfExpandedNodes;
/**
* Create a new instance of the A* shortest path algorithm.
*
* @param graph the input graph
*/
public AStarShortestPath(Graph graph)
{
if (graph == null) {
throw new IllegalArgumentException("Graph cannot be null!");
}
this.graph = graph;
}
/**
* Initializes the data structures
*
* @param admissibleHeuristic admissible heuristic
*/
private void initialize(AStarAdmissibleHeuristic admissibleHeuristic)
{
this.admissibleHeuristic = admissibleHeuristic;
openList = new FibonacciHeap<>();
vertexToHeapNodeMap = new HashMap<>();
closedList = new HashSet<>();
gScoreMap = new HashMap<>();
cameFrom = new HashMap<>();
numberOfExpandedNodes = 0;
}
/**
* Calculates (and returns) the shortest path from the sourceVertex to the targetVertex. Note:
* each time you invoke this method, the path gets recomputed.
*
* @param sourceVertex source vertex
* @param targetVertex target vertex
* @param admissibleHeuristic admissible heuristic which estimates the distance from a node to
* the target node.
*
* @return the shortest path from sourceVertex to targetVertex
*/
public GraphPath getShortestPath(
V sourceVertex, V targetVertex, AStarAdmissibleHeuristic admissibleHeuristic)
{
if (!graph.containsVertex(sourceVertex) || !graph.containsVertex(targetVertex)) {
throw new IllegalArgumentException(
"Source or target vertex not contained in the graph!");
}
this.initialize(admissibleHeuristic);
gScoreMap.put(sourceVertex, 0.0);
FibonacciHeapNode heapNode = new FibonacciHeapNode<>(sourceVertex);
openList.insert(heapNode, 0.0);
vertexToHeapNodeMap.put(sourceVertex, heapNode);
do {
FibonacciHeapNode currentNode = openList.removeMin();
// Check whether we reached the target vertex
if (currentNode.getData().equals(targetVertex)) {
// Build the path
return this.buildGraphPath(sourceVertex, targetVertex, currentNode.getKey());
}
// We haven't reached the target vertex yet; expand the node
expandNode(currentNode, targetVertex);
closedList.add(currentNode.getData());
} while (!openList.isEmpty());
// No path exists from sourceVertex to TargetVertex
return null;
}
private void expandNode(FibonacciHeapNode currentNode, V endVertex)
{
numberOfExpandedNodes++;
Set outgoingEdges = null;
if (graph instanceof UndirectedGraph) {
outgoingEdges = graph.edgesOf(currentNode.getData());
} else if (graph instanceof DirectedGraph) {
outgoingEdges = ((DirectedGraph) graph).outgoingEdgesOf(currentNode.getData());
}
for (E edge : outgoingEdges) {
V successor = Graphs.getOppositeVertex(graph, edge, currentNode.getData());
if (successor == currentNode.getData()) // Ignore self-loop
continue;
double gScore_current = gScoreMap.get(currentNode.getData());
double tentativeGScore = gScore_current + graph.getEdgeWeight(edge);
double fScore =
tentativeGScore + admissibleHeuristic.getCostEstimate(successor, endVertex);
if (vertexToHeapNodeMap.containsKey(successor)) { // We re-encountered a vertex. It's
// either in the open or closed list.
if (tentativeGScore >= gScoreMap.get(successor)) // Ignore path since it is
// non-improving
continue;
cameFrom.put(successor, edge);
gScoreMap.put(successor, tentativeGScore);
if (closedList.contains(successor)) { // it's in the closed list. Move node back to
// open list, since we discovered a shorter
// path to this node
closedList.remove(successor);
openList.insert(vertexToHeapNodeMap.get(successor), fScore);
} else { // It's in the open list
openList.decreaseKey(vertexToHeapNodeMap.get(successor), fScore);
}
} else { // We've encountered a new vertex.
cameFrom.put(successor, edge);
gScoreMap.put(successor, tentativeGScore);
FibonacciHeapNode heapNode = new FibonacciHeapNode<>(successor);
openList.insert(heapNode, fScore);
vertexToHeapNodeMap.put(successor, heapNode);
}
}
}
/**
* Builds the graph path
*
* @param startVertex starting vertex of the path
* @param targetVertex ending vertex of the path
* @param pathLength length of the path
*
* @return the shortest path from startVertex to endVertex
*/
private GraphPath buildGraphPath(V startVertex, V targetVertex, double pathLength)
{
List edgeList = new ArrayList<>();
List vertexList = new ArrayList<>();
vertexList.add(targetVertex);
V v = targetVertex;
while (v != startVertex) {
edgeList.add(cameFrom.get(v));
v = Graphs.getOppositeVertex(graph, cameFrom.get(v), v);
vertexList.add(v);
}
Collections.reverse(edgeList);
Collections.reverse(vertexList);
return new GraphWalk<>(graph, startVertex, targetVertex, vertexList, edgeList, pathLength);
}
/**
* Returns how many nodes have been expanded in the A* search procedure in its last invocation.
* A node is expanded if it is removed from the open list.
*
* @return number of expanded nodes
*/
public int getNumberOfExpandedNodes()
{
return numberOfExpandedNodes;
}
/**
* Returns true if the provided heuristic is a consistent or monotone heuristic
* wrt the graph provided at construction time. A heuristic is monotonic if its estimate is
* always less than or equal to the estimated distance from any neighboring vertex to the goal,
* plus the step cost of reaching that neighbor. For details, refer to https://en.wikipedia.org/wiki/Consistent_heuristic.
* In short, a heuristic is consistent iff h(u)≤ d(u,v)+h(v)
, for every edge
* (u,v), where d(u,v) is the weight of edge (u,v) and h(u) is the estimated cost to reach the
* target node from vertex u. Most natural admissible heuristics, such as Manhattan or Euclidian
* distance, are consistent heuristics.
*
* @param admissibleHeuristic admissible heuristic
* @return true is the heuristic is consistent
*/
public boolean isConsistentHeuristic(AStarAdmissibleHeuristic admissibleHeuristic)
{
for (V targetVertex : graph.vertexSet()) {
for (E e : graph.edgeSet()) {
double weight = graph.getEdgeWeight(e);
V edgeSource = graph.getEdgeSource(e);
V edgeTarget = graph.getEdgeTarget(e);
double h_x = admissibleHeuristic.getCostEstimate(edgeSource, targetVertex);
double h_y = admissibleHeuristic.getCostEstimate(edgeTarget, targetVertex);
if (h_x > weight + h_y)
return false;
}
}
return true;
}
}
// End AStarShortestPath.java