williamr@2
|
1 |
// Copyright 2004, 2005 The Trustees of Indiana University.
|
williamr@2
|
2 |
|
williamr@2
|
3 |
// Distributed under the Boost Software License, Version 1.0.
|
williamr@2
|
4 |
// (See accompanying file LICENSE_1_0.txt or copy at
|
williamr@2
|
5 |
// http://www.boost.org/LICENSE_1_0.txt)
|
williamr@2
|
6 |
|
williamr@2
|
7 |
// Authors: Jeremiah Willcock
|
williamr@2
|
8 |
// Douglas Gregor
|
williamr@2
|
9 |
// Andrew Lumsdaine
|
williamr@2
|
10 |
#ifndef BOOST_GRAPH_ERDOS_RENYI_GENERATOR_HPP
|
williamr@2
|
11 |
#define BOOST_GRAPH_ERDOS_RENYI_GENERATOR_HPP
|
williamr@2
|
12 |
|
williamr@2
|
13 |
#include <cassert>
|
williamr@2
|
14 |
#include <iterator>
|
williamr@2
|
15 |
#include <utility>
|
williamr@2
|
16 |
#include <boost/shared_ptr.hpp>
|
williamr@2
|
17 |
#include <boost/random/uniform_int.hpp>
|
williamr@2
|
18 |
#include <boost/graph/graph_traits.hpp>
|
williamr@2
|
19 |
#include <boost/random/geometric_distribution.hpp>
|
williamr@2
|
20 |
#include <boost/type_traits/is_base_and_derived.hpp>
|
williamr@2
|
21 |
#include <boost/type_traits/is_same.hpp>
|
williamr@2
|
22 |
#include <cmath>
|
williamr@2
|
23 |
|
williamr@2
|
24 |
namespace boost {
|
williamr@2
|
25 |
|
williamr@2
|
26 |
template<typename RandomGenerator, typename Graph>
|
williamr@2
|
27 |
class erdos_renyi_iterator
|
williamr@2
|
28 |
{
|
williamr@2
|
29 |
typedef typename graph_traits<Graph>::directed_category directed_category;
|
williamr@2
|
30 |
typedef typename graph_traits<Graph>::vertices_size_type vertices_size_type;
|
williamr@2
|
31 |
typedef typename graph_traits<Graph>::edges_size_type edges_size_type;
|
williamr@2
|
32 |
|
williamr@2
|
33 |
BOOST_STATIC_CONSTANT
|
williamr@2
|
34 |
(bool,
|
williamr@2
|
35 |
is_undirected = (is_base_and_derived<undirected_tag,
|
williamr@2
|
36 |
directed_category>::value
|
williamr@2
|
37 |
|| is_same<undirected_tag, directed_category>::value));
|
williamr@2
|
38 |
|
williamr@2
|
39 |
public:
|
williamr@2
|
40 |
typedef std::input_iterator_tag iterator_category;
|
williamr@2
|
41 |
typedef std::pair<vertices_size_type, vertices_size_type> value_type;
|
williamr@2
|
42 |
typedef const value_type& reference;
|
williamr@2
|
43 |
typedef const value_type* pointer;
|
williamr@2
|
44 |
typedef void difference_type;
|
williamr@2
|
45 |
|
williamr@2
|
46 |
erdos_renyi_iterator() : gen(), n(0), edges(0), allow_self_loops(false) {}
|
williamr@2
|
47 |
erdos_renyi_iterator(RandomGenerator& gen, vertices_size_type n,
|
williamr@2
|
48 |
double fraction = 0.0, bool allow_self_loops = false)
|
williamr@2
|
49 |
: gen(&gen), n(n), edges(edges_size_type(fraction * n * n)),
|
williamr@2
|
50 |
allow_self_loops(allow_self_loops)
|
williamr@2
|
51 |
{
|
williamr@2
|
52 |
if (is_undirected) edges = edges / 2;
|
williamr@2
|
53 |
next();
|
williamr@2
|
54 |
}
|
williamr@2
|
55 |
|
williamr@2
|
56 |
erdos_renyi_iterator(RandomGenerator& gen, vertices_size_type n,
|
williamr@2
|
57 |
edges_size_type m, bool allow_self_loops = false)
|
williamr@2
|
58 |
: gen(&gen), n(n), edges(m),
|
williamr@2
|
59 |
allow_self_loops(allow_self_loops)
|
williamr@2
|
60 |
{
|
williamr@2
|
61 |
next();
|
williamr@2
|
62 |
}
|
williamr@2
|
63 |
|
williamr@2
|
64 |
reference operator*() const { return current; }
|
williamr@2
|
65 |
pointer operator->() const { return ¤t; }
|
williamr@2
|
66 |
|
williamr@2
|
67 |
erdos_renyi_iterator& operator++()
|
williamr@2
|
68 |
{
|
williamr@2
|
69 |
--edges;
|
williamr@2
|
70 |
next();
|
williamr@2
|
71 |
return *this;
|
williamr@2
|
72 |
}
|
williamr@2
|
73 |
|
williamr@2
|
74 |
erdos_renyi_iterator operator++(int)
|
williamr@2
|
75 |
{
|
williamr@2
|
76 |
erdos_renyi_iterator temp(*this);
|
williamr@2
|
77 |
++(*this);
|
williamr@2
|
78 |
return temp;
|
williamr@2
|
79 |
}
|
williamr@2
|
80 |
|
williamr@2
|
81 |
bool operator==(const erdos_renyi_iterator& other) const
|
williamr@2
|
82 |
{ return edges == other.edges; }
|
williamr@2
|
83 |
|
williamr@2
|
84 |
bool operator!=(const erdos_renyi_iterator& other) const
|
williamr@2
|
85 |
{ return !(*this == other); }
|
williamr@2
|
86 |
|
williamr@2
|
87 |
private:
|
williamr@2
|
88 |
void next()
|
williamr@2
|
89 |
{
|
williamr@2
|
90 |
uniform_int<vertices_size_type> rand_vertex(0, n-1);
|
williamr@2
|
91 |
current.first = rand_vertex(*gen);
|
williamr@2
|
92 |
do {
|
williamr@2
|
93 |
current.second = rand_vertex(*gen);
|
williamr@2
|
94 |
} while (current.first == current.second && !allow_self_loops);
|
williamr@2
|
95 |
}
|
williamr@2
|
96 |
|
williamr@2
|
97 |
RandomGenerator* gen;
|
williamr@2
|
98 |
vertices_size_type n;
|
williamr@2
|
99 |
edges_size_type edges;
|
williamr@2
|
100 |
bool allow_self_loops;
|
williamr@2
|
101 |
value_type current;
|
williamr@2
|
102 |
};
|
williamr@2
|
103 |
|
williamr@2
|
104 |
template<typename RandomGenerator, typename Graph>
|
williamr@2
|
105 |
class sorted_erdos_renyi_iterator
|
williamr@2
|
106 |
{
|
williamr@2
|
107 |
typedef typename graph_traits<Graph>::directed_category directed_category;
|
williamr@2
|
108 |
typedef typename graph_traits<Graph>::vertices_size_type vertices_size_type;
|
williamr@2
|
109 |
typedef typename graph_traits<Graph>::edges_size_type edges_size_type;
|
williamr@2
|
110 |
|
williamr@2
|
111 |
BOOST_STATIC_CONSTANT
|
williamr@2
|
112 |
(bool,
|
williamr@2
|
113 |
is_undirected = (is_base_and_derived<undirected_tag,
|
williamr@2
|
114 |
directed_category>::value
|
williamr@2
|
115 |
|| is_same<undirected_tag, directed_category>::value));
|
williamr@2
|
116 |
|
williamr@2
|
117 |
public:
|
williamr@2
|
118 |
typedef std::input_iterator_tag iterator_category;
|
williamr@2
|
119 |
typedef std::pair<vertices_size_type, vertices_size_type> value_type;
|
williamr@2
|
120 |
typedef const value_type& reference;
|
williamr@2
|
121 |
typedef const value_type* pointer;
|
williamr@2
|
122 |
typedef void difference_type;
|
williamr@2
|
123 |
|
williamr@2
|
124 |
sorted_erdos_renyi_iterator()
|
williamr@2
|
125 |
: gen(), rand_vertex(0.5), n(0), allow_self_loops(false),
|
williamr@2
|
126 |
src((std::numeric_limits<vertices_size_type>::max)()), tgt(0), prob(0) {}
|
williamr@2
|
127 |
sorted_erdos_renyi_iterator(RandomGenerator& gen, vertices_size_type n,
|
williamr@2
|
128 |
double prob = 0.0,
|
williamr@2
|
129 |
bool allow_self_loops = false)
|
williamr@2
|
130 |
: gen(),
|
williamr@2
|
131 |
// The "1.0 - prob" in the next line is to work around a Boost.Random
|
williamr@2
|
132 |
// (and TR1) bug in the specification of geometric_distribution. It
|
williamr@2
|
133 |
// should be replaced by "prob" when the issue is fixed.
|
williamr@2
|
134 |
rand_vertex(1.0 - prob),
|
williamr@2
|
135 |
n(n), allow_self_loops(allow_self_loops), src(0), tgt(0), prob(prob)
|
williamr@2
|
136 |
{
|
williamr@2
|
137 |
this->gen.reset(new uniform_01<RandomGenerator>(gen));
|
williamr@2
|
138 |
|
williamr@2
|
139 |
if (prob == 0.0) {src = (std::numeric_limits<vertices_size_type>::max)(); return;}
|
williamr@2
|
140 |
next();
|
williamr@2
|
141 |
}
|
williamr@2
|
142 |
|
williamr@2
|
143 |
reference operator*() const { return current; }
|
williamr@2
|
144 |
pointer operator->() const { return ¤t; }
|
williamr@2
|
145 |
|
williamr@2
|
146 |
sorted_erdos_renyi_iterator& operator++()
|
williamr@2
|
147 |
{
|
williamr@2
|
148 |
next();
|
williamr@2
|
149 |
return *this;
|
williamr@2
|
150 |
}
|
williamr@2
|
151 |
|
williamr@2
|
152 |
sorted_erdos_renyi_iterator operator++(int)
|
williamr@2
|
153 |
{
|
williamr@2
|
154 |
sorted_erdos_renyi_iterator temp(*this);
|
williamr@2
|
155 |
++(*this);
|
williamr@2
|
156 |
return temp;
|
williamr@2
|
157 |
}
|
williamr@2
|
158 |
|
williamr@2
|
159 |
bool operator==(const sorted_erdos_renyi_iterator& other) const
|
williamr@2
|
160 |
{ return src == other.src && tgt == other.tgt; }
|
williamr@2
|
161 |
|
williamr@2
|
162 |
bool operator!=(const sorted_erdos_renyi_iterator& other) const
|
williamr@2
|
163 |
{ return !(*this == other); }
|
williamr@2
|
164 |
|
williamr@2
|
165 |
private:
|
williamr@2
|
166 |
void next()
|
williamr@2
|
167 |
{
|
williamr@2
|
168 |
using std::sqrt;
|
williamr@2
|
169 |
using std::floor;
|
williamr@2
|
170 |
|
williamr@2
|
171 |
// In order to get the edges from the generator in sorted order, one
|
williamr@2
|
172 |
// effective (but slow) procedure would be to use a
|
williamr@2
|
173 |
// bernoulli_distribution for each legal (src, tgt) pair. Because of the
|
williamr@2
|
174 |
// O(n^2) cost of that, a geometric distribution is used. The geometric
|
williamr@2
|
175 |
// distribution tells how many times the bernoulli_distribution would
|
williamr@2
|
176 |
// need to be run until it returns true. Thus, this distribution can be
|
williamr@2
|
177 |
// used to step through the edges which are actually present. Everything
|
williamr@2
|
178 |
// beyond "tgt += increment" is done to effectively convert linear
|
williamr@2
|
179 |
// indexing (the partial sums of the geometric distribution output) into
|
williamr@2
|
180 |
// graph edges.
|
williamr@2
|
181 |
assert (src != (std::numeric_limits<vertices_size_type>::max)());
|
williamr@2
|
182 |
vertices_size_type increment = rand_vertex(*gen);
|
williamr@2
|
183 |
tgt += increment;
|
williamr@2
|
184 |
if (is_undirected) {
|
williamr@2
|
185 |
// Update src and tgt based on position of tgt
|
williamr@2
|
186 |
// Basically, we want the greatest src_increment such that (in \bbQ):
|
williamr@2
|
187 |
// src_increment * (src + allow_self_loops + src_increment - 1/2) <= tgt
|
williamr@2
|
188 |
// The result of the LHS of this, evaluated with the computed
|
williamr@2
|
189 |
// src_increment, is then subtracted from tgt
|
williamr@2
|
190 |
double src_minus_half = (src + allow_self_loops) - 0.5;
|
williamr@2
|
191 |
double disc = src_minus_half * src_minus_half + 2 * tgt;
|
williamr@2
|
192 |
double src_increment_fp = floor(sqrt(disc) - src_minus_half);
|
williamr@2
|
193 |
vertices_size_type src_increment = vertices_size_type(src_increment_fp);
|
williamr@2
|
194 |
if (src + src_increment >= n) {
|
williamr@2
|
195 |
src = n;
|
williamr@2
|
196 |
} else {
|
williamr@2
|
197 |
tgt -= (src + allow_self_loops) * src_increment +
|
williamr@2
|
198 |
src_increment * (src_increment - 1) / 2;
|
williamr@2
|
199 |
src += src_increment;
|
williamr@2
|
200 |
}
|
williamr@2
|
201 |
} else {
|
williamr@2
|
202 |
// Number of out edge positions possible from each vertex in this graph
|
williamr@2
|
203 |
vertices_size_type possible_out_edges = n - (allow_self_loops ? 0 : 1);
|
williamr@2
|
204 |
src += (std::min)(n - src, tgt / possible_out_edges);
|
williamr@2
|
205 |
tgt %= possible_out_edges;
|
williamr@2
|
206 |
}
|
williamr@2
|
207 |
// Set end of graph code so (src, tgt) will be the same as for the end
|
williamr@2
|
208 |
// sorted_erdos_renyi_iterator
|
williamr@2
|
209 |
if (src >= n) {src = (std::numeric_limits<vertices_size_type>::max)(); tgt = 0;}
|
williamr@2
|
210 |
// Copy (src, tgt) into current
|
williamr@2
|
211 |
current.first = src;
|
williamr@2
|
212 |
current.second = tgt;
|
williamr@2
|
213 |
// Adjust for (src, src) edge being forbidden
|
williamr@2
|
214 |
if (!allow_self_loops && tgt >= src) ++current.second;
|
williamr@2
|
215 |
}
|
williamr@2
|
216 |
|
williamr@2
|
217 |
shared_ptr<uniform_01<RandomGenerator> > gen;
|
williamr@2
|
218 |
geometric_distribution<vertices_size_type> rand_vertex;
|
williamr@2
|
219 |
vertices_size_type n;
|
williamr@2
|
220 |
bool allow_self_loops;
|
williamr@2
|
221 |
vertices_size_type src, tgt;
|
williamr@2
|
222 |
value_type current;
|
williamr@2
|
223 |
double prob;
|
williamr@2
|
224 |
};
|
williamr@2
|
225 |
|
williamr@2
|
226 |
} // end namespace boost
|
williamr@2
|
227 |
|
williamr@2
|
228 |
#endif // BOOST_GRAPH_ERDOS_RENYI_GENERATOR_HPP
|