BGL: A* search & implicit graph

Hi, Some weeks ago, I wrote on this mailing list, that I succeed achieving an A* search using an implicit graph. A Boost user emailed me to send him an example of my implementation. As the web is lacking of such example, I think the best place to send such example would be here as it might help others in the future. My example might not be the best, the most optimized or most well-coded. I am open to suggestions, corrections and comments. Let me know if something unclear. The code comes below. Few words to quickly explain the code: The goal is to A* search a graph (named m_SearchGraph) that grows (i.e. m_SearchGraph is implicit) based on another graph (named MyGraph). The search starts from 2D point (_startX, _startZ) and must get enough close (less than error "_epsilon") to 2D point (_goalX, _goalZ). The example is dumb and simply increment current position whatever the node in MyGraph (i.e. currX++, currZ++, currWeight += sqrt(2)). User need to adapt this dummy behavior depending on its needs (search for "//to change"). Most adaptations have to be done in "examine_vertex" (and also by providing ref/ptr to needed objects to the visitor constructor) PS: The implementation assumes both graphs are adjacency_list with "vecs" for both edges and vertices (in which a vertex/edge descriptor is equivalent to an "int", for my understanding) PS2: Id == descriptor (e.g. edgeId == edge_descriptor) ==================================================================================== ==================================================================================== astarSearch.h ==================================================================================== #ifndef ASTARSEARCH_H #define ASTARSEARCH_H #include <boost/graph/astar_search.hpp> #include "myGraph.h" class AStarSearch { public: AStarSearch(MyGraph * _myGraph); virtual ~AStarSearch(); bool findPath(MyGraph::VertexId _startMyGraphVertextId, double _startX, double _startZ, double _goalX, double _goalZ, double _epsilon); private: //search graph struct m_Vertex { m_Vertex() { currX = 0.0; currZ = 0.0; refMyGraphVertexId = -1; prevVertexId = -1; double inf = numeric_limits<double>::max BOOST_PREVENT_MACRO_SUBSTITUTION (); distance = inf; cost = inf; color = white_color; } //properties for heuristic / visitor double currX; double currZ; int refMyGraphVertexId; //reference to correspondant vertex in myGraph //properties required by astar_search_no_init(...) int prevVertexId; // == predecessor double distance; double cost; default_color_type color; }; struct m_Edge { m_Edge() { weight = 0.0; } double weight; }; //graph property = save the vertex id when goal is reached //saving goal vertex could be also saved by a reference to an int passed to the visitor class typedef property<graph_name_t, int> m_GraphProperty; typedef adjacency_list<vecS, vecS, directedS, m_Vertex, m_Edge, m_GraphProperty> m_SearchGraph; typedef graph_traits<m_SearchGraph>::vertex_descriptor m_VertexId; typedef graph_traits<m_SearchGraph>::edge_descriptor m_EdgeId; typedef graph_traits<m_SearchGraph>::vertex_iterator m_VertexIt; typedef graph_traits<m_SearchGraph>::adjacency_iterator m_AdjacencyIt; typedef graph_traits<m_SearchGraph>::edge_iterator m_EdgeIt; typedef graph_traits<m_SearchGraph>::out_edge_iterator m_OutEdgeIt; //heuristic class m_Heuristic : public astar_heuristic<m_SearchGraph, double> { public: m_Heuristic(m_SearchGraph & _searchGraph, double _goalX, double _goalZ); virtual ~m_Heuristic(); double operator()(m_VertexId _currVertexId); private: m_SearchGraph & m_searchGraph; double m_goalX; double m_goalZ; }; //exception to end search struct m_endSearch{}; //visitor class m_Visitor : public default_astar_visitor { public: m_Visitor(MyGraph * _myGraph, m_SearchGraph & _searchGraph, double _goalX, double _goalZ, double _epsilon); virtual ~m_Visitor(); void examine_vertex(m_VertexId _currVertexId, const m_SearchGraph & _graph); private: MyGraph * m_myGraph; m_SearchGraph & m_searchGraph; double m_goalX; double m_goalZ; double m_epsilon; }; void m_printSearchGraph(m_SearchGraph & _searchGraph); list<int> m_getPath(m_SearchGraph & _searchGraph, m_VertexId _vertexId); vector< list<int> > m_getAllPaths(m_SearchGraph & _searchGraph); void m_printPath(list<int> _path); void m_printShortestPath(m_SearchGraph & _searchGraph); void m_printAllPaths(m_SearchGraph & _searchGraph); MyGraph * m_myGraph; }; #endif ==================================================================================== ==================================================================================== ==================================================================================== aStarSearch.cpp ==================================================================================== ==================================================================================== #include "aStarSearch.h" AStarSearch::AStarSearch(MyGraph * _myGraph): m_myGraph(_myGraph) { } AStarSearch::~AStarSearch() { } bool AStarSearch::findPath(MyGraph::VertexId _startMyGraphVertextId, double _startX, double _startZ, double _goalX, double _goalZ, double _epsilon) { m_SearchGraph searchGraph; m_Heuristic heuristic(searchGraph, _goalX, _goalZ); m_Visitor visitor(m_myGraph, searchGraph, _goalX, _goalZ, _epsilon); //typedef property maps typedef property_map<m_SearchGraph, vertex_index_t>::type IndexMap; typedef property_map<m_SearchGraph, double m_Edge::*>::type WeightMap; typedef property_map<m_SearchGraph, int m_Vertex::*>::type PredecessorMap; typedef property_map<m_SearchGraph, double m_Vertex::*>::type DistanceMap; typedef property_map<m_SearchGraph, double m_Vertex::*>::type CostMap; typedef property_map<m_SearchGraph, default_color_type m_Vertex::*>::type ColorMap; //create property maps IndexMap indexMap = get(vertex_index, searchGraph); WeightMap weightMap = get(&m_Edge::weight, searchGraph); PredecessorMap predecessorMap = get(&m_Vertex::prevVertexId, searchGraph); DistanceMap distanceMap = get(&m_Vertex::distance, searchGraph); CostMap costMap = get(&m_Vertex::cost, searchGraph); ColorMap colorMap = get(&m_Vertex::color, searchGraph); double inf = numeric_limits<double>::max BOOST_PREVENT_MACRO_SUBSTITUTION (); double zero = double(); //add & init first vertex m_VertexId startVertexId = add_vertex(searchGraph); m_Vertex & startVertex = searchGraph[startVertexId]; startVertex.currX = _startX; startVertex.currZ = _startZ; startVertex.refMyGraphVertexId = _startMyGraphVertextId; startVertex.prevVertexId = startVertexId; startVertex.distance = zero; startVertex.cost = heuristic(startVertexId); try { astar_search_no_init(searchGraph, startVertexId, heuristic, visitor, predecessorMap, costMap, distanceMap, weightMap, colorMap, indexMap, std::less<double>(), std::plus<double>(), inf, zero); } catch(m_endSearch end) { m_printSearchGraph(searchGraph); m_printShortestPath(searchGraph); m_printAllPaths(searchGraph); return true; } return false; } AStarSearch::m_Heuristic::m_Heuristic(m_SearchGraph & _searchGraph, double _goalX, double _goalZ): m_searchGraph(_searchGraph), m_goalX(_goalX), m_goalZ(_goalZ) { } AStarSearch::m_Heuristic::~m_Heuristic() { } double AStarSearch::m_Heuristic::operator()(m_VertexId _currVertexId) { m_Vertex currVertex = m_searchGraph[_currVertexId]; return sqrt(pow(m_goalX - currVertex.currX, 2) + pow(m_goalZ - currVertex.currZ, 2)); //for A* search //return 0; //for BFS search } AStarSearch::m_Visitor::m_Visitor(MyGraph * _myGraph, m_SearchGraph & _searchGraph, double _goalX, double _goalZ, double _epsilon): m_myGraph(_myGraph), m_searchGraph(_searchGraph), m_goalX(_goalX), m_goalZ(_goalZ), m_epsilon(_epsilon) { } AStarSearch::m_Visitor::~m_Visitor() { } void AStarSearch::m_Visitor::examine_vertex(m_VertexId _currVertexId, const m_SearchGraph & _graph) { //get currVertex / currX / currZ m_Vertex currVertex = m_searchGraph[_currVertexId]; double currX = currVertex.currX; double currZ = currVertex.currZ; //check if goal is reached if(sqrt(pow(m_goalX - currX, 2) + pow(m_goalZ - currZ, 2)) < m_epsilon) { set_property(m_searchGraph, graph_name, _currVertexId); //_currVertexId == goal vertex id throw m_endSearch(); } //declare currMyGraphVertexId / nextMyGraphVertexId / currMyGraphEdgeId / currMyGraphEdge MyGraph::VertexId currMyGraphVertexId = currVertex.refMyGraphVertexId; MyGraph::VertexId nextMyGraphVertexId; MyGraph::EdgeId currMyGraphEdgeId; bool currMyGraphEdgeExists; MyGraph::Edge currMyGraphEdge; //declare newVertexId / newEdgeId / newEdge m_VertexId newVertexId; m_EdgeId newEdgeId; bool newEdgeCreated; //generate neighbors to currVertex MyGraph::OutEdgeIt outMyGraph_i, outMyGraph_end; for(tie(outMyGraph_i, outMyGraph_end) = out_edges(currMyGraphVertexId, *m_myGraph); outMyGraph_i != outMyGraph_end ; ++outMyGraph_i) { //get nexMyGraphVertexId / currMyGraphEdgeId / currMyGraphEdge nextMyGraphVertexId = target(*outMyGraph_i, *m_myGraph); tie(currMyGraphEdgeId, currMyGraphEdgeExists) = edge(currMyGraphVertexId, nextMyGraphVertexId, *m_myGraph); if(currMyGraphEdgeExists) currMyGraphEdge = (*m_myGraph)[currMyGraphEdgeId]; else exit(1); //add/set newVertex newVertexId = add_vertex(m_searchGraph); m_Vertex & newVertex = m_searchGraph[newVertexId]; newVertex.currX = currX + 1; //to change newVertex.currZ = currZ + 1; //to change newVertex.refMyGraphVertexId = nextMyGraphVertexId; newVertex.prevVertexId = newVertexId; //add/set newEdge tie(newEdgeId, newEdgeCreated) = add_edge(_currVertexId, newVertexId, m_searchGraph); if(newEdgeCreated) { m_Edge & newEdge = m_searchGraph[newEdgeId]; newEdge.weight = sqrt(2); //to change } else exit(1); } } void AStarSearch::m_printSearchGraph(m_SearchGraph & _searchGraph) { m_Vertex currVertex; m_VertexIt v_i, v_end; printf("id\t"); printf("id prev\t"); printf("id ref\t"); printf("dist\t"); printf("cost\t\t"); printf("color\t"); printf("\n"); for(tie(v_i, v_end) = vertices(_searchGraph); v_i != v_end; ++v_i) { currVertex = _searchGraph[*v_i]; printf("%i\t", *v_i); printf("%i\t", currVertex.prevVertexId); printf("%i\t", currVertex.refMyGraphVertexId); printf("%f\t", currVertex.distance); printf("%f\t", currVertex.cost); printf("%i\t", currVertex.color); printf("\n"); } printf("\n"); } list<int> AStarSearch::m_getPath(m_SearchGraph & _searchGraph, m_VertexId _vertexId) { list<int> path; for(m_VertexId currVertexId = _vertexId; ; //cf if()... break; below for stopping condition currVertexId = _searchGraph[currVertexId].prevVertexId) { path.push_front(currVertexId); if(_searchGraph[currVertexId].prevVertexId == int(currVertexId)) break; } return path; } vector< list<int> > AStarSearch::m_getAllPaths(m_SearchGraph & _searchGraph) { m_VertexIt v_i, v_begin, v_end; for(tie(v_i, v_end) = vertices(_searchGraph); v_i != v_end; ++v_i) { _searchGraph[*v_i].color = white_color; } vector< list<int> > allPaths; tie(v_begin, v_i) = vertices(_searchGraph); for(--v_i;; --v_i) { //printf("curr vertex %i\tcolor:%i\n", *v_i, _searchGraph[*v_i].color); if(_searchGraph[*v_i].color == white_color) { list<int> newPath = m_getPath(_searchGraph, *v_i); list<int>::iterator currVertex; for(currVertex = newPath.begin(); currVertex != newPath.end(); ++currVertex) { _searchGraph[*currVertex].color = black_color; } allPaths.push_back(newPath); } if(v_i == v_begin) break; } return allPaths; } void AStarSearch::m_printPath(list<int> _path) { list<int>::iterator pathIt; for(pathIt = _path.begin(); pathIt != _path.end(); ++pathIt) { printf("%i\t", *pathIt); } printf("\n"); } void AStarSearch::m_printShortestPath(m_SearchGraph & _searchGraph) { printf("shortest path:\n"); m_printPath(m_getPath(_searchGraph, get_property(_searchGraph, graph_name))); } void AStarSearch::m_printAllPaths(m_SearchGraph & _searchGraph) { vector< list<int> > allPaths = m_getAllPaths(_searchGraph); printf("all paths:\n"); vector< list<int> >::iterator currPath; for(currPath = allPaths.begin(); currPath != allPaths.end(); ++currPath) { m_printPath(*currPath); } } ==================================================================================== Cheers, Damien

On Mon, 1 Mar 2010, Damien Maupu wrote:
Hi,
Some weeks ago, I wrote on this mailing list, that I succeed achieving an A* search using an implicit graph. A Boost user emailed me to send him an example of my implementation. As the web is lacking of such example, I think the best place to send such example would be here as it might help others in the future.
My example might not be the best, the most optimized or most well-coded. I am open to suggestions, corrections and comments. Let me know if something unclear. The code comes below.
Are you willing to license this under the Boost Software License? If so, please put in that banner (and the accompanying copyright notice) and report the code. After that, I can add it to the examples directory in BGL. Is that the intent: that this becomes an example? Or did you intend it to become something else? Why is there a separate header file from the implementation?
Few words to quickly explain the code: The goal is to A* search a graph (named m_SearchGraph) that grows (i.e. m_SearchGraph is implicit) based on another graph (named MyGraph). The search starts from 2D point (_startX, _startZ) and must get enough close (less than error "_epsilon") to 2D point (_goalX, _goalZ). The example is dumb and simply increment current position whatever the node in MyGraph (i.e. currX++, currZ++, currWeight += sqrt(2)). User need to adapt this dummy behavior depending on its needs (search for "//to change"). Most adaptations have to be done in "examine_vertex" (and also by providing ref/ptr to needed objects to the visitor constructor)
PS: The implementation assumes both graphs are adjacency_list with "vecs" for both edges and vertices (in which a vertex/edge descriptor is equivalent to an "int", for my understanding)
Why not use a vertex_index_map?
PS2: Id == descriptor (e.g. edgeId == edge_descriptor)
That is very unlikely to be true; for example, compressed_sparse_row_graph has a structure as an edge descriptor but its edge indices are still numbers. -- Jeremiah Willcock
participants (2)
-
Damien Maupu
-
Jeremiah Willcock