Ticket #3078: BGLTest.3.cpp

File BGLTest.3.cpp, 10.3 KB (added by Jeremiah Willcock, 13 years ago)

Fixed version of test

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