Turi Create  4.0
random.hpp
1 /* Copyright © 2017 Apple Inc. All rights reserved.
2  *
3  * Use of this source code is governed by a BSD-3-clause license that can
4  * be found in the LICENSE.txt file or at https://opensource.org/licenses/BSD-3-Clause
5  */
6 #ifndef TURI_RANDOM_HPP
7 #define TURI_RANDOM_HPP
8 
9 #include <cstdlib>
10 #include <stdint.h>
11 
12 
13 #include <vector>
14 #include <limits>
15 #include <algorithm>
16 
17 #include <boost/random.hpp>
18 #include <timer/timer.hpp>
19 #include <core/parallel/pthread_tools.hpp>
20 
21 namespace turi {
22 
23  /**
24  * \ingroup random
25  * A collection of thread safe random number routines. Each thread
26  * is assigned its own generator however assigning a seed affects
27  * all current and future generators.
28  */
29  namespace random {
30 
31  /** A function that returns a new random seed value every time it's called.
32  *
33  */
34  uint64_t pure_random_seed();
35 
36 
37 
38 
39  ///////////////////////////////////////////////////////////////////////
40  //// Underlying generator definition
41 
42 
43 
44  namespace distributions {
45  /**
46  * The uniform distribution struct is used for partial function
47  * specialization. Generating uniform random real numbers is
48  * accomplished slightly differently than for integers.
49  * Therefore the base case is for integers and we then
50  * specialize the two real number types (floats and doubles).
51  */
52  template<typename IntType>
53  struct uniform {
54  typedef boost::uniform_int<IntType> distribution_type;
55  template<typename RealRNG, typename DiscreteRNG>
56  static inline IntType sample(RealRNG& real_rng,
57  DiscreteRNG& discrete_rng,
58  const IntType& min, const IntType& max) {
59  return distribution_type(min, max)(discrete_rng);
60  }
61  };
62  template<>
63  struct uniform<double> {
64  typedef boost::uniform_real<double> distribution_type;
65  template<typename RealRNG, typename DiscreteRNG>
66  static inline double sample(RealRNG& real_rng,
67  DiscreteRNG& discrete_rng,
68  const double& min, const double& max) {
69  return distribution_type(min, max)(real_rng);
70  }
71  };
72  template<>
73  struct uniform<float> {
74  typedef boost::uniform_real<float> distribution_type;
75  template<typename RealRNG, typename DiscreteRNG>
76  static inline float sample(RealRNG& real_rng,
77  DiscreteRNG& discrete_rng,
78  const float& min, const float& max) {
79  return distribution_type(min, max)(real_rng);
80  }
81  };
82  }; // end of namespace distributions
83 
84  /**
85  * The generator class is the base underlying type used to
86  * generate random numbers. User threads should use the functions
87  * provided in the random namespace.
88  */
89  class generator {
90  public:
91  // base Generator types
92  typedef boost::lagged_fibonacci607 real_rng_type;
93  typedef boost::mt11213b discrete_rng_type;
94  typedef boost::rand48 fast_discrete_rng_type;
95 
96  generator() {
97  time_seed();
98  }
99 
100  //! Seed the generator using the default seed
101  inline void seed() {
102  mut.lock();
103  real_rng.seed();
104  discrete_rng.seed();
105  fast_discrete_rng.seed();
106  mut.unlock();
107  }
108 
109  //! Seed the generator nondeterministically
110  void nondet_seed();
111 
112 
113  //! Seed the generator using the current time in microseconds
114  inline void time_seed() {
116  }
117 
118  //! Seed the random number generator based on a number
119  void seed(size_t number) {
120  uint32_t _seed = static_cast<uint32_t>(number);
121  if(size_t(_seed) != number) {
122  _seed = static_cast<uint32_t>(std::hash<size_t>{}(number));
123  }
124 
125  mut.lock();
126  fast_discrete_rng.seed(_seed);
127  real_rng.seed(fast_discrete_rng);
128  discrete_rng.seed(fast_discrete_rng);
129  mut.unlock();
130  }
131 
132  //! Seed the generator using another generator
133  void seed(generator& other){
134  mut.lock();
135  real_rng.seed(other.real_rng);
136  discrete_rng.seed(other.discrete_rng);
137  fast_discrete_rng.seed(other.fast_discrete_rng());
138  mut.unlock();
139  }
140 
141  /**
142  * Generate a random number in the uniform real with range [min,
143  * max) or [min, max] if the number type is discrete.
144  */
145  template<typename NumType>
146  inline NumType uniform(const NumType min, const NumType max) {
147  mut.lock();
148  const NumType result = distributions::uniform<NumType>::
149  sample(real_rng, discrete_rng, min, max);
150  mut.unlock();
151  return result;
152  } // end of uniform
153 
154  /**
155  * Generate a random number in the uniform real with range [min,
156  * max) or [min, max] if the number type is discrete.
157  */
158  template<typename NumType>
159  inline NumType fast_uniform(const NumType min, const NumType max) {
160  mut.lock();
161  const NumType result = distributions::uniform<NumType>::
162  sample(real_rng, fast_discrete_rng, min, max);
163  mut.unlock();
164  return result;
165  } // end of fast_uniform
166 
167 
168  /**
169  * Generate a random number in the uniform real with range [min,
170  * max);
171  */
172  inline double gamma(const double alpha = double(1)) {
173  boost::gamma_distribution<double> gamma_dist(alpha);
174  mut.lock();
175  const double result = gamma_dist(real_rng);
176  mut.unlock();
177  return result;
178  } // end of gamma
179 
180 
181  /**
182  * Generate a gaussian random variable with zero mean and unit
183  * variance.
184  */
185  inline double gaussian(const double mean = double(0),
186  const double stdev = double(1)) {
187  boost::normal_distribution<double> normal_dist(mean, stdev);
188  mut.lock();
189  const double result = normal_dist(real_rng);
190  mut.unlock();
191  return result;
192  } // end of gaussian
193 
194  /**
195  * Generate a gaussian random variable with zero mean and unit
196  * variance.
197  */
198  inline double normal(const double mean = double(0),
199  const double stdev = double(1)) {
200  return gaussian(mean, stdev);
201  } // end of normal
202 
203  /**
204  * Generate a cauchy random variable with zero location and unit
205  * scale.
206  */
207  inline double cauchy(const double location = double(0),
208  const double scale = double(1)) {
209  boost::cauchy_distribution<double> cauchy_dist(location, scale);
210  mut.lock();
211  const double result = cauchy_dist(real_rng);
212  mut.unlock();
213  return result;
214  } // end of cauchy
215 
216  inline bool bernoulli(const double p = double(0.5)) {
217  boost::bernoulli_distribution<double> dist(p);
218  mut.lock();
219  const double result(dist(discrete_rng));
220  mut.unlock();
221  return result;
222  } // end of bernoulli
223 
224  inline bool fast_bernoulli(const double p = double(0.5)) {
225  boost::bernoulli_distribution<double> dist(p);
226  mut.lock();
227  const double result(dist(fast_discrete_rng));
228  mut.unlock();
229  return result;
230  } // end of bernoulli
231 
232 
233  /**
234  * Draw a random number from a multinomial
235  */
236  template<typename Double>
237  size_t multinomial(const std::vector<Double>& prb) {
238  ASSERT_GT(prb.size(),0);
239  if (prb.size() == 1) { return 0; }
240  Double sum(0);
241  for(size_t i = 0; i < prb.size(); ++i) {
242  ASSERT_GE(prb[i], 0); // Each entry must be P[i] >= 0
243  sum += prb[i];
244  }
245  ASSERT_GT(sum, 0); // Normalizer must be positive
246  // actually draw the random number
247  const Double rnd(uniform<Double>(0,1));
248  size_t ind = 0;
249  for(Double cumsum(prb[ind]/sum);
250  rnd > cumsum && (ind+1) < prb.size();
251  cumsum += (prb[++ind]/sum));
252  return ind;
253  } // end of multinomial
254 
255  /**
256  * Draw a random number from a multinomial with normalizing
257  * constant provided.
258  */
259  template <typename VecType, typename VType>
260  size_t multinomial(const VecType& prb, VType norm) {
261 
262  if(norm < 1e-20) {
263  return fast_uniform<VType>(0, prb.size() - 1);
264  }
265 
266 #ifndef NDEBUG
267  VType total = 0;
268 
269  for(size_t i = 0; i < size_t(prb.size()); ++i) {
270  total += VType(prb[i]);
271  }
272 
273  ASSERT_LT(double(std::abs(norm - total)), std::max(1e-20, 1e-6 * norm));
274 #endif
275 
276  VType rnd = fast_uniform<VType>(0,norm - (std::is_integral<VType>::value ? 1 : 0));
277 
278  for(size_t i = 0; i < size_t(prb.size()); ++i) {
279  if(rnd <= prb[i]) {
280  return i;
281  } else {
282  rnd -= prb[i];
283  }
284  }
285 
286  return 0;
287  } // end of multinomial
288 
289 
290  /**
291  * Generate a draw from a multinomial using a CDF. This is
292  * slightly more efficient since normalization is not required
293  * and a binary search can be used.
294  */
295  template<typename Double>
296  inline size_t multinomial_cdf(const std::vector<Double>& cdf) {
297  return std::upper_bound(cdf.begin(), cdf.end(),
298  uniform<Double>(0,1)) - cdf.begin();
299 
300  } // end of multinomial_cdf
301 
302 
303  /**
304  * Construct a random permutation
305  */
306  template<typename T>
307  inline std::vector<T> permutation(const size_t nelems) {
308  std::vector<T> perm(nelems);
309  for(T i = 0; i < nelems; ++i) perm[i] = i;
310  shuffle(perm);
311  return perm;
312  } // end of construct a permutation
313 
314  /**
315  * Shuffle a standard vector
316  */
317  template<typename T>
318  void shuffle(std::vector<T>& vec) { shuffle(vec.begin(), vec.end()); }
319 
320  /**
321  * Shuffle a range using the begin and end iterators
322  */
323  template<typename Iterator>
324  void shuffle(Iterator begin, Iterator end) {
325  mut.lock();
326  shuffle_functor functor(*this);
327  std::random_shuffle(begin, end, functor);
328  mut.unlock();
329  } // end of shuffle
330 
331  private:
332  //////////////////////////////////////////////////////
333  /// Data members
334  struct shuffle_functor {
335  generator& gen;
336  inline shuffle_functor(generator& gen) : gen(gen) { }
337  inline std::ptrdiff_t operator()(std::ptrdiff_t end) {
339  sample(gen.real_rng, gen.fast_discrete_rng, 0, end-1);
340  }
341  };
342 
343 
344  //! The real random number generator
345  real_rng_type real_rng;
346  //! The discrete random number generator
347  discrete_rng_type discrete_rng;
348  //! The fast discrete random number generator
349  fast_discrete_rng_type fast_discrete_rng;
350  //! lock used to access local members
351  mutex mut;
352  }; // end of class generator
353 
354 
355 
356 
357 
358 
359 
360 
361 
362 
363 
364 
365 
366 
367 
368  /**
369  * \ingroup random
370  * Seed all generators using the default seed
371  */
372  void seed();
373 
374  /**
375  * \ingroup random
376  * Seed all generators using an integer
377  */
378  void seed(size_t seed_value);
379 
380  /**
381  * \ingroup random
382  * Seed all generators using a nondeterministic source
383  */
384  void nondet_seed();
385 
386  /**
387  * \ingroup random
388  * Seed all generators using the current time in microseconds
389  */
390  void time_seed();
391 
392 
393  /**
394  * \ingroup random
395  * Get the local generator
396  */
398 
399  /**
400  * \ingroup random
401  * Generate a random number in the uniform real with range [min,
402  * max) or [min, max] if the number type is discrete.
403  */
404  template<typename NumType>
405  inline NumType uniform(const NumType min, const NumType max) {
406  if (min == max) return min;
407  return get_source().uniform<NumType>(min, max);
408  } // end of uniform
409 
410  /**
411  * \ingroup random
412  * Generate a random number in the uniform real with range [min,
413  * max) or [min, max] if the number type is discrete.
414  */
415  template<typename NumType>
416  inline NumType fast_uniform(const NumType min, const NumType max) {
417  if (min == max) return min;
418  return get_source().fast_uniform<NumType>(min, max);
419  } // end of fast_uniform
420 
421  /**
422  * \ingroup random
423  * Generate a random number between 0 and 1
424  */
425  inline double rand01() { return uniform<double>(0, 1); }
426 
427  /**
428  * \ingroup random
429  * Simulates the standard rand function as defined in cstdlib
430  */
431  inline int rand() { return fast_uniform(0, RAND_MAX); }
432 
433 
434  /**
435  * \ingroup random
436  * Generate a random number from a gamma distribution.
437  */
438  inline double gamma(const double alpha = double(1)) {
439  return get_source().gamma(alpha);
440  }
441 
442 
443 
444  /**
445  * \ingroup random
446  * Generate a gaussian random variable with zero mean and unit
447  * standard deviation.
448  */
449  inline double gaussian(const double mean = double(0),
450  const double stdev = double(1)) {
451  return get_source().gaussian(mean, stdev);
452  }
453 
454  /**
455  * \ingroup random
456  * Generate a gaussian random variable with zero mean and unit
457  * standard deviation.
458  */
459  inline double normal(const double mean = double(0),
460  const double stdev = double(1)) {
461  return get_source().normal(mean, stdev);
462  }
463 
464  /**
465  * \ingroup random
466  * Generate a cauchy random variable with zero location and unit
467  * scale.
468  */
469  inline double cauchy(const double location = double(0),
470  const double scale = double(1)) {
471  return get_source().cauchy(location, scale);
472  }
473 
474  /**
475  * \ingroup random
476  * Draw a sample from a bernoulli distribution
477  */
478  inline bool bernoulli(const double p = double(0.5)) {
479  return get_source().bernoulli(p);
480  }
481 
482  /**
483  * \ingroup random
484  * Draw a sample form a bernoulli distribution using the faster generator
485  */
486  inline bool fast_bernoulli(const double p = double(0.5)) {
487  return get_source().fast_bernoulli(p);
488  }
489 
490  /**
491  * \ingroup random
492  * Generate a draw from a multinomial. This function
493  * automatically normalizes as well.
494  */
495  template<typename Double>
496  inline size_t multinomial(const std::vector<Double>& prb) {
497  return get_source().multinomial(prb);
498  }
499 
500  /**
501  * \ingroup random
502  * Generate a draw from a multinomial, with preknown normalization This function
503  * automatically normalizes as well.
504  */
505  template<typename VecLike, typename Double>
506  inline size_t multinomial(const VecLike& prb, Double norm) {
507  return get_source().multinomial(prb, norm);
508  }
509 
510  /**
511  * \ingroup random
512  * Generate a draw from a cdf;
513  */
514  template<typename Double>
515  inline size_t multinomial_cdf(const std::vector<Double>& cdf) {
516  return get_source().multinomial_cdf(cdf);
517  }
518 
519 
520 
521  /**
522  * \ingroup random
523  * Construct a random permutation
524  */
525  template<typename T>
526  inline std::vector<T> permutation(const size_t nelems) {
527  return get_source().permutation<T>(nelems);
528  }
529 
530 
531  /**
532  * \ingroup random
533  * Shuffle a standard vector
534  */
535  template<typename T>
536  inline void shuffle(std::vector<T>& vec) {
537  get_source().shuffle(vec);
538  }
539 
540  /**
541  * \ingroup random
542  * Shuffle a range using the begin and end iterators
543  */
544  template<typename Iterator>
545  inline void shuffle(Iterator begin, Iterator end) {
546  get_source().shuffle(begin, end);
547  }
548 
549  /**
550  * Converts a discrete PDF into a CDF
551  */
552  void pdf2cdf(std::vector<double>& pdf);
553 
554 
555 
556  }; // end of random
557 }; // end of turicreate
558 
559 
560 #endif
std::vector< T > permutation(const size_t nelems)
Definition: random.hpp:307
void seed(generator &other)
Seed the generator using another generator.
Definition: random.hpp:133
size_t multinomial_cdf(const std::vector< Double > &cdf)
Definition: random.hpp:296
NumType uniform(const NumType min, const NumType max)
Definition: random.hpp:405
bool fast_bernoulli(const double p=double(0.5))
Definition: random.hpp:486
NumType fast_uniform(const NumType min, const NumType max)
Definition: random.hpp:159
double gaussian(const double mean=double(0), const double stdev=double(1))
Definition: random.hpp:449
size_t multinomial(const std::vector< Double > &prb)
Definition: random.hpp:237
size_t multinomial_cdf(const std::vector< Double > &cdf)
Definition: random.hpp:515
NumType uniform(const NumType min, const NumType max)
Definition: random.hpp:146
size_t multinomial(const VecType &prb, VType norm)
Definition: random.hpp:260
void seed()
Seed the generator using the default seed.
Definition: random.hpp:101
size_t multinomial(const std::vector< Double > &prb)
Definition: random.hpp:496
static size_t usec_of_day()
Returns only the micro-second component of the time since the Unix Epoch.
Definition: timer.hpp:118
void time_seed()
Seed the generator using the current time in microseconds.
Definition: random.hpp:114
void shuffle(std::vector< T > &vec)
Definition: random.hpp:536
double cauchy(const double location=double(0), const double scale=double(1))
Definition: random.hpp:207
double rand01()
Definition: random.hpp:425
void pdf2cdf(std::vector< double > &pdf)
std::vector< T > permutation(const size_t nelems)
Definition: random.hpp:526
generator & get_source()
double gamma(const double alpha=double(1))
Definition: random.hpp:172
double gamma(const double alpha=double(1))
Definition: random.hpp:438
void time_seed()
bool bernoulli(const double p=double(0.5))
Definition: random.hpp:478
uint64_t pure_random_seed()
double gaussian(const double mean=double(0), const double stdev=double(1))
Definition: random.hpp:185
double normal(const double mean=double(0), const double stdev=double(1))
Definition: random.hpp:198
void seed(size_t number)
Seed the random number generator based on a number.
Definition: random.hpp:119
int rand()
Definition: random.hpp:431
double cauchy(const double location=double(0), const double scale=double(1))
Definition: random.hpp:469
void shuffle(Iterator begin, Iterator end)
Definition: random.hpp:324
void nondet_seed()
NumType fast_uniform(const NumType min, const NumType max)
Definition: random.hpp:416
void shuffle(std::vector< T > &vec)
Definition: random.hpp:318
double normal(const double mean=double(0), const double stdev=double(1))
Definition: random.hpp:459