1 // Boost.Signals library
2 
3 // Copyright Douglas Gregor 2001-2004. Use, modification and
4 // distribution is subject to the Boost Software License, Version
5 // 1.0. (See accompanying file LICENSE_1_0.txt or copy at
6 // http://www.boost.org/LICENSE_1_0.txt)
7 
8 // For more information, see http://www.boost.org
9 
10 #include <boost/signal.hpp>
11 #include <boost/graph/adjacency_list.hpp>
12 #include <boost/graph/breadth_first_search.hpp>
13 #include <boost/graph/dijkstra_shortest_paths.hpp>
14 #include <boost/property_map/property_map.hpp>
15 #include <boost/random.hpp>
16 #include <map>
17 #include <set>
18 #include <stdlib.h>
19 #include <time.h>
20 #include <boost/test/minimal.hpp>
21 
22 using namespace boost;
23 using namespace boost::signals;
24 
25 struct signal_tag {
26   typedef vertex_property_tag kind;
27 };
28 
29 struct connection_tag {
30   typedef edge_property_tag kind;
31 };
32 
33 typedef signal4<void, int, int, double, int&> signal_type;
34 typedef adjacency_list<listS, listS, directedS,
35                        // Vertex properties
36                        property<signal_tag, signal_type*,
37   //                       property<vertex_color_t, default_color_type,
38                        property<vertex_index_t, int> >,
39                        // Edge properties
40                        property<connection_tag, connection,
41                        property<edge_weight_t, int> > >
42   signal_graph_type;
43 typedef signal_graph_type::vertex_descriptor vertex_descriptor;
44 typedef signal_graph_type::edge_descriptor edge_descriptor;
45 
46 // The signal graph
47 static signal_graph_type signal_graph;
48 
49 // Mapping from a signal to its associated vertex descriptor
50 static std::map<signal_type*, vertex_descriptor> signal_to_descriptor;
51 
52 // Mapping from a connection to its associated edge descriptor
53 static std::map<connection, edge_descriptor> connection_to_descriptor;
54 
55 std::map<signal_type*, int> min_signal_propagate_distance;
56 
remove_disconnected_connections()57 void remove_disconnected_connections()
58 {
59   // remove disconnected connections
60   std::map<connection, edge_descriptor>::iterator i =
61     connection_to_descriptor.begin();
62   while (i != connection_to_descriptor.end()) {
63     if (!i->first.connected()) {
64       connection_to_descriptor.erase(i++);
65     }
66     else {
67       ++i;
68     }
69   }
70 }
71 
remove_signal(signal_type * sig)72 void remove_signal(signal_type* sig)
73 {
74   clear_vertex(signal_to_descriptor[sig], signal_graph);
75   remove_vertex(signal_to_descriptor[sig], signal_graph);
76   delete sig;
77   signal_to_descriptor.erase(sig);
78   remove_disconnected_connections();
79 }
80 
81 void random_remove_signal(minstd_rand& rand_gen);
82 
83 struct tracking_bridge {
tracking_bridgetracking_bridge84   tracking_bridge(const tracking_bridge& other)
85     : sig(other.sig), rand_gen(other.rand_gen)
86   { ++bridge_count; }
87 
tracking_bridgetracking_bridge88   tracking_bridge(signal_type* s, minstd_rand& rg) : sig(s), rand_gen(rg)
89   { ++bridge_count; }
90 
~tracking_bridgetracking_bridge91   ~tracking_bridge()
92   { --bridge_count; }
93 
operator ()tracking_bridge94   void operator()(int cur_dist, int max_dist, double deletion_prob,
95                   int& deletions_left) const
96   {
97     if (signal_to_descriptor.find(sig) == signal_to_descriptor.end())
98       return;
99 
100     ++cur_dist;
101 
102     // Update the directed Bacon distance
103     if (min_signal_propagate_distance.find(sig) ==
104           min_signal_propagate_distance.end()) {
105       min_signal_propagate_distance[sig] = cur_dist;
106     }
107     else if (cur_dist < min_signal_propagate_distance[sig]) {
108       min_signal_propagate_distance[sig] = cur_dist;
109     }
110     else if (deletion_prob == 0.0) {
111       // don't bother calling because we've already found a better route here
112       return;
113     }
114 
115     // Maybe delete the signal
116     if (uniform_01<minstd_rand>(rand_gen)() < deletion_prob &&
117         deletions_left-- && signal_to_descriptor.size() > 1) {
118       random_remove_signal(rand_gen);
119     }
120     // propagate the signal
121     else if (cur_dist < max_dist) {
122       (*sig)(cur_dist, max_dist, deletion_prob, deletions_left);
123     }
124   }
125 
126   signal_type* sig;
127   minstd_rand& rand_gen;
128   static int bridge_count;
129 };
130 
131 int tracking_bridge::bridge_count = 0;
132 
133 namespace boost {
134   template<typename V>
visit_each(V & v,const tracking_bridge & t,int)135   void visit_each(V& v, const tracking_bridge& t, int)
136   {
137     v(t);
138     v(t.sig);
139   }
140 }
141 
add_signal()142 signal_type* add_signal()
143 {
144   signal_type* sig = new signal_type();
145   vertex_descriptor v = add_vertex(signal_graph);
146   signal_to_descriptor[sig] = v;
147   put(signal_tag(), signal_graph, v, sig);
148 
149   return sig;
150 }
151 
add_connection(signal_type * sig1,signal_type * sig2,minstd_rand & rand_gen)152 connection add_connection(signal_type* sig1, signal_type* sig2,
153                           minstd_rand& rand_gen)
154 {
155   std::cout << "  Adding connection: " << sig1 << " -> " << sig2 << std::endl;
156 
157   connection c = sig1->connect(tracking_bridge(sig2, rand_gen));
158   edge_descriptor e =
159     add_edge(signal_to_descriptor[sig1], signal_to_descriptor[sig2],
160              signal_graph).first;
161   connection_to_descriptor[c] = e;
162   put(connection_tag(), signal_graph, e, c);
163   put(edge_weight, signal_graph, e, 1);
164   return c;
165 }
166 
remove_connection(connection c)167 void remove_connection(connection c)
168 {
169   signal_type* sig1 = get(signal_tag(), signal_graph,
170                           source(connection_to_descriptor[c], signal_graph));
171   signal_type* sig2 = get(signal_tag(), signal_graph,
172                           target(connection_to_descriptor[c], signal_graph));
173   std::cout << "  Removing connection: " << sig1 << " -> " << sig2
174             << std::endl;
175   c.disconnect();
176   remove_edge(connection_to_descriptor[c], signal_graph);
177   connection_to_descriptor.erase(c);
178 }
179 
signal_connection_exists(signal_type * sig1,signal_type * sig2,edge_descriptor & edge_desc)180 bool signal_connection_exists(signal_type* sig1, signal_type* sig2,
181                               edge_descriptor& edge_desc)
182 {
183   vertex_descriptor source_sig = signal_to_descriptor[sig1];
184   vertex_descriptor target_sig = signal_to_descriptor[sig2];
185   signal_graph_type::out_edge_iterator e;
186   for (e = out_edges(source_sig, signal_graph).first;
187        e != out_edges(source_sig, signal_graph).second; ++e) {
188     if (target(*e, signal_graph) == target_sig) {
189       edge_desc = *e;
190       return true;
191     }
192   }
193   return false;
194 }
195 
signal_connection_exists(signal_type * sig1,signal_type * sig2)196 bool signal_connection_exists(signal_type* sig1, signal_type* sig2)
197 {
198   edge_descriptor e;
199   return signal_connection_exists(sig1, sig2, e);
200 }
201 
202 std::map<signal_type*, vertex_descriptor>::iterator
choose_random_signal(minstd_rand & rand_gen)203 choose_random_signal(minstd_rand& rand_gen)
204 {
205   int signal_idx
206     = uniform_int<>(0, signal_to_descriptor.size() - 1)(rand_gen);
207   std::map<signal_type*, vertex_descriptor>::iterator result =
208     signal_to_descriptor.begin();
209   for(; signal_idx; --signal_idx)
210     ++result;
211 
212   return result;
213 }
214 
random_remove_signal(minstd_rand & rand_gen)215 void random_remove_signal(minstd_rand& rand_gen)
216 {
217   std::map<signal_type*, vertex_descriptor>::iterator victim =
218     choose_random_signal(rand_gen);
219   std::cout << "  Removing signal " << victim->first << std::endl;
220   remove_signal(victim->first);
221 }
222 
random_add_connection(minstd_rand & rand_gen)223 void random_add_connection(minstd_rand& rand_gen)
224 {
225   std::map<signal_type*, vertex_descriptor>::iterator source;
226   std::map<signal_type*, vertex_descriptor>::iterator target;
227   do {
228     source = choose_random_signal(rand_gen);
229     target = choose_random_signal(rand_gen);
230   } while (signal_connection_exists(source->first, target->first));
231 
232   add_connection(source->first, target->first, rand_gen);
233 }
234 
random_remove_connection(minstd_rand & rand_gen)235 void random_remove_connection(minstd_rand& rand_gen)
236 {
237   int victim_idx =
238     uniform_int<>(0, num_edges(signal_graph)-1)(rand_gen);
239   signal_graph_type::edge_iterator e = edges(signal_graph).first;
240   while (victim_idx--) {
241     ++e;
242   }
243 
244   remove_connection(get(connection_tag(), signal_graph, *e));
245 }
246 
random_bacon_test(minstd_rand & rand_gen)247 void random_bacon_test(minstd_rand& rand_gen)
248 {
249   signal_type* kevin = choose_random_signal(rand_gen)->first;
250   min_signal_propagate_distance.clear();
251   min_signal_propagate_distance[kevin] = 0;
252 
253   const int horizon = 10; // only go to depth 10 at most
254 
255   std::cout << "  Bacon test: kevin is " << kevin
256             << "\n    Propagating signal...";
257 
258   // Propagate the signal out to the horizon
259   int deletions_left = 0;
260   (*kevin)(0, horizon, 0.0, deletions_left);
261 
262   std::cout << "OK\n    Finding shortest paths...";
263 
264   // Initialize all colors to white
265   {
266     unsigned int num = 0;
267     for (signal_graph_type::vertex_iterator v = vertices(signal_graph).first;
268          v != vertices(signal_graph).second;
269          ++v) {
270       //      put(vertex_color, signal_graph, *v, white_color);
271       put(vertex_index, signal_graph, *v, num++);
272     }
273 
274     BOOST_CHECK(num == num_vertices(signal_graph));
275   }
276 
277   // Perform a breadth-first search starting at kevin, and record the
278   // distances from kevin to each reachable node.
279   std::map<vertex_descriptor, int> bacon_distance_map;
280 
281 #if 0
282   bacon_distance_map[signal_to_descriptor[kevin]] = 0;
283   breadth_first_visit(signal_graph, signal_to_descriptor[kevin],
284                       visitor(
285                         make_bfs_visitor(
286                          record_distances(
287                            make_assoc_property_map(bacon_distance_map),
288                            on_examine_edge()))).
289                       color_map(get(vertex_color, signal_graph)));
290 #endif
291 
292   dijkstra_shortest_paths(signal_graph, signal_to_descriptor[kevin],
293                           distance_map(make_assoc_property_map(bacon_distance_map)));
294   std::cout << "OK\n";
295   // Make sure the bacon distances agree (prior to the horizon)
296   {
297     std::map<signal_type*, int>::iterator i;
298     for (i = min_signal_propagate_distance.begin();
299          i != min_signal_propagate_distance.end();
300          ++i) {
301       if (i->second != bacon_distance_map[signal_to_descriptor[i->first]]) {
302         std::cout << "Signal distance to " << i->first << " was "
303                   << i->second << std::endl;
304         std::cout << "Graph distance was "
305                   << bacon_distance_map[signal_to_descriptor[i->first]]
306                   << std::endl;
307       }
308       BOOST_CHECK(i->second == bacon_distance_map[signal_to_descriptor[i->first]]);
309     }
310   }
311 }
312 
randomly_create_connections(minstd_rand & rand_gen,double edge_probability)313 void randomly_create_connections(minstd_rand& rand_gen, double edge_probability)
314 {
315   // Randomly create connections
316   uniform_01<minstd_rand> random(rand_gen);
317   for (signal_graph_type::vertex_iterator v1 = vertices(signal_graph).first;
318        v1 != vertices(signal_graph).second; ++v1) {
319     for (signal_graph_type::vertex_iterator v2 = vertices(signal_graph).first;
320          v2 != vertices(signal_graph).second; ++v2) {
321       if (random() < edge_probability) {
322         add_connection(get(signal_tag(), signal_graph, *v1),
323                        get(signal_tag(), signal_graph, *v2),
324                        rand_gen);
325       }
326     }
327   }
328 }
329 
random_recursive_deletion(minstd_rand & rand_gen)330 void random_recursive_deletion(minstd_rand& rand_gen)
331 {
332   signal_type* kevin = choose_random_signal(rand_gen)->first;
333   min_signal_propagate_distance.clear();
334   min_signal_propagate_distance[kevin] = 0;
335 
336   const int horizon = 4; // only go to depth "horizon" at most
337 
338   std::cout << "  Recursive deletion test: start is " << kevin << std::endl;
339 
340   // Propagate the signal out to the horizon
341   int deletions_left = (int)(0.05*num_vertices(signal_graph));
342   (*kevin)(0, horizon, 0.05, deletions_left);
343 }
344 
test_main(int argc,char * argv[])345 int test_main(int argc, char* argv[])
346 {
347   if (argc < 4) {
348     std::cerr << "Usage: random_signal_system <# of initial signals> "
349               << "<edge probability> <iterations>" << std::endl;
350     return 1;
351   }
352 
353   int number_of_initial_signals = atoi(argv[1]);
354   double edge_probability = atof(argv[2]);
355   int iterations = atoi(argv[3]);
356 
357   int seed;
358   if (argc == 5)
359     seed = atoi(argv[4]);
360   else
361     seed = time(0);
362 
363   std::cout << "Number of initial signals: " << number_of_initial_signals
364             << std::endl;
365   std::cout << "Edge probability: " << edge_probability << std::endl;
366   std::cout << "Iterations: " << iterations << std::endl;
367   std::cout << "Seed: " << seed << std::endl;
368 
369   // Initialize random number generator
370   minstd_rand rand_gen;
371   rand_gen.seed(seed);
372 
373   for (int iter = 0; iter < iterations; ++iter) {
374     if (num_vertices(signal_graph) < 2) {
375       for (int i = 0; i < number_of_initial_signals; ++i)
376         add_signal();
377     }
378 
379     while (num_edges(signal_graph) < 2) {
380       randomly_create_connections(rand_gen, edge_probability);
381     }
382 
383     std::cerr << "Iteration #" << (iter+1) << std::endl;
384 
385     uniform_int<> random_action(0, 7);
386     switch (random_action(rand_gen)) {
387     case 0:
388       std::cout << "  Adding new signal: " << add_signal() << std::endl;
389       break;
390 
391     case 1:
392       random_remove_signal(rand_gen);
393       break;
394 
395     case 2:
396       if (num_edges(signal_graph) <
397             num_vertices(signal_graph)*num_vertices(signal_graph)) {
398         random_add_connection(rand_gen);
399       }
400       break;
401 
402     case 3:
403       random_remove_connection(rand_gen);
404       break;
405 
406     case 4:
407     case 5:
408     case 6:
409       random_bacon_test(rand_gen);
410       break;
411 
412     case 7:
413       random_recursive_deletion(rand_gen);
414       break;
415     }
416   }
417 
418   for (signal_graph_type::vertex_iterator v = vertices(signal_graph).first;
419        v != vertices(signal_graph).second;
420        ++v) {
421     delete get(signal_tag(), signal_graph, *v);
422   }
423 
424   BOOST_CHECK(tracking_bridge::bridge_count == 0);
425   if (tracking_bridge::bridge_count != 0) {
426     std::cerr << tracking_bridge::bridge_count << " connections remain.\n";
427   }
428   return 0;
429 }
430