omega_h
Reliable mesh adaptation
Omega_h_random_inline.hpp
1 #ifndef OMEGA_H_RANDOM_INLINE_HPP
2 #define OMEGA_H_RANDOM_INLINE_HPP
3 
4 #include <random123/philox.h>
5 #include <Omega_h_few.hpp>
6 #include <Omega_h_scalar.hpp>
7 #include <Omega_h_vector.hpp>
8 
9 namespace Omega_h {
10 
11 OMEGA_H_INLINE Few<std::uint64_t, 2> run_philox_cbrng(
12  Few<std::uint64_t, 2> ctr, std::uint64_t key) {
13  using PhiloxType = r123::Philox4x32;
14  using ctr_type = typename PhiloxType::ctr_type;
15  using key_type = typename PhiloxType::key_type;
16  PhiloxType philox_cbrng;
17  ctr_type ctr_philox;
18  key_type key_philox;
19  ctr_philox[0] = static_cast<std::uint32_t>(ctr[0]);
20  ctr_philox[1] = static_cast<std::uint32_t>(ctr[0] >> 32);
21  ctr_philox[2] = static_cast<std::uint32_t>(ctr[1]);
22  ctr_philox[3] = static_cast<std::uint32_t>(ctr[1] >> 32);
23  key_philox[0] = static_cast<std::uint32_t>(key);
24  key_philox[1] = static_cast<std::uint32_t>(key >> 32);
25  ctr_philox = philox_cbrng(ctr_philox, key_philox);
26  ctr[0] = ctr_philox[1];
27  ctr[0] <<= 32;
28  ctr[0] |= ctr_philox[0];
29  ctr[1] = ctr_philox[3];
30  ctr[1] <<= 32;
31  ctr[1] |= ctr_philox[2];
32  return ctr;
33 }
34 
35 OMEGA_H_INLINE Real unit_uniform_deviate_from_uint64(std::uint64_t x) {
36  return static_cast<Real>(x) /
37  static_cast<Real>(ArithTraits<std::uint64_t>::max());
38 }
39 
41  public:
42  OMEGA_H_INLINE UnitUniformDistribution(
43  I64 seed_in, I64 key_in, I64 counter_in) {
44  counter[1] = static_cast<std::uint64_t>(seed_in);
45  key = static_cast<std::uint64_t>(key_in);
46  counter[0] = static_cast<std::uint64_t>(counter_in);
47  if (counter[0] % 2)
48  even_step();
49  else
50  state[1] = 0; // Silences GCC 7.2.0 -Wmaybe-uninitialized about state[1]
51  // in operator()
52  }
53  OMEGA_H_INLINE Real operator()() {
54  Real ret;
55  if (counter[0] % 2) {
56  ret = unit_uniform_deviate_from_uint64(state[1]);
57  } else {
58  even_step();
59  ret = unit_uniform_deviate_from_uint64(state[0]);
60  }
61  ++counter[0];
62  return ret;
63  }
64 
65  private:
66  OMEGA_H_INLINE void even_step() {
67  counter[0] >>= 1;
68  state = run_philox_cbrng(counter, key);
69  counter[0] <<= 1;
70  }
71  Few<std::uint64_t, 2> counter;
72  std::uint64_t key;
74 };
75 
76 /* I was initially inspired by GCC's implementation of std::normal_distribution,
77  which is in turn taken from:
78 
79  Devroye, Luc. "Non-Uniform Random Variate Generation".
80  Springer, New York, NY, 1986. 1-26.
81  Chapter 4: "Non-Uniform Random Variate Generation"
82  Section V.4: "Polar Method"
83  SubSection 4.4: "Generating normal random variates in batches"
84  Page 235
85 
86  However, the rejection method used to sample the unit circle would mean
87  that counters advance differently for different random streams, which would
88  complicate programming.
89 
90  Thus, we return to the older but more closed-form approach from:
91  G. E. P. Box and Mervin E. Muller
92  "A Note on the Generation of Random Normal Deviates"
93  The Annals of Mathematical Statistics, 1958
94 
95  */
96 
98  public:
99  OMEGA_H_INLINE StandardNormalDistribution() : counter(0) {}
100  OMEGA_H_INLINE Real operator()(UnitUniformDistribution& uniform_rng) {
101  Real ret;
102  if (counter) {
103  ret = state[1];
104  } else {
105  auto U_1 = uniform_rng();
106  auto U_2 = uniform_rng();
107  /* this is the suspicious part. we can't feed exact zero to std::log,
108  or we'll get a pole error. other implementations will use rejection
109  here, but again I want the number of uniform deviates generated
110  to be a known constant, not a function of their values */
111  if (U_1 == 0.0) U_1 = DBL_MIN;
112  auto common = std::sqrt(-2.0 * std::log(U_1));
113  state[0] = common * std::cos(2.0 * Omega_h::PI * U_2);
114  state[1] = common * std::sin(2.0 * Omega_h::PI * U_2);
115  ret = state[0];
116  }
117  return ret;
118  }
119 
120  private:
121  int counter;
122  Vector<2> state;
123 };
124 
125 /* fortunately, the Weibull distribution has a closed-form inverse CDF
126 (quantile)
127 
128 https://en.wikipedia.org/wiki/Weibull_distribution#Cumulative_distribution_function
129  */
130 OMEGA_H_INLINE Real weibull_quantile(Real shape, Real scale, Real p) {
131  auto one_minus_p = 1.0 - p;
132  if (one_minus_p == 0) one_minus_p = DBL_MIN;
133  return scale * std::pow(double(-std::log(one_minus_p)), double(1.0 / shape));
134 }
135 
136 OMEGA_H_INLINE Real standard_normal_density(Real value) {
137  constexpr auto one_over_sqrt_two_pi = 0.3989422804014327;
138  return one_over_sqrt_two_pi * std::exp((-1.0 / 2.0) * square(value));
139 }
140 
141 OMEGA_H_INLINE Real general_normal_density(
142  Real mean, Real standard_deviation, Real value) {
143  return (1.0 / standard_deviation) *
144  standard_normal_density((value - mean) / standard_deviation);
145 }
146 
147 OMEGA_H_INLINE Real weibull_density(Real shape, Real scale, Real value) {
148  if (value < 0.0) return 0.0;
149  return (shape / scale) * std::pow(value / scale, shape - 1.0) *
150  std::exp(-std::pow(value / scale, shape));
151 }
152 
153 // regularized lower incomplete gamma function, by series expansion
154 OMEGA_H_INLINE Real regularized_lower_incomplete_gamma(Real s, Real z) {
155  Real sum = 1.0;
156  Real x = 1.0;
157  for (Int k = 1; k < 100; ++k) {
158  x *= z / (s + k);
159  sum += x;
160  if (sum > 1e100) return 1.0;
161  if (x / sum < 1e-14) break;
162  }
163  auto a = s * std::log(z) - z - std::lgamma(s + 1.0);
164  return std::exp(a + std::log(sum));
165 }
166 
167 OMEGA_H_INLINE Real cumulative_chi_squared_density(
168  Real ndofs, Real chi_squared) {
169  if (chi_squared < 0.0 || ndofs < 1.0) return 0.0;
170  return regularized_lower_incomplete_gamma(ndofs / 2.0, chi_squared / 2.0);
171 }
172 
173 OMEGA_H_INLINE Real cumulative_standard_normal_density(Real x) {
174  return (1.0 / 2.0) * (1.0 + std::erf(x / std::sqrt(2.0)));
175 }
176 
177 OMEGA_H_INLINE Real cumulative_general_normal_density(
178  Real mean, Real standard_deviation, Real x) {
179  return cumulative_standard_normal_density((x - mean) / standard_deviation);
180 }
181 
182 OMEGA_H_INLINE Real cumulative_weibull_density(Real shape, Real scale, Real x) {
183  return 1.0 - std::exp(-std::pow(x / scale, shape));
184 }
185 
186 } // namespace Omega_h
187 
188 #endif
Definition: Omega_h_random_inline.hpp:97
Definition: Omega_h_random_inline.hpp:40
Definition: amr_mpi_test.cpp:6