Version: SMASH-3.4
random.h
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (c) 2012-2020,2026
4  * SMASH Team
5  *
6  * GNU General Public License (GPLv3 or later)
7  *
8  */
9 
10 #ifndef SRC_INCLUDE_SMASH_RANDOM_H_
11 #define SRC_INCLUDE_SMASH_RANDOM_H_
12 
13 #include <cassert>
14 #include <limits>
15 #include <random>
16 #include <sstream>
17 #include <utility>
18 #include <vector>
19 
20 #include "smash/macros.h"
21 
22 namespace smash {
23 
24 /** Namespace random provides functions for random Number Generation.
25  */
26 
27 namespace random {
28 
29 /// The random number engine used is the Mersenne Twister.
30 using Engine = std::mt19937_64;
31 
32 /// The engine that is used commonly by all distributions.
33 extern /*thread_local (see commit 897d0b8)*/ Engine engine;
34 
35 /** Provides uniform random numbers on a fixed interval.
36  *
37  * objects of uniform_dist can be used to provide a large number of
38  * random numbers in the same interval. Example:
39  *
40  * \code
41  * using namespace random;
42  * double sum = 0.0;
43  * auto uniform_0_to_3 = uniform_dist(0., 3.);
44  * for (MANY_TIMES) {
45  * sum += uniform_0_to_3();
46  * }
47  * \endcode
48  *
49  * The random number engine is completely hidden inside the object.
50  */
51 template <typename T>
52 class uniform_dist {
53  public:
54  /**
55  * Creates the object and fixes the interval.
56  *
57  * \param min Lower bound of interval.
58  * \param max Upper bound of interval.
59  * */
60  uniform_dist(T min, T max) : distribution(min, max) {}
61  /** \returns A random number in the interval. */
62  T operator()() { return distribution(engine); }
63 
64  private:
65  /** The distribution object that is being used. */
66  std::uniform_real_distribution<T> distribution;
67 };
68 
69 /** Generates a seed with a truly random 63-bit value, if possible */
70 int64_t generate_63bit_seed();
71 
72 /** Sets the seed of the random number engine. */
73 template <typename T>
74 void set_seed(T &&seed) {
75  static_assert(std::is_same<Engine::result_type, uint64_t>::value,
76  "experiment.cc needs the seed to be 64 bits");
77  engine.seed(std::forward<T>(seed));
78 }
79 
80 /// Advance the engine's state and return the generated value.
81 inline Engine::result_type advance() { return engine(); }
82 
83 /**
84  * \returns A uniformly distributed random real number \f$\chi \in [{\rm
85  * min}, {\rm max})\f$
86  *
87  * \param min Minimal sampled value.
88  * \param max Maximal sampled value.
89  */
90 template <typename T>
91 T uniform(T min, T max) {
92  /* Strictly speaking, a distribution could be created with min == max, but
93  * then it would be UB to use the call operator(), which we do here. */
94  assert(min < max);
95  return std::uniform_real_distribution<T>(min, max)(engine);
96 }
97 
98 /**
99  * \return A uniformly distributed random integer number \f$\chi \in [{\rm
100  * min}, {\rm max})\f$
101  *
102  * \param min Minimal sampled value.
103  * \param max Maximal sampled value.
104  */
105 template <typename T>
106 T uniform_int(T min, T max) {
107  /* Strictly speaking, a distribution could be created with min == max, but
108  * then it would be UB to use the call operator(), which we do here. */
109  assert(min < max);
110  return std::uniform_int_distribution<T>(min, max)(engine);
111 }
112 
113 /**
114  * \return a uniformly distributed random number \f$\chi \in [0,1)\f$.
115  *
116  * Note that the popular implementations in GCC and clang may return 1:
117  *
118  * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64351
119  * https://llvm.org/bugs/show_bug.cgi?id=18767
120  */
121 template <typename T = double>
123  return std::generate_canonical<T, std::numeric_limits<double>::digits>(
124  engine);
125 }
126 
127 /**
128  * \return A uniformly distributed random number \f$\chi \in (0,1]\f$.
129  */
130 template <typename T = double>
132  // use 'nextafter' to generate a value that is guaranteed to be larger than 0
133  return std::nextafter(
134  std::generate_canonical<T, std::numeric_limits<double>::digits>(engine),
135  T(1));
136 }
137 
138 /**
139  * \return A uniform_dist object.
140  * \param min Lower bound of interval.
141  * \param max Upper bound of interval.
142  * */
143 template <typename T>
145  return uniform_dist<T>(min, max);
146 }
147 
148 /**
149  * Draws an exponentially distributed random number.
150  *
151  * Probability for a given return value \f$\chi\f$ is \f$p(\chi) =
152  * \Theta(\chi) \cdot \exp(-t)\f$.
153  *
154  * \param lambda Rate parameter.
155  * \return Sampled random number.
156  */
157 template <typename T = double>
158 T exponential(T lambda) {
159  // We are not using std::exponential_distribution because of a bug in the
160  // implementations by clang and gcc.
161  return -std::log(canonical_nonzero()) / lambda;
162 }
163 
164 /**
165  * Draws a random number x from an exponential distribution exp(A*x), where A is
166  * assumed to be positive, and x is typically negative.
167  * The result x is restricted to lie between x1 and x2 (with x2 < x <= x1).
168  *
169  * \param A Positive shape parameter.
170  * \param x1 Maximal sampled value.
171  * \param x2 Minimal sampled value.
172  * \return Sampled random number.
173  *
174  * \throw std::logic_error if the computed sampling interval is degenerate,
175  * reversed, or otherwise invalid.
176  */
177 template <typename T = double>
178 T expo(T A, T x1, T x2) {
179  const T a1 = A * x1, a2 = A * x2;
180  const T a_min = std::log(std::numeric_limits<T>::min());
181  assert(A > T(0.) && x1 > x2 && a1 > a_min);
182  const T high = std::exp(a1);
183  const T low = a2 > a_min ? std::exp(a2) : T(0.); // prevent underflow
184  if (unlikely(!(low < high))) { // catches NANs, too
185  std::ostringstream error_message{};
186  error_message << "Function " << __func__
187  << ": internal invariant 'low < high' violated (low = " << low
188  << ", high = " << high << ")";
189  throw std::logic_error(error_message.str());
190  }
191  T x{};
192  do {
193  /* sample repeatedly until x is in the requested range
194  * (it can get outside due to numerical errors). */
195  x = std::log(uniform(low, high)) / A;
196  } while (!(x <= x1 && x > x2));
197  return x;
198 }
199 
200 /**
201  * Signum function.
202  *
203  * \param val The input value.
204  * \return The sign of the input value.
205  */
206 template <typename T>
207 int sgn(T val) {
208  return (T(0) < val) - (val < T(0));
209 }
210 
211 /**
212 
213 * Sample from a power-law probability density proportional to |x|^n.
214 * The sample is drawn on the interval [xMin, xMax]. The interval must lie
215 * entirely on one side of zero; intervals crossing zero are not supported.
216 * Negative intervals are handled by sampling the absolute values and restoring
217 * the negative sign.
218 * For n ≈ -1, the distribution is sampled using the logarithmic limit.
219 * \tparam T Floating-point type.
220 * \param n Power-law exponent.
221 * \param xMin Lower interval bound.
222 * \param xMax Upper interval bound.
223 * \return Random value distributed as p(x) ∝ |x|^n on the given interval.
224 * \throws std::invalid_argument if the interval crosses zero.
225 * \throws std::invalid_argument if the interval touches zero and n <= -1,
226 * where the distribution is not normalizable.
227 */
228 template <typename T = double>
229 T power(T n, T xMin, T xMax) {
230  const T n1 = n + T(1);
231 
232  if ((xMin < 0 && xMax > 0) || (xMax < 0 && xMin > 0)) {
233  throw std::invalid_argument(
234  "power: interval crossing zero is not supported");
235  } else if ((xMin == 0 || xMax == 0) && n <= -1) {
236  throw std::invalid_argument("power: distribution not normalizable at x=0");
237  }
238 
239  if (xMin > xMax) {
240  std::swap(xMin, xMax);
241  }
242  const T sign = xMax < T(0) ? T(-1) : T(1);
243 
244  const T lo = std::abs(xMin);
245  const T hi = std::abs(xMax);
246 
247  if (std::abs(n1) < T(1e-3)) {
248  return sign * lo * std::pow(hi / lo, canonical());
249  }
250 
251  T a = std::pow(lo, n1);
252  T b = std::pow(hi, n1);
253 
254  if (a > b) {
255  std::swap(a, b);
256  }
257 
258  return sign * std::pow(uniform(a, b), T(1) / n1);
259 }
260 /**
261  * Returns a Poisson distributed random number.
262  *
263  * Probability for a given return value \f$\chi\f$ is \f$p(\chi) =
264  * \chi^i/i! \cdot \exp(-\chi)\f$
265  *
266  * \param lam Mean value of the distribution.
267  * \return Sampled random number.
268  */
269 template <typename T>
270 int poisson(const T &lam) {
271  return std::poisson_distribution<int>(lam)(engine);
272 }
273 
274 /**
275  * Returns a binomially distributed random number.
276  *
277  * \param N Number of trials.
278  * \param p Probability of a trial generating true.
279  * \return Sampled random number.
280  */
281 template <typename T>
282 int binomial(const int N, const T &p) {
283  return std::binomial_distribution<int>(N, p)(engine);
284 }
285 
286 /**
287  * Returns a random number drawn from a normal distribution.
288  *
289  * \param mean Mean value of the distribution.
290  * \param sigma Standard deviation of the distribution.
291  * \return Sampled random number.
292  */
293 template <typename T>
294 double normal(const T &mean, const T &sigma) {
295  return std::normal_distribution<double>(mean, sigma)(engine);
296 }
297 
298 /**
299  * Discrete distribution with weight given by probability vector.
300  */
301 template <typename T>
303  public:
304  /** Default discrete distribution.
305  *
306  * Always draws 0.
307  */
309 
310  /** Construct from probability vector.
311  * \param plist Vector with probabilities such that P(i) = vec[i]
312  */
313  explicit discrete_dist(const std::vector<T> &plist)
314  : distribution(plist.begin(), plist.end()) {}
315 
316  /** Construct from probability list.
317  * \param l Initializer list with probabilities such that P(i) = l[i]
318  */
319  explicit discrete_dist(std::initializer_list<T> l) : distribution(l) {}
320 
321  /** Reset the discrete distribution from a new probability list.
322  * \param plist Vector with probabilities such that P(i) = vec[i]
323  */
324  void reset_weights(const std::vector<T> &plist) {
325  distribution = std::discrete_distribution<>(plist.begin(), plist.end());
326  }
327  /** Draw a random number from the discrete distribution.
328  * \return Sampled value
329  */
330  int operator()() { return distribution(engine); }
331 
332  private:
333  /** The distribution object that is being used. */
334  std::discrete_distribution<> distribution;
335 };
336 
337 /**
338  * Draws a random number from a Cauchy distribution (sometimes also called
339  * Lorentz or non-relativistic Breit-Wigner distribution) with the given
340  * parameters (constant width!) inside the range [min,max]. This function is
341  * similar to std::cauchy_distribution, but can return values inside a limited
342  * interval.
343  * \param pole Pole parameter of the Cauchy function, i.e. location of the peak.
344  * \param width Width parameter of the Cauchy function, determining the
345  * sharpness of the peak.
346  * \param min Minimum value to be returned.
347  * \param max Maximum value to be returned.
348  * \return Sampled random number.
349  */
350 template <typename T = double>
351 T cauchy(T pole, T width, T min, T max) {
352  /* Use double-precision variables, in order to work around a glibc bug in
353  * tanf:
354  * https://sourceware.org/bugzilla/show_bug.cgi?id=18221 */
355  const double u_min = std::atan((min - pole) / width);
356  const double u_max = std::atan((max - pole) / width);
357  const double u = uniform(u_min, u_max);
358  return pole + width * std::tan(u);
359 }
360 
361 /**
362  * Draws a random number from a beta-distribution, where probability density of
363  * \f$x\f$ is \f$p(x) = frac{\Gamma(a)\Gamma(b)}{Gamma(a+b)}
364  * x^{a-1} (1-x)^{b-1}\f$. This distribution is necessary for string
365  * formation. The implementation uses a property connecting beta distribution
366  * to gamma-distribution. Interchanging a and b will not change results.
367  *
368  * \param a Shape parameter.
369  * \param b Scale parameter.
370  * \return Sampled random number.
371  */
372 template <typename T = double>
373 T beta(T a, T b) {
374  // Otherwise the integral over probability density diverges
375  assert(a > T(0.0) && b > T(0.0));
376  const T x1 = std::gamma_distribution<T>(a)(engine);
377  const T x2 = std::gamma_distribution<T>(b)(engine);
378  return x1 / (x1 + x2);
379 }
380 
381 /**
382  * Draws a random number from a beta-distribution with a = 0. In this case
383  * the probability density is \f$p(x) = 1/x (1-x)^b\f$. The integral from
384  * 0 to 1 over this distribution diverges, so the sampling is performed
385  * in the interval (xmin, 1). This distribution is necessary for string
386  * formation. The implementation uses the following property:
387  * \f$p(x)dx = dx/x (1-x)^b = (1-x)^b d ln(x) = (1 - e^{-y})^b dy\f$, where
388  * \f$ y = - ln(x) \f$.
389  *
390  * \param xmin Minimal sampled value.
391  * \param b Second shape parameter.
392  * \return Sampled random number.
393  */
394 template <typename T = double>
395 T beta_a0(T xmin, T b) {
396  assert(xmin > T(0.0) && xmin < T(1.0));
397  T y;
398  do {
399  y = uniform(0.0, -std::log(xmin));
400  } while (std::pow((1.0 - std::exp(-y)), b) < canonical());
401  return std::exp(-y);
402 }
403 
404 /**
405  * The intention of this class is to efficiently sample \f$ (N_1, N_2) \f$
406  * from the Bessel distribution \f$ p(N_1,N_2) \sim \mathrm{Poi}(\nu_1)
407  * \mathrm{Poi}(\nu_2) \delta(N_1 - N_2 = N)\f$, where \f$\mathrm{Poi}(\nu)\f$
408  * denotes Poisson distribution with mean \f$\nu\f$. In other words, this
409  * class samples two Poisson numbers with a given mean and a fixed difference.
410  * The intended use is to sample the number of baryons and antibaryons, given
411  * their means and net baryon number.
412  *
413  * The distribution of \f$ min(N_1,N_2) \f$ is a so-called Bessel distribution.
414  * Denoting \f$ a = \sqrt{\nu_1 \nu_2}\f$, \f$ p(N_{smaller} = k) =
415  * \frac{(a/2)^{2k+N}}{I_N(a) k! (N+k)!} \f$. We sample this distribution using
416  * the method suggested by Yuan and Kalbfleisch \cite Yuan2000 : if
417  * \f$ m = \frac{1}{2} (\sqrt{a^2 + N^2} - N) > 6\f$, then the distribution is
418  * approximated well by a Gaussian, else probabilities are computed explicitely
419  * and a table sampling is applied.
420  */
422  public:
423  /**
424  * Construct a \ref BesselSampler.
425  *
426  * \param[in] poisson_mean1 Mean of the first number's Poisson distribution.
427  * \param[in] poisson_mean2 Mean of the second number's Poisson distribution.
428  * \param[in] fixed_difference Difference between the sampled numbers.
429  * \return Constructed sampler.
430  */
431  BesselSampler(const double poisson_mean1, const double poisson_mean2,
432  const int fixed_difference);
433 
434  /**
435  * Sample two numbers from given Poissonians with a fixed difference.
436  *
437  * \return Pair of first and second sampled number.
438  */
439  std::pair<int, int> sample();
440 
441  private:
442  /**
443  * Compute the ratio of two Bessel functions
444  * r(n,a) = bessel_I(n+1,a)/bessel_I(n,a) using the continued fraction
445  * representation (see \cite Yuan2000).
446  *
447  * \param[in] n First Bessel parameter.
448  * \param[in] a Second Bessel parameter.
449  * \return Ratio bessel_I(n+1,a)/bessel_I(n,a).
450  */
451  static double r_(int n, double a);
452 
453  /// Vector to store tabulated values of probabilities for small m case (m <6).
455 
456  /// Mode of the Bessel function, see \cite Yuan2000 for details.
457  double m_;
458 
459  /// Second parameter of Bessel distribution, see \cite Yuan2000 for details.
460  const double a_;
461 
462  /// First parameter of Bessel distribution (= \f$ \nu \f$ in \cite Yuan2000).
463  const int N_;
464 
465  /// Boolean variable to verify that N > 0.
466  const bool N_is_positive_;
467 
468  /**
469  * Switching mode to normal approximation.
470  * \note Normal approximation of Bessel functions is possible for modes >= 6.
471  * See \cite Yuan2000 for details.
472  */
473  static constexpr double m_switch_method_ = 6.0;
474 
475  /// Probabilities smaller than negligibly_probability are neglected.
476  static constexpr double negligible_probability_ = 1.e-12;
477 
478  /// Mean of the Bessel distribution.
479  double mu_;
480 
481  /// Standard deviation of the Bessel distribution.
482  double sigma_;
483 };
484 
485 } // namespace random
486 } // namespace smash
487 
488 #endif // SRC_INCLUDE_SMASH_RANDOM_H_
The intention of this class is to efficiently sample from the Bessel distribution ,...
Definition: random.h:421
double mu_
Mean of the Bessel distribution.
Definition: random.h:479
double sigma_
Standard deviation of the Bessel distribution.
Definition: random.h:482
std::pair< int, int > sample()
Sample two numbers from given Poissonians with a fixed difference.
Definition: random.cc:74
const bool N_is_positive_
Boolean variable to verify that N > 0.
Definition: random.h:466
static double r_(int n, double a)
Compute the ratio of two Bessel functions r(n,a) = bessel_I(n+1,a)/bessel_I(n,a) using the continued ...
Definition: random.cc:82
static constexpr double negligible_probability_
Probabilities smaller than negligibly_probability are neglected.
Definition: random.h:476
double m_
Mode of the Bessel function, see for details.
Definition: random.h:457
static constexpr double m_switch_method_
Switching mode to normal approximation.
Definition: random.h:473
random::discrete_dist< double > dist_
Vector to store tabulated values of probabilities for small m case (m <6).
Definition: random.h:454
BesselSampler(const double poisson_mean1, const double poisson_mean2, const int fixed_difference)
Construct a BesselSampler.
Definition: random.cc:32
const int N_
First parameter of Bessel distribution (= in ).
Definition: random.h:463
const double a_
Second parameter of Bessel distribution, see for details.
Definition: random.h:460
Discrete distribution with weight given by probability vector.
Definition: random.h:302
discrete_dist()
Default discrete distribution.
Definition: random.h:308
std::discrete_distribution distribution
The distribution object that is being used.
Definition: random.h:334
int operator()()
Draw a random number from the discrete distribution.
Definition: random.h:330
discrete_dist(const std::vector< T > &plist)
Construct from probability vector.
Definition: random.h:313
discrete_dist(std::initializer_list< T > l)
Construct from probability list.
Definition: random.h:319
void reset_weights(const std::vector< T > &plist)
Reset the discrete distribution from a new probability list.
Definition: random.h:324
Provides uniform random numbers on a fixed interval.
Definition: random.h:52
uniform_dist(T min, T max)
Creates the object and fixes the interval.
Definition: random.h:60
std::uniform_real_distribution< T > distribution
The distribution object that is being used.
Definition: random.h:66
#define unlikely(x)
Tell the branch predictor that this expression is likely false.
Definition: macros.h:16
constexpr int p
Proton.
constexpr int n
Neutron.
int poisson(const T &lam)
Returns a Poisson distributed random number.
Definition: random.h:270
T power(T n, T xMin, T xMax)
Sample from a power-law probability density proportional to |x|^n.
Definition: random.h:229
T exponential(T lambda)
Draws an exponentially distributed random number.
Definition: random.h:158
T beta_a0(T xmin, T b)
Draws a random number from a beta-distribution with a = 0.
Definition: random.h:395
T beta(T a, T b)
Draws a random number from a beta-distribution, where probability density of is .
Definition: random.h:373
uniform_dist< T > make_uniform_distribution(T min, T max)
Definition: random.h:144
Engine::result_type advance()
Advance the engine's state and return the generated value.
Definition: random.h:81
T expo(T A, T x1, T x2)
Draws a random number x from an exponential distribution exp(A*x), where A is assumed to be positive,...
Definition: random.h:178
T canonical_nonzero()
Definition: random.h:131
Engine engine
The engine that is used commonly by all distributions.
Definition: random.cc:19
int64_t generate_63bit_seed()
Generates a seed with a truly random 63-bit value, if possible.
Definition: random.cc:21
std::mt19937_64 Engine
The random number engine used is the Mersenne Twister.
Definition: random.h:30
double normal(const T &mean, const T &sigma)
Returns a random number drawn from a normal distribution.
Definition: random.h:294
T uniform_int(T min, T max)
Definition: random.h:106
int binomial(const int N, const T &p)
Returns a binomially distributed random number.
Definition: random.h:282
T uniform(T min, T max)
Definition: random.h:91
T cauchy(T pole, T width, T min, T max)
Draws a random number from a Cauchy distribution (sometimes also called Lorentz or non-relativistic B...
Definition: random.h:351
int sgn(T val)
Signum function.
Definition: random.h:207
T canonical()
Definition: random.h:122
void set_seed(T &&seed)
Sets the seed of the random number engine.
Definition: random.h:74
Definition: action.h:24