// Boost.Signals library // Copyright Douglas Gregor 2001-2004. Use, modification and // distribution is subject to the Boost Software License, Version // 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // For more information, see http://www.boost.org #include #include #include #include #include #include #include #include #include #include #include using namespace boost; using namespace boost::signals; struct signal_tag { typedef vertex_property_tag kind; }; struct connection_tag { typedef edge_property_tag kind; }; typedef signal4 signal_type; typedef adjacency_list >, // Edge properties property > > signal_graph_type; typedef signal_graph_type::vertex_descriptor vertex_descriptor; typedef signal_graph_type::edge_descriptor edge_descriptor; // The signal graph static signal_graph_type signal_graph; // Mapping from a signal to its associated vertex descriptor static std::map signal_to_descriptor; // Mapping from a connection to its associated edge descriptor static std::map connection_to_descriptor; std::map min_signal_propagate_distance; void remove_disconnected_connections() { // remove disconnected connections std::map::iterator i = connection_to_descriptor.begin(); while (i != connection_to_descriptor.end()) { if (!i->first.connected()) { connection_to_descriptor.erase(i++); } else { ++i; } } } void remove_signal(signal_type* sig) { clear_vertex(signal_to_descriptor[sig], signal_graph); remove_vertex(signal_to_descriptor[sig], signal_graph); delete sig; signal_to_descriptor.erase(sig); remove_disconnected_connections(); } void random_remove_signal(minstd_rand& rand_gen); struct tracking_bridge { tracking_bridge(const tracking_bridge& other) : sig(other.sig), rand_gen(other.rand_gen) { ++bridge_count; } tracking_bridge(signal_type* s, minstd_rand& rg) : sig(s), rand_gen(rg) { ++bridge_count; } ~tracking_bridge() { --bridge_count; } void operator()(int cur_dist, int max_dist, double deletion_prob, int& deletions_left) const { if (signal_to_descriptor.find(sig) == signal_to_descriptor.end()) return; ++cur_dist; // Update the directed Bacon distance if (min_signal_propagate_distance.find(sig) == min_signal_propagate_distance.end()) { min_signal_propagate_distance[sig] = cur_dist; } else if (cur_dist < min_signal_propagate_distance[sig]) { min_signal_propagate_distance[sig] = cur_dist; } else if (deletion_prob == 0.0) { // don't bother calling because we've already found a better route here return; } // Maybe delete the signal if (uniform_01(rand_gen)() < deletion_prob && deletions_left-- && signal_to_descriptor.size() > 1) { random_remove_signal(rand_gen); } // propagate the signal else if (cur_dist < max_dist) { (*sig)(cur_dist, max_dist, deletion_prob, deletions_left); } } signal_type* sig; minstd_rand& rand_gen; static int bridge_count; }; int tracking_bridge::bridge_count = 0; namespace boost { template void visit_each(V& v, const tracking_bridge& t, int) { v(t); v(t.sig); } } signal_type* add_signal() { signal_type* sig = new signal_type(); vertex_descriptor v = add_vertex(signal_graph); signal_to_descriptor[sig] = v; put(signal_tag(), signal_graph, v, sig); return sig; } connection add_connection(signal_type* sig1, signal_type* sig2, minstd_rand& rand_gen) { std::cout << " Adding connection: " << sig1 << " -> " << sig2 << std::endl; connection c = sig1->connect(tracking_bridge(sig2, rand_gen)); edge_descriptor e = add_edge(signal_to_descriptor[sig1], signal_to_descriptor[sig2], signal_graph).first; connection_to_descriptor[c] = e; put(connection_tag(), signal_graph, e, c); put(edge_weight, signal_graph, e, 1); return c; } void remove_connection(connection c) { signal_type* sig1 = get(signal_tag(), signal_graph, source(connection_to_descriptor[c], signal_graph)); signal_type* sig2 = get(signal_tag(), signal_graph, target(connection_to_descriptor[c], signal_graph)); std::cout << " Removing connection: " << sig1 << " -> " << sig2 << std::endl; c.disconnect(); remove_edge(connection_to_descriptor[c], signal_graph); connection_to_descriptor.erase(c); } bool signal_connection_exists(signal_type* sig1, signal_type* sig2, edge_descriptor& edge_desc) { vertex_descriptor source_sig = signal_to_descriptor[sig1]; vertex_descriptor target_sig = signal_to_descriptor[sig2]; signal_graph_type::out_edge_iterator e; for (e = out_edges(source_sig, signal_graph).first; e != out_edges(source_sig, signal_graph).second; ++e) { if (target(*e, signal_graph) == target_sig) { edge_desc = *e; return true; } } return false; } bool signal_connection_exists(signal_type* sig1, signal_type* sig2) { edge_descriptor e; return signal_connection_exists(sig1, sig2, e); } std::map::iterator choose_random_signal(minstd_rand& rand_gen) { int signal_idx = uniform_int<>(0, signal_to_descriptor.size() - 1)(rand_gen); std::map::iterator result = signal_to_descriptor.begin(); for(; signal_idx; --signal_idx) ++result; return result; } void random_remove_signal(minstd_rand& rand_gen) { std::map::iterator victim = choose_random_signal(rand_gen); std::cout << " Removing signal " << victim->first << std::endl; remove_signal(victim->first); } void random_add_connection(minstd_rand& rand_gen) { std::map::iterator source; std::map::iterator target; do { source = choose_random_signal(rand_gen); target = choose_random_signal(rand_gen); } while (signal_connection_exists(source->first, target->first)); add_connection(source->first, target->first, rand_gen); } void random_remove_connection(minstd_rand& rand_gen) { int victim_idx = uniform_int<>(0, num_edges(signal_graph)-1)(rand_gen); signal_graph_type::edge_iterator e = edges(signal_graph).first; while (victim_idx--) { ++e; } remove_connection(get(connection_tag(), signal_graph, *e)); } void random_bacon_test(minstd_rand& rand_gen) { signal_type* kevin = choose_random_signal(rand_gen)->first; min_signal_propagate_distance.clear(); min_signal_propagate_distance[kevin] = 0; const int horizon = 10; // only go to depth 10 at most std::cout << " Bacon test: kevin is " << kevin << "\n Propagating signal..."; // Propagate the signal out to the horizon int deletions_left = 0; (*kevin)(0, horizon, 0.0, deletions_left); std::cout << "OK\n Finding shortest paths..."; // Initialize all colors to white { unsigned int num = 0; for (signal_graph_type::vertex_iterator v = vertices(signal_graph).first; v != vertices(signal_graph).second; ++v) { // put(vertex_color, signal_graph, *v, white_color); put(vertex_index, signal_graph, *v, num++); } BOOST_CHECK(num == num_vertices(signal_graph)); } // Perform a breadth-first search starting at kevin, and record the // distances from kevin to each reachable node. std::map bacon_distance_map; #if 0 bacon_distance_map[signal_to_descriptor[kevin]] = 0; breadth_first_visit(signal_graph, signal_to_descriptor[kevin], visitor( make_bfs_visitor( record_distances( make_assoc_property_map(bacon_distance_map), on_examine_edge()))). color_map(get(vertex_color, signal_graph))); #endif dijkstra_shortest_paths(signal_graph, signal_to_descriptor[kevin], distance_map(make_assoc_property_map(bacon_distance_map))); std::cout << "OK\n"; // Make sure the bacon distances agree (prior to the horizon) { std::map::iterator i; for (i = min_signal_propagate_distance.begin(); i != min_signal_propagate_distance.end(); ++i) { if (i->second != bacon_distance_map[signal_to_descriptor[i->first]]) { std::cout << "Signal distance to " << i->first << " was " << i->second << std::endl; std::cout << "Graph distance was " << bacon_distance_map[signal_to_descriptor[i->first]] << std::endl; } BOOST_CHECK(i->second == bacon_distance_map[signal_to_descriptor[i->first]]); } } } void randomly_create_connections(minstd_rand& rand_gen, double edge_probability) { // Randomly create connections uniform_01 random(rand_gen); for (signal_graph_type::vertex_iterator v1 = vertices(signal_graph).first; v1 != vertices(signal_graph).second; ++v1) { for (signal_graph_type::vertex_iterator v2 = vertices(signal_graph).first; v2 != vertices(signal_graph).second; ++v2) { if (random() < edge_probability) { add_connection(get(signal_tag(), signal_graph, *v1), get(signal_tag(), signal_graph, *v2), rand_gen); } } } } void random_recursive_deletion(minstd_rand& rand_gen) { signal_type* kevin = choose_random_signal(rand_gen)->first; min_signal_propagate_distance.clear(); min_signal_propagate_distance[kevin] = 0; const int horizon = 4; // only go to depth "horizon" at most std::cout << " Recursive deletion test: start is " << kevin << std::endl; // Propagate the signal out to the horizon int deletions_left = (int)(0.05*num_vertices(signal_graph)); (*kevin)(0, horizon, 0.05, deletions_left); } int test_main(int argc, char* argv[]) { if (argc < 4) { std::cerr << "Usage: random_signal_system <# of initial signals> " << " " << std::endl; return 1; } int number_of_initial_signals = atoi(argv[1]); double edge_probability = atof(argv[2]); int iterations = atoi(argv[3]); int seed; if (argc == 5) seed = atoi(argv[4]); else seed = time(0); std::cout << "Number of initial signals: " << number_of_initial_signals << std::endl; std::cout << "Edge probability: " << edge_probability << std::endl; std::cout << "Iterations: " << iterations << std::endl; std::cout << "Seed: " << seed << std::endl; // Initialize random number generator minstd_rand rand_gen; rand_gen.seed(seed); for (int iter = 0; iter < iterations; ++iter) { if (num_vertices(signal_graph) < 2) { for (int i = 0; i < number_of_initial_signals; ++i) add_signal(); } while (num_edges(signal_graph) < 2) { randomly_create_connections(rand_gen, edge_probability); } std::cerr << "Iteration #" << (iter+1) << std::endl; uniform_int<> random_action(0, 7); switch (random_action(rand_gen)) { case 0: std::cout << " Adding new signal: " << add_signal() << std::endl; break; case 1: random_remove_signal(rand_gen); break; case 2: if (num_edges(signal_graph) < num_vertices(signal_graph)*num_vertices(signal_graph)) { random_add_connection(rand_gen); } break; case 3: random_remove_connection(rand_gen); break; case 4: case 5: case 6: random_bacon_test(rand_gen); break; case 7: random_recursive_deletion(rand_gen); break; } } for (signal_graph_type::vertex_iterator v = vertices(signal_graph).first; v != vertices(signal_graph).second; ++v) { delete get(signal_tag(), signal_graph, *v); } BOOST_CHECK(tracking_bridge::bridge_count == 0); if (tracking_bridge::bridge_count != 0) { std::cerr << tracking_bridge::bridge_count << " connections remain.\n"; } return 0; }