Ticket #3078: BGLTest.cpp

File BGLTest.cpp, 9.8 KB (added by Artyom Arabadji <sneg.vx@…>, 13 years ago)

example to reproduce the rpoblem

Line 
1#include <stdlib.h>
2#include <time.h>
3
4#include <iostream>
5#include <deque>
6
7#include <boost/graph/adjacency_list.hpp>
8#include <boost/graph/visitors.hpp>
9#include <boost/graph/graph_utility.hpp>
10
11/**
12* Singleton design pattern is used to
13* to make an instance of a particular class unique.
14*/
15template< typename X > class Singleton
16{
17public:
18 inline static X& getInstance()
19 {
20 static X x;
21
22 return x;
23 }
24
25protected:
26 Singleton( void ) { }
27 ~Singleton( void ) { }
28 Singleton( const Singleton& ) { }
29 inline Singleton& operator = ( const Singleton& x )
30 {
31 return x;
32 }
33};
34
35
36/***
37 * @desc
38 * PathRecorder class is a template for creating
39 * path recorders which utilise visitor concepts
40 * in BGL. I.e. as a search algorithm runs,
41 * PathRecorder is called to fill in a "predecessor map"
42 * which records how to get to a specific vertex and stores
43 * edges with their targets as keys.
44 ***/
45template< class Graph, class Vertex, class Edge, class VertexIterator, class EdgeIterator > class PathRecorder
46{
47 typedef typename boost::property_map< typename Graph, boost::vertex_index_t >::type VertexId_PMap;
48 typedef boost::iterator_property_map< typename std::vector< Vertex >::iterator, VertexId_PMap, typename Vertex, typename Vertex & > PredMap;
49 typedef boost::iterator_property_map< typename std::vector< Edge >::iterator, VertexId_PMap, typename Edge, typename Edge & > PredEdgeMap;
50
51public:
52 /**
53 * @desc
54 * Update an entry inside a map
55 **/
56 void put( Vertex target, Vertex src )
57 {
58 boost::put( predMap, target, src );
59 }
60
61 /**
62 * @desc
63 * Update an entry inside a map
64 **/
65 void put( Vertex target, Edge e )
66 {
67 boost::put( predEdgeMap, target, e );
68 }
69
70 /**
71 * @desc
72 * Get predecessor (ancestor) of target
73 **/
74 Vertex getPredecessor( Vertex target ) const
75 {
76 return boost::get( predMap, target );
77 }
78
79 /**
80 * @desc
81 * Get Edge to target
82 **/
83 Edge getEdge( Vertex target ) const
84 {
85 return boost::get( predEdgeMap, target );
86 }
87
88 /**
89 * @desc
90 * Get edge from ancestor to sibling (if such edge exists)
91 **/
92 Edge PathRecorder :: getEdgeToSibling( Vertex ancestor ) const
93 {
94 VertexIterator i, end;
95
96 for( boost::tie( i, end ) = boost::vertices( g ); i != end; ++i )
97 {
98 Vertex pred = boost::get( predMap, *i );
99
100 if( pred == ancestor )
101 {
102 return boost::get( predEdgeMap, *i );
103 }
104 }
105
106 return Edge();
107 }
108
109 /***
110 * @desc reset path recorder for
111 * another search
112 ***/
113 virtual void reset( void )
114 {
115 edges.assign( edges.size(), Edge() );
116 vertices.assign( vertices.size(), Vertex() );
117 }
118protected:
119 PathRecorder( const Graph &graph ) : g( graph ),
120 vertices( std::vector< Vertex >( boost::num_vertices( g ) ) ),
121 edges( std::vector< Edge >( boost::num_vertices( g ) ) ),
122 vertex_id( boost::get( boost::vertex_index, g ) ),
123 predMap( vertices.begin(), vertex_id ),
124 predEdgeMap( edges.begin(), vertex_id )
125 {
126 }
127
128 const Graph g;
129
130 ~PathRecorder( void ) { }
131 PathRecorder( const PathRecorder & ) { }
132
133 std::vector< Vertex > vertices;
134 std::vector< Edge > edges;
135 VertexId_PMap vertex_id;
136
137 PredMap predMap;
138 PredEdgeMap predEdgeMap;
139};
140
141/**
142 * @desc
143 * Terminator is a class which simply tells DFS
144 * to stop once the target vertex is reached.
145 **/
146template< class Vertex, class Graph > struct Terminator
147{
148 Terminator( Vertex v ) : destination( v )
149 {
150 }
151
152 bool operator()( Vertex v, const Graph &g )
153 {
154 return ( v == destination );
155 }
156
157private:
158 const Vertex destination;
159};
160
161/***
162 * Vertex Structure (bundled properties)
163 * This is empty, but in original src code
164 * this has some data inside.
165 ***/
166struct Clip_t
167{
168 Clip_t( void ) { }
169};
170
171/***
172 * Edge Structure (bundled properties)
173 * This is empty, but in original src code
174 * this has some data inside.
175 ***/
176struct Transition_t
177{
178 Transition_t( void ) { }
179
180 Transition_t( double d ) : distance( d )
181 {
182 }
183
184 double distance;
185};
186
187/***
188 * Type defines for our graph ( Called MotionGraph )
189 ***/
190typedef boost::adjacency_list< boost::listS, boost::vecS, boost::bidirectionalS, Clip_t, Transition_t > MotionGraph_t;
191typedef boost::graph_traits< MotionGraph_t >::vertex_iterator ClipIterator;
192typedef boost::graph_traits< MotionGraph_t >::vertex_descriptor Clip;
193typedef boost::graph_traits< MotionGraph_t >::out_edge_iterator TransitionIterator;
194typedef boost::graph_traits< MotionGraph_t >::edge_descriptor Transition;
195
196/***
197 * Singleton graph structure so we can access it anywhere we like
198 * getBase is a way of passing the graph to BGL algorithm templates
199 * if you pass this class directly templates will not compile due
200 * to constructors and destructors being protected.
201 ***/
202class MotionGraph : public Singleton< MotionGraph >, public MotionGraph_t
203{
204 friend Singleton< MotionGraph >;
205public:
206 MotionGraph_t *getBase( void )
207 {
208 return static_cast< MotionGraph_t * >( &getInstance() );
209 }
210protected:
211 ~MotionGraph() { }
212 MotionGraph() { }
213 MotionGraph( const MotionGraph & ) { }
214
215};
216
217/**
218@desc
219ForwardEdge is a visitor which tries to decide whether
220to replace a newly discovered edge with the edge inside
221a DFS tree (predecessor map).
222Forward Edge is an edge (U,V) and both U, V have already
223been discovered by DFS.
224**/
225struct TestForwardEdge : public boost::base_visitor< TestForwardEdge >
226{
227 typedef boost::on_forward_or_cross_edge event_filter;
228
229 template < class Edge, class Graph > void operator()( Edge e, const Graph &g );
230};
231
232/**
233@desc
234PredRecorder adds a tree edge to predecessor map.
235Tree edge is the first edge for pair of vertices
236(U, V) that was ever discovered.
237**/
238struct TestPredRecorder : public boost::base_visitor< TestPredRecorder >
239{
240 typedef boost::on_tree_edge event_filter;
241
242 template < class Edge, class Graph > void operator()( Edge e, const Graph& g );
243};
244
245/***
246 * Quick test implementation of PathRecorder interface.
247 ***/
248class TestPathRecorder : public PathRecorder< MotionGraph_t, Clip, Transition, ClipIterator, TransitionIterator >, public Singleton< TestPathRecorder >
249{
250 friend class Singleton< TestPathRecorder >;
251public:
252 template< typename Container > void search( Container &agenda, Clip start, Clip end )
253 {
254 std::vector< unsigned short > c( boost::num_vertices( g ) );
255
256 reset();
257
258 typedef boost::property_map< MotionGraph_t, boost::vertex_index_t >::type VertexId_PMap;
259 VertexId_PMap vertex_id = boost::get( boost::vertex_index, g );
260 typedef boost::iterator_property_map< std::vector< unsigned short >::iterator, VertexId_PMap, unsigned short, unsigned short & > ColourMap;
261
262 boost::depth_first_visit(
263 g,
264 start,
265 boost::make_dfs_visitor( boost::make_list( TestPredRecorder(), TestForwardEdge() ) ),
266 ColourMap( c.begin(), vertex_id ),
267 Terminator< Clip, MotionGraph_t >( end )
268 );
269
270 getPath( agenda, start, end );
271 }
272
273 template< typename Container > void getPath( Container &agenda, Clip start, Clip end ) const
274 {
275 Transition cur = boost::get( predEdgeMap, end );
276
277 while( cur.m_eproperty )
278 {
279 agenda.push_front( boost::target( cur, g ) );
280 agenda.push_front( boost::source( cur, g ) );
281
282 cur = boost::get( predEdgeMap, boost::source( cur, g ) );
283 }
284
285 if( agenda.empty() || agenda.front() != start )
286 {
287 agenda.clear();
288 }
289 }
290
291protected:
292 TestPathRecorder( void ) : PathRecorder( *MotionGraph::getInstance().getBase() ) { }
293 ~TestPathRecorder( void ) { }
294 TestPathRecorder( const TestPathRecorder &mpr ) : PathRecorder( mpr.g ) { }
295};
296
297/**** Definitions of methods for visitor classes declared before TestPathRecorder ****/
298
299template < class Edge, class Graph > void TestPredRecorder :: operator()( Edge e, const Graph &g )
300{
301 // simplified method, simply overwrite
302 TestPathRecorder::getInstance().put( boost::target( e, g ), boost::source( e, g ) );
303 TestPathRecorder::getInstance().put( boost::target( e, g ), e );
304}
305
306template < class Edge, class Graph > void TestForwardEdge :: operator()( Edge e, const Graph &g )
307{
308 // simplified method, simply overwrite
309 TestPathRecorder::getInstance().put( boost::target( e, g ), boost::source( e, g ) );
310 TestPathRecorder::getInstance().put( boost::target( e, g ), e );
311}
312
313/******************************/
314
315/*** Roll N-sided die ***/
316int roll( int sides )
317{
318 return rand() % sides;
319}
320
321/*** Roll N-sided die so that result is different to prev ***/
322int rollUnique( int sides, int prev )
323{
324 int r = roll( sides );
325
326 while( r == prev )
327 {
328 r = roll( sides );
329 }
330
331 return r;
332}
333
334/*** Choose a random src and target and attempt to find a path ***/
335void randomTest( void )
336{
337 std::deque< Clip > mg;
338
339 int start = rand() % boost::num_vertices( MotionGraph::getInstance() );
340 int end = rand() % boost::num_vertices( MotionGraph::getInstance() );
341
342 TestPathRecorder::getInstance().search( mg, start, end );
343
344 if( mg.empty() )
345 {
346 std::cout << "Destination unreachable" << std::endl;
347 return;
348 }
349
350 std::cout << "Path from " << start << " to " << end << ":" << std::endl;
351
352 for( int i = 0; i < mg.size(); ++i )
353 {
354 std::cout << mg.at( i ) << ( i != mg.size() - 1 ? " -> " : "\n" );
355 }
356}
357
358int main( void )
359{
360 srand( time( NULL ) );
361
362 // randomly choose number of vertices
363 int vertices = roll( 500 );
364
365 // add vertices
366 for( int i = 0; i < vertices; ++i )
367 {
368 boost::add_vertex( Clip_t(), MotionGraph::getInstance() );
369 }
370
371 // for each vertex V choose a random number of edges and connect them to random vertices (excluding the V itself)
372 for( int i = 0; i < vertices; ++i )
373 {
374 int edges = roll( vertices - 1 );
375
376 for( int j = 0; j < edges; j++ )
377 {
378 int target = rollUnique( vertices, i );
379
380 boost::add_edge( i, target, Transition_t(), MotionGraph::getInstance() );
381 }
382 }
383
384 // run a random path finding test for half the number of vertices
385 for( int i = 0; i < vertices / 2; i++ )
386 {
387 randomTest();
388 }
389
390 return 0;
391}
392