Version: SMASH-2.0
random.cc
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (c) 2014-2019
4  * SMASH Team
5  *
6  * GNU General Public License (GPLv3 or later)
7  *
8  */
9 
10 #include "smash/random.h"
11 #include <random>
12 #include "smash/logging.h"
13 
14 namespace smash {
15 static constexpr int LGrandcanThermalizer = LogArea::GrandcanThermalizer::id;
16 /*thread_local (see #3075)*/ random::Engine random::engine;
17 
19  std::random_device rd;
20  static_assert(std::is_same<decltype(rd()), uint32_t>::value,
21  "random_device is assumed to generate uint32_t");
22  uint64_t unsigned_seed =
23  (static_cast<uint64_t>(rd()) << 32) | static_cast<uint64_t>(rd());
24  // Discard the highest bit to make sure it fits into a positive int64_t
25  const int64_t seed = static_cast<int64_t>(unsigned_seed >> 1);
26  return seed;
27 }
28 
29 random::BesselSampler::BesselSampler(const double poisson_mean1,
30  const double poisson_mean2,
31  const int fixed_difference)
32  : a_(2.0 * std::sqrt(poisson_mean1 * poisson_mean2)),
33  N_(std::abs(fixed_difference)),
34  N_is_positive_(fixed_difference >= 0) {
35  assert(poisson_mean1 >= 0.0);
36  assert(poisson_mean2 >= 0.0);
37  logg[LGrandcanThermalizer].debug("Bessel sampler",
38  ": Poisson mean N1 = ", poisson_mean1,
39  ", Poisson mean N2 = ", poisson_mean2,
40  ", N1 - N2 fixed to ", fixed_difference);
41  m_ = 0.5 * (std::sqrt(a_ * a_ + N_ * N_) - N_);
42  if (m_ >= m_switch_method_) {
43  mu_ = 0.5 * a_ * r_(N_, a_);
44  const double mean_sqr = mu_ * (1.0 + 0.5 * a_ * r_(N_ + 1, a_));
45  sigma_ = std::sqrt(mean_sqr - mu_ * mu_);
47  "m = ", m_, " -> using gaussian sampling with mean = ", mu_,
48  ", sigma = ", sigma_);
49  } else {
50  logg[LGrandcanThermalizer].debug("m = ", m_,
51  " -> using direct sampling method");
52  std::vector<double> probabilities;
53  double wi = 1.0, sum = 0.0;
54  int i = 0;
55  do {
56  sum += wi;
57  probabilities.push_back(wi);
58  wi *= 0.25 * a_ * a_ / (i + 1) / (N_ + i + 1);
59  i++;
60  } while (wi > negligible_probability_);
61  i = 0;
62  for (double p : probabilities) {
63  p /= sum;
64  logg[LGrandcanThermalizer].debug("Probability (", i, ") = ", p);
65  i++;
66  }
67  dist_.reset_weights(probabilities);
68  }
69 }
70 
71 std::pair<int, int> random::BesselSampler::sample() {
72  const int N_smaller = (m_ >= m_switch_method_)
73  ? std::round(random::normal(mu_, sigma_))
74  : dist_();
75  return N_is_positive_ ? std::make_pair(N_smaller + N_, N_smaller)
76  : std::make_pair(N_smaller, N_smaller + N_);
77 }
78 
79 double random::BesselSampler::r_(int n, double a) {
80  const double a_inv = 1.0 / a;
81  double res = 0.0;
82  // |x - continued fraction of order n| < 2^(-n+1), see the book
83  // "Continued fractions" by Khinchin. For 10^-16 = ~2^-50 precision
84  // 50 iterations should be sufficient. However, I found that for some
85  // numerical reason at least 100 terms are needed.
86  int i = 200;
87  for (; i > 0; i--) {
88  res = 1.0 / (a_inv * 2 * (n + i) + res);
89  }
90  // Check the known property of r(n,a) function, see \cite Yuan2000.
91  assert(a / (std::sqrt(a * a + (n + 1) * (n + 1)) + n + 1) <= res);
92  assert(res <= a / (std::sqrt(a * a + n * n) + n));
93  return res;
94 }
95 
96 } // namespace smash
smash
Definition: action.h:24
smash::random::normal
double normal(const T &mean, const T &sigma)
Returns a random number drawn from a normal distribution.
Definition: random.h:250
smash::random::BesselSampler::negligible_probability_
static constexpr double negligible_probability_
Probabilities smaller than negligibly_probability are neglected.
Definition: random.h:432
smash::random::BesselSampler::mu_
double mu_
Mean of the Bessel distribution.
Definition: random.h:435
smash::random::BesselSampler::m_
double m_
Mode of the Bessel function, see for details.
Definition: random.h:413
smash::random::Engine
std::mt19937_64 Engine
The random number engine used is the Mersenne Twister.
Definition: random.h:27
smash::random::BesselSampler::BesselSampler
BesselSampler(const double poisson_mean1, const double poisson_mean2, const int fixed_difference)
Construct a BesselSampler.
Definition: random.cc:29
smash::random::BesselSampler::N_
const int N_
First parameter of Bessel distribution (= in ).
Definition: random.h:419
smash::random::generate_63bit_seed
int64_t generate_63bit_seed()
Generates a seed with a truly random 63-bit value, if possible.
Definition: random.cc:18
smash::random::discrete_dist::reset_weights
void reset_weights(const std::vector< T > &plist)
Reset the discrete distribution from a new probability list.
Definition: random.h:280
smash::random::BesselSampler::dist_
random::discrete_dist< double > dist_
Vector to store tabulated values of probabilities for small m case (m <6).
Definition: random.h:410
smash::logg
std::array< einhard::Logger<>, std::tuple_size< LogArea::AreaTuple >::value > logg
An array that stores all pre-configured Logger objects.
Definition: logging.cc:39
random.h
smash::random::BesselSampler::sample
std::pair< int, int > sample()
Sample two numbers from given Poissonians with a fixed difference.
Definition: random.cc:71
smash::random::engine
Engine engine
The engine that is used commonly by all distributions.
Definition: random.cc:16
smash::random::BesselSampler::m_switch_method_
static constexpr double m_switch_method_
Switching mode to normal approximation.
Definition: random.h:429
smash::random::BesselSampler::a_
const double a_
Second parameter of Bessel distribution, see for details.
Definition: random.h:416
smash::random::BesselSampler::r_
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:79
logging.h
smash::pdg::p
constexpr int p
Proton.
Definition: pdgcode_constants.h:28
smash::random::BesselSampler::sigma_
double sigma_
Standard deviation of the Bessel distribution.
Definition: random.h:438
smash::pdg::n
constexpr int n
Neutron.
Definition: pdgcode_constants.h:30
smash::LGrandcanThermalizer
static constexpr int LGrandcanThermalizer
Definition: grandcan_thermalizer.cc:21