A* Heuristic Search
Implements a heuristic search on a weighted, directed or undirected graph for the case where all edge weights are non-negative.
Complexity: O((E + V) log V)
Defined in: <boost/graph/astar_search.hpp>
Example
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/astar_search.hpp>
#include <iostream>
#include <vector>
#include <limits>
struct Edge { int weight; };
using namespace boost;
using Graph = adjacency_list<vecS, vecS, directedS, no_property, Edge>;
using Vertex = graph_traits<Graph>::vertex_descriptor;
struct zero_heuristic : public astar_heuristic<Graph, int> {
int operator()(Vertex) const { return 0; }
};
// Stop when target is found
struct found_target { Vertex v; };
struct target_visitor : public default_astar_visitor {
target_visitor(Vertex t) : m_target{t} {}
void examine_vertex(Vertex u, const Graph&) const {
if (u == m_target) { throw found_target{u}; }
}
Vertex m_target;
};
int main() {
Graph g{4};
add_edge(0, 1, Edge{2}, g);
add_edge(1, 2, Edge{3}, g);
add_edge(0, 2, Edge{8}, g);
add_edge(2, 3, Edge{1}, g);
using CostType = int;
using IndexMap = decltype(get(vertex_index, std::declval<Graph&>()));
auto n = num_vertices(g);
std::vector<CostType> dist(n, (std::numeric_limits<CostType>::max)());
std::vector<CostType> cost(n, (std::numeric_limits<CostType>::max)());
std::vector<Vertex> pred(n);
auto index_map = get(vertex_index, g);
auto dist_map = make_iterator_property_map(dist.begin(), index_map);
auto cost_map = make_iterator_property_map(cost.begin(), index_map);
auto pred_map = make_iterator_property_map(pred.begin(), index_map);
auto weight_map = get(&Edge::weight, g);
two_bit_color_map<IndexMap> color_map{n, index_map};
CostType inf = (std::numeric_limits<CostType>::max)();
CostType zero_val = CostType{};
try {
astar_search(g, vertex(0, g), zero_heuristic(),
target_visitor{3},
pred_map, cost_map, dist_map, weight_map,
index_map, color_map,
std::less<CostType>{}, closed_plus<CostType>{inf},
inf, zero_val);
} catch (const found_target&) {}
for (auto v : make_iterator_range(vertices(g))) {
std::cout << "distance to " << v << " = " << dist[v] << "\n";
}
}
distance to 0 = 0
distance to 1 = 2
distance to 2 = 5
distance to 3 = 6
(1) Positional version
template <typename VertexListGraph, typename AStarHeuristic,
typename AStarVisitor, typename PredecessorMap,
typename CostMap, typename DistanceMap,
typename WeightMap, typename VertexIndexMap,
typename ColorMap,
typename CompareFunction, typename CombineFunction,
typename CostInf, typename CostZero>
inline void astar_search(
const VertexListGraph& g,
typename graph_traits<VertexListGraph>::vertex_descriptor s,
AStarHeuristic h, AStarVisitor vis,
PredecessorMap predecessor, CostMap cost,
DistanceMap distance, WeightMap weight,
VertexIndexMap index_map, ColorMap color,
CompareFunction compare, CombineFunction combine,
CostInf inf, CostZero zero);
| Direction | Parameter | Description |
|---|---|---|
IN |
|
The graph object on which the algorithm will be applied.
The type |
IN |
|
The start vertex for the search. All distances will be calculated from this vertex, and the shortest paths tree (recorded in the predecessor map) will be rooted at this vertex. |
IN |
|
The heuristic function that guides the search. The type |
IN |
|
Use this to specify actions that you would like to happen during certain
event points within the algorithm. The type |
OUT |
|
The predecessor map records the edges in the minimum spanning tree. Upon
completion of the algorithm, the edges (p[u],u) for all u in V
are in the minimum spanning tree. If p[u] = u then u is either the
start vertex or a vertex that is not reachable from the start. The
|
UTIL/OUT |
|
The f-value for each vertex. The f-value is defined as the sum of
the cost to get to a vertex from the start vertex, and the estimated
cost (as returned by the heuristic function |
UTIL/OUT |
|
The shortest path weight from the start vertex |
IN |
|
The weight or "length" of each edge in the graph. The weights must all
be non-negative; the algorithm will throw a
|
IN |
|
This maps each vertex to an integer in the range |
UTIL/OUT |
|
This is used during the execution of the algorithm to mark the vertices,
indicating whether they are on the OPEN or CLOSED lists. The vertices
start out white and become gray when they are inserted into the OPEN
list. They then turn black when they are examined and placed on the
CLOSED list. At the end of the algorithm, vertices reachable from the
source vertex will have been colored black. All other vertices will
still be white. The type |
IN |
|
This function is used to compare distances to determine which vertex
is closer to the start vertex, and to compare f-values to determine
which vertex on the OPEN list to examine next. The |
IN |
|
This function is used to combine distances to compute the distance of a
path, and to combine distance and heuristic values to compute the
f-value of a vertex. The |
IN |
|
The |
IN |
|
The |
(2) Positional tree version
template <typename VertexListGraph, typename AStarHeuristic,
typename AStarVisitor, typename PredecessorMap,
typename CostMap, typename DistanceMap,
typename WeightMap,
typename CompareFunction, typename CombineFunction,
typename CostInf, typename CostZero>
inline void astar_search_tree(
const VertexListGraph& g,
typename graph_traits<VertexListGraph>::vertex_descriptor s,
AStarHeuristic h, AStarVisitor vis,
PredecessorMap predecessor, CostMap cost,
DistanceMap distance, WeightMap weight,
CompareFunction compare, CombineFunction combine,
CostInf inf, CostZero zero);
Same parameters as (1), but without VertexIndexMap and ColorMap. For the tree versions of the algorithm, all vertices are assumed to always be white, leading to checking for repeated vertices being done using the distance map. If a dummy value is used for the distance map and the graph contains cycles, the algorithm will probably enter an infinite loop.
(3) Positional no-init version
template <typename IncidenceGraph, typename AStarHeuristic,
typename AStarVisitor, typename PredecessorMap,
typename CostMap, typename DistanceMap,
typename WeightMap, typename ColorMap,
typename VertexIndexMap,
typename CompareFunction, typename CombineFunction,
typename CostInf, typename CostZero>
inline void astar_search_no_init(
const IncidenceGraph& g,
typename graph_traits<IncidenceGraph>::vertex_descriptor s,
AStarHeuristic h, AStarVisitor vis,
PredecessorMap predecessor, CostMap cost,
DistanceMap distance, WeightMap weight,
ColorMap color, VertexIndexMap index_map,
CompareFunction compare, CombineFunction combine,
CostInf inf, CostZero zero);
Same parameters as (1), but does not initialize the property maps. Use this for implicit graphs. The type IncidenceGraph must be a model of the Incidence Graph concept.
The index_map and color parameters are swapped in astar_search_no_init() relative to astar_search(); the named parameter interfaces are not affected.
|
(4) Positional no-init tree version
template <typename IncidenceGraph, typename AStarHeuristic,
typename AStarVisitor, typename PredecessorMap,
typename CostMap, typename DistanceMap,
typename WeightMap,
typename CompareFunction, typename CombineFunction,
typename CostInf, typename CostZero>
inline void astar_search_no_init_tree(
const IncidenceGraph& g,
typename graph_traits<IncidenceGraph>::vertex_descriptor s,
AStarHeuristic h, AStarVisitor vis,
PredecessorMap predecessor, CostMap cost,
DistanceMap distance, WeightMap weight,
CompareFunction compare, CombineFunction combine,
CostInf inf, CostZero zero);
Same parameters as (3), but without ColorMap and VertexIndexMap. Combines the no-init and tree behaviors.
(5) Named parameter version
template <typename VertexListGraph,
typename AStarHeuristic,
typename P, typename T, typename R>
void astar_search(
const VertexListGraph& g,
typename graph_traits<VertexListGraph>::vertex_descriptor s,
AStarHeuristic h,
const bgl_named_params<P, T, R>& params);
| Direction | Parameter | Description |
|---|---|---|
IN |
|
The graph object. Must model Vertex List Graph and Incidence Graph. |
IN |
|
The start vertex for the search. All distances will be calculated from this vertex, and the shortest paths tree will be rooted at this vertex. |
IN |
|
The heuristic function that guides the search. The type |
IN |
|
Named parameters passed via |
| Direction | Named Parameter | Description / Default |
|---|---|---|
IN |
|
Edge weights (see (1) |
IN |
|
Vertex-to-integer mapping (see (1) |
OUT |
|
Predecessor map (see (1) |
UTIL/OUT |
|
Distance map (see (1) |
UTIL/OUT |
|
The f-value for each vertex (see (1) |
UTIL/OUT |
|
Color map (see (1) |
IN |
|
Distance comparison function (see (1) |
IN |
|
Distance combination function (see (1) |
IN |
|
Infinity value (see (1) |
IN |
|
Identity element (see (1) |
OUT |
|
Visitor (see (1) |
(6) Named parameter no-init version
template <typename VertexListGraph,
typename AStarHeuristic,
typename P, typename T, typename R>
void astar_search_no_init(
const IncidenceGraph& g,
typename graph_traits<VertexListGraph>::vertex_descriptor s,
AStarHeuristic h,
const bgl_named_params<P, T, R>& params);
Same named parameters as (5). Does not initialize property maps. Use this for implicit graphs. The type IncidenceGraph must be a model of the Incidence Graph concept.
(7) Named parameter tree version
template <typename VertexListGraph,
typename AStarHeuristic,
typename P, typename T, typename R>
void astar_search_tree(
const VertexListGraph& g,
typename graph_traits<VertexListGraph>::vertex_descriptor s,
AStarHeuristic h,
const bgl_named_params<P, T, R>& params);
Same named parameters as (5). Uses the tree formulation (no color map).
(8) Named parameter no-init tree version
template <typename VertexListGraph,
typename AStarHeuristic,
typename P, typename T, typename R>
void astar_search_no_init_tree(
const IncidenceGraph& g,
typename graph_traits<VertexListGraph>::vertex_descriptor s,
AStarHeuristic h,
const bgl_named_params<P, T, R>& params);
Same named parameters as (5). Combines the no-init and tree behaviors.
Description
The A* algorithm
is a heuristic graph search algorithm: an A* search is "guided" by a heuristic function. A heuristic function h(v) is one which estimates the cost from a non-goal state (v) in the graph to some goal state, g. Intuitively, A* follows paths (through the graph) to the goal that are estimated by the heuristic function to be the best paths. Unlike best-first search, A* takes into account the known cost from the start of the search to v; the paths A* takes are guided by a function f(v) = g(v) + h(v), where h(v) is the heuristic function, and g(v) (sometimes denoted c(s, v)) is the known cost from the start to v. Clearly, the efficiency of A* is highly dependent on the heuristic function with which it is used.
The A* algorithm is very similar to Dijkstra’s Shortest Paths algorithm. This implementation finds all the shortest paths from the start vertex to every other vertex by creating a search tree, examining vertices according to their remaining cost to some goal, as estimated by a heuristic function. Most commonly, A* is used to find some specific goal vertex or vertices in a graph, after which the search is terminated.
A* is particularly useful for searching implicit graphs. Implicit graphs are graphs that are not completely known at the beginning of the search. Upon visiting a vertex, its neighbors are "generated" and added to the search. Implicit graphs are particularly useful for searching large state spaces — in game-playing scenarios (e.g. chess), for example — in which it may not be possible to store the entire graph. Implicit searches can be performed with this implementation of A* by creating special visitors that generate neighbors of newly-expanded vertices. Please note that astar_search_no_init() or astar_search_no_init_tree() must be used for implicit graphs; the basic astar_search() function requires a graph that models the Vertex List Graph concept. Both versions also require the graph type to model the Incidence Graph concept.
For the non-tree versions of the algorithm, this implementation of A* is based on an OPEN/CLOSED list formulation. Vertices on the OPEN list have been "discovered" by the algorithm, but not "expanded" (we have not discovered their adjacent vertices). Vertices on the CLOSED list have been completely examined by our search (we have expanded them and added their children to the OPEN list). Vertices that are on neither list have not been encountered in any context so far in our search. A major advantage of this formulation of the A* algorithm over other approaches is that it avoids "cycles" in the state space; the search will not become trapped by loops in the graph. The OPEN/CLOSED lists are implemented using BGL’s vertex coloring mechanisms. Vertices in OPEN are colored gray, vertices in CLOSED are colored black, and undiscovered vertices are colored white. For the versions of the algorithm whose names end in _tree, all vertices are assumed to always be white, leading to checking for repeated vertices being done using the distance map. If a dummy value is used for the distance map and the graph contains cycles, the algorithm will probably enter an infinite loop.
The criteria for expanding a vertex on the OPEN list is that it has the lowest f(v) = g(v) + h(v) value of all vertices on OPEN. Cost information about vertices is stored in a property map.
Pseudo-Code
The following is the pseudocode for the A* heuristic search algorithm. In the pseudocode, h is the heuristic function, w is the edge weight, d is the distance of a vertex from s, and Q is a priority queue, sorted by f, the estimated cost to the goal of the path through a vertex. p is a predecessor map. The visitor event points for the algorithm are indicated by the labels on the right.
|
|
Visitor Event Points
-
vis.initialize_vertex(u, g)is invoked on each vertex in the graph before the start of the algorithm. -
vis.discover_vertex(v, g)is invoked when a vertex is first discovered and is added to the OPEN list. -
vis.examine_vertex(u, g)is invoked when a vertex is popped from the queue (i.e., it has the lowest cost on the OPEN list). -
vis.examine_edge(e, g)is invoked on each out-edge of a vertex immediately after it is examined. -
vis.edge_relaxed(e, g)is invoked on edge (u,v) if d[u] + w(u,v) < d[v]. -
vis.edge_not_relaxed(e, g)is invoked if the edge is not relaxed (see above). -
vis.black_target(e, g)is invoked when a vertex that is on the CLOSED list is "rediscovered" via a more efficient path, and is re-added to the OPEN list. -
vis.finish_vertex(u, g)is invoked on a vertex when it is added to the CLOSED list, which happens after all of its out edges have been examined.
Example
See example/astar-cities.cpp for an example of using A* search.