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
91 uint64_t n = hila::myrank();
92 if (hila::partitions.number() > 1)
93 n += hila::partitions.mylattice() * hila::number_of_nodes();
94
95 if (seed == 0) {
96 // get seed from time
97 if (hila::myrank() == 0) {
98 struct timespec tp;
99
100 clock_gettime(CLOCK_MONOTONIC, &tp);
101 seed = tp.tv_sec;
102 seed = (seed << 30) ^ tp.tv_nsec;
103 hila::out0 << "Random seed from time: " << seed << '\n';
104 }
105 hila::broadcast(seed);
106 }
107
108#ifndef SITERAND
109
110 hila::out0 << "Using node random numbers, seed for node 0: " << seed << std::endl;
111
113
114#if defined(CUDA) || defined(HIP)
115
116 // we can use the same seed, the generator is different
117 if (device_init) {
119 } else {
120 hila::out0 << "Not initializing GPU random numbers\n";
121 }
122
123#endif
124
125 // taus_initialize();
126
127#else
128
129 // TODO: SITERAND is not yet implemented!
130 // This is usually used only for occasional benchmarking, where identical output
131 // independent of the node number is desired
132
133 hila::out0 << "*** SITERAND is in use!\n";
134
135 random_seed_arr = (unsigned short(*)[3])memalloc(3 * node.sites * sizeof(unsigned short));
136 forallsites(i) {
137 random_seed_arr[i][0] = (unsigned short)(seed + site[i].index);
138 random_seed_arr[i][1] = (unsigned short)(seed + 2 * site[i].index);
139 random_seed_arr[i][2] = (unsigned short)(seed + 3 * site[i].index);
140 }
141
142 random_seed_ptr = random_seed_arr[0];
143
144#endif
145}
146
147////////////////////////////////////////////////////////////////////
148// Def here gpu rng functions for non-gpu
149////////////////////////////////////////////////////////////////////
150
151#if !(defined(CUDA) || defined(HIP))
152
153/**
154 *@details `hila::random()` does not work inside `onsites()` after this,
155 *unless seeded again using `initialize_device_rng()`. Frees the memory RNG takes on the device.
156 */
157void hila::free_device_rng() {}
158
159/**
160 *@details Returns `true` on non-GPU archs.
161 */
163 return true;
164}
165
166/**
167 *@details This function shuffles the seed for different MPI ranks on MPI. Called by `seed_random()` unless its 2nd
168 *argument is `hila::device_rng_off`. This can reinitialize device RNG free'd by `free_device_rng()`.
169 */
170void hila::initialize_device_rng(uint64_t seed) {}
171
172#endif
173
174
175#define VARIANCE 1.0
176
177double hila::gaussrand2(double &out2) {
178
179 double phi, urnd, r;
180 phi = 2.0 * M_PI * hila::random();
181
182 // this should not really trigger
183 do {
184 urnd = 1.0 - hila::random();
185 } while (urnd == 0.0);
186
187 r = sqrt(-::log(urnd) * (2.0 * VARIANCE));
188 out2 = r * cos(phi);
189 return r * sin(phi);
190}
191
192#if !defined(CUDA) && !defined(HIP)
193
194/**
195 * @details By default these gives random numbers with variance \f$1.0\f$ and expectation value \f$0.0\f$, i.e.
196 * \f[
197 * e^{-(\frac{x^{2}}{2})}
198 * \f]
199 * with variance
200 * \f[
201 * < x^{2}-0.0> = 1
202 * \f]
203 *
204 * If you want random numbers with variance \f$ \sigma^{2} \f$, multiply the
205 * result by \f$ \sqrt{\sigma^{2}} \f$ i.e.,
206 * \code {.cpp}
207 * sqrt(sigma * sigma) * gaussrand();
208 * \endcode
209 *
210 * @return double
211 */
213 static double second;
214 static bool draw_new = true;
215 if (draw_new) {
216 draw_new = false;
217 return hila::gaussrand2(second);
218 }
219 draw_new = true;
220 return second;
221}
222
223#else
224
225// Cuda and other stuff which does not accept
226// static variables - just throw away another gaussian number.
227
228// #pragma hila loop function contains rng
229double hila::gaussrand() {
230 double second;
231 return hila::gaussrand2(second);
232}
233
234#endif
235
236/**
237 *@returns bool
238 */
240 return rng_is_initialized;
241}
242
243
244///////////////////////////////////////////////////////////////
245// RNG initialization check - emitted on loops
246///////////////////////////////////////////////////////////////
247
248/**
249 *@details program quits with error message if RNG is not initialized.
250 *It also quit with error messages if the device RNG is not initialized.
251 */
253 bool isinit;
254
255 if (!rng_is_initialized) {
256 hila::out0 << "ERROR: trying to use random numbers without initializing the generator"
257 << std::endl;
259 }
260#if defined(CUDA) || defined(HIP)
261 if (!hila::is_device_rng_on()) {
262 hila::out0 << "ERROR: GPU random number generator is not initialized and onsites()-loop is "
263 "using random numbers"
264 << std::endl;
266 }
267#endif
268}
269
270
271
272
273
274
Invert diagonal + const. matrix using Sherman-Morrison formula.
Definition array.h:920
void initialize_device_rng(uint64_t seed)
Initialize device random number generator on GPUs, if application run on GPU platform....
Definition hila_gpu.cpp:70
void free_device_rng()
Free GPU RNG state, does nothing on non-GPU archs.
Definition hila_gpu.cpp:106
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:57
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:120
double gaussrand()
Gaussian random generation routine.
Definition random.cpp:212
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:239
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:177
void check_that_rng_is_initialized()
Check if RNG is initialized, do what the name says.
Definition random.cpp:252
void terminate(int status)