adjlist_build_test.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. // Copyright (C) 2007 Douglas Gregor
  2. // Use, modification and distribution is subject to the Boost Software
  3. // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  4. // http://www.boost.org/LICENSE_1_0.txt)
  5. #include <boost/graph/use_mpi.hpp>
  6. #include <boost/config.hpp>
  7. #include <boost/throw_exception.hpp>
  8. #include <boost/graph/distributed/adjacency_list.hpp>
  9. #include <boost/graph/distributed/mpi_process_group.hpp>
  10. #include <boost/test/minimal.hpp>
  11. #include <boost/random/linear_congruential.hpp>
  12. #include <boost/graph/erdos_renyi_generator.hpp>
  13. #include <boost/lexical_cast.hpp>
  14. #include <ctime>
  15. using namespace boost;
  16. using boost::graph::distributed::mpi_process_group;
  17. #ifdef BOOST_NO_EXCEPTIONS
  18. void
  19. boost::throw_exception(std::exception const& ex)
  20. {
  21. std::cout << ex.what() << std::endl;
  22. abort();
  23. }
  24. #endif
  25. int test_main(int argc, char** argv)
  26. {
  27. boost::mpi::environment env(argc, argv);
  28. int n = 10000;
  29. double p = 3e-3;
  30. int seed = std::time(0);
  31. int immediate_response_percent = 10;
  32. if (argc > 1) n = lexical_cast<int>(argv[1]);
  33. if (argc > 2) p = lexical_cast<double>(argv[2]);
  34. if (argc > 3) seed = lexical_cast<int>(argv[3]);
  35. typedef adjacency_list<listS,
  36. distributedS<mpi_process_group, vecS>,
  37. bidirectionalS> Graph;
  38. mpi_process_group pg;
  39. int rank = process_id(pg);
  40. int numprocs = num_processes(pg);
  41. bool i_am_root = rank == 0;
  42. // broadcast the seed
  43. broadcast(pg, seed, 0);
  44. // Random number generator
  45. minstd_rand gen;
  46. minstd_rand require_response_gen;
  47. if (i_am_root) {
  48. std::cout << "n = " << n << ", p = " << p << ", seed = " << seed
  49. << "\nBuilding graph with the iterator constructor... ";
  50. std::cout.flush();
  51. }
  52. // Build a graph using the iterator constructor, where each of the
  53. // processors has exactly the same information.
  54. gen.seed(seed);
  55. Graph g1(erdos_renyi_iterator<minstd_rand, Graph>(gen, n, p),
  56. erdos_renyi_iterator<minstd_rand, Graph>(),
  57. n, pg, Graph::graph_property_type());
  58. // NGE: Grrr, the default graph property is needed to resolve an
  59. // ambiguous overload in the adjaceny list constructor
  60. // Build another, identical graph using add_edge
  61. if (i_am_root) {
  62. std::cout << "done.\nBuilding graph with add_edge from the root...";
  63. std::cout.flush();
  64. }
  65. gen.seed(seed);
  66. require_response_gen.seed(1);
  67. Graph g2(n, pg);
  68. if (i_am_root) {
  69. // The root will add all of the edges, some percentage of which
  70. // will require an immediate response from the owner of the edge.
  71. for (erdos_renyi_iterator<minstd_rand, Graph> first(gen, n, p), last;
  72. first != last; ++first) {
  73. Graph::lazy_add_edge lazy
  74. = add_edge(vertex(first->first, g2), vertex(first->second, g2), g2);
  75. if ((int)require_response_gen() % 100 < immediate_response_percent) {
  76. // Send out-of-band to require a response
  77. std::pair<graph_traits<Graph>::edge_descriptor, bool> result(lazy);
  78. BOOST_CHECK(source(result.first, g2) == vertex(first->first, g2));
  79. BOOST_CHECK(target(result.first, g2) == vertex(first->second, g2));
  80. }
  81. }
  82. }
  83. if (i_am_root) {
  84. std::cout << "synchronizing...";
  85. std::cout.flush();
  86. }
  87. synchronize(g2);
  88. // Verify that the two graphs are indeed identical.
  89. if (i_am_root) {
  90. std::cout << "done.\nVerifying graphs...";
  91. std::cout.flush();
  92. }
  93. // Check the number of vertices
  94. if (num_vertices(g1) != num_vertices(g2)) {
  95. std::cerr << g1.processor() << ": g1 has " << num_vertices(g1)
  96. << " vertices, g2 has " << num_vertices(g2) << " vertices.\n";
  97. communicator(pg).abort(-1);
  98. }
  99. // Check the number of edges
  100. if (num_edges(g1) != num_edges(g2)) {
  101. std::cerr << g1.processor() << ": g1 has " << num_edges(g1)
  102. << " edges, g2 has " << num_edges(g2) << " edges.\n";
  103. communicator(pg).abort(-1);
  104. }
  105. // Check the in-degree and out-degree of each vertex
  106. graph_traits<Graph>::vertex_iterator vfirst1, vlast1, vfirst2, vlast2;
  107. boost::tie(vfirst1, vlast1) = vertices(g1);
  108. boost::tie(vfirst2, vlast2) = vertices(g2);
  109. for(; vfirst1 != vlast1 && vfirst2 != vlast2; ++vfirst1, ++vfirst2) {
  110. if (out_degree(*vfirst1, g1) != out_degree(*vfirst2, g2)) {
  111. std::cerr << g1.processor() << ": out-degree mismatch ("
  112. << out_degree(*vfirst1, g1) << " vs. "
  113. << out_degree(*vfirst2, g2) << ").\n";
  114. communicator(pg).abort(-1);
  115. }
  116. if (in_degree(*vfirst1, g1) != in_degree(*vfirst2, g2)) {
  117. std::cerr << g1.processor() << ": in-degree mismatch ("
  118. << in_degree(*vfirst1, g1) << " vs. "
  119. << in_degree(*vfirst2, g2) << ").\n";
  120. communicator(pg).abort(-1);
  121. }
  122. }
  123. // TODO: Check the actual edge targets
  124. // Build another, identical graph using add_edge
  125. if (i_am_root) {
  126. std::cout << "done.\nBuilding graph with add_edge from everywhere...";
  127. std::cout.flush();
  128. }
  129. gen.seed(seed);
  130. require_response_gen.seed(1);
  131. Graph g3(n, pg);
  132. {
  133. // Each processor will take a chunk of incoming edges and add
  134. // them. Some percentage of the edge additions will require an
  135. // immediate response from the owner of the edge. This should
  136. // create a lot of traffic when building the graph, but should
  137. // produce a graph identical to the other graphs.
  138. typedef graph_traits<Graph>::edges_size_type edges_size_type;
  139. erdos_renyi_iterator<minstd_rand, Graph> first(gen, n, p);
  140. edges_size_type chunk_size = edges_size_type(p*n*n)/numprocs;
  141. edges_size_type start = chunk_size * rank;
  142. edges_size_type remaining_edges =
  143. (rank < numprocs - 1? chunk_size
  144. : edges_size_type(p*n*n) - start);
  145. // Spin the generator to the first edge we're responsible for
  146. for (; start; ++first, --start) ;
  147. for (; remaining_edges; --remaining_edges, ++first) {
  148. Graph::lazy_add_edge lazy
  149. = add_edge(vertex(first->first, g3), vertex(first->second, g3), g3);
  150. if ((int)require_response_gen() % 100 < immediate_response_percent) {
  151. // Send out-of-band to require a response
  152. std::pair<graph_traits<Graph>::edge_descriptor, bool> result(lazy);
  153. BOOST_CHECK(source(result.first, g3) == vertex(first->first, g3));
  154. BOOST_CHECK(target(result.first, g3) == vertex(first->second, g3));
  155. }
  156. }
  157. }
  158. if (i_am_root) {
  159. std::cout << "synchronizing...";
  160. std::cout.flush();
  161. }
  162. synchronize(g3);
  163. // Verify that the two graphs are indeed identical.
  164. if (i_am_root) {
  165. std::cout << "done.\nVerifying graphs...";
  166. std::cout.flush();
  167. }
  168. // Check the number of vertices
  169. if (num_vertices(g1) != num_vertices(g3)) {
  170. std::cerr << g1.processor() << ": g1 has " << num_vertices(g1)
  171. << " vertices, g3 has " << num_vertices(g3) << " vertices.\n";
  172. communicator(pg).abort(-1);
  173. }
  174. // Check the number of edges
  175. if (num_edges(g1) != num_edges(g3)) {
  176. std::cerr << g1.processor() << ": g1 has " << num_edges(g1)
  177. << " edges, g3 has " << num_edges(g3) << " edges.\n";
  178. communicator(pg).abort(-1);
  179. }
  180. // Check the in-degree and out-degree of each vertex
  181. boost::tie(vfirst1, vlast1) = vertices(g1);
  182. boost::tie(vfirst2, vlast2) = vertices(g3);
  183. for(; vfirst1 != vlast1 && vfirst2 != vlast2; ++vfirst1, ++vfirst2) {
  184. if (out_degree(*vfirst1, g1) != out_degree(*vfirst2, g3)) {
  185. std::cerr << g1.processor() << ": out-degree mismatch ("
  186. << out_degree(*vfirst1, g1) << " vs. "
  187. << out_degree(*vfirst2, g3) << ").\n";
  188. communicator(pg).abort(-1);
  189. }
  190. if (in_degree(*vfirst1, g1) != in_degree(*vfirst2, g3)) {
  191. std::cerr << g1.processor() << ": in-degree mismatch ("
  192. << in_degree(*vfirst1, g1) << " vs. "
  193. << in_degree(*vfirst2, g3) << ").\n";
  194. communicator(pg).abort(-1);
  195. }
  196. }
  197. // TODO: Check the actual edge targets
  198. if (i_am_root) {
  199. std::cout << "done.\n";
  200. std::cout.flush();
  201. }
  202. return 0;
  203. }