OpenRAND  0.9
OpenRAND: A C++ Library for Reproducible Random Number Generation in Parallel Computing Environments
base_state.h
1 // @HEADER
2 // *******************************************************************************
3 // OpenRAND *
4 // A Performance Portable, Reproducible Random Number Generation Library *
5 // *
6 // Copyright (c) 2023, Michigan State University *
7 // *
8 // Permission is hereby granted, free of charge, to any person obtaining a copy *
9 // of this software and associated documentation files (the "Software"), to deal *
10 // in the Software without restriction, including without limitation the rights *
11 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell *
12 // copies of the Software, and to permit persons to whom the Software is *
13 // furnished to do so, subject to the following conditions: *
14 // *
15 // The above copyright notice and this permission notice shall be included in *
16 // all copies or substantial portions of the Software. *
17 // *
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE *
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, *
23 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE *
24 // SOFTWARE. *
25 //********************************************************************************
26 // @HEADER
27 
28 #ifndef OPENRAND_BASE_STATE_H_
29 #define OPENRAND_BASE_STATE_H_
30 
31 #include <openrand/util.h>
32 
33 #include <cstdint>
34 #include <limits>
35 #include <type_traits>
36 
37 namespace openrand {
38 
49 template <typename RNG>
50 class BaseRNG {
51  public:
52  using result_type = uint32_t;
53 
54  static constexpr result_type min() {
55  return 0u;
56  }
57  static constexpr result_type max() {
58  return ~((result_type)0);
59  }
60 
68  OPENRAND_DEVICE result_type operator()() {
69  return gen().template draw<uint32_t>();
70  }
71 
83  template <typename T = float>
84  OPENRAND_DEVICE T rand() {
85  if constexpr (sizeof(T) <= 4) {
86  const uint32_t x = gen().template draw<uint32_t>();
87  if constexpr (std::is_integral_v<T>)
88  return static_cast<T>(x);
89  else
90  return u01<float, uint32_t>(x);
91  } else {
92  const uint64_t x = gen().template draw<uint64_t>();
93  if constexpr (std::is_integral_v<T>)
94  return static_cast<T>(x);
95  else
96  return u01<double, uint64_t>(x);
97  }
98  }
99 
111  template <typename T = float>
112  OPENRAND_DEVICE T uniform(const T low, const T high) {
113  // TODO: Allow 64 bit integers
114  static_assert(!(std::is_integral_v<T> && sizeof(T) > sizeof(int32_t)),
115  "64 bit int not yet supported");
116 
117  T r = high - low;
118 
119  if constexpr (std::is_floating_point_v<T>) {
120  return low + r * rand<T>();
121  } else if constexpr (std::is_integral_v<T>) {
122  return low + range<true, T>(r);
123  }
124  }
125 
126  /*
127  * @brief Fills an array with random numbers from a uniform distribution [0, 1)
128  *
129  * @Note This array is filled serially. `N` ideally should not be large.
130  */
131  template <typename T = float>
132  OPENRAND_DEVICE void fill_random(T *array, const int N) {
133  for (int i = 0; i < N; i++) array[i] = rand<T>();
134  }
135 
147  template <typename T = float>
148  OPENRAND_DEVICE T randn() {
149  static_assert(std::is_floating_point_v<T>);
150  constexpr T M_PI2 = 2 * static_cast<T>(M_PI);
151 
152  T u = rand<T>();
153  T v = rand<T>();
154  T r = openrand::sqrt(T(-2.0) * openrand::log(u));
155  T theta = v * M_PI2;
156  return r * openrand::cos(theta);
157  }
158 
165  template <typename T = float>
166  OPENRAND_DEVICE vec2<T> randn2() {
167  // Implements box-muller method
168  static_assert(std::is_floating_point_v<T>);
169  constexpr T M_PI2 = 2 * static_cast<T>(M_PI);
170 
171  T u = rand<T>();
172  T v = rand<T>();
173  T r = sqrt(T(-2.0) * log(u));
174  T theta = v * M_PI2;
175  return {r * cos(theta), r * sin(theta)};
176  }
177 
187  template <typename T = float>
188  OPENRAND_DEVICE T randn(const T mean, const T std_dev) {
189  return mean + randn<T>() * std_dev;
190  }
191 
215  template <bool biased = true, typename T = int>
216  OPENRAND_DEVICE T range(const T N) {
217  // static_assert(std::is_integral_v<T> && sizeof(T) <= sizeof(int32_t),
218  // "64 bit int not yet supported");
219 
220  uint32_t x = gen().template draw<uint32_t>();
221  uint64_t res = static_cast<uint64_t>(x) * static_cast<uint64_t>(N);
222 
223  if constexpr (biased) {
224  return static_cast<T>(res >> 32);
225  } else {
226  uint32_t leftover = static_cast<uint32_t>(res);
227  if (leftover < N) {
228  uint32_t threshold = -N % N;
229  while (leftover < threshold) {
230  x = gen().template draw<uint32_t>();
231  res = static_cast<uint64_t>(x) * static_cast<uint64_t>(N);
232  leftover = static_cast<uint32_t>(res);
233  }
234  }
235  return static_cast<T>(res);
236  }
237  }
238 
252  template <typename T = float>
253  OPENRAND_DEVICE inline T gamma(T alpha, T b) {
254  T d = alpha - T((1. / 3.));
255  T c = T(1.) / sqrt(9.f * d);
256  T v, x;
257  while (true) {
258  do {
259  x = randn<T>();
260  v = T(1.0) + c * x;
261  } while (v <= T(0.));
262  v = v * v * v;
263  T u = rand<T>();
264 
265  const T x2 = x * x;
266  if (u < 1.0f - 0.0331f * x2 * x2) return (d * v * b);
267 
268  if (log(u) < 0.5f * x2 + d * (1.0f - v + log(v))) return (d * v * b);
269  }
270  }
271 
282  template <typename T = RNG>
283  typename std::enable_if_t<has_counter<T>::value, RNG> forward_state(
284  int n) const {
285  RNG rng = *static_cast<const RNG *>(this); // copy
286  rng._ctr += n;
287  return rng;
288  }
289 
290  protected:
291  /*
292  * @brief Converts a random number integer to a floating point number between [0., 1.)
293  */
294  template <typename Ftype, typename Utype>
295  inline OPENRAND_DEVICE Ftype u01(const Utype in) const {
296  constexpr Ftype factor =
297  Ftype(1.) / (Ftype(~static_cast<Utype>(0)) + Ftype(1.));
298  constexpr Ftype halffactor = Ftype(0.5) * factor;
299  return static_cast<Ftype>(in) * factor + halffactor;
300  }
301 
302  private:
306  OPENRAND_DEVICE __inline__ RNG &gen() {
307  return *static_cast<RNG *>(this);
308  }
309 
310 }; // class BaseRNG
311 
312 } // namespace openrand
313 
314 #endif // OPENRAND_BASE_STATE_H_
Base class for random number generators.
Definition: base_state.h:50
OPENRAND_DEVICE T rand()
Generates a random number from a uniform distribution between 0 and 1.
Definition: base_state.h:84
OPENRAND_DEVICE T uniform(const T low, const T high)
Generates a number from a uniform distribution between a and b.
Definition: base_state.h:112
OPENRAND_DEVICE vec2< T > randn2()
More efficient version of randn, returns two values at once.
Definition: base_state.h:166
OPENRAND_DEVICE T range(const T N)
Generates a random integer of certain range.
Definition: base_state.h:216
OPENRAND_DEVICE T gamma(T alpha, T b)
Generates a random number from a gamma distribution with shape alpha and scale b.
Definition: base_state.h:253
OPENRAND_DEVICE T randn()
Generates a random number from a normal distribution with mean 0 and std 1.
Definition: base_state.h:148
OPENRAND_DEVICE result_type operator()()
Generates a 32 bit unsigned integer from a uniform distribution.
Definition: base_state.h:68
std::enable_if_t< has_counter< T >::value, RNG > forward_state(int n) const
Returns a new generator with the internal state forwarded by a given number.
Definition: base_state.h:283
OPENRAND_DEVICE T randn(const T mean, const T std_dev)
Generates a random number from a normal distribution with mean and std.
Definition: base_state.h:188
Definition: util.h:85