]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/graph_parallel/test/ssca.cpp
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / boost / libs / graph_parallel / test / ssca.cpp
1 // Copyright 2004 The Trustees of Indiana University.
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 // Authors: Nick Edmonds
8 // Andrew Lumsdaine
9
10 #include <boost/graph/use_mpi.hpp>
11
12 #define CSR
13
14 #ifdef CSR
15 # include <boost/graph/distributed/compressed_sparse_row_graph.hpp>
16 #else
17 # include <boost/graph/distributed/adjacency_list.hpp>
18 #endif
19
20 #include <boost/core/lightweight_test.hpp>
21 #include <boost/graph/distributed/mpi_process_group.hpp>
22 #include <boost/graph/distributed/queue.hpp>
23
24 #include <boost/graph/parallel/distribution.hpp>
25 #include <boost/lexical_cast.hpp>
26 #include <boost/bind.hpp>
27 #include <sys/time.h>
28 #include <time.h>
29
30 #include <boost/random.hpp>
31 #include <boost/property_map/parallel/distributed_property_map.hpp>
32
33 #include <boost/random/linear_congruential.hpp>
34
35 #include <boost/graph/distributed/graphviz.hpp>
36 #include <boost/graph/graph_traits.hpp>
37 #include <boost/graph/iteration_macros.hpp>
38
39 #include <boost/graph/parallel/algorithm.hpp>
40 #include <boost/graph/breadth_first_search.hpp>
41 #include <boost/pending/queue.hpp>
42
43 #include <boost/graph/rmat_graph_generator.hpp>
44 #include <boost/graph/distributed/betweenness_centrality.hpp>
45 #include <boost/graph/distributed/filtered_graph.hpp>
46 #include <boost/graph/parallel/container_traits.hpp>
47
48 #include <boost/graph/properties.hpp>
49
50 #include <algorithm>
51 #include <vector>
52 #include <string>
53 #include <iostream>
54 #include <iomanip>
55 #include <fstream>
56 #include <string>
57 #include <sstream>
58 #include <stdint.h>
59
60 using namespace boost;
61
62 // #define DEBUG
63
64 typedef rand48 RandomGenerator;
65
66 /****************************************************************************
67 * Timing
68 ****************************************************************************/
69 #ifndef PBGL_ACCOUNTING
70
71 typedef double time_type;
72
73 inline time_type get_time()
74 {
75 timeval tp;
76 gettimeofday(&tp, 0);
77 return tp.tv_sec + tp.tv_usec / 1000000.0;
78 }
79
80 std::string print_time(time_type t)
81 {
82 std::ostringstream out;
83 out << std::setiosflags(std::ios::fixed) << std::setprecision(2) << t;
84 return out.str();
85 }
86
87 #endif // PBGL_ACCOUNTING
88
89 /****************************************************************************
90 * Edge weight generator iterator *
91 ****************************************************************************/
92 template<typename F, typename RandomGenerator>
93 class generator_iterator
94 {
95 public:
96 typedef std::input_iterator_tag iterator_category;
97 typedef typename F::result_type value_type;
98 typedef const value_type& reference;
99 typedef const value_type* pointer;
100 typedef void difference_type;
101
102 explicit
103 generator_iterator(RandomGenerator& gen, const F& f = F())
104 : f(f), gen(&gen)
105 {
106 value = this->f(gen);
107 }
108
109 reference operator*() const { return value; }
110 pointer operator->() const { return &value; }
111
112 generator_iterator& operator++()
113 {
114 value = f(*gen);
115 return *this;
116 }
117
118 generator_iterator operator++(int)
119 {
120 generator_iterator temp(*this);
121 ++(*this);
122 return temp;
123 }
124
125 bool operator==(const generator_iterator& other) const
126 { return f == other.f; }
127
128 bool operator!=(const generator_iterator& other) const
129 { return !(*this == other); }
130
131 private:
132 F f;
133 RandomGenerator* gen;
134 value_type value;
135 };
136
137 template<typename F, typename RandomGenerator>
138 inline generator_iterator<F, RandomGenerator>
139 make_generator_iterator( RandomGenerator& gen, const F& f)
140 { return generator_iterator<F, RandomGenerator>(gen, f); }
141
142 template<typename Graph, typename DistanceMap, typename WeightMap, typename ColorMap>
143 struct ssca_visitor : bfs_visitor<>
144 {
145 typedef typename property_traits<WeightMap>::value_type Weight;
146 typedef typename property_traits<ColorMap>::value_type ColorValue;
147 typedef color_traits<ColorValue> Color;
148
149 ssca_visitor(DistanceMap& distance, const WeightMap& weight, ColorMap& color,
150 Weight max_)
151 : distance(distance), weight(weight), color(color), max_(max_) {}
152
153 template<typename Edge>
154 void tree_edge(Edge e, const Graph& g)
155 {
156 int new_distance = get(weight, e) == (std::numeric_limits<Weight>::max)() ?
157 (std::numeric_limits<Weight>::max)() : get(distance, source(e, g)) + get(weight, e);
158
159 put(distance, target(e, g), new_distance);
160 if (new_distance > max_)
161 put(color, target(e, g), Color::black());
162 }
163
164 private:
165 DistanceMap& distance;
166 const WeightMap& weight;
167 ColorMap& color;
168 Weight max_;
169 };
170
171 // Generate source vertices for approximate BC
172 template <typename Graph, typename Buffer>
173 void
174 generate_sources(const Graph& g, Buffer sources,
175 typename graph_traits<Graph>::vertices_size_type num_sources)
176 {
177 typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
178 typedef typename graph_traits<Graph>::vertices_size_type vertices_size_type;
179 typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
180
181 typedef typename boost::graph::parallel::process_group_type<Graph>::type
182 process_group_type;
183 process_group_type pg = g.process_group();
184
185 typename process_group_type::process_id_type id = process_id(pg);
186 typename process_group_type::process_size_type p = num_processes(pg);
187
188 // Don't feel like adding a special case for num_sources < p
189 assert(num_sources >= p);
190
191 minstd_rand gen;
192 uniform_int<vertices_size_type> rand_vertex(0, num_vertices(g) - 1);
193 std::vector<vertex_descriptor> all_sources, local_sources;
194 vertices_size_type local_vertices = vertices_size_type(floor((double)num_sources / p));
195 local_vertices += (id < (num_sources - (p * local_vertices)) ? 1 : 0);
196
197 while (local_vertices > 0) {
198 vertex_iterator iter = vertices(g).first;
199 std::advance(iter, rand_vertex(gen));
200
201 if (out_degree(*iter, g) != 0
202 && std::find(local_sources.begin(), local_sources.end(), *iter) == local_sources.end()) {
203 local_sources.push_back(*iter);
204 --local_vertices;
205 }
206 }
207 all_gather(pg, local_sources.begin(), local_sources.end(), all_sources);
208 std::sort(all_sources.begin(), all_sources.end());
209 for (typename std::vector<vertex_descriptor>::iterator iter = all_sources.begin();
210 iter != all_sources.end(); ++iter)
211 sources.push(*iter);
212 }
213
214 // Kernel 2 - Classify large sets
215 template <typename Graph, typename WeightMap>
216 void classify_sets(const Graph& g, const WeightMap& weight_map,
217 std::vector<std::pair<typename graph_traits<Graph>::vertex_descriptor,
218 typename graph_traits<Graph>::vertex_descriptor> > & global_S)
219 {
220 typedef typename boost::graph::parallel::process_group_type<Graph>::type
221 process_group_type;
222 process_group_type pg = g.process_group();
223
224 typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
225 std::vector<std::pair<vertex_descriptor, vertex_descriptor> > S;
226
227 time_type start = get_time();
228
229 #ifdef CSR
230 typedef typename property_map<Graph, vertex_owner_t>::const_type OwnerMap;
231 typedef typename property_map<Graph, vertex_local_t>::const_type LocalMap;
232 OwnerMap owner = get(vertex_owner, g);
233 LocalMap local = get(vertex_local, g);
234 #endif
235
236 int max_ = 0;
237 BGL_FORALL_EDGES_T(e, g, Graph) {
238 #ifdef CSR
239 if (get(owner, source(e, g)) == process_id(pg)) {
240 #endif
241 int w = get(weight_map, e);
242 if (w > max_) {
243 max_ = w;
244 S.clear();
245 }
246
247 if (w >= max_)
248 S.push_back(std::make_pair(source(e, g), target(e, g)));
249 #ifdef CSR
250 }
251 #endif
252 }
253
254 int global_max = all_reduce(pg, max_, boost::parallel::maximum<int>());
255 if (max_ < global_max)
256 S.clear();
257
258 global_S.clear();
259
260 all_gather(pg, S.begin(), S.end(), global_S);
261
262 // This is probably unnecessary as long as the sets of edges owned by procs is disjoint
263 std::sort(global_S.begin(), global_S.end());
264 std::unique(global_S.begin(), global_S.end());
265
266 synchronize(pg);
267
268 time_type end = get_time();
269
270 if (process_id(pg) == 0) {
271 std::cerr << " Distributed Graph: " << print_time(end - start) << std::endl
272 << " Max int weight = " << global_max << std::endl;
273 }
274 }
275
276 template <typename ProcessGroup, typename Graph, typename WeightMap,
277 typename EdgeVector>
278 void seq_classify_sets(const ProcessGroup& pg, const Graph& g,
279 const WeightMap& weight_map, EdgeVector& S)
280 {
281 typedef typename graph_traits<Graph>::edge_descriptor edge_descriptor;
282 typedef typename property_traits<WeightMap>::value_type edge_weight_type;
283
284 time_type start = get_time();
285
286 edge_weight_type max_ = 0;
287 BGL_FORALL_EDGES_T(e, g, Graph) {
288 edge_weight_type w = get(weight_map, e);
289 if (w > max_) {
290 max_ = w;
291 S.clear();
292 }
293
294 if (w >= max_)
295 S.push_back(e);
296 }
297
298 synchronize(pg);
299
300 time_type end = get_time();
301
302 if (process_id(pg) == 0)
303 std::cerr << " Non-Distributed Graph: " << print_time(end - start) << std::endl
304 << " Max int weight = " << max_ << std::endl;
305 }
306
307 // Kernel 3 - Graph Extraction
308 template <typename Graph, typename OwnerMap, typename LocalMap,
309 typename WeightMap, typename DistanceMap, typename ColorMap,
310 typename EdgeVector>
311 void subgraph_extraction(Graph& g, const OwnerMap& owner, const LocalMap& local,
312 const WeightMap& weight_map, DistanceMap distances,
313 ColorMap color_map, const EdgeVector& S,
314 int subGraphEdgeLength)
315 {
316 // Nick: I think turning the vertex black when the maximum distance is
317 // exceeded will prevent BFS from exploring beyond the subgraph.
318 // Unfortunately we can't run subgraph extraction in parallel
319 // because the subgraphs may overlap
320
321 typedef typename property_traits<ColorMap>::value_type ColorValue;
322 typedef color_traits<ColorValue> Color;
323
324 typedef typename graph_traits<Graph>::edge_descriptor edge_descriptor;
325 typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
326
327 typedef typename boost::graph::parallel::process_group_type<Graph>::type
328 process_group_type;
329
330 typedef boost::graph::distributed::distributed_queue<process_group_type,
331 OwnerMap, queue<vertex_descriptor> > queue_t;
332
333 process_group_type pg = g.process_group();
334 typename process_group_type::process_id_type id = process_id(pg);
335
336 queue_t Q(pg, owner);
337
338 EdgeVector sources(S.begin(), S.end());
339
340 #ifdef DEBUG
341 std::vector<std::vector<vertex_descriptor> > subgraphs;
342 #endif
343
344 synchronize(pg);
345
346 typedef typename std::vector<std::pair<vertex_descriptor, vertex_descriptor> >::iterator
347 source_iterator;
348
349 time_type start = get_time();
350 for (source_iterator iter = sources.begin(); iter != sources.end(); ++iter) {
351 // Reinitialize distance and color maps every BFS
352 BGL_FORALL_VERTICES_T(v, g, Graph) {
353 if (get(owner, v) == id) {
354 local_put(color_map, v, Color::white());
355 local_put(distances, v, (std::numeric_limits<int>::max)());
356 }
357 }
358
359 vertex_descriptor u = iter->first, v = iter->second;
360
361 local_put(distances, u, 0);
362 local_put(distances, v, 0);
363
364 while (!Q.empty()) Q.pop();
365 if (get(owner, u) == id)
366 Q.push(u);
367
368 local_put(color_map, u, Color::gray());
369
370 breadth_first_search(g, v, Q,
371 ssca_visitor<Graph, DistanceMap, WeightMap, ColorMap>
372 (distances, weight_map, color_map, subGraphEdgeLength),
373 color_map);
374
375 // At this point all vertices with distance > 0 in addition to the
376 // starting vertices compose the subgraph.
377 #ifdef DEBUG
378 subgraphs.push_back(std::vector<vertex_descriptor>());
379 std::vector<vertex_descriptor>& subgraph = subgraphs.back();
380
381 BGL_FORALL_VERTICES_T(v, g, Graph) {
382 if (get(distances, v) < (std::numeric_limits<int>::max)())
383 subgraph.push_back(v);
384 }
385 #endif
386 }
387
388 synchronize(pg);
389
390 time_type end = get_time();
391
392 #ifdef DEBUG
393 for (unsigned int i = 0; i < subgraphs.size(); i++) {
394 all_gather(pg, subgraphs[i].begin(), subgraphs[i].end(), subgraphs[i]);
395 std::sort(subgraphs[i].begin(), subgraphs[i].end());
396 subgraphs[i].erase(std::unique(subgraphs[i].begin(), subgraphs[i].end()),
397 subgraphs[i].end());
398 }
399
400 if (process_id(pg) == 0)
401 for (int i = 0; abs(i) < subgraphs.size(); i++) {
402 std::cerr << "Subgraph " << i << " :\n";
403 for (int j = 0; abs(j) < subgraphs[i].size(); j++)
404 std::cerr << " " << get(local, subgraphs[i][j]) << "@"
405 << get(owner, subgraphs[i][j]) << std::endl;
406 }
407 #endif
408
409 if (process_id(pg) == 0)
410 std::cerr << " Distributed Graph: " << print_time(end - start) << std::endl;
411 }
412
413 template <typename ProcessGroup, typename Graph, typename WeightMap,
414 typename DistanceMap, typename ColorMap, typename EdgeVector>
415 void seq_subgraph_extraction(const ProcessGroup& pg, const Graph& g,
416 const WeightMap& weight_map, DistanceMap distances,
417 ColorMap color_map, const EdgeVector& S,
418 int subGraphEdgeLength)
419 {
420 // Nick: I think turning the vertex black when the maximum distance is
421 // exceeded will prevent BFS from exploring beyond the subgraph.
422
423 using boost::graph::distributed::mpi_process_group;
424
425 typedef typename property_traits<ColorMap>::value_type ColorValue;
426 typedef color_traits<ColorValue> Color;
427
428 typedef typename graph_traits<Graph>::edge_descriptor edge_descriptor;
429 typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
430
431 boost::queue<vertex_descriptor> Q;
432
433 std::vector<edge_descriptor> sources(S.begin(), S.end());
434
435 #ifdef DEBUG
436 std::vector<std::vector<vertex_descriptor> > subgraphs;
437 #endif
438
439 synchronize(pg);
440
441 typedef ProcessGroup process_group_type;
442
443 typename process_group_type::process_id_type id = process_id(pg);
444 typename process_group_type::process_size_type p = num_processes(pg);
445
446 time_type start = get_time();
447
448 for (int i = id; i < sources.size(); i += p) {
449
450 // Reinitialize distance and color maps every BFS
451 BGL_FORALL_VERTICES_T(v, g, Graph) {
452 put(color_map, v, Color::white());
453 put(distances, v, (std::numeric_limits<int>::max)());
454 }
455
456 vertex_descriptor u = source(sources[i], g),
457 v = target(sources[i], g);
458
459 put(distances, u, 0);
460 put(distances, v, 0);
461
462 while (!Q.empty()) Q.pop();
463 Q.push(u);
464
465 put(color_map, u, Color::gray());
466
467 breadth_first_search(g, v, Q,
468 ssca_visitor<Graph, DistanceMap, WeightMap, ColorMap>
469 (distances, weight_map, color_map, subGraphEdgeLength),
470 color_map);
471
472 #ifdef DEBUG
473 subgraphs.push_back(std::vector<vertex_descriptor>());
474 std::vector<vertex_descriptor>& subgraph = subgraphs.back();
475
476 BGL_FORALL_VERTICES_T(v, g, Graph) {
477 if (get(distances, v) < (std::numeric_limits<int>::max)())
478 subgraph.push_back(v);
479 }
480 #endif
481 }
482
483 synchronize(pg);
484
485 time_type end = get_time();
486
487 #ifdef DEBUG
488 std::vector<vertex_descriptor> ser_subgraphs;
489
490 for (int i = 0; i < subgraphs.size(); ++i) {
491 for (int j = 0; j < subgraphs[i].size(); ++j)
492 ser_subgraphs.push_back(subgraphs[i][j]);
493 ser_subgraphs.push_back(graph_traits<Graph>::null_vertex());
494 }
495
496 all_gather(pg, ser_subgraphs.begin(), ser_subgraphs.end(), ser_subgraphs);
497
498 int i = 0;
499 typename std::vector<vertex_descriptor>::iterator iter(ser_subgraphs.begin());
500
501 while (iter != ser_subgraphs.end()) {
502 std::cerr << "Subgraph " << i << " :\n";
503 while (*iter != graph_traits<Graph>::null_vertex()) {
504 std::cerr << " " << *iter << std::endl;
505 ++iter;
506 }
507 ++i;
508 ++iter;
509 }
510 #endif
511
512 if (process_id(pg) == 0)
513 std::cerr << " Non-Distributed Graph: " << print_time(end - start) << std::endl;
514 }
515
516 template <typename ProcessGroup, typename Graph, typename CentralityMap>
517 void
518 extract_max_bc_vertices(const ProcessGroup& pg, const Graph& g, const CentralityMap& centrality,
519 std::vector<typename graph_traits<Graph>::vertex_descriptor>& max_bc_vec)
520 {
521 using boost::graph::parallel::process_group;
522 using boost::parallel::all_gather;
523 using boost::parallel::all_reduce;
524
525 // Find set of vertices with highest BC score
526 typedef typename property_traits<CentralityMap>::value_type centrality_type;
527 std::vector<centrality_type> max_bc_vertices;
528 centrality_type max_ = 0;
529
530 max_bc_vec.clear();
531
532 BGL_FORALL_VERTICES_T(v, g, Graph) {
533 if (get(centrality, v) == max_)
534 max_bc_vec.push_back(v);
535 else if (get(centrality, v) > max_) {
536 max_ = get(centrality, v);
537 max_bc_vec.clear();
538 max_bc_vec.push_back(v);
539 }
540 }
541
542 centrality_type global_max = all_reduce(pg, max_, boost::parallel::minimum<int>());
543
544 if (global_max > max_)
545 max_bc_vec.clear();
546
547 all_gather(pg, max_bc_vec.begin(), max_bc_vec.end(), max_bc_vec);
548 }
549
550
551 // Function object to filter edges divisible by 8
552 // EdgeWeightMap::value_type must be integral!
553 template <typename EdgeWeightMap>
554 struct edge_weight_not_divisible_by_eight {
555 typedef typename property_traits<EdgeWeightMap>::value_type weight_type;
556
557 edge_weight_not_divisible_by_eight() { }
558 edge_weight_not_divisible_by_eight(EdgeWeightMap weight) : m_weight(weight) { }
559 template <typename Edge>
560 bool operator()(const Edge& e) const {
561 return (get(m_weight, e) & ((std::numeric_limits<weight_type>::max)() - 7)) != get(m_weight, e);
562 }
563
564 EdgeWeightMap m_weight;
565 };
566
567 //
568 // Vertex and Edge properties
569 //
570 #ifdef CSR
571 typedef int weight_type;
572
573 struct WeightedEdge {
574 WeightedEdge(weight_type weight = 0) : weight(weight) { }
575
576 weight_type weight;
577 };
578
579 struct VertexProperties {
580 VertexProperties(int distance = 0, default_color_type color = white_color)
581 : distance(distance), color(color) { }
582
583 int distance;
584 default_color_type color;
585 };
586 #endif
587
588 template <typename RandomGenerator, typename ProcessGroup, typename vertices_size_type,
589 typename edges_size_type>
590 void
591 run_non_distributed_graph_tests(RandomGenerator& gen, const ProcessGroup& pg,
592 vertices_size_type n, edges_size_type m,
593 std::size_t maxEdgeWeight, uint64_t seed,
594 int K4Alpha, double a, double b, double c, double d,
595 int subGraphEdgeLength, bool show_degree_dist,
596 bool full_bc, bool verify)
597 {
598 #ifdef CSR
599 typedef compressed_sparse_row_graph<directedS, VertexProperties, WeightedEdge>
600 seqGraph;
601 #else
602 typedef adjacency_list<vecS, vecS, directedS,
603 // Vertex properties
604 property<vertex_distance_t, int,
605 property<vertex_color_t, default_color_type> >,
606 // Edge properties
607 property<edge_weight_t, int> > seqGraph;
608 #endif
609
610 // Generate sequential graph for non_distributed betweenness centrality
611 // Reseed the PRNG to get the same graph
612 gen.seed(seed);
613
614 synchronize(pg);
615
616 time_type start = get_time();
617
618 #ifdef CSR
619 seqGraph sg(edges_are_sorted,
620 sorted_unique_rmat_iterator<RandomGenerator, seqGraph>(gen, n, m, a, b, c, d),
621 sorted_unique_rmat_iterator<RandomGenerator, seqGraph>(),
622 make_generator_iterator(gen, uniform_int<int>(0, maxEdgeWeight)),
623 n);
624 #else
625 seqGraph sg(unique_rmat_iterator<RandomGenerator, seqGraph>(gen, n, m, a, b, c, d),
626 unique_rmat_iterator<RandomGenerator, seqGraph>(),
627 make_generator_iterator(gen, uniform_int<int>(0, maxEdgeWeight)),
628 n);
629 #endif
630
631 // Not strictly necessary to synchronize here, but it make sure that the
632 // time we measure is the time needed for all copies of the graph to be
633 // constructed
634 synchronize(pg);
635
636 time_type end = get_time();
637
638 if (process_id(pg) == 0)
639 std::cerr<< "Kernel 1:\n"
640 << " Non-Distributed Graph: " << print_time(end - start) << std::endl;
641
642 std::map<int, int> degree_dist;
643 if ( show_degree_dist ) {
644 BGL_FORALL_VERTICES_T(v, sg, seqGraph) {
645 degree_dist[out_degree(v, sg)]++;
646 }
647
648 std::cerr << "Degree - Fraction of vertices of that degree\n";
649 for (std::map<int, int>::iterator iter = degree_dist.begin();
650 iter != degree_dist.end(); ++iter)
651 std::cerr << " " << iter->first << " - " << double(iter->second) / num_vertices(sg) << std::endl << std::endl;
652 }
653
654 //
655 // Kernel 2 - Classify large sets
656 //
657 std::vector<graph_traits<seqGraph>::edge_descriptor> seqS;
658
659 if (process_id(pg) == 0)
660 std::cerr << "Kernel 2:\n";
661
662 seq_classify_sets(pg, sg,
663 #ifdef CSR
664 get(&WeightedEdge::weight, sg),
665 #else
666 get(edge_weight, sg),
667 #endif
668 seqS);
669
670 //
671 // Kernel 3 - Graph Extraction
672 //
673 #ifdef CSR
674 typedef weight_type weight_t;
675 weight_t unit_weight(1);
676 #else
677 int unit_weight(1);;
678 #endif
679
680 if (process_id(pg) == 0)
681 std::cerr << "Kernel 3:\n";
682
683 seq_subgraph_extraction(pg, sg,
684 #ifdef CSR
685 // get(&WeightedEdge::weight, sg),
686 ref_property_map<graph_traits<seqGraph>::edge_descriptor, weight_t>(unit_weight),
687 get(&VertexProperties::distance, sg),
688 get(&VertexProperties::color, sg),
689 #else
690 // get(edge_weight, sg),
691 ref_property_map<graph_traits<seqGraph>::edge_descriptor, int>(unit_weight),
692 get(vertex_distance, sg),
693 get(vertex_color, sg),
694 #endif
695 seqS, subGraphEdgeLength);
696
697 #ifdef CSR
698 typedef property_map<seqGraph, weight_type WeightedEdge::*>::type seqEdgeWeightMap;
699 edge_weight_not_divisible_by_eight<seqEdgeWeightMap> sg_filter(get(&WeightedEdge::weight, sg));
700 #else
701 typedef property_map<seqGraph, edge_weight_t>::type seqEdgeWeightMap;
702 edge_weight_not_divisible_by_eight<seqEdgeWeightMap> sg_filter(get(edge_weight, sg));
703 #endif
704
705 typedef filtered_graph<const seqGraph, edge_weight_not_divisible_by_eight<seqEdgeWeightMap> >
706 filteredSeqGraph;
707
708 filteredSeqGraph fsg(sg, sg_filter);
709
710 std::vector<graph_traits<seqGraph>::vertex_descriptor> max_seq_bc_vec;
711
712 // Non-Distributed Centrality Map
713 typedef property_map<seqGraph, vertex_index_t>::const_type seqIndexMap;
714 typedef iterator_property_map<std::vector<int>::iterator, seqIndexMap> seqCentralityMap;
715
716 std::vector<int> non_distributed_centralityS(num_vertices(sg), 0);
717 seqCentralityMap non_distributed_centrality(non_distributed_centralityS.begin(),
718 get(vertex_index, sg));
719
720 vertices_size_type n0 = 0;
721 BGL_FORALL_VERTICES_T(v, fsg, filteredSeqGraph) {
722 if (out_degree(v, fsg) == 0) ++n0;
723 }
724
725 if (process_id(pg) == 0)
726 std::cerr << "Kernel 4:\n";
727
728 // Run Betweenness Centrality
729 if (full_bc) {
730
731 // Non-Distributed Graph BC
732 start = get_time();
733 non_distributed_brandes_betweenness_centrality(pg, fsg, non_distributed_centrality);
734 extract_max_bc_vertices(pg, fsg, non_distributed_centrality, max_seq_bc_vec);
735 end = get_time();
736
737 edges_size_type nonDistributedExactTEPs = edges_size_type(floor(7 * n* (n - n0) / (end - start)));
738
739 if (process_id(pg) == 0)
740 std::cerr << " non-Distributed Graph Exact = " << print_time(end - start) << " ("
741 << nonDistributedExactTEPs << " TEPs)\n";
742 }
743
744 // Non-Distributed Graph Approximate BC
745 std::vector<int> nonDistributedApproxCentralityS(num_vertices(sg), 0);
746 seqCentralityMap nonDistributedApproxCentrality(nonDistributedApproxCentralityS.begin(),
747 get(vertex_index, sg));
748
749 queue<typename graph_traits<filteredSeqGraph>::vertex_descriptor> sources;
750 {
751 minstd_rand gen;
752 uniform_int<vertices_size_type> rand_vertex(0, num_vertices(fsg) - 1);
753 int remaining_sources = floor(pow(2, K4Alpha));
754 std::vector<typename graph_traits<filteredSeqGraph>::vertex_descriptor> temp_sources;
755
756 while (remaining_sources > 0) {
757 typename graph_traits<filteredSeqGraph>::vertex_descriptor v =
758 vertex(rand_vertex(gen), fsg);
759
760 if (out_degree(v, fsg) != 0
761 && std::find(temp_sources.begin(), temp_sources.end(), v) == temp_sources.end()) {
762 temp_sources.push_back(v);
763 --remaining_sources;
764 }
765 }
766
767 for (int i = 0; i < temp_sources.size(); ++i)
768 sources.push(temp_sources[i]);
769 }
770
771 start = get_time();
772 non_distributed_brandes_betweenness_centrality(pg, fsg, buffer(sources).
773 centrality_map(nonDistributedApproxCentrality));
774 extract_max_bc_vertices(pg, fsg, nonDistributedApproxCentrality, max_seq_bc_vec);
775 end = get_time();
776
777 edges_size_type nonDistributedApproxTEPs = edges_size_type(floor(7 * n * pow(2, K4Alpha) / (end - start)));
778
779 if (process_id(pg) == 0)
780 std::cerr << " Non-Distributed Graph Approximate (" << floor(pow(2, K4Alpha)) << " sources) = "
781 << print_time(end - start) << " (" << nonDistributedApproxTEPs << " TEPs)\n";
782
783 // Verify Correctness of Kernel 4
784 if (full_bc && verify && process_id(pg) == 0) {
785
786 std::vector<int> seq_centralityS(num_vertices(fsg), 0);
787 seqCentralityMap seq_centrality(seq_centralityS.begin(), get(vertex_index, fsg));
788
789 max_seq_bc_vec.clear();
790 property_traits<seqCentralityMap>::value_type max_ = 0;
791
792 start = get_time();
793 brandes_betweenness_centrality(fsg, seq_centrality);
794
795 typedef filtered_graph<const seqGraph, edge_weight_not_divisible_by_eight<seqEdgeWeightMap> >
796 filteredSeqGraph;
797
798 BGL_FORALL_VERTICES_T(v, fsg, filteredSeqGraph ) {
799 if (get(seq_centrality, v) == max_)
800 max_seq_bc_vec.push_back(v);
801 else if (get(seq_centrality, v) > max_) {
802 max_ = get(seq_centrality, v);
803 max_seq_bc_vec.clear();
804 max_seq_bc_vec.push_back(v);
805 }
806 }
807
808 end = get_time();
809
810 edges_size_type sequentialTEPs = edges_size_type(floor(7 * n* (n - n0) / (end - start)));
811
812 std::cerr << " Sequential = " << print_time(end - start) << " (" << sequentialTEPs << " TEPs)\n";
813
814 typename ProcessGroup::process_id_type id = process_id(pg);
815 typename ProcessGroup::process_size_type p = num_processes(pg);
816
817 assert((double)n/p == floor((double)n/p));
818
819 std::cerr << "\nVerifying non-scalable betweenness centrality...\n";
820
821 {
822 bool passed = true;
823
824 // Verify non-scalable betweenness centrality
825 BGL_FORALL_VERTICES_T(v, sg, seqGraph) {
826 if (get(non_distributed_centrality, v) != get(seq_centrality, v)) {
827 std::cerr << " " << id << ": Error - centrality of " << v
828 << " does not match the sequential result ("
829 << get(non_distributed_centrality, v) << " vs. "
830 << get(seq_centrality, v) << ")\n";
831 passed = false;
832 }
833 }
834
835 if (passed)
836 std::cerr << " PASSED\n";
837 }
838
839 }
840
841 }
842
843 template <typename RandomGenerator, typename ProcessGroup, typename vertices_size_type,
844 typename edges_size_type>
845 void
846 run_distributed_graph_tests(RandomGenerator& gen, const ProcessGroup& pg,
847 vertices_size_type n, edges_size_type m,
848 std::size_t maxEdgeWeight, uint64_t seed,
849 int K4Alpha, double a, double b, double c, double d,
850 int subGraphEdgeLength, bool show_degree_dist,
851 bool emit_dot_file, bool full_bc, bool verify)
852 {
853 #ifdef CSR
854 typedef compressed_sparse_row_graph<directedS, VertexProperties, WeightedEdge, no_property,
855 distributedS<ProcessGroup> > Graph;
856 #else
857 typedef adjacency_list<vecS,
858 distributedS<ProcessGroup, vecS>,
859 directedS,
860 // Vertex properties
861 property<vertex_distance_t, int,
862 property<vertex_color_t, default_color_type> >,
863 // Edge properties
864 property<edge_weight_t, int> > Graph;
865 #endif
866
867 gen.seed(seed);
868
869 parallel::variant_distribution<ProcessGroup> distrib
870 = parallel::block(pg, n);
871
872 typedef typename ProcessGroup::process_id_type process_id_type;
873 process_id_type id = process_id(pg);
874
875 typedef typename property_map<Graph, vertex_owner_t>::const_type OwnerMap;
876 typedef typename property_map<Graph, vertex_local_t>::const_type LocalMap;
877
878 typedef keep_local_edges<parallel::variant_distribution<ProcessGroup>,
879 process_id_type>
880 EdgeFilter;
881
882 //
883 // Kernel 1 - Graph construction
884 // Nick: The benchmark specifies that we only have to time graph generation from
885 // edge tuples, the generator generates the edge tuples at graph construction
886 // time so we're timing some overhead in the random number generator, etc.
887 synchronize(pg);
888
889 time_type start = get_time();
890
891 #ifdef CSR
892 // typedef sorted_unique_rmat_iterator<RandomGenerator, Graph, EdgeFilter> RMATIter;
893 typedef sorted_rmat_iterator<RandomGenerator, Graph, keep_all_edges> RMATIter;
894
895 Graph g(//RMATIter(gen, n, m, a, b, c, d, false, true, EdgeFilter(distrib, id)),
896 RMATIter(gen, n, m, a, b, c, d, true, keep_all_edges()),
897 RMATIter(),
898 make_generator_iterator(gen, uniform_int<int>(0, maxEdgeWeight)),
899 n, pg, distrib);
900 #else
901 typedef unique_rmat_iterator<RandomGenerator, Graph, EdgeFilter> RMATIter;
902 Graph g(RMATIter(gen, n, m, a, b, c, d, true EdgeFilter(distrib, id)),
903 RMATIter(),
904 make_generator_iterator(gen, uniform_int<int>(0, maxEdgeWeight)),
905 n, pg, distrib);
906 #endif
907
908 synchronize(pg);
909
910 time_type end = get_time();
911
912 if (id == 0)
913 std::cerr<< "Kernel 1:\n"
914 << " Distributed Graph: " << print_time(end - start) << std::endl;
915
916 if ( emit_dot_file )
917 write_graphviz("ssca.dot", g);
918
919 //
920 // Kernel 2 - Classify large sets
921 //
922 typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
923 std::vector<std::pair<vertex_descriptor, vertex_descriptor> > S;
924
925 if (id == 0)
926 std::cerr << "Kernel 2:\n";
927
928 classify_sets(g,
929 #ifdef CSR
930 get(&WeightedEdge::weight, g),
931 #else
932 get(edge_weight, g),
933 #endif
934 S);
935
936 //
937 // Kernel 3 - Graph Extraction
938 //
939 OwnerMap owner = get(vertex_owner, g);
940 LocalMap local = get(vertex_local, g);
941
942 if (id == 0)
943 std::cerr << "Kernel 3:\n";
944
945 #ifdef CSR
946 typedef weight_type weight_t;
947 weight_t unit_weight(1);
948 #else
949 int unit_weight(1);;
950 #endif
951
952 subgraph_extraction(g, owner, local,
953 #ifdef CSR
954 // get(&WeightedEdge::weight, g),
955 ref_property_map<typename graph_traits<Graph>::edge_descriptor, weight_t>(unit_weight),
956 get(&VertexProperties::distance, g),
957 get(&VertexProperties::color, g),
958 #else
959 // get(edge_weight, g),
960 ref_property_map<graph_traits<Graph>::edge_descriptor, int>(unit_weight),
961 get(vertex_distance, g),
962 get(vertex_color, g),
963 #endif
964 S, subGraphEdgeLength);
965
966 //
967 // Kernel 4 - Betweenness Centrality
968 //
969
970 // Filter edges with weights divisible by 8
971 #ifdef CSR
972 typedef typename property_map<Graph, weight_type WeightedEdge::*>::type EdgeWeightMap;
973 edge_weight_not_divisible_by_eight<EdgeWeightMap> filter(get(&WeightedEdge::weight, g));
974 #else
975 typedef typename property_map<Graph, edge_weight_t>::type EdgeWeightMap;
976 edge_weight_not_divisible_by_eight<EdgeWeightMap> filter(get(edge_weight, g));
977 #endif
978
979 typedef filtered_graph<const Graph, edge_weight_not_divisible_by_eight<EdgeWeightMap> >
980 filteredGraph;
981 filteredGraph fg(g, filter);
982
983 // Vectors of max BC scores for all tests
984 std::vector<typename graph_traits<Graph>::vertex_descriptor> max_bc_vec;
985
986 // Distributed Centrality Map
987 typedef typename property_map<Graph, vertex_index_t>::const_type IndexMap;
988 typedef iterator_property_map<std::vector<int>::iterator, IndexMap> CentralityMap;
989
990 std::vector<int> centralityS(num_vertices(g), 0);
991 CentralityMap centrality(centralityS.begin(), get(vertex_index, g));
992
993 // Calculate number of vertices of degree 0
994 vertices_size_type local_n0 = 0, n0;
995 BGL_FORALL_VERTICES_T(v, fg, filteredGraph) {
996 if (out_degree(v, g) == 0) local_n0++;
997 }
998 n0 = boost::parallel::all_reduce(pg, local_n0, std::plus<vertices_size_type>());
999
1000 if (id == 0)
1001 std::cerr << "Kernel 4:\n";
1002
1003 // Run Betweenness Centrality
1004 if (full_bc) {
1005
1006 // Distributed Graph Full BC
1007 start = get_time();
1008 brandes_betweenness_centrality(fg, centrality);
1009 extract_max_bc_vertices(pg, g, centrality, max_bc_vec);
1010 end = get_time();
1011
1012 edges_size_type exactTEPs = edges_size_type(floor(7 * n* (n - n0) / (end - start)));
1013
1014 if (id == 0)
1015 std::cerr << " Exact = " << print_time(end - start) << " ("
1016 << exactTEPs << " TEPs)\n";
1017 }
1018
1019 // Distributed Graph Approximate BC
1020 std::vector<int> approxCentralityS(num_vertices(g), 0);
1021 CentralityMap approxCentrality(approxCentralityS.begin(), get(vertex_index, g));
1022
1023 queue<vertex_descriptor> sources;
1024 generate_sources(g, sources, vertices_size_type(floor(pow(2, K4Alpha))));
1025
1026 start = get_time();
1027 brandes_betweenness_centrality(fg, buffer(sources).centrality_map(approxCentrality));
1028 extract_max_bc_vertices(pg, fg, approxCentrality, max_bc_vec);
1029 end = get_time();
1030
1031 edges_size_type approxTEPs = edges_size_type(floor(7 * n * pow(2, K4Alpha) / (end - start)));
1032
1033 if (id == 0)
1034 std::cerr << " Approximate (" << floor(pow(2, K4Alpha)) << " sources) = "
1035 << print_time(end - start) << " (" << approxTEPs << " TEPs)\n";
1036
1037
1038 // Verify Correctness of Kernel 4
1039 if (full_bc && verify && id == 0) {
1040
1041 // Build non-distributed graph to verify against
1042 typedef adjacency_list<vecS, vecS, directedS,
1043 // Vertex properties
1044 property<vertex_distance_t, int,
1045 property<vertex_color_t, default_color_type> >,
1046 // Edge properties
1047 property<edge_weight_t, int> > seqGraph;
1048
1049 gen.seed(seed);
1050
1051 #ifdef CSR
1052 seqGraph sg(sorted_unique_rmat_iterator<RandomGenerator, seqGraph>(gen, n, m, a, b, c, d),
1053 sorted_unique_rmat_iterator<RandomGenerator, seqGraph>(),
1054 make_generator_iterator(gen, uniform_int<int>(0, maxEdgeWeight)),
1055 n);
1056 #else
1057 seqGraph sg(unique_rmat_iterator<RandomGenerator, seqGraph>(gen, n, m, a, b, c, d),
1058 unique_rmat_iterator<RandomGenerator, seqGraph>(),
1059 make_generator_iterator(gen, uniform_int<int>(0, maxEdgeWeight)),
1060 n);
1061 #endif
1062
1063 typedef property_map<seqGraph, edge_weight_t>::type seqEdgeWeightMap;
1064 edge_weight_not_divisible_by_eight<seqEdgeWeightMap> sg_filter(get(edge_weight, sg));
1065
1066 filtered_graph<const seqGraph, edge_weight_not_divisible_by_eight<seqEdgeWeightMap> >
1067 fsg(sg, sg_filter);
1068
1069 // Build sequential centrality map
1070 typedef property_map<seqGraph, vertex_index_t>::const_type seqIndexMap;
1071 typedef iterator_property_map<std::vector<int>::iterator, seqIndexMap> seqCentralityMap;
1072
1073 std::vector<int> seq_centralityS(num_vertices(sg), 0);
1074 seqCentralityMap seq_centrality(seq_centralityS.begin(), get(vertex_index, sg));
1075
1076 std::vector<graph_traits<seqGraph>::vertex_descriptor> max_seq_bc_vec;
1077
1078 max_seq_bc_vec.clear();
1079 property_traits<seqCentralityMap>::value_type max_ = 0;
1080
1081 start = get_time();
1082 brandes_betweenness_centrality(fsg, seq_centrality);
1083
1084 typedef filtered_graph<const seqGraph, edge_weight_not_divisible_by_eight<seqEdgeWeightMap> >
1085 filteredSeqGraph;
1086
1087 BGL_FORALL_VERTICES_T(v, fsg, filteredSeqGraph ) {
1088 if (get(seq_centrality, v) == max_)
1089 max_seq_bc_vec.push_back(v);
1090 else if (get(seq_centrality, v) > max_) {
1091 max_ = get(seq_centrality, v);
1092 max_seq_bc_vec.clear();
1093 max_seq_bc_vec.push_back(v);
1094 }
1095 }
1096
1097 end = get_time();
1098
1099 edges_size_type sequentialTEPs = edges_size_type(floor(7 * n* (n - n0) / (end - start)));
1100
1101 std::cerr << " Sequential = " << print_time(end - start) << " (" << sequentialTEPs << " TEPs)\n";
1102
1103 typename ProcessGroup::process_size_type p = num_processes(pg);
1104
1105 assert((double)n/p == floor((double)n/p));
1106
1107 std::cerr << "\nVerifying betweenness centrality...\n";
1108
1109 {
1110 bool passed = true;
1111
1112 // Verify exact betweenness centrality
1113 BGL_FORALL_VERTICES_T(v, g, Graph) {
1114 if (get(centrality, v) != seq_centralityS[(n/p) * get(owner, v) + get(local, v)]) {
1115 std::cerr << " " << id << ": Error - centrality of " << get(local, v) << "@" << get(owner, v)
1116 << " does not match the sequential result (" << get(centrality, v) << " vs. "
1117 << seq_centralityS[(n/p) * get(owner, v) + get(local, v)] << ")\n";
1118 passed = false;
1119 }
1120 }
1121
1122 if (passed)
1123 std::cerr << " PASSED\n";
1124 }
1125 }
1126 }
1127
1128 void usage()
1129 {
1130 std::cerr << "SSCA benchmark.\n\n"
1131 << "Usage : ssca [options]\n\n"
1132 << "Options are:\n"
1133 << "\t--vertices v\t\t\tNumber of vertices in the graph\n"
1134 << "\t--edges v\t\t\tNumber of edges in the graph\n"
1135 << "\t--seed s\t\t\tSeed for synchronized random number generator\n"
1136 << "\t--full-bc\t\t\tRun full (exact) Betweenness Centrality\n"
1137 << "\t--max-weight miw\t\tMaximum integer edge weight\n"
1138 << "\t--subgraph-edge-length sel\tEdge length of subgraphs to extract in Kernel 3\n"
1139 << "\t--k4alpha k\t\t\tValue of K4Alpha in Kernel 4\n"
1140 << "\t--scale s\t\t\tSCALE parameter for the SSCA benchmark (sets n, m, and C)\n"
1141 << "\t--dot\t\t\t\tEmit a dot file containing the graph\n"
1142 << "\t--verify\t\t\tVerify result\n"
1143 << "\t--degree-dist\t\t\t Output degree distribution of graph\n"
1144 << "\t--no-distributed-graph\t\tOmit distributed graph tests\n";
1145 }
1146
1147 int main(int argc, char* argv[])
1148 {
1149 mpi::environment env(argc, argv);
1150
1151 using boost::graph::distributed::mpi_process_group;
1152
1153 #ifdef CSR
1154 typedef compressed_sparse_row_graph<directedS, VertexProperties, WeightedEdge, no_property,
1155 distributedS<mpi_process_group> > Graph;
1156 #else
1157 typedef adjacency_list<vecS,
1158 distributedS<mpi_process_group, vecS>,
1159 directedS,
1160 // Vertex properties
1161 property<vertex_distance_t, int,
1162 property<vertex_color_t, default_color_type> >,
1163 // Edge properties
1164 property<edge_weight_t, int> > Graph;
1165 #endif
1166
1167 typedef graph_traits<Graph>::vertices_size_type vertices_size_type;
1168 typedef graph_traits<Graph>::edges_size_type edges_size_type;
1169
1170 RandomGenerator gen;
1171
1172 // Default args
1173 vertices_size_type n = 100;
1174 edges_size_type m = 8*n;
1175 uint64_t seed = 1;
1176 int maxEdgeWeight = 100,
1177 subGraphEdgeLength = 8,
1178 K4Alpha = 0.5;
1179 double a = 0.57, b = 0.19, c = 0.19, d = 0.05;
1180 bool emit_dot_file = false, verify = false, full_bc = true,
1181 distributed_graph = true, show_degree_dist = false,
1182 non_distributed_graph = true;
1183
1184 mpi_process_group pg;
1185
1186 if (argc == 1) {
1187 if (process_id(pg) == 0)
1188 usage();
1189 exit(-1);
1190 }
1191
1192 // Parse args
1193 for (int i = 1; i < argc; ++i) {
1194 std::string arg = argv[i];
1195
1196 if (arg == "--vertices")
1197 n = boost::lexical_cast<vertices_size_type>( argv[i+1] );
1198
1199 if (arg == "--seed")
1200 seed = boost::lexical_cast<uint64_t>( argv[i+1] );
1201
1202 if (arg == "--full-bc")
1203 full_bc = (argv[i+1]== "true");
1204
1205 if (arg == "--max-weight")
1206 maxEdgeWeight = boost::lexical_cast<int>( argv[i+1] );
1207
1208 if (arg == "--subgraph-edge-length")
1209 subGraphEdgeLength = boost::lexical_cast<int>( argv[i+1] );
1210
1211 if (arg == "--edges")
1212 m = boost::lexical_cast<edges_size_type>( argv[i+1] );
1213
1214 if (arg == "--k4alpha")
1215 K4Alpha = boost::lexical_cast<int>( argv[i+1] );
1216
1217 if (arg == "--dot")
1218 emit_dot_file = true;
1219
1220 if (arg == "--verify")
1221 verify = true;
1222
1223 if (arg == "--degree-dist")
1224 show_degree_dist = true;
1225
1226 if (arg == "--no-distributed-graph")
1227 distributed_graph = false;
1228
1229 if (arg == "--no-non-distributed-graph")
1230 non_distributed_graph = false;
1231
1232 if (arg == "--scale") {
1233 vertices_size_type scale = boost::lexical_cast<vertices_size_type>( argv[i+1] );
1234 maxEdgeWeight = n = vertices_size_type(floor(pow(2, scale)));
1235 m = 8 * n;
1236 }
1237
1238 if (arg == "--help") {
1239 if (process_id(pg) == 0)
1240 usage();
1241 exit(-1);
1242 }
1243 }
1244
1245 if (non_distributed_graph) {
1246 if (process_id(pg) == 0)
1247 std::cerr << "Non-Distributed Graph Tests\n";
1248
1249 run_non_distributed_graph_tests(gen, pg, n, m, maxEdgeWeight, seed, K4Alpha, a, b, c, d,
1250 subGraphEdgeLength, show_degree_dist, full_bc, verify);
1251 }
1252
1253 if (distributed_graph) {
1254 if (process_id(pg) == 0)
1255 std::cerr << "Distributed Graph Tests\n";
1256
1257 run_distributed_graph_tests(gen, pg, n, m, maxEdgeWeight, seed, K4Alpha, a, b, c, d,
1258 subGraphEdgeLength, show_degree_dist, emit_dot_file,
1259 full_bc, verify);
1260 }
1261
1262 return boost::report_errors();
1263 }