| 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.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 | |
|---|
| 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 | |
|---|
| 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 { |
|---|
| 84 | tracking_bridge(const tracking_bridge& other) |
|---|
| 85 | : sig(other.sig), rand_gen(other.rand_gen) |
|---|
| 86 | { ++bridge_count; } |
|---|
| 87 | |
|---|
| 88 | tracking_bridge(signal_type* s, minstd_rand& rg) : sig(s), rand_gen(rg) |
|---|
| 89 | { ++bridge_count; } |
|---|
| 90 | |
|---|
| 91 | ~tracking_bridge() |
|---|
| 92 | { --bridge_count; } |
|---|
| 93 | |
|---|
| 94 | 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> |
|---|
| 135 | void visit_each(V& v, const tracking_bridge& t, int) |
|---|
| 136 | { |
|---|
| 137 | v(t); |
|---|
| 138 | v(t.sig); |
|---|
| 139 | } |
|---|
| 140 | } |
|---|
| 141 | |
|---|
| 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 | |
|---|
| 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 | |
|---|
| 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 | |
|---|
| 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 | |
|---|
| 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 |
|---|
| 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 | |
|---|
| 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 | |
|---|
| 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 | |
|---|
| 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 | |
|---|
| 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 | |
|---|
| 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 | |
|---|
| 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 | |
|---|
| 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 | } |
|---|