HILA
Loading...
Searching...
No Matches
random.cpp
1#include <cmath>
2#include "hila.h"
3#include "plumbing/random.h"
4
5/////////////////////////////////////////////////////////////////////////
6
7#include <random>
8
9// #ifndef OPENMP
10
11// static variable which holds the random state
12// Use 64-bit mersenne twister
13static std::mt19937_64 mersenne_twister_gen;
14
15// random numbers are in interval [0,1)
16static std::uniform_real_distribution<double> real_rnd_dist(0.0, 1.0);
17
18// #endif
19
20
21// In GPU code hila::random() defined in hila_gpu.cpp
22#if !defined(CUDA) && !defined(HIP)
23double hila::random() {
24 return real_rnd_dist(mersenne_twister_gen);
25}
26
27#endif
28
29// Generate random number in non-kernel (non-loop) code. Not meant to
30// be used in "user code"
31double hila::host_random() {
32 return real_rnd_dist(mersenne_twister_gen);
33}
34
35/////////////////////////////////////////////////////////////////////////
36
37static bool rng_is_initialized = false;
38
39namespace hila {
40
41/**
42 * @brief Random shuffling of rng seed for MPI nodes.
43 * @details Do it in a manner makes it difficult to give the same seed by mistake
44 * and also avoids giving the same seed for 2 nodes
45 * For single MPI node seed remains unchanged
46 */
47uint64_t shuffle_rng_seed(uint64_t seed) {
48
49 uint64_t n = hila::myrank();
50 if (hila::partitions.number() > 1)
51 n += hila::partitions.mylattice() * hila::number_of_nodes();
52
53 return (seed + n) ^ (n << 31);
54}
55
56
57/**
58 *@param seed unsigned int_64
59 *@note On MPI, this function shuffles different seed values for all MPI ranks.
60 */
61void initialize_host_rng(uint64_t seed) {
62
63 seed = hila::shuffle_rng_seed(seed);
64
65 // #if !defined(OPENMP)
66 mersenne_twister_gen.seed(seed);
67 // warm it up
68 for (int i = 0; i < 9000; i++)
69 mersenne_twister_gen();
70 // #endif
71}
72
73} // namespace hila
74
75
76/**
77 *@details The optional 2nd argument indicates whether to initialize the RNG on GPU device:
78 * `hila::device_rng_on` (default) or `hila::device_rng_off`. This argument does nothing if no GPU
79 * platform. If `hila::device_rng_off` is used, `onsites()` -loops cannot contain random number calls
80 * (Runtime error will be flagged and program exits).
81 *
82 * Seed is shuffled so that different nodes
83 * get different rng seeds. If `seed == 0`,
84 * seed is generated through using the `time()` -function.
85 */
86void hila::seed_random(uint64_t seed, bool device_init) {
87
88 rng_is_initialized = true;
89
90 if (!lattice.is_initialized()) {
91 hila::error("lattice.initialize() must be called before hila::seed_random()");
92 }
93
94
95 uint64_t n = hila::myrank();
96 if (hila::partitions.number() > 1)
97 n += hila::partitions.mylattice() * hila::number_of_nodes();
98
99 if (seed == 0) {
100 // get seed from time
101 if (hila::myrank() == 0) {
102 struct timespec tp;
103
104 clock_gettime(CLOCK_MONOTONIC, &tp);
105 seed = tp.tv_sec;
106 seed = (seed << 30) ^ tp.tv_nsec;
107 hila::out0 << "Random seed from time: " << seed << '\n';
108 }
109 hila::broadcast(seed);
110 }
111
112#ifndef SITERAND
113
114 hila::out0 << "Using node random numbers, seed for node 0: " << seed << std::endl;
115
117
118#if defined(CUDA) || defined(HIP)
119
120 // we can use the same seed, the generator is different
121 if (device_init) {
123 } else {
124 hila::out0 << "Not initializing GPU random numbers\n";
125 }
126
127#endif
128
129 // taus_initialize();
130
131#else
132
133 // TODO: SITERAND is not yet implemented!
134 // This is usually used only for occasional benchmarking, where identical output
135 // independent of the node number is desired
136
137 hila::out0 << "*** SITERAND is in use!\n";
138
139 random_seed_arr = (unsigned short(*)[3])memalloc(3 * node.sites * sizeof(unsigned short));
140 forallsites(i) {
141 random_seed_arr[i][0] = (unsigned short)(seed + site[i].index);
142 random_seed_arr[i][1] = (unsigned short)(seed + 2 * site[i].index);
143 random_seed_arr[i][2] = (unsigned short)(seed + 3 * site[i].index);
144 }
145
146 random_seed_ptr = random_seed_arr[0];
147
148#endif
149}
150
151////////////////////////////////////////////////////////////////////
152// Def here gpu rng functions for non-gpu
153////////////////////////////////////////////////////////////////////
154
155#if !(defined(CUDA) || defined(HIP))
156
157/**
158 *@details `hila::random()` does not work inside `onsites()` after this,
159 *unless seeded again using `initialize_device_rng()`. Frees the memory RNG takes on the device.
160 */
161void hila::free_device_rng() {}
162
163/**
164 *@details Returns `true` on non-GPU archs.
165 */
167 return true;
168}
169
170/**
171 *@details This function shuffles the seed for different MPI ranks on MPI. Called by `seed_random()` unless its 2nd
172 *argument is `hila::device_rng_off`. This can reinitialize device RNG free'd by `free_device_rng()`.
173 */
174void hila::initialize_device_rng(uint64_t seed) {}
175
176#endif
177
178
179#define VARIANCE 1.0
180
181double hila::gaussrand2(double &out2) {
182
183 double phi, urnd, r;
184 phi = 2.0 * M_PI * hila::random();
185
186 // this should not really trigger
187 do {
188 urnd = 1.0 - hila::random();
189 } while (urnd == 0.0);
190
191 r = sqrt(-::log(urnd) * (2.0 * VARIANCE));
192 out2 = r * cos(phi);
193 return r * sin(phi);
194}
195
196#if !defined(CUDA) && !defined(HIP)
197
198/**
199 * @details By default these gives random numbers with variance \f$1.0\f$ and expectation value \f$0.0\f$, i.e.
200 * \f[
201 * e^{-(\frac{x^{2}}{2})}
202 * \f]
203 * with variance
204 * \f[
205 * < x^{2}-0.0> = 1
206 * \f]
207 *
208 * If you want random numbers with variance \f$ \sigma^{2} \f$, multiply the
209 * result by \f$ \sqrt{\sigma^{2}} \f$ i.e.,
210 * \code {.cpp}
211 * sqrt(sigma * sigma) * gaussrand();
212 * \endcode
213 *
214 * @return double
215 */
217 static double second;
218 static bool draw_new = true;
219 if (draw_new) {
220 draw_new = false;
221 return hila::gaussrand2(second);
222 }
223 draw_new = true;
224 return second;
225}
226
227#else
228
229// Cuda and other stuff which does not accept
230// static variables - just throw away another gaussian number.
231
232// #pragma hila loop function contains rng
233double hila::gaussrand() {
234 double second;
235 return hila::gaussrand2(second);
236}
237
238#endif
239
240/**
241 *@returns bool
242 */
244 return rng_is_initialized;
245}
246
247
248///////////////////////////////////////////////////////////////
249// RNG initialization check - emitted on loops
250///////////////////////////////////////////////////////////////
251
252/**
253 *@details program quits with error message if RNG is not initialized.
254 *It also quit with error messages if the device RNG is not initialized.
255 */
257 bool isinit;
258
259 if (!rng_is_initialized) {
260 hila::out0 << "ERROR: trying to use random numbers without initializing the generator"
261 << std::endl;
263 }
264#if defined(CUDA) || defined(HIP)
265 if (!hila::is_device_rng_on()) {
266 hila::out0 << "ERROR: GPU random number generator is not initialized and onsites()-loop is "
267 "using random numbers"
268 << std::endl;
270 }
271#endif
272}
273
274
275
276
277
278
Array< n, m, T > cos(Array< n, m, T > a)
Cosine.
Definition array.h:1089
Array< n, m, T > sqrt(Array< n, m, T > a)
Square root.
Definition array.h:1039
Array< n, m, T > sin(Array< n, m, T > a)
Sine.
Definition array.h:1079
Array< n, m, T > log(Array< n, m, T > a)
Logarithm.
Definition array.h:1069
bool is_initialized() const
is the lattice initialized
Definition lattice.h:231
Implement hila::swap for gauge fields.
Definition array.h:981
void initialize_device_rng(uint64_t seed)
Initialize device random number generator on GPUs, if application run on GPU platform....
Definition hila_gpu.cpp:68
void free_device_rng()
Free GPU RNG state, does nothing on non-GPU archs.
Definition hila_gpu.cpp:104
int myrank()
rank of this node
Definition com_mpi.cpp:235
int number_of_nodes()
how many nodes there are
Definition com_mpi.cpp:246
bool is_device_rng_on()
Check if the RNG on GPU is allocated and ready to use.
Definition hila_gpu.cpp:55
uint64_t shuffle_rng_seed(uint64_t seed)
Random shuffling of rng seed for MPI nodes.
Definition random.cpp:47
double random()
Real valued uniform random number generator.
Definition hila_gpu.cpp:118
double gaussrand()
Gaussian random generation routine.
Definition random.cpp:216
std::ostream out0
This writes output only from main process (node 0)
void seed_random(uint64_t seed, bool device_rng=true)
Seed random generators with 64-bit unsigned value. On MPI shuffles the seed so that different MPI ran...
Definition random.cpp:86
T broadcast(T &var, int rank=0)
Broadcast the value of var to all MPI ranks from rank (default=0).
Definition com_mpi.h:153
bool is_rng_seeded()
Check if RNG is seeded already.
Definition random.cpp:243
void initialize_host_rng(uint64_t seed)
Initialize host (CPU) random number generator separately, done implicitly by seed_random()
Definition random.cpp:61
double gaussrand2(double &out2)
hila::gaussrand2 returns 2 Gaussian distributed random numbers with variance .
Definition random.cpp:181
void check_that_rng_is_initialized()
Check if RNG is initialized, do what the name says.
Definition random.cpp:256
void terminate(int status)