low-level RNG interfaces deal in scm_t_uint32, not unsigned long
[bpt/guile.git] / libguile / random.c
1 /* Copyright (C) 1999,2000,2001, 2003, 2005, 2006, 2009, 2010 Free Software Foundation, Inc.
2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public License
4 * as published by the Free Software Foundation; either version 3 of
5 * the License, or (at your option) any later version.
6 *
7 * This library is distributed in the hope that it will be useful, but
8 * WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with this library; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15 * 02110-1301 USA
16 */
17
18
19
20 /* Author: Mikael Djurfeldt <djurfeldt@nada.kth.se> */
21
22 #ifdef HAVE_CONFIG_H
23 # include <config.h>
24 #endif
25
26 #include "libguile/_scm.h"
27
28 #include <gmp.h>
29 #include <stdio.h>
30 #include <math.h>
31 #include <string.h>
32 #include "libguile/smob.h"
33 #include "libguile/numbers.h"
34 #include "libguile/feature.h"
35 #include "libguile/strings.h"
36 #include "libguile/arrays.h"
37 #include "libguile/srfi-4.h"
38 #include "libguile/vectors.h"
39 #include "libguile/generalized-vectors.h"
40
41 #include "libguile/validate.h"
42 #include "libguile/random.h"
43
44 \f
45 /*
46 * A plugin interface for RNGs
47 *
48 * Using this interface, it is possible for the application to tell
49 * libguile to use a different RNG. This is desirable if it is
50 * necessary to use the same RNG everywhere in the application in
51 * order to prevent interference, if the application uses RNG
52 * hardware, or if the application has special demands on the RNG.
53 *
54 * Look in random.h and how the default generator is "plugged in" in
55 * scm_init_random().
56 */
57
58 scm_t_rng scm_the_rng;
59
60 \f
61 /*
62 * The prepackaged RNG
63 *
64 * This is the MWC (Multiply With Carry) random number generator
65 * described by George Marsaglia at the Department of Statistics and
66 * Supercomputer Computations Research Institute, The Florida State
67 * University (http://stat.fsu.edu/~geo).
68 *
69 * It uses 64 bits, has a period of 4578426017172946943 (4.6e18), and
70 * passes all tests in the DIEHARD test suite
71 * (http://stat.fsu.edu/~geo/diehard.html)
72 */
73
74 #define A 2131995753UL
75
76 #ifndef M_PI
77 #define M_PI 3.14159265359
78 #endif
79
80 scm_t_uint32
81 scm_i_uniform32 (scm_t_i_rstate *state)
82 {
83 scm_t_uint64 x = (scm_t_uint64) A * state->w + state->c;
84 scm_t_uint32 w = x & 0xffffffffUL;
85 state->w = w;
86 state->c = x >> 32L;
87 return w;
88 }
89
90 void
91 scm_i_init_rstate (scm_t_i_rstate *state, const char *seed, int n)
92 {
93 scm_t_uint32 w = 0L;
94 scm_t_uint32 c = 0L;
95 int i, m;
96 for (i = 0; i < n; ++i)
97 {
98 m = i % 8;
99 if (m < 4)
100 w += seed[i] << (8 * m);
101 else
102 c += seed[i] << (8 * (m - 4));
103 }
104 if ((w == 0 && c == 0) || (w == -1 && c == A - 1))
105 ++c;
106 state->w = w;
107 state->c = c;
108 }
109
110 scm_t_i_rstate *
111 scm_i_copy_rstate (scm_t_i_rstate *state)
112 {
113 scm_t_rstate *new_state;
114
115 new_state = scm_gc_malloc_pointerless (scm_the_rng.rstate_size,
116 "random-state");
117 return memcpy (new_state, state, scm_the_rng.rstate_size);
118 }
119
120 SCM_SYMBOL(scm_i_rstate_tag, "multiply-with-carry");
121
122 void
123 scm_i_init_rstate_scm (scm_t_i_rstate *state, SCM value)
124 #define FUNC_NAME "scm_i_init_rstate_scm"
125 {
126 scm_t_uint32 w, c;
127 long length;
128
129 SCM_VALIDATE_LIST_COPYLEN (SCM_ARG1, value, length);
130 SCM_ASSERT (length == 3, value, SCM_ARG1, FUNC_NAME);
131 SCM_ASSERT (scm_is_eq (SCM_CAR (value), scm_i_rstate_tag),
132 value, SCM_ARG1, FUNC_NAME);
133 SCM_VALIDATE_UINT_COPY (SCM_ARG1, SCM_CADR (value), w);
134 SCM_VALIDATE_UINT_COPY (SCM_ARG1, SCM_CADDR (value), c);
135
136 state->w = w;
137 state->c = c;
138 }
139 #undef FUNC_NAME
140
141 SCM
142 scm_i_expose_rstate (scm_t_i_rstate *state)
143 {
144 return scm_list_3 (scm_i_rstate_tag,
145 scm_from_uint32 (state->w),
146 scm_from_uint32 (state->c));
147 }
148
149 \f
150 /*
151 * Random number library functions
152 */
153
154 scm_t_rstate *
155 scm_c_make_rstate (const char *seed, int n)
156 {
157 scm_t_rstate *state;
158
159 state = scm_gc_malloc_pointerless (scm_the_rng.rstate_size,
160 "random-state");
161 state->reserved0 = 0;
162 scm_the_rng.init_rstate (state, seed, n);
163 return state;
164 }
165
166 scm_t_rstate *
167 scm_c_make_rstate_scm (SCM external)
168 {
169 scm_t_rstate *state;
170
171 state = scm_gc_malloc_pointerless (scm_the_rng.rstate_size,
172 "random-state");
173 state->reserved0 = 0;
174 scm_the_rng.init_rstate_scm (state, external);
175 return state;
176 }
177
178 scm_t_rstate *
179 scm_c_default_rstate ()
180 #define FUNC_NAME "scm_c_default_rstate"
181 {
182 SCM state = SCM_VARIABLE_REF (scm_var_random_state);
183 if (!SCM_RSTATEP (state))
184 SCM_MISC_ERROR ("*random-state* contains bogus random state", SCM_EOL);
185 return SCM_RSTATE (state);
186 }
187 #undef FUNC_NAME
188
189
190 inline double
191 scm_c_uniform01 (scm_t_rstate *state)
192 {
193 double x = (double) scm_the_rng.random_bits (state) / (double) 0xffffffffUL;
194 return ((x + (double) scm_the_rng.random_bits (state))
195 / (double) 0xffffffffUL);
196 }
197
198 double
199 scm_c_normal01 (scm_t_rstate *state)
200 {
201 if (state->reserved0)
202 {
203 state->reserved0 = 0;
204 return state->reserved1;
205 }
206 else
207 {
208 double r, a, n;
209
210 r = sqrt (-2.0 * log (scm_c_uniform01 (state)));
211 a = 2.0 * M_PI * scm_c_uniform01 (state);
212
213 n = r * sin (a);
214 state->reserved1 = r * cos (a);
215 state->reserved0 = 1;
216
217 return n;
218 }
219 }
220
221 double
222 scm_c_exp1 (scm_t_rstate *state)
223 {
224 return - log (scm_c_uniform01 (state));
225 }
226
227 unsigned char scm_masktab[256];
228
229 scm_t_uint32
230 scm_c_random (scm_t_rstate *state, scm_t_uint32 m)
231 {
232 scm_t_uint32 r, mask;
233 mask = (m < 0x100
234 ? scm_masktab[m]
235 : (m < 0x10000
236 ? scm_masktab[m >> 8] << 8 | 0xff
237 : (m < 0x1000000
238 ? scm_masktab[m >> 16] << 16 | 0xffff
239 : scm_masktab[m >> 24] << 24 | 0xffffff)));
240 while ((r = scm_the_rng.random_bits (state) & mask) >= m);
241 return r;
242 }
243
244 /*
245 SCM scm_c_random_bignum (scm_t_rstate *state, SCM m)
246
247 Takes a random state (source of random bits) and a bignum m.
248 Returns a bignum b, 0 <= b < m.
249
250 It does this by allocating a bignum b with as many base 65536 digits
251 as m, filling b with random bits (in 32 bit chunks) up to the most
252 significant 1 in m, and, finally checking if the resultant b is too
253 large (>= m). If too large, we simply repeat the process again. (It
254 is important to throw away all generated random bits if b >= m,
255 otherwise we'll end up with a distorted distribution.)
256
257 */
258
259 SCM
260 scm_c_random_bignum (scm_t_rstate *state, SCM m)
261 {
262 SCM result = scm_i_mkbig ();
263 const size_t m_bits = mpz_sizeinbase (SCM_I_BIG_MPZ (m), 2);
264 /* how many bits would only partially fill the last scm_t_uint32? */
265 const size_t end_bits = m_bits % (sizeof (scm_t_uint32) * SCM_CHAR_BIT);
266 scm_t_uint32 *random_chunks = NULL;
267 const scm_t_uint32 num_full_chunks =
268 m_bits / (sizeof (scm_t_uint32) * SCM_CHAR_BIT);
269 const scm_t_uint32 num_chunks = num_full_chunks + ((end_bits) ? 1 : 0);
270
271 /* we know the result will be this big */
272 mpz_realloc2 (SCM_I_BIG_MPZ (result), m_bits);
273
274 random_chunks =
275 (scm_t_uint32 *) scm_gc_calloc (num_chunks * sizeof (scm_t_uint32),
276 "random bignum chunks");
277
278 do
279 {
280 scm_t_uint32 *current_chunk = random_chunks + (num_chunks - 1);
281 scm_t_uint32 chunks_left = num_chunks;
282
283 mpz_set_ui (SCM_I_BIG_MPZ (result), 0);
284
285 if (end_bits)
286 {
287 /* generate a mask with ones in the end_bits position, i.e. if
288 end_bits is 3, then we'd have a mask of ...0000000111 */
289 const scm_t_uint32 rndbits = scm_the_rng.random_bits (state);
290 int rshift = (sizeof (scm_t_uint32) * SCM_CHAR_BIT) - end_bits;
291 scm_t_uint32 mask = ((scm_t_uint32)-1) >> rshift;
292 scm_t_uint32 highest_bits = rndbits & mask;
293 *current_chunk-- = highest_bits;
294 chunks_left--;
295 }
296
297 while (chunks_left)
298 {
299 /* now fill in the remaining scm_t_uint32 sized chunks */
300 *current_chunk-- = scm_the_rng.random_bits (state);
301 chunks_left--;
302 }
303 mpz_import (SCM_I_BIG_MPZ (result),
304 num_chunks,
305 -1,
306 sizeof (scm_t_uint32),
307 0,
308 0,
309 random_chunks);
310 /* if result >= m, regenerate it (it is important to regenerate
311 all bits in order not to get a distorted distribution) */
312 } while (mpz_cmp (SCM_I_BIG_MPZ (result), SCM_I_BIG_MPZ (m)) >= 0);
313 scm_gc_free (random_chunks,
314 num_chunks * sizeof (scm_t_uint32),
315 "random bignum chunks");
316 return scm_i_normbig (result);
317 }
318
319 /*
320 * Scheme level representation of random states.
321 */
322
323 scm_t_bits scm_tc16_rstate;
324
325 static SCM
326 make_rstate (scm_t_rstate *state)
327 {
328 SCM_RETURN_NEWSMOB (scm_tc16_rstate, state);
329 }
330
331
332 /*
333 * Scheme level interface.
334 */
335
336 SCM_GLOBAL_VARIABLE_INIT (scm_var_random_state, "*random-state*", scm_seed_to_random_state (scm_from_locale_string ("URL:http://stat.fsu.edu/~geo/diehard.html")));
337
338 SCM_DEFINE (scm_random, "random", 1, 1, 0,
339 (SCM n, SCM state),
340 "Return a number in [0, N).\n"
341 "\n"
342 "Accepts a positive integer or real n and returns a\n"
343 "number of the same type between zero (inclusive) and\n"
344 "N (exclusive). The values returned have a uniform\n"
345 "distribution.\n"
346 "\n"
347 "The optional argument @var{state} must be of the type produced\n"
348 "by @code{seed->random-state}. It defaults to the value of the\n"
349 "variable @var{*random-state*}. This object is used to maintain\n"
350 "the state of the pseudo-random-number generator and is altered\n"
351 "as a side effect of the random operation.")
352 #define FUNC_NAME s_scm_random
353 {
354 if (SCM_UNBNDP (state))
355 state = SCM_VARIABLE_REF (scm_var_random_state);
356 SCM_VALIDATE_RSTATE (2, state);
357 if (SCM_I_INUMP (n))
358 {
359 scm_t_uint32 m = SCM_I_INUM (n);
360 SCM_ASSERT_RANGE (1, n, m > 0);
361 return scm_from_uint32 (scm_c_random (SCM_RSTATE (state), m));
362 }
363 SCM_VALIDATE_NIM (1, n);
364 if (SCM_REALP (n))
365 return scm_from_double (SCM_REAL_VALUE (n)
366 * scm_c_uniform01 (SCM_RSTATE (state)));
367
368 if (!SCM_BIGP (n))
369 SCM_WRONG_TYPE_ARG (1, n);
370 return scm_c_random_bignum (SCM_RSTATE (state), n);
371 }
372 #undef FUNC_NAME
373
374 SCM_DEFINE (scm_copy_random_state, "copy-random-state", 0, 1, 0,
375 (SCM state),
376 "Return a copy of the random state @var{state}.")
377 #define FUNC_NAME s_scm_copy_random_state
378 {
379 if (SCM_UNBNDP (state))
380 state = SCM_VARIABLE_REF (scm_var_random_state);
381 SCM_VALIDATE_RSTATE (1, state);
382 return make_rstate (scm_the_rng.copy_rstate (SCM_RSTATE (state)));
383 }
384 #undef FUNC_NAME
385
386 SCM_DEFINE (scm_seed_to_random_state, "seed->random-state", 1, 0, 0,
387 (SCM seed),
388 "Return a new random state using @var{seed}.")
389 #define FUNC_NAME s_scm_seed_to_random_state
390 {
391 SCM res;
392 if (SCM_NUMBERP (seed))
393 seed = scm_number_to_string (seed, SCM_UNDEFINED);
394 SCM_VALIDATE_STRING (1, seed);
395 res = make_rstate (scm_c_make_rstate (scm_i_string_chars (seed),
396 scm_i_string_length (seed)));
397 scm_remember_upto_here_1 (seed);
398 return res;
399
400 }
401 #undef FUNC_NAME
402
403 SCM_DEFINE (scm_external_to_random_state, "external->random-state", 1, 0, 0,
404 (SCM external),
405 "Return a new random state using @var{external}.\n"
406 "\n"
407 "@var{external} must be an external state representation obtained\n"
408 "from @code{random-state->external}.")
409 #define FUNC_NAME s_scm_external_to_random_state
410 {
411 return make_rstate (scm_c_make_rstate_scm (external));
412 }
413 #undef FUNC_NAME
414
415 SCM_DEFINE (scm_random_state_to_external, "random-state->external", 1, 0, 0,
416 (SCM state),
417 "Return an external representation of @var{state}.")
418 #define FUNC_NAME s_scm_random_state_to_external
419 {
420 SCM_VALIDATE_RSTATE (1, state);
421 return scm_the_rng.expose_rstate (SCM_RSTATE (state));
422 }
423 #undef FUNC_NAME
424
425 SCM_DEFINE (scm_random_uniform, "random:uniform", 0, 1, 0,
426 (SCM state),
427 "Return a uniformly distributed inexact real random number in\n"
428 "[0,1).")
429 #define FUNC_NAME s_scm_random_uniform
430 {
431 if (SCM_UNBNDP (state))
432 state = SCM_VARIABLE_REF (scm_var_random_state);
433 SCM_VALIDATE_RSTATE (1, state);
434 return scm_from_double (scm_c_uniform01 (SCM_RSTATE (state)));
435 }
436 #undef FUNC_NAME
437
438 SCM_DEFINE (scm_random_normal, "random:normal", 0, 1, 0,
439 (SCM state),
440 "Return an inexact real in a normal distribution. The\n"
441 "distribution used has mean 0 and standard deviation 1. For a\n"
442 "normal distribution with mean m and standard deviation d use\n"
443 "@code{(+ m (* d (random:normal)))}.")
444 #define FUNC_NAME s_scm_random_normal
445 {
446 if (SCM_UNBNDP (state))
447 state = SCM_VARIABLE_REF (scm_var_random_state);
448 SCM_VALIDATE_RSTATE (1, state);
449 return scm_from_double (scm_c_normal01 (SCM_RSTATE (state)));
450 }
451 #undef FUNC_NAME
452
453 static void
454 vector_scale_x (SCM v, double c)
455 {
456 size_t n;
457 if (scm_is_simple_vector (v))
458 {
459 n = SCM_SIMPLE_VECTOR_LENGTH (v);
460 while (n-- > 0)
461 SCM_REAL_VALUE (SCM_SIMPLE_VECTOR_REF (v, n)) *= c;
462 }
463 else
464 {
465 /* must be a f64vector. */
466 scm_t_array_handle handle;
467 size_t i, len;
468 ssize_t inc;
469 double *elts;
470
471 elts = scm_f64vector_writable_elements (v, &handle, &len, &inc);
472
473 for (i = 0; i < len; i++, elts += inc)
474 *elts *= c;
475
476 scm_array_handle_release (&handle);
477 }
478 }
479
480 static double
481 vector_sum_squares (SCM v)
482 {
483 double x, sum = 0.0;
484 size_t n;
485 if (scm_is_simple_vector (v))
486 {
487 n = SCM_SIMPLE_VECTOR_LENGTH (v);
488 while (n-- > 0)
489 {
490 x = SCM_REAL_VALUE (SCM_SIMPLE_VECTOR_REF (v, n));
491 sum += x * x;
492 }
493 }
494 else
495 {
496 /* must be a f64vector. */
497 scm_t_array_handle handle;
498 size_t i, len;
499 ssize_t inc;
500 const double *elts;
501
502 elts = scm_f64vector_elements (v, &handle, &len, &inc);
503
504 for (i = 0; i < len; i++, elts += inc)
505 {
506 x = *elts;
507 sum += x * x;
508 }
509
510 scm_array_handle_release (&handle);
511 }
512 return sum;
513 }
514
515 /* For the uniform distribution on the solid sphere, note that in
516 * this distribution the length r of the vector has cumulative
517 * distribution r^n; i.e., u=r^n is uniform [0,1], so r can be
518 * generated as r=u^(1/n).
519 */
520 SCM_DEFINE (scm_random_solid_sphere_x, "random:solid-sphere!", 1, 1, 0,
521 (SCM v, SCM state),
522 "Fills @var{vect} with inexact real random numbers the sum of\n"
523 "whose squares is less than 1.0. Thinking of @var{vect} as\n"
524 "coordinates in space of dimension @var{n} @math{=}\n"
525 "@code{(vector-length @var{vect})}, the coordinates are\n"
526 "uniformly distributed within the unit @var{n}-sphere.")
527 #define FUNC_NAME s_scm_random_solid_sphere_x
528 {
529 if (SCM_UNBNDP (state))
530 state = SCM_VARIABLE_REF (scm_var_random_state);
531 SCM_VALIDATE_RSTATE (2, state);
532 scm_random_normal_vector_x (v, state);
533 vector_scale_x (v,
534 pow (scm_c_uniform01 (SCM_RSTATE (state)),
535 1.0 / scm_c_generalized_vector_length (v))
536 / sqrt (vector_sum_squares (v)));
537 return SCM_UNSPECIFIED;
538 }
539 #undef FUNC_NAME
540
541 SCM_DEFINE (scm_random_hollow_sphere_x, "random:hollow-sphere!", 1, 1, 0,
542 (SCM v, SCM state),
543 "Fills vect with inexact real random numbers\n"
544 "the sum of whose squares is equal to 1.0.\n"
545 "Thinking of vect as coordinates in space of\n"
546 "dimension n = (vector-length vect), the coordinates\n"
547 "are uniformly distributed over the surface of the\n"
548 "unit n-sphere.")
549 #define FUNC_NAME s_scm_random_hollow_sphere_x
550 {
551 if (SCM_UNBNDP (state))
552 state = SCM_VARIABLE_REF (scm_var_random_state);
553 SCM_VALIDATE_RSTATE (2, state);
554 scm_random_normal_vector_x (v, state);
555 vector_scale_x (v, 1 / sqrt (vector_sum_squares (v)));
556 return SCM_UNSPECIFIED;
557 }
558 #undef FUNC_NAME
559
560
561 SCM_DEFINE (scm_random_normal_vector_x, "random:normal-vector!", 1, 1, 0,
562 (SCM v, SCM state),
563 "Fills vect with inexact real random numbers that are\n"
564 "independent and standard normally distributed\n"
565 "(i.e., with mean 0 and variance 1).")
566 #define FUNC_NAME s_scm_random_normal_vector_x
567 {
568 long i;
569 scm_t_array_handle handle;
570 scm_t_array_dim *dim;
571
572 if (SCM_UNBNDP (state))
573 state = SCM_VARIABLE_REF (scm_var_random_state);
574 SCM_VALIDATE_RSTATE (2, state);
575
576 scm_generalized_vector_get_handle (v, &handle);
577 dim = scm_array_handle_dims (&handle);
578
579 if (scm_is_vector (v))
580 {
581 SCM *elts = scm_array_handle_writable_elements (&handle);
582 for (i = dim->lbnd; i <= dim->ubnd; i++, elts += dim->inc)
583 *elts = scm_from_double (scm_c_normal01 (SCM_RSTATE (state)));
584 }
585 else
586 {
587 /* must be a f64vector. */
588 double *elts = scm_array_handle_f64_writable_elements (&handle);
589 for (i = dim->lbnd; i <= dim->ubnd; i++, elts += dim->inc)
590 *elts = scm_c_normal01 (SCM_RSTATE (state));
591 }
592
593 scm_array_handle_release (&handle);
594
595 return SCM_UNSPECIFIED;
596 }
597 #undef FUNC_NAME
598
599 SCM_DEFINE (scm_random_exp, "random:exp", 0, 1, 0,
600 (SCM state),
601 "Return an inexact real in an exponential distribution with mean\n"
602 "1. For an exponential distribution with mean u use (* u\n"
603 "(random:exp)).")
604 #define FUNC_NAME s_scm_random_exp
605 {
606 if (SCM_UNBNDP (state))
607 state = SCM_VARIABLE_REF (scm_var_random_state);
608 SCM_VALIDATE_RSTATE (1, state);
609 return scm_from_double (scm_c_exp1 (SCM_RSTATE (state)));
610 }
611 #undef FUNC_NAME
612
613 void
614 scm_init_random ()
615 {
616 int i, m;
617 /* plug in default RNG */
618 scm_t_rng rng =
619 {
620 sizeof (scm_t_i_rstate),
621 (scm_t_uint32 (*)()) scm_i_uniform32,
622 (void (*)()) scm_i_init_rstate,
623 (scm_t_rstate *(*)()) scm_i_copy_rstate,
624 (void (*)(scm_t_rstate *, SCM)) scm_i_init_rstate_scm,
625 (SCM (*)(scm_t_rstate *)) scm_i_expose_rstate
626 };
627 scm_the_rng = rng;
628
629 scm_tc16_rstate = scm_make_smob_type ("random-state", 0);
630
631 for (m = 1; m <= 0x100; m <<= 1)
632 for (i = m >> 1; i < m; ++i)
633 scm_masktab[i] = m - 1;
634
635 #include "libguile/random.x"
636
637 scm_add_feature ("random");
638 }
639
640 /*
641 Local Variables:
642 c-file-style: "gnu"
643 End:
644 */