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