// Copyright 2004 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace boost; // #define DEBUG typedef rand48 RandomGenerator; /**************************************************************************** * Timing ****************************************************************************/ #ifndef PBGL_ACCOUNTING typedef double time_type; inline time_type get_time() { timeval tp; gettimeofday(&tp, 0); return tp.tv_sec + tp.tv_usec / 1000000.0; } std::string print_time(time_type t) { std::ostringstream out; out << std::setiosflags(std::ios::fixed) << std::setprecision(2) << t; return out.str(); } #endif // PBGL_ACCOUNTING /**************************************************************************** * 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); } template struct ssca_visitor : bfs_visitor<> { typedef typename property_traits::value_type Weight; typedef typename property_traits::value_type ColorValue; typedef color_traits Color; ssca_visitor(DistanceMap& distance, const WeightMap& weight, ColorMap& color, Weight max_) : distance(distance), weight(weight), color(color), max_(max_) {} template void tree_edge(Edge e, const Graph& g) { int new_distance = get(weight, e) == (std::numeric_limits::max)() ? (std::numeric_limits::max)() : get(distance, source(e, g)) + get(weight, e); put(distance, target(e, g), new_distance); if (new_distance > max_) put(color, target(e, g), Color::black()); } private: DistanceMap& distance; const WeightMap& weight; ColorMap& color; Weight max_; }; // Generate source vertices for approximate BC template void generate_sources(const Graph& g, Buffer sources, typename graph_traits::vertices_size_type num_sources) { typedef typename graph_traits::vertex_descriptor vertex_descriptor; typedef typename graph_traits::vertices_size_type vertices_size_type; typedef typename graph_traits::vertex_iterator vertex_iterator; typedef typename boost::graph::parallel::process_group_type::type process_group_type; process_group_type pg = g.process_group(); typename process_group_type::process_id_type id = process_id(pg); typename process_group_type::process_size_type p = num_processes(pg); // Don't feel like adding a special case for num_sources < p assert(num_sources >= p); minstd_rand gen; uniform_int rand_vertex(0, num_vertices(g) - 1); std::vector all_sources, local_sources; vertices_size_type local_vertices = vertices_size_type(floor((double)num_sources / p)); local_vertices += (id < (num_sources - (p * local_vertices)) ? 1 : 0); while (local_vertices > 0) { vertex_iterator iter = vertices(g).first; std::advance(iter, rand_vertex(gen)); if (out_degree(*iter, g) != 0 && std::find(local_sources.begin(), local_sources.end(), *iter) == local_sources.end()) { local_sources.push_back(*iter); --local_vertices; } } all_gather(pg, local_sources.begin(), local_sources.end(), all_sources); std::sort(all_sources.begin(), all_sources.end()); for (typename std::vector::iterator iter = all_sources.begin(); iter != all_sources.end(); ++iter) sources.push(*iter); } // Kernel 2 - Classify large sets template void classify_sets(const Graph& g, const WeightMap& weight_map, std::vector::vertex_descriptor, typename graph_traits::vertex_descriptor> > & global_S) { typedef typename boost::graph::parallel::process_group_type::type process_group_type; process_group_type pg = g.process_group(); typedef typename graph_traits::vertex_descriptor vertex_descriptor; std::vector > S; time_type start = get_time(); #ifdef CSR typedef typename property_map::const_type OwnerMap; typedef typename property_map::const_type LocalMap; OwnerMap owner = get(vertex_owner, g); LocalMap local = get(vertex_local, g); #endif int max_ = 0; BGL_FORALL_EDGES_T(e, g, Graph) { #ifdef CSR if (get(owner, source(e, g)) == process_id(pg)) { #endif int w = get(weight_map, e); if (w > max_) { max_ = w; S.clear(); } if (w >= max_) S.push_back(std::make_pair(source(e, g), target(e, g))); #ifdef CSR } #endif } int global_max = all_reduce(pg, max_, boost::parallel::maximum()); if (max_ < global_max) S.clear(); global_S.clear(); all_gather(pg, S.begin(), S.end(), global_S); // This is probably unnecessary as long as the sets of edges owned by procs is disjoint std::sort(global_S.begin(), global_S.end()); std::unique(global_S.begin(), global_S.end()); synchronize(pg); time_type end = get_time(); if (process_id(pg) == 0) { std::cerr << " Distributed Graph: " << print_time(end - start) << std::endl << " Max int weight = " << global_max << std::endl; } } template void seq_classify_sets(const ProcessGroup& pg, const Graph& g, const WeightMap& weight_map, EdgeVector& S) { typedef typename graph_traits::edge_descriptor edge_descriptor; typedef typename property_traits::value_type edge_weight_type; time_type start = get_time(); edge_weight_type max_ = 0; BGL_FORALL_EDGES_T(e, g, Graph) { edge_weight_type w = get(weight_map, e); if (w > max_) { max_ = w; S.clear(); } if (w >= max_) S.push_back(e); } synchronize(pg); time_type end = get_time(); if (process_id(pg) == 0) std::cerr << " Non-Distributed Graph: " << print_time(end - start) << std::endl << " Max int weight = " << max_ << std::endl; } // Kernel 3 - Graph Extraction template void subgraph_extraction(Graph& g, const OwnerMap& owner, const LocalMap& local, const WeightMap& weight_map, DistanceMap distances, ColorMap color_map, const EdgeVector& S, int subGraphEdgeLength) { // Nick: I think turning the vertex black when the maximum distance is // exceeded will prevent BFS from exploring beyond the subgraph. // Unfortunately we can't run subgraph extraction in parallel // because the subgraphs may overlap typedef typename property_traits::value_type ColorValue; typedef color_traits Color; typedef typename graph_traits::edge_descriptor edge_descriptor; typedef typename graph_traits::vertex_descriptor vertex_descriptor; typedef typename boost::graph::parallel::process_group_type::type process_group_type; typedef boost::graph::distributed::distributed_queue > queue_t; process_group_type pg = g.process_group(); typename process_group_type::process_id_type id = process_id(pg); queue_t Q(pg, owner); EdgeVector sources(S.begin(), S.end()); #ifdef DEBUG std::vector > subgraphs; #endif synchronize(pg); typedef typename std::vector >::iterator source_iterator; time_type start = get_time(); for (source_iterator iter = sources.begin(); iter != sources.end(); ++iter) { // Reinitialize distance and color maps every BFS BGL_FORALL_VERTICES_T(v, g, Graph) { if (get(owner, v) == id) { local_put(color_map, v, Color::white()); local_put(distances, v, (std::numeric_limits::max)()); } } vertex_descriptor u = iter->first, v = iter->second; local_put(distances, u, 0); local_put(distances, v, 0); while (!Q.empty()) Q.pop(); if (get(owner, u) == id) Q.push(u); local_put(color_map, u, Color::gray()); breadth_first_search(g, v, Q, ssca_visitor (distances, weight_map, color_map, subGraphEdgeLength), color_map); // At this point all vertices with distance > 0 in addition to the // starting vertices compose the subgraph. #ifdef DEBUG subgraphs.push_back(std::vector()); std::vector& subgraph = subgraphs.back(); BGL_FORALL_VERTICES_T(v, g, Graph) { if (get(distances, v) < (std::numeric_limits::max)()) subgraph.push_back(v); } #endif } synchronize(pg); time_type end = get_time(); #ifdef DEBUG for (unsigned int i = 0; i < subgraphs.size(); i++) { all_gather(pg, subgraphs[i].begin(), subgraphs[i].end(), subgraphs[i]); std::sort(subgraphs[i].begin(), subgraphs[i].end()); subgraphs[i].erase(std::unique(subgraphs[i].begin(), subgraphs[i].end()), subgraphs[i].end()); } if (process_id(pg) == 0) for (int i = 0; abs(i) < subgraphs.size(); i++) { std::cerr << "Subgraph " << i << " :\n"; for (int j = 0; abs(j) < subgraphs[i].size(); j++) std::cerr << " " << get(local, subgraphs[i][j]) << "@" << get(owner, subgraphs[i][j]) << std::endl; } #endif if (process_id(pg) == 0) std::cerr << " Distributed Graph: " << print_time(end - start) << std::endl; } template void seq_subgraph_extraction(const ProcessGroup& pg, const Graph& g, const WeightMap& weight_map, DistanceMap distances, ColorMap color_map, const EdgeVector& S, int subGraphEdgeLength) { // Nick: I think turning the vertex black when the maximum distance is // exceeded will prevent BFS from exploring beyond the subgraph. using boost::graph::distributed::mpi_process_group; typedef typename property_traits::value_type ColorValue; typedef color_traits Color; typedef typename graph_traits::edge_descriptor edge_descriptor; typedef typename graph_traits::vertex_descriptor vertex_descriptor; boost::queue Q; std::vector sources(S.begin(), S.end()); #ifdef DEBUG std::vector > subgraphs; #endif synchronize(pg); typedef ProcessGroup process_group_type; typename process_group_type::process_id_type id = process_id(pg); typename process_group_type::process_size_type p = num_processes(pg); time_type start = get_time(); for (int i = id; i < sources.size(); i += p) { // Reinitialize distance and color maps every BFS BGL_FORALL_VERTICES_T(v, g, Graph) { put(color_map, v, Color::white()); put(distances, v, (std::numeric_limits::max)()); } vertex_descriptor u = source(sources[i], g), v = target(sources[i], g); put(distances, u, 0); put(distances, v, 0); while (!Q.empty()) Q.pop(); Q.push(u); put(color_map, u, Color::gray()); breadth_first_search(g, v, Q, ssca_visitor (distances, weight_map, color_map, subGraphEdgeLength), color_map); #ifdef DEBUG subgraphs.push_back(std::vector()); std::vector& subgraph = subgraphs.back(); BGL_FORALL_VERTICES_T(v, g, Graph) { if (get(distances, v) < (std::numeric_limits::max)()) subgraph.push_back(v); } #endif } synchronize(pg); time_type end = get_time(); #ifdef DEBUG std::vector ser_subgraphs; for (int i = 0; i < subgraphs.size(); ++i) { for (int j = 0; j < subgraphs[i].size(); ++j) ser_subgraphs.push_back(subgraphs[i][j]); ser_subgraphs.push_back(graph_traits::null_vertex()); } all_gather(pg, ser_subgraphs.begin(), ser_subgraphs.end(), ser_subgraphs); int i = 0; typename std::vector::iterator iter(ser_subgraphs.begin()); while (iter != ser_subgraphs.end()) { std::cerr << "Subgraph " << i << " :\n"; while (*iter != graph_traits::null_vertex()) { std::cerr << " " << *iter << std::endl; ++iter; } ++i; ++iter; } #endif if (process_id(pg) == 0) std::cerr << " Non-Distributed Graph: " << print_time(end - start) << std::endl; } template void extract_max_bc_vertices(const ProcessGroup& pg, const Graph& g, const CentralityMap& centrality, std::vector::vertex_descriptor>& max_bc_vec) { using boost::graph::parallel::process_group; using boost::parallel::all_gather; using boost::parallel::all_reduce; // Find set of vertices with highest BC score typedef typename property_traits::value_type centrality_type; std::vector max_bc_vertices; centrality_type max_ = 0; max_bc_vec.clear(); BGL_FORALL_VERTICES_T(v, g, Graph) { if (get(centrality, v) == max_) max_bc_vec.push_back(v); else if (get(centrality, v) > max_) { max_ = get(centrality, v); max_bc_vec.clear(); max_bc_vec.push_back(v); } } centrality_type global_max = all_reduce(pg, max_, boost::parallel::minimum()); if (global_max > max_) max_bc_vec.clear(); all_gather(pg, max_bc_vec.begin(), max_bc_vec.end(), max_bc_vec); } // Function object to filter edges divisible by 8 // EdgeWeightMap::value_type must be integral! template struct edge_weight_not_divisible_by_eight { typedef typename property_traits::value_type weight_type; edge_weight_not_divisible_by_eight() { } edge_weight_not_divisible_by_eight(EdgeWeightMap weight) : m_weight(weight) { } template bool operator()(const Edge& e) const { return (get(m_weight, e) & ((std::numeric_limits::max)() - 7)) != get(m_weight, e); } EdgeWeightMap m_weight; }; // // Vertex and Edge properties // #ifdef CSR typedef int weight_type; struct WeightedEdge { WeightedEdge(weight_type weight = 0) : weight(weight) { } weight_type weight; }; struct VertexProperties { VertexProperties(int distance = 0, default_color_type color = white_color) : distance(distance), color(color) { } int distance; default_color_type color; }; #endif template void run_non_distributed_graph_tests(RandomGenerator& gen, const ProcessGroup& pg, vertices_size_type n, edges_size_type m, std::size_t maxEdgeWeight, uint64_t seed, int K4Alpha, double a, double b, double c, double d, int subGraphEdgeLength, bool show_degree_dist, bool full_bc, bool verify) { #ifdef CSR typedef compressed_sparse_row_graph seqGraph; #else typedef adjacency_list >, // Edge properties property > seqGraph; #endif // Generate sequential graph for non_distributed betweenness centrality // Reseed the PRNG to get the same graph gen.seed(seed); synchronize(pg); time_type start = get_time(); #ifdef CSR seqGraph sg(edges_are_sorted, sorted_unique_rmat_iterator(gen, n, m, a, b, c, d), sorted_unique_rmat_iterator(), make_generator_iterator(gen, uniform_int(0, maxEdgeWeight)), n); #else seqGraph sg(unique_rmat_iterator(gen, n, m, a, b, c, d), unique_rmat_iterator(), make_generator_iterator(gen, uniform_int(0, maxEdgeWeight)), n); #endif // Not strictly necessary to synchronize here, but it make sure that the // time we measure is the time needed for all copies of the graph to be // constructed synchronize(pg); time_type end = get_time(); if (process_id(pg) == 0) std::cerr<< "Kernel 1:\n" << " Non-Distributed Graph: " << print_time(end - start) << std::endl; std::map degree_dist; if ( show_degree_dist ) { BGL_FORALL_VERTICES_T(v, sg, seqGraph) { degree_dist[out_degree(v, sg)]++; } std::cerr << "Degree - Fraction of vertices of that degree\n"; for (std::map::iterator iter = degree_dist.begin(); iter != degree_dist.end(); ++iter) std::cerr << " " << iter->first << " - " << double(iter->second) / num_vertices(sg) << std::endl << std::endl; } // // Kernel 2 - Classify large sets // std::vector::edge_descriptor> seqS; if (process_id(pg) == 0) std::cerr << "Kernel 2:\n"; seq_classify_sets(pg, sg, #ifdef CSR get(&WeightedEdge::weight, sg), #else get(edge_weight, sg), #endif seqS); // // Kernel 3 - Graph Extraction // #ifdef CSR typedef weight_type weight_t; weight_t unit_weight(1); #else int unit_weight(1);; #endif if (process_id(pg) == 0) std::cerr << "Kernel 3:\n"; seq_subgraph_extraction(pg, sg, #ifdef CSR // get(&WeightedEdge::weight, sg), ref_property_map::edge_descriptor, weight_t>(unit_weight), get(&VertexProperties::distance, sg), get(&VertexProperties::color, sg), #else // get(edge_weight, sg), ref_property_map::edge_descriptor, int>(unit_weight), get(vertex_distance, sg), get(vertex_color, sg), #endif seqS, subGraphEdgeLength); #ifdef CSR typedef property_map::type seqEdgeWeightMap; edge_weight_not_divisible_by_eight sg_filter(get(&WeightedEdge::weight, sg)); #else typedef property_map::type seqEdgeWeightMap; edge_weight_not_divisible_by_eight sg_filter(get(edge_weight, sg)); #endif typedef filtered_graph > filteredSeqGraph; filteredSeqGraph fsg(sg, sg_filter); std::vector::vertex_descriptor> max_seq_bc_vec; // Non-Distributed Centrality Map typedef property_map::const_type seqIndexMap; typedef iterator_property_map::iterator, seqIndexMap> seqCentralityMap; std::vector non_distributed_centralityS(num_vertices(sg), 0); seqCentralityMap non_distributed_centrality(non_distributed_centralityS.begin(), get(vertex_index, sg)); vertices_size_type n0 = 0; BGL_FORALL_VERTICES_T(v, fsg, filteredSeqGraph) { if (out_degree(v, fsg) == 0) ++n0; } if (process_id(pg) == 0) std::cerr << "Kernel 4:\n"; // Run Betweenness Centrality if (full_bc) { // Non-Distributed Graph BC start = get_time(); non_distributed_brandes_betweenness_centrality(pg, fsg, non_distributed_centrality); extract_max_bc_vertices(pg, fsg, non_distributed_centrality, max_seq_bc_vec); end = get_time(); edges_size_type nonDistributedExactTEPs = edges_size_type(floor(7 * n* (n - n0) / (end - start))); if (process_id(pg) == 0) std::cerr << " non-Distributed Graph Exact = " << print_time(end - start) << " (" << nonDistributedExactTEPs << " TEPs)\n"; } // Non-Distributed Graph Approximate BC std::vector nonDistributedApproxCentralityS(num_vertices(sg), 0); seqCentralityMap nonDistributedApproxCentrality(nonDistributedApproxCentralityS.begin(), get(vertex_index, sg)); queue::vertex_descriptor> sources; { minstd_rand gen; uniform_int rand_vertex(0, num_vertices(fsg) - 1); int remaining_sources = floor(pow(2, K4Alpha)); std::vector::vertex_descriptor> temp_sources; while (remaining_sources > 0) { typename graph_traits::vertex_descriptor v = vertex(rand_vertex(gen), fsg); if (out_degree(v, fsg) != 0 && std::find(temp_sources.begin(), temp_sources.end(), v) == temp_sources.end()) { temp_sources.push_back(v); --remaining_sources; } } for (int i = 0; i < temp_sources.size(); ++i) sources.push(temp_sources[i]); } start = get_time(); non_distributed_brandes_betweenness_centrality(pg, fsg, buffer(sources). centrality_map(nonDistributedApproxCentrality)); extract_max_bc_vertices(pg, fsg, nonDistributedApproxCentrality, max_seq_bc_vec); end = get_time(); edges_size_type nonDistributedApproxTEPs = edges_size_type(floor(7 * n * pow(2, K4Alpha) / (end - start))); if (process_id(pg) == 0) std::cerr << " Non-Distributed Graph Approximate (" << floor(pow(2, K4Alpha)) << " sources) = " << print_time(end - start) << " (" << nonDistributedApproxTEPs << " TEPs)\n"; // Verify Correctness of Kernel 4 if (full_bc && verify && process_id(pg) == 0) { std::vector seq_centralityS(num_vertices(fsg), 0); seqCentralityMap seq_centrality(seq_centralityS.begin(), get(vertex_index, fsg)); max_seq_bc_vec.clear(); property_traits::value_type max_ = 0; start = get_time(); brandes_betweenness_centrality(fsg, seq_centrality); typedef filtered_graph > filteredSeqGraph; BGL_FORALL_VERTICES_T(v, fsg, filteredSeqGraph ) { if (get(seq_centrality, v) == max_) max_seq_bc_vec.push_back(v); else if (get(seq_centrality, v) > max_) { max_ = get(seq_centrality, v); max_seq_bc_vec.clear(); max_seq_bc_vec.push_back(v); } } end = get_time(); edges_size_type sequentialTEPs = edges_size_type(floor(7 * n* (n - n0) / (end - start))); std::cerr << " Sequential = " << print_time(end - start) << " (" << sequentialTEPs << " TEPs)\n"; typename ProcessGroup::process_id_type id = process_id(pg); typename ProcessGroup::process_size_type p = num_processes(pg); assert((double)n/p == floor((double)n/p)); std::cerr << "\nVerifying non-scalable betweenness centrality...\n"; { bool passed = true; // Verify non-scalable betweenness centrality BGL_FORALL_VERTICES_T(v, sg, seqGraph) { if (get(non_distributed_centrality, v) != get(seq_centrality, v)) { std::cerr << " " << id << ": Error - centrality of " << v << " does not match the sequential result (" << get(non_distributed_centrality, v) << " vs. " << get(seq_centrality, v) << ")\n"; passed = false; } } if (passed) std::cerr << " PASSED\n"; } } } template void run_distributed_graph_tests(RandomGenerator& gen, const ProcessGroup& pg, vertices_size_type n, edges_size_type m, std::size_t maxEdgeWeight, uint64_t seed, int K4Alpha, double a, double b, double c, double d, int subGraphEdgeLength, bool show_degree_dist, bool emit_dot_file, bool full_bc, bool verify) { #ifdef CSR typedef compressed_sparse_row_graph > Graph; #else typedef adjacency_list, directedS, // Vertex properties property >, // Edge properties property > Graph; #endif gen.seed(seed); parallel::variant_distribution distrib = parallel::block(pg, n); typedef typename ProcessGroup::process_id_type process_id_type; process_id_type id = process_id(pg); typedef typename property_map::const_type OwnerMap; typedef typename property_map::const_type LocalMap; typedef keep_local_edges, process_id_type> EdgeFilter; // // Kernel 1 - Graph construction // Nick: The benchmark specifies that we only have to time graph generation from // edge tuples, the generator generates the edge tuples at graph construction // time so we're timing some overhead in the random number generator, etc. synchronize(pg); time_type start = get_time(); #ifdef CSR // typedef sorted_unique_rmat_iterator RMATIter; typedef sorted_rmat_iterator RMATIter; Graph g(//RMATIter(gen, n, m, a, b, c, d, false, true, EdgeFilter(distrib, id)), RMATIter(gen, n, m, a, b, c, d, true, keep_all_edges()), RMATIter(), make_generator_iterator(gen, uniform_int(0, maxEdgeWeight)), n, pg, distrib); #else typedef unique_rmat_iterator RMATIter; Graph g(RMATIter(gen, n, m, a, b, c, d, true EdgeFilter(distrib, id)), RMATIter(), make_generator_iterator(gen, uniform_int(0, maxEdgeWeight)), n, pg, distrib); #endif synchronize(pg); time_type end = get_time(); if (id == 0) std::cerr<< "Kernel 1:\n" << " Distributed Graph: " << print_time(end - start) << std::endl; if ( emit_dot_file ) write_graphviz("ssca.dot", g); // // Kernel 2 - Classify large sets // typedef typename graph_traits::vertex_descriptor vertex_descriptor; std::vector > S; if (id == 0) std::cerr << "Kernel 2:\n"; classify_sets(g, #ifdef CSR get(&WeightedEdge::weight, g), #else get(edge_weight, g), #endif S); // // Kernel 3 - Graph Extraction // OwnerMap owner = get(vertex_owner, g); LocalMap local = get(vertex_local, g); if (id == 0) std::cerr << "Kernel 3:\n"; #ifdef CSR typedef weight_type weight_t; weight_t unit_weight(1); #else int unit_weight(1);; #endif subgraph_extraction(g, owner, local, #ifdef CSR // get(&WeightedEdge::weight, g), ref_property_map::edge_descriptor, weight_t>(unit_weight), get(&VertexProperties::distance, g), get(&VertexProperties::color, g), #else // get(edge_weight, g), ref_property_map::edge_descriptor, int>(unit_weight), get(vertex_distance, g), get(vertex_color, g), #endif S, subGraphEdgeLength); // // Kernel 4 - Betweenness Centrality // // Filter edges with weights divisible by 8 #ifdef CSR typedef typename property_map::type EdgeWeightMap; edge_weight_not_divisible_by_eight filter(get(&WeightedEdge::weight, g)); #else typedef typename property_map::type EdgeWeightMap; edge_weight_not_divisible_by_eight filter(get(edge_weight, g)); #endif typedef filtered_graph > filteredGraph; filteredGraph fg(g, filter); // Vectors of max BC scores for all tests std::vector::vertex_descriptor> max_bc_vec; // Distributed Centrality Map typedef typename 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)); // Calculate number of vertices of degree 0 vertices_size_type local_n0 = 0, n0; BGL_FORALL_VERTICES_T(v, fg, filteredGraph) { if (out_degree(v, g) == 0) local_n0++; } n0 = boost::parallel::all_reduce(pg, local_n0, std::plus()); if (id == 0) std::cerr << "Kernel 4:\n"; // Run Betweenness Centrality if (full_bc) { // Distributed Graph Full BC start = get_time(); brandes_betweenness_centrality(fg, centrality); extract_max_bc_vertices(pg, g, centrality, max_bc_vec); end = get_time(); edges_size_type exactTEPs = edges_size_type(floor(7 * n* (n - n0) / (end - start))); if (id == 0) std::cerr << " Exact = " << print_time(end - start) << " (" << exactTEPs << " TEPs)\n"; } // Distributed Graph Approximate BC std::vector approxCentralityS(num_vertices(g), 0); CentralityMap approxCentrality(approxCentralityS.begin(), get(vertex_index, g)); queue sources; generate_sources(g, sources, vertices_size_type(floor(pow(2, K4Alpha)))); start = get_time(); brandes_betweenness_centrality(fg, buffer(sources).centrality_map(approxCentrality)); extract_max_bc_vertices(pg, fg, approxCentrality, max_bc_vec); end = get_time(); edges_size_type approxTEPs = edges_size_type(floor(7 * n * pow(2, K4Alpha) / (end - start))); if (id == 0) std::cerr << " Approximate (" << floor(pow(2, K4Alpha)) << " sources) = " << print_time(end - start) << " (" << approxTEPs << " TEPs)\n"; // Verify Correctness of Kernel 4 if (full_bc && verify && id == 0) { // Build non-distributed graph to verify against typedef adjacency_list >, // Edge properties property > seqGraph; gen.seed(seed); #ifdef CSR seqGraph sg(sorted_unique_rmat_iterator(gen, n, m, a, b, c, d), sorted_unique_rmat_iterator(), make_generator_iterator(gen, uniform_int(0, maxEdgeWeight)), n); #else seqGraph sg(unique_rmat_iterator(gen, n, m, a, b, c, d), unique_rmat_iterator(), make_generator_iterator(gen, uniform_int(0, maxEdgeWeight)), n); #endif typedef property_map::type seqEdgeWeightMap; edge_weight_not_divisible_by_eight sg_filter(get(edge_weight, sg)); filtered_graph > fsg(sg, sg_filter); // Build sequential centrality map typedef property_map::const_type seqIndexMap; typedef iterator_property_map::iterator, seqIndexMap> seqCentralityMap; std::vector seq_centralityS(num_vertices(sg), 0); seqCentralityMap seq_centrality(seq_centralityS.begin(), get(vertex_index, sg)); std::vector::vertex_descriptor> max_seq_bc_vec; max_seq_bc_vec.clear(); property_traits::value_type max_ = 0; start = get_time(); brandes_betweenness_centrality(fsg, seq_centrality); typedef filtered_graph > filteredSeqGraph; BGL_FORALL_VERTICES_T(v, fsg, filteredSeqGraph ) { if (get(seq_centrality, v) == max_) max_seq_bc_vec.push_back(v); else if (get(seq_centrality, v) > max_) { max_ = get(seq_centrality, v); max_seq_bc_vec.clear(); max_seq_bc_vec.push_back(v); } } end = get_time(); edges_size_type sequentialTEPs = edges_size_type(floor(7 * n* (n - n0) / (end - start))); std::cerr << " Sequential = " << print_time(end - start) << " (" << sequentialTEPs << " TEPs)\n"; typename ProcessGroup::process_size_type p = num_processes(pg); assert((double)n/p == floor((double)n/p)); std::cerr << "\nVerifying betweenness centrality...\n"; { bool passed = true; // Verify exact betweenness centrality BGL_FORALL_VERTICES_T(v, g, Graph) { if (get(centrality, v) != seq_centralityS[(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. " << seq_centralityS[(n/p) * get(owner, v) + get(local, v)] << ")\n"; passed = false; } } if (passed) std::cerr << " PASSED\n"; } } } void usage() { std::cerr << "SSCA benchmark.\n\n" << "Usage : ssca [options]\n\n" << "Options are:\n" << "\t--vertices v\t\t\tNumber of vertices in the graph\n" << "\t--edges v\t\t\tNumber of edges in the graph\n" << "\t--seed s\t\t\tSeed for synchronized random number generator\n" << "\t--full-bc\t\t\tRun full (exact) Betweenness Centrality\n" << "\t--max-weight miw\t\tMaximum integer edge weight\n" << "\t--subgraph-edge-length sel\tEdge length of subgraphs to extract in Kernel 3\n" << "\t--k4alpha k\t\t\tValue of K4Alpha in Kernel 4\n" << "\t--scale s\t\t\tSCALE parameter for the SSCA benchmark (sets n, m, and C)\n" << "\t--dot\t\t\t\tEmit a dot file containing the graph\n" << "\t--verify\t\t\tVerify result\n" << "\t--degree-dist\t\t\t Output degree distribution of graph\n" << "\t--no-distributed-graph\t\tOmit distributed graph tests\n"; } int test_main(int argc, char* argv[]) { mpi::environment env(argc, argv); using boost::graph::distributed::mpi_process_group; #ifdef CSR typedef compressed_sparse_row_graph > Graph; #else typedef adjacency_list, directedS, // Vertex properties property >, // Edge properties property > Graph; #endif typedef graph_traits::vertices_size_type vertices_size_type; typedef graph_traits::edges_size_type edges_size_type; RandomGenerator gen; // Default args vertices_size_type n = 100; edges_size_type m = 8*n; uint64_t seed = 1; int maxEdgeWeight = 100, subGraphEdgeLength = 8, K4Alpha = 0.5; double a = 0.57, b = 0.19, c = 0.19, d = 0.05; bool emit_dot_file = false, verify = false, full_bc = true, distributed_graph = true, show_degree_dist = false, non_distributed_graph = true; mpi_process_group pg; if (argc == 1) { if (process_id(pg) == 0) usage(); exit(-1); } // Parse args for (int i = 1; i < argc; ++i) { std::string arg = argv[i]; if (arg == "--vertices") n = boost::lexical_cast( argv[i+1] ); if (arg == "--seed") seed = boost::lexical_cast( argv[i+1] ); if (arg == "--full-bc") full_bc = (argv[i+1]== "true"); if (arg == "--max-weight") maxEdgeWeight = boost::lexical_cast( argv[i+1] ); if (arg == "--subgraph-edge-length") subGraphEdgeLength = boost::lexical_cast( argv[i+1] ); if (arg == "--edges") m = boost::lexical_cast( argv[i+1] ); if (arg == "--k4alpha") K4Alpha = boost::lexical_cast( argv[i+1] ); if (arg == "--dot") emit_dot_file = true; if (arg == "--verify") verify = true; if (arg == "--degree-dist") show_degree_dist = true; if (arg == "--no-distributed-graph") distributed_graph = false; if (arg == "--no-non-distributed-graph") non_distributed_graph = false; if (arg == "--scale") { vertices_size_type scale = boost::lexical_cast( argv[i+1] ); maxEdgeWeight = n = vertices_size_type(floor(pow(2, scale))); m = 8 * n; } if (arg == "--help") { if (process_id(pg) == 0) usage(); exit(-1); } } if (non_distributed_graph) { if (process_id(pg) == 0) std::cerr << "Non-Distributed Graph Tests\n"; run_non_distributed_graph_tests(gen, pg, n, m, maxEdgeWeight, seed, K4Alpha, a, b, c, d, subGraphEdgeLength, show_degree_dist, full_bc, verify); } if (distributed_graph) { if (process_id(pg) == 0) std::cerr << "Distributed Graph Tests\n"; run_distributed_graph_tests(gen, pg, n, m, maxEdgeWeight, seed, K4Alpha, a, b, c, d, subGraphEdgeLength, show_degree_dist, emit_dot_file, full_bc, verify); } return 0; }