-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathrmat.h
More file actions
144 lines (130 loc) · 4.63 KB
/
rmat.h
File metadata and controls
144 lines (130 loc) · 4.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// Adapted from the stinger project <https://github.com/stingergraph/stinger> rmat.c
#include <cinttypes>
#include <random>
#include "prng_engine.hpp"
namespace DynoGraph {
/*
* RMAT edge generator
* Implements discard() to skip ahead in the random stream in constant time
*/
class rmat_edge_generator {
private:
/*
* RANDOM NUMBER GENERATION
*/
// Generates uniformly distributed uint32_t's
// Implements discard() in constant time
typedef sitmo::prng_engine prng_engine;
//typedef NotRandomEngine prng_engine;
prng_engine rng_engine;
// Converts values from the rng_engine into double type
std::uniform_real_distribution<double> rng_distribution;
// Helper function to get a number from the distribution
double rng() { return rng_distribution(rng_engine); }
/* Stores the number of times std::uniform_real_distribution<double> will
* call the generator for each number generated
*/
size_t k;
size_t getk() const {
/*
* From http://en.cppreference.com/w/cpp/numeric/random/generate_canonical
* To generate enough entropy, generate_canonical() will call the generator g() exactly k times,
* where k = max(1, ceil(b / log2(R)))
* and b = std::min<std::size_t>(bits, std::numeric_limits<RealType>::digits)
* and R = g.max() - g.min() + 1.
*
* In our case,
* bits = std::numeric_limits<prng_engine::result_type>::digits
* and RealType = double
* and __urng = prng_engine
*/
typedef double _RealType;
size_t __bits = std::numeric_limits<_RealType>::digits;
const prng_engine& __urng = rng_engine;
/*
* Remaining code copied from std::generate_canonical(), defined in bits/random.tcc
* Even if that implementation changes, the equation should still be the same
*/
const size_t __b
= std::min(static_cast<size_t>(std::numeric_limits<_RealType>::digits),
__bits);
const long double __r = static_cast<long double>(__urng.max())
- static_cast<long double>(__urng.min()) + 1.0L;
const size_t __log2r = std::log(__r) / std::log(2.0L);
size_t __k = std::max<size_t>(1UL, (__b + __log2r - 1UL) / __log2r);
return __k;
}
/*
* RMAT PARAMETERS
*/
// log2(num_vertices)
int64_t SCALE;
// RMAT parameters
double a, b, c, d;
public:
rmat_edge_generator(int64_t nv, double a, double b, double c, double d, uint32_t seed=0)
: rng_engine(seed)
, rng_distribution(0, 1)
, k(getk())
, SCALE(0), a(a), b(b), c(c), d(d)
{
while (nv >>= 1) { ++SCALE; }
}
// Skips past the next n randomly generated edges
void discard(uint64_t n)
{
// The loop in next_edge iterates SCALE-1 times, using 5 random numbers in each iteration
// The final iteration before the break uses one more random number
n *= 5 * (SCALE-1) + 1;
// std::uniform_real_distribution<double> calls the generator multiple times to get enough entropy
n *= k;
rng_engine.discard(n);
}
void next_edge(int64_t *src, int64_t *dst)
{
double A = a;
double B = b;
double C = c;
double D = d;
int64_t i = 0, j = 0;
int64_t bit = ((int64_t) 1) << (SCALE - 1);
while (1) {
const double r = rng();
if (r > A) { /* outside quadrant 1 */
if (r <= A + B) /* in quadrant 2 */
j |= bit;
else if (r <= A + B + C) /* in quadrant 3 */
i |= bit;
else { /* in quadrant 4 */
j |= bit;
i |= bit;
}
}
if (1 == bit)
break;
/*
Assuming R is in (0, 1), 0.95 + 0.1 * R is in (0.95, 1.05).
So the new probabilities are *not* the old +/- 10% but
instead the old +/- 5%.
*/
A *= (9.5 + rng()) / 10;
B *= (9.5 + rng()) / 10;
C *= (9.5 + rng()) / 10;
D *= (9.5 + rng()) / 10;
/* Used 5 random numbers. */
{
const double norm = 1.0 / (A + B + C + D);
A *= norm;
B *= norm;
C *= norm;
}
/* So long as +/- are monotonic, ensure a+b+c+d <= 1.0 */
D = 1.0 - (A + B + C);
bit >>= 1;
}
/* Iterates SCALE times. */
*src = i;
*dst = j;
}
};
} // end namespace DynoGraph