OpenRAND  0.9
OpenRAND: A C++ Library for Reproducible Random Number Generation in Parallel Computing Environments
squares.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_SQUARES_H_
29 #define OPENRAND_SQUARES_H_
30 
31 #include <openrand/base_state.h>
32 #include <stdint.h>
33 
34 namespace {
35 
36 #define K 0xc58efd154ce32f6d
37 
38 /*
39 In Squares, since the seed (i.e. key) has to have certain properties to
40 generate good random numbers, we can't allow user to set arbritray seed.
41 To avoid the pitfall of weak user supplied seed, we need to hash that seed.
42 */
43 // inline OPENRAND_DEVICE uint64_t hash_seed(uint64_t seed){
44 // return (seed + 1) * K;
45 // }
46 
47 // inline unsigned nthset(uint16_t x, unsigned n) {
48 // return _tzcnt_u64(_pdep_u64(1U << n, x));
49 // }
50 
51 OPENRAND_DEVICE unsigned int get_digit(uint64_t i, char *mask) {
52  unsigned int j = 0;
53  while (1) {
54  while (mask[j] == 0) j++;
55  if (i == 0) return j;
56  i--;
57  j++;
58  }
59 }
60 
61 OPENRAND_DEVICE uint64_t hash_seed(uint32_t seed) {
62  constexpr uint64_t factorials[16] = {
63  1, 1, 2, 6,
64  24, 120, 720, 5040,
65  40320, 362880, 3628800, 39916800,
66  479001600, 6227020800, 87178291200, 1307674368000};
67  uint64_t base = (1ULL << 60);
68  uint64_t i = static_cast<uint64_t>(seed);
69 
70  // NOTE: For CPUs with Bit Manipulation Instruction Set 2 support, we should
71  // use the commented out uint16_t mask and nthset function.
72 
73  // uint16_t mask = 0b1111'1111'1111'1111;
74  char mask[16] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
75 
76  uint64_t res = 0;
77  int n = 15;
78  while (n) {
79  uint64_t v = factorials[n];
80  unsigned int d = get_digit(i / v, mask);
81  // const unsigned int d = nthset(mask, i / v);
82  mask[d] = 0;
83  // mask = mask & ~(1<<d);
84 
85  i = i % v;
86  res += d * base;
87  base = base >> 4;
88  n--;
89  }
90  // unsigned int d = nthset(mask, 0);
91  unsigned int d = get_digit(0, mask);
92  res += d;
93  return res;
94 }
95 } // namespace
96 
97 namespace openrand {
98 
99 class Squares : public BaseRNG<Squares> {
100  public:
112  OPENRAND_DEVICE Squares(uint32_t seed, uint32_t ctr,
113  uint32_t global_seed = openrand::DEFAULT_GLOBAL_SEED)
114  : seed(hash_seed(seed) ^ global_seed), counter(ctr) {
115  }
116 
117  template <typename T = uint32_t>
118  OPENRAND_DEVICE T draw() {
119  auto round = [](uint64_t x, uint64_t w) {
120  x = x * x + w;
121  return (x >> 32) | (x << 32);
122  };
123  _ctr++;
124  uint64_t ctr = (static_cast<uint64_t>(counter) << 32) | _ctr;
125  uint64_t x = ctr * seed;
126  uint64_t y = x;
127  uint64_t z = y + seed;
128 
129  if constexpr (std::is_same_v<T, uint32_t>) {
130  x = round(x, y);
131  x = round(x, z);
132  x = round(x, y);
133  return static_cast<uint32_t>((x * x + z) >> 32);
134  } else {
135  x = round(x, y);
136  x = round(x, z);
137  x = round(x, y);
138  x = round(x, z);
139  return x ^ ((x * x + y) >> 32);
140  }
141  }
142 
143  private:
144  const uint64_t seed;
145  const uint32_t counter = 0;
146 
147  // internal counter
148  public:
149  uint32_t _ctr = 0;
150 }; // class Squares
151 
152 } // namespace openrand
153 
154 #endif // OPENRAND_SQUARES_H_
Base class for random number generators.
Definition: base_state.h:50
Definition: squares.h:99
OPENRAND_DEVICE Squares(uint32_t seed, uint32_t ctr, uint32_t global_seed=openrand::DEFAULT_GLOBAL_SEED)
Construct a new Squares generator.
Definition: squares.h:112