CR2RE Pipeline Reference Manual 1.6.7
hdrl_random.c
1/*
2 * This file is part of the HDRL
3 * Copyright (C) 2016 European Southern Observatory
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 *
20 *
21 * uniform_double, poisson, normal partially based on numpy/random/mtrand
22 * Copyright 2005 Robert Kern (robert.kern@gmail.com)
23 *
24 * Permission is hereby granted, free of charge, to any person obtaining a
25 * copy of this software and associated documentation files (the
26 * "Software"), to deal in the Software without restriction, including
27 * without limitation the rights to use, copy, modify, merge, publish,
28 * distribute, sublicense, and/or sell copies of the Software, and to
29 * permit persons to whom the Software is furnished to do so, subject to
30 * the following conditions:
31 *
32 * The above copyright notice and this permission notice shall be included
33 * in all copies or substantial portions of the Software.
34 *
35 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
36 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
37 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
38 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
39 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
40 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
41 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
42 *
43 *
44 *
45 * PCG generator implementation is licensed under Apache License 2.0
46 */
47
48
49#ifdef HAVE_CONFIG_H
50#include <config.h>
51#endif
52
53/*-----------------------------------------------------------------------------
54 Includes
55 -----------------------------------------------------------------------------*/
56
57#include <hdrl_random.h>
58#include <stdint.h>
59#include <math.h>
60
61/*-----------------------------------------------------------------------------
62 Functions
63 -----------------------------------------------------------------------------*/
64
65struct hdrl_random_state_{
66 uint64_t state;
67 uint64_t inc;
68 uint64_t has_normal;
69 double normal;
70};
71
72/* ---------------------------------------------------------------------------*/
80/* ---------------------------------------------------------------------------*/
81static uint32_t hdrl_random_uniform_uint32(hdrl_random_state * rng)
82{
83 /* from:
84 * PCG: A Family of Simple Fast Space-Efficient Statistically Good
85 * Algorithms for Random Number Generation */
86 uint64_t oldstate = rng->state;
87 // Advance internal state
88 rng->state = oldstate * 6364136223846793005ULL + (rng->inc|1);
89 // Calculate output function (XSH RR), uses old state for max ILP
90 uint32_t xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
91 uint32_t rot = oldstate >> 59u;
92 return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
93}
94
95/* ---------------------------------------------------------------------------*/
107/* ---------------------------------------------------------------------------*/
108hdrl_random_state * hdrl_random_state_new(int type, uint64_t * seed)
109{
110 cpl_error_ensure(type == 1, CPL_ERROR_UNSUPPORTED_MODE,
111 return NULL, "type needs to be 1");
112 hdrl_random_state * state = cpl_calloc(sizeof(*state), 1);
113 uint64_t s1 = seed ? seed[0] : (uint64_t)rand();
114 uint64_t s2 = seed ? seed[1] : (uint64_t)rand();
115 state->state = 0;
116 state->inc = s2;
117 hdrl_random_uniform_uint32(state);
118 state->state += s1;
119 hdrl_random_uniform_uint32(state);
120 return state;
121}
122
123/* ---------------------------------------------------------------------------*/
129/* ---------------------------------------------------------------------------*/
130void hdrl_random_state_delete(hdrl_random_state * state)
131{
132 cpl_free(state);
133}
134
135static uint64_t hdrl_random_uniform_uint64_full(hdrl_random_state * state)
136{
137 return ((uint64_t)hdrl_random_uniform_uint32(state) << 32) |
138 (uint64_t)hdrl_random_uniform_uint32(state);
139}
140
141/* ---------------------------------------------------------------------------*/
151/* ---------------------------------------------------------------------------*/
152int64_t
153hdrl_random_uniform_int64(hdrl_random_state * state,
154 int64_t minval, int64_t maxval)
155{
156 uint64_t res, mask;
157 if (maxval < minval) {
158 cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
159 "maximum value smaller than minimum value");
160 return 0;
161 }
162 uint64_t umaxval = (uint64_t)maxval - (uint64_t)minval;
163 if (umaxval == 0) {
164 return 0;
165 }
166
167 /* create all 1 bitmask larger than maxval */
168 mask = umaxval;
169 mask |= mask >> 1;
170 mask |= mask >> 2;
171 mask |= mask >> 4;
172 mask |= mask >> 8;
173 mask |= mask >> 16;
174 mask |= mask >> 32;
175
176 /* draw numbers until the value is smaller than maxval */
177 while (1) {
178 res = hdrl_random_uniform_uint64_full(state) & mask;
179 if (res <= umaxval) {
180 break;
181 }
182 }
183 return (int64_t)((uint64_t)minval + res);
184}
185
186static double hdrl_random_uniform_double_one(hdrl_random_state * state)
187{
188 /* shifts : 67108864 = 0x4000000, 9007199254740992 = 0x20000000000000 */
189 uint64_t a = hdrl_random_uniform_uint32(state) >> 5;
190 uint64_t b = hdrl_random_uniform_uint32(state) >> 6;
191 return (a * 67108864.0 + b) / 9007199254740992.0;
192}
193
194
195/* ---------------------------------------------------------------------------*/
205/* ---------------------------------------------------------------------------*/
206double hdrl_random_uniform_double(hdrl_random_state * state,
207 double minval, double maxval)
208{
209 double scale = fabs(maxval - minval);
210 return minval + scale * hdrl_random_uniform_double_one(state);
211}
212
213static uint64_t hdrl_random_poisson_low(hdrl_random_state * state, double lam)
214{
215 const double explam = exp(-lam);
216 double prod = hdrl_random_uniform_double_one(state);
217 uint64_t r = 0;
218
219 while (prod > explam) {
220 r++;
221 prod *= hdrl_random_uniform_double_one(state);
222 }
223
224 return r;
225}
226
227/*
228 * The transformed rejection method for generating Poisson random variables
229 * W. Hoermann
230 * Insurance: Mathematics and Economics 12, 39-45 (1993)
231 */
232static uint64_t hdrl_random_poisson_ptrs(hdrl_random_state * state, double lam)
233{
234 const double slam = sqrt(lam);
235 const double loglam = log(lam);
236 const double b = 0.931 + 2.53 * slam;
237 const double a = -0.059 + 0.02483 * b;
238 const double invalpha = 1.1239 + 1.1328 / (b - 3.4);
239 const double vr = 0.9277 - 3.6224 / (b - 2. );
240
241 while (1) {
242
243 double U = hdrl_random_uniform_double_one(state) - 0.5;
244 double V = hdrl_random_uniform_double_one(state);
245
246 double us = 0.5 - fabs(U);
247 cpl_boolean eval = (us >= 0.07);
248
249 double aux = 2 * a / us;
250 int64_t k = (long)floor((aux + b) * U + lam + 0.43);
251
252 if(eval && V <= vr) {
253 return k;
254 }
255
256 if (k < 0 || (us < 0.013 && V > us) ) {
257 continue;
258 }
259
260 double lgamk = lgamma(k + 1);
261
262 if ((log(V) + log(invalpha) - log(a/(us*us)+b)) <=
263 (-lam + k * loglam - lgamk)) {
264 return k;
265 }
266 }
267}
268
269/* ---------------------------------------------------------------------------*/
278/* ---------------------------------------------------------------------------*/
279uint64_t hdrl_random_poisson(hdrl_random_state * state, double lam)
280{
281 if (lam >= 10.) {
282 return hdrl_random_poisson_ptrs(state, lam);
283 }
284 else if (lam == 0.) {
285 return 0;
286 }
287 else if (lam < 0.) {
288 cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
289 "lam must not be negative");
290 return 0;
291 }
292 else {
293 return hdrl_random_poisson_low(state, lam);
294 }
295}
296
297/* ---------------------------------------------------------------------------*/
307/* ---------------------------------------------------------------------------*/
308double hdrl_random_normal(hdrl_random_state * state, double mean, double sigma)
309{
310 if (sigma < 0.) {
311 cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
312 "sigma must not be negative");
313 return 0;
314 }
315 if (state->has_normal) {
316 state->has_normal = 0;
317 return mean + sigma * state->normal;
318 }
319 else {
320 double f, x1, x2, r;
321
322 do {
323 x1 = 2.0 * hdrl_random_uniform_double_one(state) - 1.0;
324 x2 = 2.0 * hdrl_random_uniform_double_one(state) - 1.0;
325 r = x1 * x1 + x2 * x2;
326 }
327 while (r >= 1.0 || r == 0.0);
328
329 /* Box-Muller transform */
330 f = sqrt(-2.0*log(r)/r);
331 /* Keep second value for next call */
332 state->normal = f * x1;
333 state->has_normal = 1;
334 return mean + sigma * f * x2;
335 }
336}