RandomLib  1.10
 All Classes Namespaces Files Functions Variables Typedefs Enumerator Friends Macros Pages
DiscreteNormalAlt.hpp
Go to the documentation of this file.
1 /**
2  * \file DiscreteNormalAlt.hpp
3  * \brief Header for DiscreteNormalAlt
4  *
5  * Sample exactly from the discrete normal distribution (alternate version).
6  *
7  * Copyright (c) Charles Karney (2013) <charles@karney.com> and licensed
8  * under the MIT/X11 License. For more information, see
9  * http://randomlib.sourceforge.net/
10  **********************************************************************/
11 
12 #if !defined(RANDOMLIB_DISCRETENORMALALT_HPP)
13 #define RANDOMLIB_DISCRETENORMALALT_HPP 1
14 
17 #include <limits>
18 
19 namespace RandomLib {
20  /**
21  * \brief The discrete normal distribution (alternate version).
22  *
23  * Sample integers \e i with probability proportional to
24  * \f[
25  * \exp\biggl[-\frac12\biggl(\frac{i-\mu}{\sigma}\biggr)^2\biggr],
26  * \f]
27  * where &sigma; and &mu; are given as rationals (the ratio of two integers).
28  * The sampling is exact (provided that the random generator is ideal). The
29  * results of this class are UniformInteger objects which represent a
30  * contiguous range which is a power of 2<sup>\e bits</sup>. This can be
31  * narrowed down to a specific integer as follows
32  * \code
33  #include <iostream>
34  #include <RandomLib/Random.hpp>
35  #include <RandomLib/UniformInteger.hpp>
36  #include <RandomLib/DiscreteNormalAlt.hpp>
37 
38  int main() {
39  RandomLib::Random r; // Create r
40  r.Reseed(); // and give it a unique seed
41  int sigma_num = 7, sigma_den = 1, mu_num = 1, mu_den = 3;
42  RandomLib::DiscreteNormalAlt<int,1> d(sigma_num, sigma_den,
43  mu_num, mu_den);
44  for (int i = 0; i < 100; ++i) {
45  RandomLib::UniformInteger<int,1> u = d(r);
46  std::cout << u << " = ";
47  std::cout << u(r) << "\n";
48  }
49  }
50  \endcode
51  * prints out 100 samples with &sigma; = 7 and &mu; = 1/3; the samples are
52  * first given as a range and then <code>u(r)</code> is used to obtain a
53  * specific integer. In some applications, it might be possible to avoid
54  * sampling all the additional digits to get to a specific integer. (An
55  * example would be drawing a sample which satisfies an inequality.) This
56  * version is slower (by a factor of about 4 for \e bits = 1) than
57  * DiscreteNormal on a conventional computer, but may be faster when random
58  * bits are generated by a slow hardware device.
59  *
60  * The basic algorithm is the same as for DiscreteNormal. However randomness
61  * in metered out \e bits random bits at a time. The algorithm uses the
62  * least random bits (and is slowest!) when \e bits = 1. This rationing of
63  * random bits also applies to sampling \e j in the expression
64  * \f[
65  * x = x_0 + j/\sigma.
66  * \f]
67  * Rather than sampling a definite value for \e j, which then might be
68  * rejected, \e j is sampled using UniformInteger. UniformInteger uses
69  * Lumbroso's method from sampling an integer uniformly in [0, \e m) using at
70  * most 2 + log<sub>2</sub>\e m bits on average (for \e bits = 1). However
71  * when a UniformInteger object is first constructed it samples at most 3
72  * bits (on average) to obtain a range for \e j which is power of 2. This
73  * allows the algorithm to achieve ideal scaling in the limit of large
74  * &sigma; consuming constant + log<sub>2</sub>&sigma; bits on average. In
75  * addition it can deliver the resuls in the form of a UniformInteger
76  * consuming a constant number of bits on average (and then about
77  * log<sub>2</sub>&sigma; additional bits are required to convert the
78  * UniformInteger into an integer sample). The consumption of random bits
79  * (for \e bits = 1) is summarized here \verbatim
80  min mean max
81  bits for UniformInteger result 30.4 34.3 35.7
82  bits for integer result - log2(sigma) 28.8 31.2 32.5
83  toll 26.8 29.1 30.4 \endverbatim
84  * These results are averaged over many samples. The "min" results apply
85  * when &sigma; is a power of 2; the "max" results apply when &sigma; is
86  * slightly more than a power of two; the "mean" results are averaged over
87  * &sigma;. The toll is the excess number of bits over the entropy of the
88  * distribution, which is log<sub>2</sub>(2&pi;\e e)/2 +
89  * log<sub>2</sub>&sigma; (for &sigma; large).
90  *
91  * The data for the first two lines in the table above are taken for the blue
92  * and red lines in the figure below where the abscissa is the fractional
93  * part of log<sub>2</sub>&sigma;
94  * \image html
95  * discrete-bits.png "Random bits to sample from discrete normal distribution"
96  *
97  * This class uses a mutable RandomNumber objects. So a single
98  * DiscreteNormalAlt object cannot safely be used by multiple threads. In a
99  * multi-processing environment, each thread should use a thread-specific
100  * DiscreteNormalAlt object.
101  *
102  * @tparam IntType the integer type to use (default int).
103  **********************************************************************/
104  template<typename IntType = int, int bits = 1> class DiscreteNormalAlt {
105  public:
106  /**
107  * Constructor.
108  *
109  * @param[in] sigma_num the numerator of &sigma;.
110  * @param[in] sigma_den the denominator of &sigma; (default 1).
111  * @param[in] mu_num the numerator of &mu; (default 0).
112  * @param[in] mu_den the denominator of &mu; (default 1).
113  *
114  * This may throw an exception is the parameters are such that overflow is
115  * possible.
116  **********************************************************************/
117  DiscreteNormalAlt(IntType sigma_num, IntType sigma_den = 1,
118  IntType mu_num = 0, IntType mu_den = 1);
119  /**
120  * Return a sample.
121  *
122  * @tparam Random the type of the random generator.
123  * @param[in,out] r a random generator.
124  * @return discrete normal sample in the form of a UniformInteger object.
125  **********************************************************************/
126  template<class Random>
128  private:
129  /**
130  * sigma = _sig / _d, mu = _imu + _mu / _d, _isig = ceil(sigma)
131  **********************************************************************/
132  IntType _sig, _mu, _d, _isig, _imu;
133 
134  mutable RandomNumber<bits> _p;
135  mutable RandomNumber<bits> _q;
136 
137  // ceil(n/d) for d > 0
138  static IntType iceil(IntType n, IntType d);
139  // abs(n) needed because Visual Studio's std::abs has problems
140  static IntType iabs(IntType n);
141  static IntType gcd(IntType u, IntType v);
142 
143  /**
144  * Implement outcomes for choosing with prob (\e x + 2\e k) / (2\e k + 2);
145  * return:
146  * - 1 (succeed unconditionally) with prob (\e m &minus; 2) / \e m,
147  * - 0 (succeed with probability x) with prob 1 / \e m,
148  * - &minus;1 (fail unconditionally) with prob 1 / \e m.
149  **********************************************************************/
150  template<class Random> static int Choose(Random& r, int m);
151 
152  /**
153  * Return true with probability exp(&minus;1/2).
154  **********************************************************************/
155  template<class Random> bool ExpProbH(Random& r) const;
156 
157  /**
158  * Return true with probability exp(&minus;<i>n</i>/2).
159  **********************************************************************/
160  template<class Random> bool ExpProb(Random& r, int n) const;
161 
162  /**
163  * Return \e n with probability exp(&minus;<i>n</i>/2)
164  * (1&minus;exp(&minus;1/2)).
165  **********************************************************************/
166  template<class Random> int ExpProbN(Random& r) const;
167 
168  /**
169  * Algorithm B: true with prob exp(-x * (2*k + x) / (2*k + 2)) where
170  * x = (xn0 + _d * j) / _sig
171  **********************************************************************/
172  template<class Random>
173  bool B(Random& r, int k, IntType xn0, UniformInteger<IntType, bits>& j)
174  const;
175  };
176 
177  template<typename IntType, int bits>
179  (IntType sigma_num, IntType sigma_den, IntType mu_num, IntType mu_den) {
180  STATIC_ASSERT(std::numeric_limits<IntType>::is_integer,
181  "DiscreteNormalAlt: invalid integer type IntType");
182  STATIC_ASSERT(std::numeric_limits<IntType>::is_signed,
183  "DiscreteNormalAlt: IntType must be a signed type");
184  if (!( sigma_num > 0 && sigma_den > 0 && mu_den > 0 ))
185  throw RandomErr("DiscreteNormalAlt: need sigma > 0");
186  _imu = mu_num / mu_den;
187  if (_imu == (std::numeric_limits<IntType>::min)())
188  throw RandomErr("DiscreteNormalAlt: abs(mu) too large");
189  mu_num -= _imu * mu_den;
190  IntType l;
191  l = gcd(sigma_num, sigma_den); sigma_num /= l; sigma_den /= l;
192  l = gcd(mu_num, mu_den); mu_num /= l; mu_den /= l;
193  _isig = iceil(sigma_num, sigma_den);
194  l = gcd(sigma_den, mu_den);
195  _sig = sigma_num * (mu_den / l);
196  _mu = mu_num * (sigma_den / l);
197  _d = sigma_den * (mu_den / l);
198  // The rest of the constructor tests for possible overflow
199  // Check for overflow in computing member variables
200  IntType maxint = (std::numeric_limits<IntType>::max)();
201  if (!( mu_den / l <= maxint / sigma_num &&
202  iabs(mu_num) <= maxint / (sigma_den / l) &&
203  mu_den / l <= maxint / sigma_den ))
204  throw RandomErr("DiscreteNormalAlt: sigma or mu overflow");
205  if (!UniformInteger<IntType, bits>::Check(_isig, _d))
206  throw RandomErr("DiscreteNormalAlt: overflow in UniformInteger");
207  // The probability that k = kmax is about 10^-543.
208  int kmax = 50;
209  // Check that max plausible result fits in an IntType, i.e.,
210  // _isig * (kmax + 1) + abs(_imu) does not lead to overflow.
211  if (!( kmax + 1 <= maxint / _isig &&
212  _isig * (kmax + 1) <= maxint - iabs(_imu) ))
213  throw RandomErr("DiscreteNormalAlt: possible overflow a");
214  // Check xn0 = _sig * k + s * _mu;
215  if (!( kmax <= maxint / _sig &&
216  _sig * kmax <= maxint - iabs(_mu) ))
217  throw RandomErr("DiscreteNormalAlt: possible overflow b");
218  // Check for overflow in RandomNumber::LessThan(..., UniformInteger& j)
219  // p0 * b, p0 = arg2 = xn0 = _d - 1
220  // c *= b, c = arg3 = _d
221  // digit(D, k) * q, digit(D, k) = b - 1, q = arg4 = _sig
222  if (!( _d <= maxint >> bits ))
223  throw std::runtime_error("DiscreteNormalAlt: overflow in RandomNumber a");
224  if (!( (IntType(1) << bits) - 1 <= maxint / _sig ))
225  throw std::runtime_error("DiscreteNormalAlt: overflow in RandomNumber b");
226  }
227 
228  template<typename IntType, int bits> template<class Random>
231  for (;;) {
232  int k = ExpProbN(r);
233  if (!ExpProb(r, k * (k - 1))) continue;
234  UniformInteger<IntType, bits> j(r, _isig);
235  IntType
236  s = r.Boolean() ? -1 : 1,
237  xn0 = _sig * IntType(k) + s * _mu,
238  i0 = iceil(xn0, _d); // i = i0 + j
239  xn0 = i0 * _d - xn0; // xn = xn0 + _d * j
240  if (!j.LessThan(r, _sig - xn0, _d) ||
241  (k == 0 && s < 0 && !j.GreaterThan(r, -xn0, _d))) continue;
242  int h = k + 1; while (h-- && B(r, k, xn0, j));
243  if (!(h < 0)) continue;
244  j.Add(i0 + s * _imu);
245  if (s < 0) j.Negate();
246  return j;
247  }
248  }
249 
250  template<typename IntType, int bits>
251  IntType DiscreteNormalAlt<IntType, bits>::iceil(IntType n, IntType d)
252  { IntType k = n / d; return k + (k * d < n ? 1 : 0); }
253 
254  template<typename IntType, int bits>
255  IntType DiscreteNormalAlt<IntType, bits>::iabs(IntType n)
256  { return n < 0 ? -n : n; }
257 
258  template<typename IntType, int bits>
259  IntType DiscreteNormalAlt<IntType, bits>::gcd(IntType u, IntType v) {
260  // Knuth, TAOCP, vol 2, 4.5.2, Algorithm A
261  u = iabs(u); v = iabs(v);
262  while (v > 0) { IntType r = u % v; u = v; v = r; }
263  return u;
264  }
265 
266  template<typename IntType, int bits> template<class Random>
267  int DiscreteNormalAlt<IntType, bits>::Choose(Random& r, int m) {
268  // Limit base to 2^15 to avoid integer overflow
269  const int b = bits > 15 ? 15 : bits;
270  const unsigned mask = (1u << b) - 1;
271  int n1 = m - 2, n2 = m - 1;
272  // Evaluate u < n/m where u is a random real number in [0,1). Write u =
273  // (d + u') / 2^b where d is a random integer in [0,2^b) and u' is in
274  // [0,1). Then u < n/m becomes u' < n'/m where n' = 2^b * n - d * m and
275  // exit if n' <= 0 (false) or n' >= m (true).
276  for (;;) {
277  int d = (mask & RandomNumber<bits>::RandomDigit(r)) * m;
278  n1 = (std::max)((n1 << b) - d, 0);
279  if (n1 >= m) return 1;
280  n2 = (std::min)((n2 << b) - d, m);
281  if (n2 <= 0) return -1;
282  if (n1 == 0 && n2 == m) return 0;
283  }
284  }
285 
286  template<typename IntType, int bits> template<class Random>
287  bool DiscreteNormalAlt<IntType, bits>::ExpProbH(Random& r) const {
288  _p.Init();
289  if (_p.Digit(r, 0) >> (bits - 1)) return true;
290  for (;;) {
291  _q.Init(); if (!_q.LessThan(r, _p)) return false;
292  _p.Init(); if (!_p.LessThan(r, _q)) return true;
293  }
294  }
295 
296  template<typename IntType, int bits> template<class Random>
297  bool DiscreteNormalAlt<IntType, bits>::ExpProb(Random& r, int n) const {
298  while (n-- > 0) { if (!ExpProbH(r)) return false; }
299  return true;
300  }
301 
302  template<typename IntType, int bits> template<class Random>
303  int DiscreteNormalAlt<IntType, bits>::ExpProbN(Random& r) const {
304  int n = 0;
305  while (ExpProbH(r)) ++n;
306  return n;
307  }
308 
309  template<typename IntType, int bits> template<class Random> bool
310  DiscreteNormalAlt<IntType, bits>::B(Random& r, int k, IntType xn0,
311  UniformInteger<IntType, bits>& j)
312  const {
313  int n = 0, m = 2 * k + 2, f;
314  for (;; ++n) {
315  if ( ((f = k ? 0 : Choose(r, m)) < 0) ||
316  (_q.Init(),
317  !(n ? _q.LessThan(r, _p) : _q.LessThan(r, xn0, _d, _sig, j))) ||
318  ((f = k ? Choose(r, m) : f) < 0) ||
319  (f == 0 && (_p.Init(), !_p.LessThan(r, xn0, _d, _sig, j))) )
320  break;
321  _p.swap(_q); // an efficient way of doing p = q
322  }
323  return (n % 2) == 0;
324  }
325 
326 } // namespace RandomLib
327 
328 #endif // RANDOMLIB_DISCRETENORMALALT_HPP
bool LessThan(Random &r, IntType p, IntType q)
Header for UniformInteger.
Header for RandomNumber.
Generate random integers, reals, and booleans.
Infinite precision random numbers.
The partial uniform integer distribution.
DiscreteNormalAlt(IntType sigma_num, IntType sigma_den=1, IntType mu_num=0, IntType mu_den=1)
The discrete normal distribution (alternate version).
Exception handling for RandomLib.
Definition: Random.hpp:103
static unsigned RandomDigit(Random &r)
#define STATIC_ASSERT(cond, reason)
Definition: MPFRRandom.hpp:31
RandomCanonical< RandomGenerator > Random
Definition: Random.hpp:135
UniformInteger< IntType, bits > operator()(Random &r) const
bool GreaterThan(Random &r, IntType p, IntType q)