// Copyright (C) 2006 The Trustees of Indiana University. // 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) // Authors: Nick Edmonds // Andrew Lumsdaine #include #define CSR #ifdef CSR # include #else # include #endif #include #include #include #include #include #include #include #include #include #include #ifdef BOOST_NO_EXCEPTIONS void boost::throw_exception(std::exception const& ex) { std::cout << ex.what() << std::endl; abort(); } #endif /**************************************************************************** * Edge weight generator iterator * ****************************************************************************/ template class generator_iterator { public: typedef std::input_iterator_tag iterator_category; typedef typename F::result_type value_type; typedef const value_type& reference; typedef const value_type* pointer; typedef void difference_type; explicit generator_iterator(RandomGenerator& gen, const F& f = F()) : f(f), gen(&gen) { value = this->f(gen); } reference operator*() const { return value; } pointer operator->() const { return &value; } generator_iterator& operator++() { value = f(*gen); return *this; } generator_iterator operator++(int) { generator_iterator temp(*this); ++(*this); return temp; } bool operator==(const generator_iterator& other) const { return f == other.f; } bool operator!=(const generator_iterator& other) const { return !(*this == other); } private: F f; RandomGenerator* gen; value_type value; }; template inline generator_iterator make_generator_iterator( RandomGenerator& gen, const F& f) { return generator_iterator(gen, f); } using namespace boost; using boost::graph::distributed::mpi_process_group; typedef int weight_type; struct WeightedEdge { WeightedEdge(weight_type weight = 0) : weight(weight) { } weight_type weight; template void serialize(Archiver& ar, const unsigned int /*version*/) { ar & weight; } }; int test_main(int argc, char* argv[]) { mpi::environment env(argc, argv); #ifdef CSR typedef compressed_sparse_row_graph > Graph; typedef compressed_sparse_row_graph seqGraph; #else typedef adjacency_list, directedS, no_property, property > Graph; typedef adjacency_list > seqGraph; #endif typedef sorted_erdos_renyi_iterator ERIter; int n = 100; double prob = 0.1; int C = 3; minstd_rand gen; gen.seed(1); // NOTE: Weighted betweenness centrality only works with non-zero edge weights // Build graphs Graph g(edges_are_sorted, ERIter(gen, n, prob), ERIter(), make_generator_iterator(gen, uniform_int(1, C)), n); gen.seed(1); // Re-seed PRNG so we get the same graph seqGraph sg( #ifdef CSR edges_are_sorted, #endif ERIter(gen, n, prob), ERIter(), make_generator_iterator(gen, uniform_int(1, C)), n); // Test Betweenness Centrality typedef property_map::const_type IndexMap; typedef iterator_property_map::iterator, IndexMap> CentralityMap; std::vector centralityS(num_vertices(g), 0); CentralityMap centrality(centralityS.begin(), get(vertex_index, g)); brandes_betweenness_centrality(g, centrality); std::vector weightedCentralityS(num_vertices(g), 0); CentralityMap weightedCentrality(weightedCentralityS.begin(), get(vertex_index, g)); brandes_betweenness_centrality(g, centrality_map(weightedCentrality). #ifdef CSR weight_map(get(&WeightedEdge::weight, g))); #else weight_map(get(edge_weight, g))); #endif int g_cpd = central_point_dominance(g, centrality); // // Non-distributed (embarassingly parallel) Betweenness Centrality // typedef boost::graph::parallel::process_group_type::type process_group_type; process_group_type pg = process_group(g); process_group_type::process_id_type id = process_id(pg); process_group_type::process_size_type p = num_processes(pg); typedef property_map::const_type seqIndexMap; typedef iterator_property_map::iterator, seqIndexMap> seqCentralityMap; std::vector nonDistributedCentralityS(num_vertices(sg), 0); seqCentralityMap nonDistributedCentrality(nonDistributedCentralityS.begin(), get(vertex_index, sg)); std::vector nonDistributedWeightedCentralityS(num_vertices(sg), 0); seqCentralityMap nonDistributedWeightedCentrality(nonDistributedWeightedCentralityS.begin(), get(vertex_index, sg)); non_distributed_brandes_betweenness_centrality(pg, sg, nonDistributedCentrality); non_distributed_brandes_betweenness_centrality(pg, sg, centrality_map(nonDistributedWeightedCentrality). #ifdef CSR weight_map(get(&WeightedEdge::weight, sg))); #else weight_map(get(edge_weight, sg))); #endif // // Verify // std::vector seqCentralityS(num_vertices(sg), 0); seqCentralityMap seqCentrality(seqCentralityS.begin(), get(vertex_index, sg)); std::vector seqWeightedCentralityS(num_vertices(sg), 0); seqCentralityMap seqWeightedCentrality(seqWeightedCentralityS.begin(), get(vertex_index, sg)); brandes_betweenness_centrality(sg, seqCentrality); brandes_betweenness_centrality(sg, centrality_map(seqWeightedCentrality). #ifdef CSR weight_map(get(&WeightedEdge::weight, sg))); #else weight_map(get(edge_weight, sg))); #endif int sg_cpd = central_point_dominance(sg, seqCentrality); // Verify exact betweenness centrality // // We're cheating when we map vertices in g to vertices in sg // since we know that g is using the block distribution here typedef property_map::const_type OwnerMap; typedef property_map::const_type LocalMap; OwnerMap owner = get(vertex_owner, g); LocalMap local = get(vertex_local, g); bool passed = true; { typedef graph_traits::vertex_iterator vertex_iterator; vertex_iterator v, v_end; for (boost::tie(v, v_end) = vertices(g); v != v_end; ++v) { if (get(centrality, *v) != seqCentralityS[(n/p) * get(owner, *v) + get(local, *v)]) { std::cerr << " " << id << ": Error - centrality of " << get(local, *v) << "@" << get(owner, *v) << " does not match the sequential result (" << get(centrality, *v) << " vs. " << seqCentralityS[(n/p) * get(owner, *v) + get(local, *v)] << ")\n"; passed = false; } if (get(weightedCentrality, *v) != seqWeightedCentralityS[(n/p) * get(owner, *v) + get(local, *v)]) { std::cerr << " " << id << ": Error - weighted centrality of " << get(local, *v) << "@" << get(owner, *v) << " does not match the sequential result (" << get(weightedCentrality, *v) << " vs. " << seqWeightedCentralityS[(n/p) * get(owner, *v) + get(local, *v)] << ")\n"; passed = false; } } } if (id == 0) { typedef graph_traits::vertex_iterator vertex_iterator; vertex_iterator v, v_end; for (boost::tie(v, v_end) = vertices(sg); v != v_end; ++v) { if (get(seqCentrality, *v) != get(nonDistributedCentrality, *v)) { std::cerr << " " << id << ": Error - non-distributed centrality of " << *v << " does not match the sequential result (" << get(nonDistributedCentrality, *v) << " vs. " << get(seqCentrality, *v) << ")\n"; passed = false; } if (get(seqWeightedCentrality, *v) != get(nonDistributedWeightedCentrality, *v)) { std::cerr << " " << id << ": Error - non-distributed weighted centrality of " << *v << " does not match the sequential result (" << get(nonDistributedWeightedCentrality, *v) << " vs. " << get(seqCentrality, *v) << ")\n"; passed = false; } } } if (g_cpd != sg_cpd) { passed = false; std::cerr << "Central point dominance did not match the sequential result\n"; } if (!passed) MPI_Abort(MPI_COMM_WORLD, -1); return 0; }