1 // Copyright 2004, 2005 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 #ifndef BOOST_GRAPH_RMAT_GENERATOR_HPP
10 #define BOOST_GRAPH_RMAT_GENERATOR_HPP
11
12 #include <math.h>
13 #include <iterator>
14 #include <utility>
15 #include <vector>
16 #include <queue>
17 #include <map>
18 #include <boost/shared_ptr.hpp>
19 #include <boost/assert.hpp>
20 #include <boost/random/uniform_int.hpp>
21 #include <boost/random/uniform_01.hpp>
22 #include <boost/graph/graph_traits.hpp>
23 #include <boost/graph/detail/mpi_include.hpp>
24 #include <boost/type_traits/is_base_and_derived.hpp>
25 #include <boost/type_traits/is_same.hpp>
26 // #include <boost/test/floating_point_comparison.hpp>
27
28 using boost::shared_ptr;
29 using boost::uniform_01;
30
31 // Returns floor(log_2(n)), and -1 when n is 0
int_log2(IntegerType n)32 template < typename IntegerType > inline int int_log2(IntegerType n)
33 {
34 int l = 0;
35 while (n > 0)
36 {
37 ++l;
38 n >>= 1;
39 }
40 return l - 1;
41 }
42
43 struct keep_all_edges
44 {
operator ()keep_all_edges45 template < typename T > bool operator()(const T&, const T&) { return true; }
46 };
47
48 template < typename Distribution, typename ProcessId > struct keep_local_edges
49 {
50
keep_local_edgeskeep_local_edges51 keep_local_edges(const Distribution& distrib, const ProcessId& id)
52 : distrib(distrib), id(id)
53 {
54 }
55
operator ()keep_local_edges56 template < typename T > bool operator()(const T& x, const T& y)
57 {
58 return distrib(x) == id || distrib(y) == id;
59 }
60
61 private:
62 const Distribution& distrib;
63 const ProcessId& id;
64 };
65
66 template < typename RandomGenerator, typename T >
generate_permutation_vector(RandomGenerator & gen,std::vector<T> & vertexPermutation,T n)67 void generate_permutation_vector(
68 RandomGenerator& gen, std::vector< T >& vertexPermutation, T n)
69 {
70 using boost::uniform_int;
71
72 vertexPermutation.resize(n);
73
74 // Generate permutation map of vertex numbers
75 uniform_int< T > rand_vertex(0, n - 1);
76 for (T i = 0; i < n; ++i)
77 vertexPermutation[i] = i;
78
79 // Can't use std::random_shuffle unless we create another (synchronized)
80 // PRNG
81 for (T i = 0; i < n; ++i)
82 std::swap(vertexPermutation[i], vertexPermutation[rand_vertex(gen)]);
83 }
84
85 template < typename RandomGenerator, typename T >
generate_edge(shared_ptr<uniform_01<RandomGenerator>> prob,T n,unsigned int SCALE,double a,double b,double c,double d)86 std::pair< T, T > generate_edge(
87 shared_ptr< uniform_01< RandomGenerator > > prob, T n, unsigned int SCALE,
88 double a, double b, double c, double d)
89 {
90 T u = 0, v = 0;
91 T step = n / 2;
92 for (unsigned int j = 0; j < SCALE; ++j)
93 {
94 double p = (*prob)();
95
96 if (p < a)
97 ;
98 else if (p >= a && p < a + b)
99 v += step;
100 else if (p >= a + b && p < a + b + c)
101 u += step;
102 else
103 { // p > a + b + c && p < a + b + c + d
104 u += step;
105 v += step;
106 }
107
108 step /= 2;
109
110 // 0.2 and 0.9 are hardcoded in the reference SSCA implementation.
111 // The maximum change in any given value should be less than 10%
112 a *= 0.9 + 0.2 * (*prob)();
113 b *= 0.9 + 0.2 * (*prob)();
114 c *= 0.9 + 0.2 * (*prob)();
115 d *= 0.9 + 0.2 * (*prob)();
116
117 double S = a + b + c + d;
118
119 a /= S;
120 b /= S;
121 c /= S;
122 // d /= S;
123 // Ensure all values add up to 1, regardless of floating point errors
124 d = 1. - a - b - c;
125 }
126
127 return std::make_pair(u, v);
128 }
129
130 namespace boost
131 {
132
133 /*
134 Chakrabarti's R-MAT scale free generator.
135
136 For all flavors of the R-MAT iterator a+b+c+d must equal 1 and for the
137 unique_rmat_iterator 'm' << 'n^2'. If 'm' is too close to 'n^2' the
138 generator may be unable to generate sufficient unique edges
139
140 To get a true scale free distribution {a, b, c, d : a > b, a > c, a > d}
141 */
142
143 template < typename RandomGenerator, typename Graph > class rmat_iterator
144 {
145 typedef typename graph_traits< Graph >::directed_category directed_category;
146 typedef
147 typename graph_traits< Graph >::vertices_size_type vertices_size_type;
148 typedef typename graph_traits< Graph >::edges_size_type edges_size_type;
149
150 public:
151 typedef std::input_iterator_tag iterator_category;
152 typedef std::pair< vertices_size_type, vertices_size_type > value_type;
153 typedef const value_type& reference;
154 typedef const value_type* pointer;
155 typedef std::ptrdiff_t difference_type; // Not used
156
157 // No argument constructor, set to terminating condition
rmat_iterator()158 rmat_iterator() : gen(), edge(0) {}
159
160 // Initialize for edge generation
rmat_iterator(RandomGenerator & gen,vertices_size_type n,edges_size_type m,double a,double b,double c,double d,bool permute_vertices=true)161 rmat_iterator(RandomGenerator& gen, vertices_size_type n, edges_size_type m,
162 double a, double b, double c, double d, bool permute_vertices = true)
163 : gen()
164 , n(n)
165 , a(a)
166 , b(b)
167 , c(c)
168 , d(d)
169 , edge(m)
170 , permute_vertices(permute_vertices)
171 , SCALE(int_log2(n))
172
173 {
174 this->gen.reset(new uniform_01< RandomGenerator >(gen));
175
176 // BOOST_ASSERT(boost::test_tools::check_is_close(a + b + c +
177 // d, 1., 1.e-5));
178
179 if (permute_vertices)
180 generate_permutation_vector(gen, vertexPermutation, n);
181
182 // TODO: Generate the entire adjacency matrix then "Clip and flip" if
183 // undirected graph
184
185 // Generate the first edge
186 vertices_size_type u, v;
187 boost::tie(u, v) = generate_edge(this->gen, n, SCALE, a, b, c, d);
188
189 if (permute_vertices)
190 current
191 = std::make_pair(vertexPermutation[u], vertexPermutation[v]);
192 else
193 current = std::make_pair(u, v);
194
195 --edge;
196 }
197
operator *() const198 reference operator*() const { return current; }
operator ->() const199 pointer operator->() const { return ¤t; }
200
operator ++()201 rmat_iterator& operator++()
202 {
203 vertices_size_type u, v;
204 boost::tie(u, v) = generate_edge(this->gen, n, SCALE, a, b, c, d);
205
206 if (permute_vertices)
207 current
208 = std::make_pair(vertexPermutation[u], vertexPermutation[v]);
209 else
210 current = std::make_pair(u, v);
211
212 --edge;
213
214 return *this;
215 }
216
operator ++(int)217 rmat_iterator operator++(int)
218 {
219 rmat_iterator temp(*this);
220 ++(*this);
221 return temp;
222 }
223
operator ==(const rmat_iterator & other) const224 bool operator==(const rmat_iterator& other) const
225 {
226 return edge == other.edge;
227 }
228
operator !=(const rmat_iterator & other) const229 bool operator!=(const rmat_iterator& other) const
230 {
231 return !(*this == other);
232 }
233
234 private:
235 // Parameters
236 shared_ptr< uniform_01< RandomGenerator > > gen;
237 vertices_size_type n;
238 double a, b, c, d;
239 int edge;
240 bool permute_vertices;
241 int SCALE;
242
243 // Internal data structures
244 std::vector< vertices_size_type > vertexPermutation;
245 value_type current;
246 };
247
248 // Sorted version for CSR
249 template < typename T > struct sort_pair
250 {
operator ()boost::sort_pair251 bool operator()(const std::pair< T, T >& x, const std::pair< T, T >& y)
252 {
253 if (x.first == y.first)
254 return x.second > y.second;
255 else
256 return x.first > y.first;
257 }
258 };
259
260 template < typename RandomGenerator, typename Graph,
261 typename EdgePredicate = keep_all_edges >
262 class sorted_rmat_iterator
263 {
264 typedef typename graph_traits< Graph >::directed_category directed_category;
265 typedef
266 typename graph_traits< Graph >::vertices_size_type vertices_size_type;
267 typedef typename graph_traits< Graph >::edges_size_type edges_size_type;
268
269 public:
270 typedef std::input_iterator_tag iterator_category;
271 typedef std::pair< vertices_size_type, vertices_size_type > value_type;
272 typedef const value_type& reference;
273 typedef const value_type* pointer;
274 typedef std::ptrdiff_t difference_type; // Not used
275
276 // No argument constructor, set to terminating condition
sorted_rmat_iterator()277 sorted_rmat_iterator()
278 : gen(), values(sort_pair< vertices_size_type >()), done(true)
279 {
280 }
281
282 // Initialize for edge generation
sorted_rmat_iterator(RandomGenerator & gen,vertices_size_type n,edges_size_type m,double a,double b,double c,double d,bool permute_vertices=true,EdgePredicate ep=keep_all_edges ())283 sorted_rmat_iterator(RandomGenerator& gen, vertices_size_type n,
284 edges_size_type m, double a, double b, double c, double d,
285 bool permute_vertices = true, EdgePredicate ep = keep_all_edges())
286 : gen()
287 , permute_vertices(permute_vertices)
288 , values(sort_pair< vertices_size_type >())
289 , done(false)
290
291 {
292 // BOOST_ASSERT(boost::test_tools::check_is_close(a + b + c +
293 // d, 1., 1.e-5));
294
295 this->gen.reset(new uniform_01< RandomGenerator >(gen));
296
297 std::vector< vertices_size_type > vertexPermutation;
298 if (permute_vertices)
299 generate_permutation_vector(gen, vertexPermutation, n);
300
301 // TODO: "Clip and flip" if undirected graph
302 int SCALE = int_log2(n);
303
304 for (edges_size_type i = 0; i < m; ++i)
305 {
306
307 vertices_size_type u, v;
308 boost::tie(u, v) = generate_edge(this->gen, n, SCALE, a, b, c, d);
309
310 if (permute_vertices)
311 {
312 if (ep(vertexPermutation[u], vertexPermutation[v]))
313 values.push(std::make_pair(
314 vertexPermutation[u], vertexPermutation[v]));
315 }
316 else
317 {
318 if (ep(u, v))
319 values.push(std::make_pair(u, v));
320 }
321 }
322
323 current = values.top();
324 values.pop();
325 }
326
operator *() const327 reference operator*() const { return current; }
operator ->() const328 pointer operator->() const { return ¤t; }
329
operator ++()330 sorted_rmat_iterator& operator++()
331 {
332 if (!values.empty())
333 {
334 current = values.top();
335 values.pop();
336 }
337 else
338 done = true;
339
340 return *this;
341 }
342
operator ++(int)343 sorted_rmat_iterator operator++(int)
344 {
345 sorted_rmat_iterator temp(*this);
346 ++(*this);
347 return temp;
348 }
349
operator ==(const sorted_rmat_iterator & other) const350 bool operator==(const sorted_rmat_iterator& other) const
351 {
352 return values.empty() && other.values.empty() && done && other.done;
353 }
354
operator !=(const sorted_rmat_iterator & other) const355 bool operator!=(const sorted_rmat_iterator& other) const
356 {
357 return !(*this == other);
358 }
359
360 private:
361 // Parameters
362 shared_ptr< uniform_01< RandomGenerator > > gen;
363 bool permute_vertices;
364
365 // Internal data structures
366 std::priority_queue< value_type, std::vector< value_type >,
367 sort_pair< vertices_size_type > >
368 values;
369 value_type current;
370 bool done;
371 };
372
373 // This version is slow but guarantees unique edges
374 template < typename RandomGenerator, typename Graph,
375 typename EdgePredicate = keep_all_edges >
376 class unique_rmat_iterator
377 {
378 typedef typename graph_traits< Graph >::directed_category directed_category;
379 typedef
380 typename graph_traits< Graph >::vertices_size_type vertices_size_type;
381 typedef typename graph_traits< Graph >::edges_size_type edges_size_type;
382
383 public:
384 typedef std::input_iterator_tag iterator_category;
385 typedef std::pair< vertices_size_type, vertices_size_type > value_type;
386 typedef const value_type& reference;
387 typedef const value_type* pointer;
388 typedef std::ptrdiff_t difference_type; // Not used
389
390 // No argument constructor, set to terminating condition
unique_rmat_iterator()391 unique_rmat_iterator() : gen(), done(true) {}
392
393 // Initialize for edge generation
unique_rmat_iterator(RandomGenerator & gen,vertices_size_type n,edges_size_type m,double a,double b,double c,double d,bool permute_vertices=true,EdgePredicate ep=keep_all_edges ())394 unique_rmat_iterator(RandomGenerator& gen, vertices_size_type n,
395 edges_size_type m, double a, double b, double c, double d,
396 bool permute_vertices = true, EdgePredicate ep = keep_all_edges())
397 : gen(), done(false)
398
399 {
400 // BOOST_ASSERT(boost::test_tools::check_is_close(a + b + c +
401 // d, 1., 1.e-5));
402
403 this->gen.reset(new uniform_01< RandomGenerator >(gen));
404
405 std::vector< vertices_size_type > vertexPermutation;
406 if (permute_vertices)
407 generate_permutation_vector(gen, vertexPermutation, n);
408
409 int SCALE = int_log2(n);
410
411 std::map< value_type, bool > edge_map;
412
413 edges_size_type edges = 0;
414 do
415 {
416 vertices_size_type u, v;
417 boost::tie(u, v) = generate_edge(this->gen, n, SCALE, a, b, c, d);
418
419 // Lowest vertex number always comes first
420 // (this means we don't have to worry about i->j and j->i being in
421 // the edge list)
422 if (u > v && is_same< directed_category, undirected_tag >::value)
423 std::swap(u, v);
424
425 if (edge_map.find(std::make_pair(u, v)) == edge_map.end())
426 {
427 edge_map[std::make_pair(u, v)] = true;
428
429 if (permute_vertices)
430 {
431 if (ep(vertexPermutation[u], vertexPermutation[v]))
432 values.push_back(std::make_pair(
433 vertexPermutation[u], vertexPermutation[v]));
434 }
435 else
436 {
437 if (ep(u, v))
438 values.push_back(std::make_pair(u, v));
439 }
440
441 edges++;
442 }
443 } while (edges < m);
444 // NGE - Asking for more than n^2 edges will result in an infinite loop
445 // here
446 // Asking for a value too close to n^2 edges may as well
447
448 current = values.back();
449 values.pop_back();
450 }
451
operator *() const452 reference operator*() const { return current; }
operator ->() const453 pointer operator->() const { return ¤t; }
454
operator ++()455 unique_rmat_iterator& operator++()
456 {
457 if (!values.empty())
458 {
459 current = values.back();
460 values.pop_back();
461 }
462 else
463 done = true;
464
465 return *this;
466 }
467
operator ++(int)468 unique_rmat_iterator operator++(int)
469 {
470 unique_rmat_iterator temp(*this);
471 ++(*this);
472 return temp;
473 }
474
operator ==(const unique_rmat_iterator & other) const475 bool operator==(const unique_rmat_iterator& other) const
476 {
477 return values.empty() && other.values.empty() && done && other.done;
478 }
479
operator !=(const unique_rmat_iterator & other) const480 bool operator!=(const unique_rmat_iterator& other) const
481 {
482 return !(*this == other);
483 }
484
485 private:
486 // Parameters
487 shared_ptr< uniform_01< RandomGenerator > > gen;
488
489 // Internal data structures
490 std::vector< value_type > values;
491 value_type current;
492 bool done;
493 };
494
495 // This version is slow but guarantees unique edges
496 template < typename RandomGenerator, typename Graph,
497 typename EdgePredicate = keep_all_edges >
498 class sorted_unique_rmat_iterator
499 {
500 typedef typename graph_traits< Graph >::directed_category directed_category;
501 typedef
502 typename graph_traits< Graph >::vertices_size_type vertices_size_type;
503 typedef typename graph_traits< Graph >::edges_size_type edges_size_type;
504
505 public:
506 typedef std::input_iterator_tag iterator_category;
507 typedef std::pair< vertices_size_type, vertices_size_type > value_type;
508 typedef const value_type& reference;
509 typedef const value_type* pointer;
510 typedef std::ptrdiff_t difference_type; // Not used
511
512 // No argument constructor, set to terminating condition
sorted_unique_rmat_iterator()513 sorted_unique_rmat_iterator()
514 : gen(), values(sort_pair< vertices_size_type >()), done(true)
515 {
516 }
517
518 // Initialize for edge generation
sorted_unique_rmat_iterator(RandomGenerator & gen,vertices_size_type n,edges_size_type m,double a,double b,double c,double d,bool bidirectional=false,bool permute_vertices=true,EdgePredicate ep=keep_all_edges ())519 sorted_unique_rmat_iterator(RandomGenerator& gen, vertices_size_type n,
520 edges_size_type m, double a, double b, double c, double d,
521 bool bidirectional = false, bool permute_vertices = true,
522 EdgePredicate ep = keep_all_edges())
523 : gen()
524 , bidirectional(bidirectional)
525 , values(sort_pair< vertices_size_type >())
526 , done(false)
527
528 {
529 // BOOST_ASSERT(boost::test_tools::check_is_close(a + b + c +
530 // d, 1., 1.e-5));
531
532 this->gen.reset(new uniform_01< RandomGenerator >(gen));
533
534 std::vector< vertices_size_type > vertexPermutation;
535 if (permute_vertices)
536 generate_permutation_vector(gen, vertexPermutation, n);
537
538 int SCALE = int_log2(n);
539
540 std::map< value_type, bool > edge_map;
541
542 edges_size_type edges = 0;
543 do
544 {
545
546 vertices_size_type u, v;
547 boost::tie(u, v) = generate_edge(this->gen, n, SCALE, a, b, c, d);
548
549 if (bidirectional)
550 {
551 if (edge_map.find(std::make_pair(u, v)) == edge_map.end())
552 {
553 edge_map[std::make_pair(u, v)] = true;
554 edge_map[std::make_pair(v, u)] = true;
555
556 if (ep(u, v))
557 {
558 if (permute_vertices)
559 {
560 values.push(std::make_pair(
561 vertexPermutation[u], vertexPermutation[v]));
562 values.push(std::make_pair(
563 vertexPermutation[v], vertexPermutation[u]));
564 }
565 else
566 {
567 values.push(std::make_pair(u, v));
568 values.push(std::make_pair(v, u));
569 }
570 }
571
572 ++edges;
573 }
574 }
575 else
576 {
577 // Lowest vertex number always comes first
578 // (this means we don't have to worry about i->j and j->i being
579 // in the edge list)
580 if (u > v
581 && is_same< directed_category, undirected_tag >::value)
582 std::swap(u, v);
583
584 if (edge_map.find(std::make_pair(u, v)) == edge_map.end())
585 {
586 edge_map[std::make_pair(u, v)] = true;
587
588 if (permute_vertices)
589 {
590 if (ep(vertexPermutation[u], vertexPermutation[v]))
591 values.push(std::make_pair(
592 vertexPermutation[u], vertexPermutation[v]));
593 }
594 else
595 {
596 if (ep(u, v))
597 values.push(std::make_pair(u, v));
598 }
599
600 ++edges;
601 }
602 }
603
604 } while (edges < m);
605 // NGE - Asking for more than n^2 edges will result in an infinite loop
606 // here
607 // Asking for a value too close to n^2 edges may as well
608
609 current = values.top();
610 values.pop();
611 }
612
operator *() const613 reference operator*() const { return current; }
operator ->() const614 pointer operator->() const { return ¤t; }
615
operator ++()616 sorted_unique_rmat_iterator& operator++()
617 {
618 if (!values.empty())
619 {
620 current = values.top();
621 values.pop();
622 }
623 else
624 done = true;
625
626 return *this;
627 }
628
operator ++(int)629 sorted_unique_rmat_iterator operator++(int)
630 {
631 sorted_unique_rmat_iterator temp(*this);
632 ++(*this);
633 return temp;
634 }
635
operator ==(const sorted_unique_rmat_iterator & other) const636 bool operator==(const sorted_unique_rmat_iterator& other) const
637 {
638 return values.empty() && other.values.empty() && done && other.done;
639 }
640
operator !=(const sorted_unique_rmat_iterator & other) const641 bool operator!=(const sorted_unique_rmat_iterator& other) const
642 {
643 return !(*this == other);
644 }
645
646 private:
647 // Parameters
648 shared_ptr< uniform_01< RandomGenerator > > gen;
649 bool bidirectional;
650
651 // Internal data structures
652 std::priority_queue< value_type, std::vector< value_type >,
653 sort_pair< vertices_size_type > >
654 values;
655 value_type current;
656 bool done;
657 };
658
659 } // end namespace boost
660
661 #include BOOST_GRAPH_MPI_INCLUDE(< boost / graph / distributed / rmat_graph_generator.hpp >)
662
663 #endif // BOOST_GRAPH_RMAT_GENERATOR_HPP
664