SDDSlib
Loading...
Searching...
No Matches
drand.c
Go to the documentation of this file.
1/**
2 * @file drand.c
3 * @brief Random number generation functions providing various distributions
4 * (uniform, Gaussian) and related utilities (seeding, ordering, etc.).
5 *
6 * @copyright
7 * - (c) 2002 The University of Chicago, as Operator of Argonne National Laboratory.
8 * - (c) 2002 The Regents of the University of California, as Operator of Los Alamos National Laboratory.
9 *
10 * @license
11 * This file is distributed under the terms of the Software License Agreement
12 * found in the file LICENSE included with this distribution.
13 *
14 * @author M. Borland, C. Saunders, R. Soliday
15 */
16
17#include "mdb.h"
18#include <time.h>
19#include <stdlib.h>
20
21#if defined(_WIN32)
22# if _MSC_VER <= 1600
23# include "fdlibm.h"
24# endif
25#endif
26#include "f2c.h"
27
28extern double dlaran_(integer *seed);
29extern double dlaran_oag(integer *seed, long increment);
30
31#define MAX_RAND_INT (1.0 * RAND_MAX)
32
33/**
34 * @brief Generate a uniform random float in [0,1].
35 *
36 * Uses the standard C rand() function. The parameter `dummy` is unused.
37 *
38 * @param[in] dummy Unused parameter.
39 * @return A random float in [0,1].
40 */
41float drand(long dummy) {
42 return ((float)(1.0 * rand() / MAX_RAND_INT));
43}
44
45/**
46 * @brief Generate a uniform random double in [lo, hi].
47 *
48 * @param[in] lo The lower bound of the range.
49 * @param[in] hi The upper bound of the range.
50 * @return A random double in [lo, hi].
51 */
52double rdrand(lo, hi) double lo, hi;
53{
54 return (lo + ((hi - lo) * rand()) / MAX_RAND_INT);
55}
56
57/* routine: tseed()
58 * purpose: seed rand() with clock time
59 */
60
61void tseed() {
62 srand((int)time((time_t)0));
63}
64
65/**
66 * @brief Generate a random point (r, θ) within an annulus defined by [r_min, r_max].
67 *
68 * The angle θ is chosen uniformly in [0, 2π), and r is chosen so that the area distribution is uniform.
69 *
70 * @param[out] r Pointer to store the generated radius.
71 * @param[out] theta Pointer to store the generated angle in radians.
72 * @param[in] r_min The inner radius of the annulus.
73 * @param[in] r_max The outer radius of the annulus.
74 */
75void r_theta_rand(double *r, double *theta, double r_min, double r_max) {
76 double area, sqr_r_min;
77
78 *theta = rdrand(0.0, PIx2);
79 sqr_r_min = sqr(r_min);
80 area = rdrand(0.0, sqr(r_max) - sqr_r_min);
81 *r = sqrt(area + sqr_r_min);
82}
83
84static short inhibitPermute = 0;
85/**
86 * @brief Enable or disable permutation of seed bits for random number generators.
87 *
88 * If state >= 0, sets the inhibitPermute flag. Otherwise, returns the current state without changing it.
89 *
90 * @param[in] state New state for inhibition (0 = no inhibition, 1 = inhibited).
91 * @return The current state of the inhibitPermute flag.
92 */
93short inhibitRandomSeedPermutation(short state) {
94 if (state < 0)
95 return inhibitPermute;
96 inhibitPermute = state;
97 return state;
98}
99
100/**
101 * @brief Permute the bit order of a seed value to improve randomness.
102 *
103 * Applies a permutation of the seed bits to avoid predictable patterns.
104 * If inhibition is enabled, returns the original input.
105 *
106 * @param[in] input0 The seed value to permute.
107 * @return The permuted seed value.
108 */
109long permuteSeedBitOrder(long input0) {
110 long offset = input0 % 1000;
111 long newValue;
112 long i;
113 unsigned long input;
114 unsigned long bitMask[32] = {
115 0x00000001UL,
116 0x00000002UL,
117 0x00000004UL,
118 0x00000008UL,
119 0x00000010UL,
120 0x00000020UL,
121 0x00000040UL,
122 0x00000080UL,
123 0x00000100UL,
124 0x00000200UL,
125 0x00000400UL,
126 0x00000800UL,
127 0x00001000UL,
128 0x00002000UL,
129 0x00004000UL,
130 0x00008000UL,
131 0x00010000UL,
132 0x00020000UL,
133 0x00040000UL,
134 0x00080000UL,
135 0x00100000UL,
136 0x00200000UL,
137 0x00400000UL,
138 0x00800000UL,
139 0x01000000UL,
140 0x02000000UL,
141 0x04000000UL,
142 0x08000000UL,
143 0x10000000UL,
144 0x20000000UL,
145 0x40000000UL,
146 0x08000000UL,
147 };
148 if (inhibitPermute)
149 return input0;
150
151 input = input0;
152 newValue = 0;
153 for (i = 0; i < 31; i++) {
154 newValue += (input & bitMask[i]) ? bitMask[(i + offset) % 31] : 0;
155 }
156 if (newValue == input0) {
157 offset += 1;
158 newValue = 0;
159 for (i = 0; i < 31; i++) {
160 newValue += (input & bitMask[i]) ? bitMask[(i + offset) % 31] : 0;
161 }
162 }
163 return newValue;
164}
165
166/**
167 * @brief Generate a uniform random double in [0,1] using a custom seed initialization.
168 *
169 * Initializes the random number generator if needed, and then produces a double in [0,1].
170 * Negative iseed values are used to re-initialize the sequence.
171 *
172 * @param[in] iseed Seed for initialization if negative, otherwise ignored after first call.
173 * @return A random double in [0,1].
174 */
175double random_1(long iseed) {
176 static short initialized = 0;
177 static integer seed[4] = {0, 0, 0, 0};
178
179 if (!initialized || iseed < 0) {
180 if (iseed < 0)
181 iseed = -iseed;
182 iseed = permuteSeedBitOrder(iseed);
183 random_2(-(iseed + 2));
184 random_3(-(iseed + 4));
185 random_4(-(iseed + 6));
186 random_5(-(iseed + 8));
187 random_6(-(iseed + 10));
188 iseed = (iseed / 2) * 2 + 1;
189 seed[3] = (iseed & 4095);
190 seed[2] = (iseed >>= 12) & 4095;
191 seed[1] = (iseed >>= 12) & 4095;
192 seed[0] = (iseed >>= 12) & 4095;
193 initialized = 1;
194 }
195 if (!initialized)
196 bomb("random_1 not properly initialized", NULL);
197
198 return dlaran_(seed);
199}
200
201/**
202 * @brief Similar to random_1(), provides a separate random sequence with its own seed handling.
203 *
204 * @param[in] iseed Seed for initialization if negative.
205 * @return A random double in [0,1].
206 */
207double random_2(long iseed) {
208 static short initialized = 0;
209 static integer seed[4] = {0, 0, 0, 0};
210
211 if (!initialized || iseed < 0) {
212 if (iseed < 0)
213 iseed = -iseed;
214 iseed = permuteSeedBitOrder(iseed);
215 seed[3] = ((iseed & 4095) / 2) * 2 + 1;
216 seed[2] = (iseed >>= 12) & 4095;
217 seed[1] = (iseed >>= 12) & 4095;
218 seed[0] = (iseed >>= 12) & 4095;
219 initialized = 1;
220 }
221 if (!initialized)
222 bomb("random_2 not properly initialized", NULL);
223
224 return dlaran_(seed);
225}
226
227/**
228 * @brief Similar to random_2(), provides another independent random sequence.
229 *
230 * @param[in] iseed Seed for initialization if negative.
231 * @return A random double in [0,1].
232 */
233double random_3(long iseed) {
234 static short initialized = 0;
235 static integer seed[4] = {0, 0, 0, 0};
236
237 if (!initialized || iseed < 0) {
238 if (iseed < 0)
239 iseed = -iseed;
240 iseed = permuteSeedBitOrder(iseed);
241 seed[3] = ((iseed & 4095) / 2) * 2 + 1;
242 seed[2] = (iseed >>= 12) & 4095;
243 seed[1] = (iseed >>= 12) & 4095;
244 seed[0] = (iseed >>= 12) & 4095;
245 initialized = 1;
246 }
247 if (!initialized)
248 bomb("random_3 not properly initialized", NULL);
249
250 return dlaran_(seed);
251}
252
253/**
254 * @brief Similar to random_3(), provides another independent random sequence.
255 *
256 * @param[in] iseed Seed for initialization if negative.
257 * @return A random double in [0,1].
258 */
259double random_4(long iseed) {
260 static short initialized = 0;
261 static integer seed[4] = {0, 0, 0, 0};
262
263 if (!initialized || iseed < 0) {
264 if (iseed < 0)
265 iseed = -iseed;
266 iseed = permuteSeedBitOrder(iseed);
267 seed[3] = ((iseed & 4095) / 2) * 2 + 1;
268 seed[2] = (iseed >>= 12) & 4095;
269 seed[1] = (iseed >>= 12) & 4095;
270 seed[0] = (iseed >>= 12) & 4095;
271 initialized = 1;
272 }
273 if (!initialized)
274 bomb("random_4 not properly initialized", NULL);
275
276 return dlaran_(seed);
277}
278
279/**
280 * @brief Similar to random_4(), provides another independent random sequence.
281 *
282 * @param[in] iseed Seed for initialization if negative.
283 * @return A random double in [0,1].
284 */
285double random_5(long iseed) {
286 static short initialized = 0;
287 static integer seed[4] = {0, 0, 0, 0};
288
289 if (!initialized || iseed < 0) {
290 if (iseed < 0)
291 iseed = -iseed;
292 iseed = permuteSeedBitOrder(iseed);
293 seed[3] = ((iseed & 4095) / 2) * 2 + 1;
294 seed[2] = (iseed >>= 12) & 4095;
295 seed[1] = (iseed >>= 12) & 4095;
296 seed[0] = (iseed >>= 12) & 4095;
297 initialized = 1;
298 }
299 if (!initialized)
300 bomb("random_5 not properly initialized", NULL);
301
302 return dlaran_(seed);
303}
304
305/**
306 * @brief Similar to random_5(), provides another independent random sequence.
307 *
308 * @param[in] iseed Seed for initialization if negative.
309 * @return A random double in [0,1].
310 */
311double random_6(long iseed) {
312 static short initialized = 0;
313 static integer seed[4] = {0, 0, 0, 0};
314
315 if (!initialized || iseed < 0) {
316 if (iseed < 0)
317 iseed = -iseed;
318 iseed = permuteSeedBitOrder(iseed);
319 seed[3] = ((iseed & 4095) / 2) * 2 + 1;
320 seed[2] = (iseed >>= 12) & 4095;
321 seed[1] = (iseed >>= 12) & 4095;
322 seed[0] = (iseed >>= 12) & 4095;
323 initialized = 1;
324 }
325 if (!initialized)
326 bomb("random_6 not properly initialized", NULL);
327
328 return dlaran_(seed);
329}
330
331/**
332 * @brief Generate a Gaussian-distributed random number with mean 0 and sigma 1.
333 *
334 * Uses the given uniform random generator `urandom` to produce Gaussian deviates
335 * via the Box–Muller transform.
336 *
337 * @param[in] iseed If negative, re-initializes the uniform RNG.
338 * @param[in] urandom Pointer to a uniform random number generator function.
339 * @return A Gaussian random deviate with mean 0 and sigma 1.
340 */
341double gauss_rn(long iseed, double (*urandom)(long iseed1)) {
342 static long valueSaved = 0;
343 static double savedValue;
344 double urn1, urn2, sine, cosine, factor;
345
346 if (iseed < 0)
347 (*urandom)(iseed);
348 if (!valueSaved) {
349 urn1 = (*urandom)(0);
350 urn2 = (*urandom)(0);
351 factor = sqrt(-2 * log(urn1));
352 cosine = cos(PIx2 * urn2);
353 sine = sin(PIx2 * urn2);
354 savedValue = factor * cosine;
355 /* to use saved values, set this to 1 instead
356 * I've disabled this feature as it doesn't work properly with multiple
357 * urandom's.
358 */
359 valueSaved = 0;
360 return factor * sine;
361 } else {
362 valueSaved = 0;
363 return savedValue;
364 }
365}
366
367/**
368 * @brief Generate a Gaussian-distributed random number with specified mean, sigma, and optional cutoff.
369 *
370 * If limit_in_sigmas > 0, values are regenerated until the deviate falls within ±limit_in_sigmas*sigma.
371 *
372 * @param[in] mean Mean of the Gaussian distribution.
373 * @param[in] sigma Standard deviation of the Gaussian distribution.
374 * @param[in] limit_in_sigmas Cutoff in multiples of sigma (if <= 0, no cutoff).
375 * @param[in] urandom Pointer to a uniform random number generator function.
376 * @return A Gaussian random deviate meeting the specified conditions.
377 */
379 double mean, double sigma,
380 double limit_in_sigmas, /* if <= 0, ignored. Otherwise, the distribution is
381 * cut off at +/-limit_in_sigmas*sigma from the mean */
382 double (*urandom)(long iseed)) {
383 double limit, value;
384
385 if (limit_in_sigmas <= 0)
386 return (mean + sigma * gauss_rn(0, urandom));
387
388 limit = limit_in_sigmas;
389 do {
390 value = gauss_rn(0, urandom);
391 } while (FABS(value) > limit);
392 return (sigma * value + mean);
393}
394
395/**
396 * @brief Convert a sequence of uniformly distributed [0,1] values into a Gaussian-distributed sequence.
397 *
398 * Uses the inverse error function (erf) to transform uniform data into Gaussian distributed data.
399 * Values that exceed the given limit are discarded.
400 *
401 * @param[in,out] data Array of input values in [0,1] to be converted.
402 * @param[in] points Number of values in the array.
403 * @param[in] limit Upper cutoff in standard deviations (if <= 0, no cutoff).
404 * @return The number of successfully converted data points.
405 */
406long convertSequenceToGaussianDistribution(double *data, long points, double limit) {
407 double u1, u2, z = 0;
408 long i, j;
409
410 if (!points)
411 return 0;
412 if (!data)
413 return 0;
414 for (i = j = 0; i < points; i++) {
415 u1 = 2 * (data[i] - 0.5);
416 if (u1 < 0)
417 u2 = -u1;
418 else
419 u2 = u1;
420#if defined(vxWorks) || defined(__rtems__)
421 fprintf(stderr, "erf function is not implemented on this architecture\n");
422 exit(1);
423#else
424 z = zeroNewton(erf, u2, 0.5, 1e-6, 500, 1e-12);
425#endif
426 data[j] = z * SQRT2;
427 if (limit <= 0 || data[j] < limit) {
428 if (u1 < 0)
429 data[j] = -data[j];
430 j++;
431 }
432 }
433 return j;
434}
435
436typedef struct {
437 char *buffer;
438 double randomValue;
440
441int randomizeOrderCmp(const void *p1, const void *p2) {
442 RANDOMIZATION_HOLDER *rh1, *rh2;
443 double diff;
444 rh1 = (RANDOMIZATION_HOLDER *)p1;
445 rh2 = (RANDOMIZATION_HOLDER *)p2;
446 if ((diff = rh1->randomValue - rh2->randomValue) > 0)
447 return 1;
448 if (diff < 0)
449 return -1;
450 return 0;
451}
452
453/**
454 * @brief Randomize the order of an array of elements.
455 *
456 * Shuffles the elements of an array using a provided uniform random generator.
457 *
458 * @param[in,out] ptr Pointer to the array to randomize.
459 * @param[in] size Size of each element in bytes.
460 * @param[in] length Number of elements in the array.
461 * @param[in] iseed Seed for initialization if negative.
462 * @param[in] urandom Pointer to a uniform random number generator function.
463 * @return Non-zero if successful, zero otherwise.
464 */
465long randomizeOrder(char *ptr, long size, long length, long iseed, double (*urandom)(long iseed1)) {
467 long i;
468 if (length < 2)
469 return 1;
470 if (!ptr)
471 return 0;
472 if (!(rh = malloc(sizeof(*rh) * length)))
473 return 0;
474 if (!urandom)
475 return 0;
476 if (iseed < 0)
477 (*urandom)(iseed);
478 for (i = 0; i < length; i++) {
479 if (!(rh[i].buffer = malloc(size)))
480 return 0;
481 memcpy(rh[i].buffer, ptr + i * size, size);
482 rh[i].randomValue = (*urandom)(0);
483 }
484 qsort((void *)rh, length, sizeof(*rh), randomizeOrderCmp);
485
486 for (i = 0; i < length; i++) {
487 memcpy(ptr + i * size, rh[i].buffer, size);
488 free(rh[i].buffer);
489 }
490 free(rh);
491 return 1;
492}
493
494/**
495 * @brief Generate a uniform random double in [0,1] using a seed and increment, optimized for certain applications.
496 *
497 * Uses a custom random number generator implemented in Fortran (dlaran_oag).
498 *
499 * @param[in] iseed Seed for initialization if negative.
500 * @param[in] increment Increment to apply for each call.
501 * @return A random double in [0,1].
502 */
503double random_oag(long iseed, long increment) {
504 static short initialized = 0;
505 static long seed[4] = {0, 0, 0, 0};
506
507 if (!initialized || iseed < 0) {
508 if (iseed < 0)
509 iseed = -iseed;
510 seed[3] = ((iseed & 4095) / 2) * 2 + 1;
511 seed[2] = (iseed >>= 12) & 4095;
512 seed[1] = (iseed >>= 12) & 4095;
513 seed[0] = (iseed >>= 12) & 4095;
514 initialized = 1;
515 }
516 if (!initialized) {
517 fprintf(stderr, "random_oag not properly initialized\n");
518 exit(1);
519 }
520
521 return dlaran_oag(seed, increment);
522}
523
524/**
525 * @brief Generate a Gaussian-distributed random number using the `random_oag` approach.
526 *
527 * Uses a modified Box–Muller method to generate Gaussian deviates from the `urandom` function provided.
528 *
529 * @param[in] iseed Seed for initialization if negative.
530 * @param[in] increment Increment step for random number generation.
531 * @param[in] urandom Pointer to an `oag`-style uniform random number generator function.
532 * @return A Gaussian random deviate with mean 0 and sigma 1.
533 */
534double gauss_rn_oag(long iseed, long increment, double (*urandom)(long iseed1, long increment)) {
535 double urn1, urn2, sine, factor;
536
537 if (increment < 1)
538 increment = 1;
539 increment = ((increment - 1) * 2) + 1;
540 urn1 = (*urandom)(iseed, increment);
541 urn2 = (*urandom)(0, 1);
542 factor = sqrt(-2 * log(urn1));
543 sine = sin(PIx2 * urn2);
544 return factor * sine;
545}
546
547/**
548 * @brief Generate a Gaussian-distributed random number with mean, sigma, and optional cutoff using `oag` RNG.
549 *
550 * If limit_in_sigmas > 0, values are regenerated until they fall within the cutoff range.
551 *
552 * @param[in] mean Mean of the Gaussian distribution.
553 * @param[in] sigma Standard deviation of the Gaussian distribution.
554 * @param[in] limit_in_sigmas Cutoff in multiples of sigma (if <= 0, no cutoff).
555 * @param[in] increment Increment step for random number generation.
556 * @param[in] urandom Pointer to an `oag`-style uniform random number generator function.
557 * @return A Gaussian random deviate meeting the specified conditions.
558 */
560 double mean, double sigma,
561 double limit_in_sigmas, /* if <= 0, ignored. Otherwise, the distribution is
562 * cut off at +/-limit_in_sigmas*sigma from the mean */
563 long increment,
564 double (*urandom)(long iseed, long increment)) {
565 double limit, value;
566 long i;
567
568 if (limit_in_sigmas <= 0)
569 return (mean + sigma * gauss_rn_oag(0, increment, urandom));
570
571 limit = limit_in_sigmas;
572 i = 0;
573
574 do {
575 value = gauss_rn_oag(0, 1, urandom);
576 if (FABS(value) <= limit)
577 i++;
578 } while ((FABS(value) > limit) || (i < increment));
579
580 return (sigma * value + mean);
581}
void bomb(char *error, char *usage)
Reports error messages to the terminal and aborts the program.
Definition bomb.c:26
double random_5(long iseed)
Similar to random_4(), provides another independent random sequence.
Definition drand.c:285
long permuteSeedBitOrder(long input0)
Permute the bit order of a seed value to improve randomness.
Definition drand.c:109
double random_1(long iseed)
Generate a uniform random double in [0,1] using a custom seed initialization.
Definition drand.c:175
double gauss_rn(long iseed, double(*urandom)(long iseed1))
Generate a Gaussian-distributed random number with mean 0 and sigma 1.
Definition drand.c:341
double random_6(long iseed)
Similar to random_5(), provides another independent random sequence.
Definition drand.c:311
void r_theta_rand(double *r, double *theta, double r_min, double r_max)
Generate a random point (r, θ) within an annulus defined by [r_min, r_max].
Definition drand.c:75
double gauss_rn_lim_oag(double mean, double sigma, double limit_in_sigmas, long increment, double(*urandom)(long iseed, long increment))
Generate a Gaussian-distributed random number with mean, sigma, and optional cutoff using oag RNG.
Definition drand.c:559
float drand(long dummy)
Generate a uniform random float in [0,1].
Definition drand.c:41
long convertSequenceToGaussianDistribution(double *data, long points, double limit)
Convert a sequence of uniformly distributed [0,1] values into a Gaussian-distributed sequence.
Definition drand.c:406
short inhibitRandomSeedPermutation(short state)
Enable or disable permutation of seed bits for random number generators.
Definition drand.c:93
double gauss_rn_oag(long iseed, long increment, double(*urandom)(long iseed1, long increment))
Generate a Gaussian-distributed random number using the random_oag approach.
Definition drand.c:534
double random_oag(long iseed, long increment)
Generate a uniform random double in [0,1] using a seed and increment, optimized for certain applicati...
Definition drand.c:503
double random_3(long iseed)
Similar to random_2(), provides another independent random sequence.
Definition drand.c:233
double random_4(long iseed)
Similar to random_3(), provides another independent random sequence.
Definition drand.c:259
long randomizeOrder(char *ptr, long size, long length, long iseed, double(*urandom)(long iseed1))
Randomize the order of an array of elements.
Definition drand.c:465
double rdrand(double lo, double hi)
Generate a uniform random double in [lo, hi].
Definition drand.c:52
double random_2(long iseed)
Similar to random_1(), provides a separate random sequence with its own seed handling.
Definition drand.c:207
double gauss_rn_lim(double mean, double sigma, double limit_in_sigmas, double(*urandom)(long iseed))
Generate a Gaussian-distributed random number with specified mean, sigma, and optional cutoff.
Definition drand.c:378
double zeroNewton(double(*fn)(), double value, double x_i, double dx, long n_passes, double _zero)
Finds the zero of a function using Newton's method with numerical derivative computation.
Definition zeroNewton.c:33