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