update docs
[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 typedef struct scm_t_i_rstate {
75 scm_t_rstate rstate;
76 scm_t_uint32 w;
77 scm_t_uint32 c;
78 } scm_t_i_rstate;
79
80
81 #define A 2131995753UL
82
83 #ifndef M_PI
84 #define M_PI 3.14159265359
85 #endif
86
87 static scm_t_uint32
88 scm_i_uniform32 (scm_t_rstate *state)
89 {
90 scm_t_i_rstate *istate = (scm_t_i_rstate*) state;
91 scm_t_uint64 x = (scm_t_uint64) A * istate->w + istate->c;
92 scm_t_uint32 w = x & 0xffffffffUL;
93 istate->w = w;
94 istate->c = x >> 32L;
95 return w;
96 }
97
98 static void
99 scm_i_init_rstate (scm_t_rstate *state, const char *seed, int n)
100 {
101 scm_t_i_rstate *istate = (scm_t_i_rstate*) state;
102 scm_t_uint32 w = 0L;
103 scm_t_uint32 c = 0L;
104 int i, m;
105 for (i = 0; i < n; ++i)
106 {
107 m = i % 8;
108 if (m < 4)
109 w += seed[i] << (8 * m);
110 else
111 c += seed[i] << (8 * (m - 4));
112 }
113 if ((w == 0 && c == 0) || (w == -1 && c == A - 1))
114 ++c;
115 istate->w = w;
116 istate->c = c;
117 }
118
119 static scm_t_rstate *
120 scm_i_copy_rstate (scm_t_rstate *state)
121 {
122 scm_t_rstate *new_state;
123
124 new_state = scm_gc_malloc_pointerless (scm_the_rng.rstate_size,
125 "random-state");
126 return memcpy (new_state, state, scm_the_rng.rstate_size);
127 }
128
129 SCM_SYMBOL(scm_i_rstate_tag, "multiply-with-carry");
130
131 static void
132 scm_i_rstate_from_datum (scm_t_rstate *state, SCM value)
133 #define FUNC_NAME "scm_i_rstate_from_datum"
134 {
135 scm_t_i_rstate *istate = (scm_t_i_rstate*) state;
136 scm_t_uint32 w, c;
137 long length;
138
139 SCM_VALIDATE_LIST_COPYLEN (SCM_ARG1, value, length);
140 SCM_ASSERT (length == 3, value, SCM_ARG1, FUNC_NAME);
141 SCM_ASSERT (scm_is_eq (SCM_CAR (value), scm_i_rstate_tag),
142 value, SCM_ARG1, FUNC_NAME);
143 SCM_VALIDATE_UINT_COPY (SCM_ARG1, SCM_CADR (value), w);
144 SCM_VALIDATE_UINT_COPY (SCM_ARG1, SCM_CADDR (value), c);
145
146 istate->w = w;
147 istate->c = c;
148 }
149 #undef FUNC_NAME
150
151 static SCM
152 scm_i_rstate_to_datum (scm_t_rstate *state)
153 {
154 scm_t_i_rstate *istate = (scm_t_i_rstate*) state;
155 return scm_list_3 (scm_i_rstate_tag,
156 scm_from_uint32 (istate->w),
157 scm_from_uint32 (istate->c));
158 }
159
160 \f
161 /*
162 * Random number library functions
163 */
164
165 scm_t_rstate *
166 scm_c_make_rstate (const char *seed, int n)
167 {
168 scm_t_rstate *state;
169
170 state = scm_gc_malloc_pointerless (scm_the_rng.rstate_size,
171 "random-state");
172 state->reserved0 = 0;
173 scm_the_rng.init_rstate (state, seed, n);
174 return state;
175 }
176
177 scm_t_rstate *
178 scm_c_rstate_from_datum (SCM datum)
179 {
180 scm_t_rstate *state;
181
182 state = scm_gc_malloc_pointerless (scm_the_rng.rstate_size,
183 "random-state");
184 state->reserved0 = 0;
185 scm_the_rng.from_datum (state, datum);
186 return state;
187 }
188
189 scm_t_rstate *
190 scm_c_default_rstate ()
191 #define FUNC_NAME "scm_c_default_rstate"
192 {
193 SCM state = SCM_VARIABLE_REF (scm_var_random_state);
194 if (!SCM_RSTATEP (state))
195 SCM_MISC_ERROR ("*random-state* contains bogus random state", SCM_EOL);
196 return SCM_RSTATE (state);
197 }
198 #undef FUNC_NAME
199
200
201 inline double
202 scm_c_uniform01 (scm_t_rstate *state)
203 {
204 double x = (double) scm_the_rng.random_bits (state) / (double) 0xffffffffUL;
205 return ((x + (double) scm_the_rng.random_bits (state))
206 / (double) 0xffffffffUL);
207 }
208
209 double
210 scm_c_normal01 (scm_t_rstate *state)
211 {
212 if (state->reserved0)
213 {
214 state->reserved0 = 0;
215 return state->reserved1;
216 }
217 else
218 {
219 double r, a, n;
220
221 r = sqrt (-2.0 * log (scm_c_uniform01 (state)));
222 a = 2.0 * M_PI * scm_c_uniform01 (state);
223
224 n = r * sin (a);
225 state->reserved1 = r * cos (a);
226 state->reserved0 = 1;
227
228 return n;
229 }
230 }
231
232 double
233 scm_c_exp1 (scm_t_rstate *state)
234 {
235 return - log (scm_c_uniform01 (state));
236 }
237
238 unsigned char scm_masktab[256];
239
240 scm_t_uint32
241 scm_c_random (scm_t_rstate *state, scm_t_uint32 m)
242 {
243 scm_t_uint32 r, mask;
244 mask = (m < 0x100
245 ? scm_masktab[m]
246 : (m < 0x10000
247 ? scm_masktab[m >> 8] << 8 | 0xff
248 : (m < 0x1000000
249 ? scm_masktab[m >> 16] << 16 | 0xffff
250 : scm_masktab[m >> 24] << 24 | 0xffffff)));
251 while ((r = scm_the_rng.random_bits (state) & mask) >= m);
252 return r;
253 }
254
255 /*
256 SCM scm_c_random_bignum (scm_t_rstate *state, SCM m)
257
258 Takes a random state (source of random bits) and a bignum m.
259 Returns a bignum b, 0 <= b < m.
260
261 It does this by allocating a bignum b with as many base 65536 digits
262 as m, filling b with random bits (in 32 bit chunks) up to the most
263 significant 1 in m, and, finally checking if the resultant b is too
264 large (>= m). If too large, we simply repeat the process again. (It
265 is important to throw away all generated random bits if b >= m,
266 otherwise we'll end up with a distorted distribution.)
267
268 */
269
270 SCM
271 scm_c_random_bignum (scm_t_rstate *state, SCM m)
272 {
273 SCM result = scm_i_mkbig ();
274 const size_t m_bits = mpz_sizeinbase (SCM_I_BIG_MPZ (m), 2);
275 /* how many bits would only partially fill the last scm_t_uint32? */
276 const size_t end_bits = m_bits % (sizeof (scm_t_uint32) * SCM_CHAR_BIT);
277 scm_t_uint32 *random_chunks = NULL;
278 const scm_t_uint32 num_full_chunks =
279 m_bits / (sizeof (scm_t_uint32) * SCM_CHAR_BIT);
280 const scm_t_uint32 num_chunks = num_full_chunks + ((end_bits) ? 1 : 0);
281
282 /* we know the result will be this big */
283 mpz_realloc2 (SCM_I_BIG_MPZ (result), m_bits);
284
285 random_chunks =
286 (scm_t_uint32 *) scm_gc_calloc (num_chunks * sizeof (scm_t_uint32),
287 "random bignum chunks");
288
289 do
290 {
291 scm_t_uint32 *current_chunk = random_chunks + (num_chunks - 1);
292 scm_t_uint32 chunks_left = num_chunks;
293
294 mpz_set_ui (SCM_I_BIG_MPZ (result), 0);
295
296 if (end_bits)
297 {
298 /* generate a mask with ones in the end_bits position, i.e. if
299 end_bits is 3, then we'd have a mask of ...0000000111 */
300 const scm_t_uint32 rndbits = scm_the_rng.random_bits (state);
301 int rshift = (sizeof (scm_t_uint32) * SCM_CHAR_BIT) - end_bits;
302 scm_t_uint32 mask = ((scm_t_uint32)-1) >> rshift;
303 scm_t_uint32 highest_bits = rndbits & mask;
304 *current_chunk-- = highest_bits;
305 chunks_left--;
306 }
307
308 while (chunks_left)
309 {
310 /* now fill in the remaining scm_t_uint32 sized chunks */
311 *current_chunk-- = scm_the_rng.random_bits (state);
312 chunks_left--;
313 }
314 mpz_import (SCM_I_BIG_MPZ (result),
315 num_chunks,
316 -1,
317 sizeof (scm_t_uint32),
318 0,
319 0,
320 random_chunks);
321 /* if result >= m, regenerate it (it is important to regenerate
322 all bits in order not to get a distorted distribution) */
323 } while (mpz_cmp (SCM_I_BIG_MPZ (result), SCM_I_BIG_MPZ (m)) >= 0);
324 scm_gc_free (random_chunks,
325 num_chunks * sizeof (scm_t_uint32),
326 "random bignum chunks");
327 return scm_i_normbig (result);
328 }
329
330 /*
331 * Scheme level representation of random states.
332 */
333
334 scm_t_bits scm_tc16_rstate;
335
336 static SCM
337 make_rstate (scm_t_rstate *state)
338 {
339 SCM_RETURN_NEWSMOB (scm_tc16_rstate, state);
340 }
341
342
343 /*
344 * Scheme level interface.
345 */
346
347 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")));
348
349 SCM_DEFINE (scm_random, "random", 1, 1, 0,
350 (SCM n, SCM state),
351 "Return a number in [0, N).\n"
352 "\n"
353 "Accepts a positive integer or real n and returns a\n"
354 "number of the same type between zero (inclusive) and\n"
355 "N (exclusive). The values returned have a uniform\n"
356 "distribution.\n"
357 "\n"
358 "The optional argument @var{state} must be of the type produced\n"
359 "by @code{seed->random-state}. It defaults to the value of the\n"
360 "variable @var{*random-state*}. This object is used to maintain\n"
361 "the state of the pseudo-random-number generator and is altered\n"
362 "as a side effect of the random operation.")
363 #define FUNC_NAME s_scm_random
364 {
365 if (SCM_UNBNDP (state))
366 state = SCM_VARIABLE_REF (scm_var_random_state);
367 SCM_VALIDATE_RSTATE (2, state);
368 if (SCM_I_INUMP (n))
369 {
370 scm_t_uint32 m = SCM_I_INUM (n);
371 SCM_ASSERT_RANGE (1, n, m > 0);
372 return scm_from_uint32 (scm_c_random (SCM_RSTATE (state), m));
373 }
374 SCM_VALIDATE_NIM (1, n);
375 if (SCM_REALP (n))
376 return scm_from_double (SCM_REAL_VALUE (n)
377 * scm_c_uniform01 (SCM_RSTATE (state)));
378
379 if (!SCM_BIGP (n))
380 SCM_WRONG_TYPE_ARG (1, n);
381 return scm_c_random_bignum (SCM_RSTATE (state), n);
382 }
383 #undef FUNC_NAME
384
385 SCM_DEFINE (scm_copy_random_state, "copy-random-state", 0, 1, 0,
386 (SCM state),
387 "Return a copy of the random state @var{state}.")
388 #define FUNC_NAME s_scm_copy_random_state
389 {
390 if (SCM_UNBNDP (state))
391 state = SCM_VARIABLE_REF (scm_var_random_state);
392 SCM_VALIDATE_RSTATE (1, state);
393 return make_rstate (scm_the_rng.copy_rstate (SCM_RSTATE (state)));
394 }
395 #undef FUNC_NAME
396
397 SCM_DEFINE (scm_seed_to_random_state, "seed->random-state", 1, 0, 0,
398 (SCM seed),
399 "Return a new random state using @var{seed}.")
400 #define FUNC_NAME s_scm_seed_to_random_state
401 {
402 SCM res;
403 if (SCM_NUMBERP (seed))
404 seed = scm_number_to_string (seed, SCM_UNDEFINED);
405 SCM_VALIDATE_STRING (1, seed);
406 res = make_rstate (scm_c_make_rstate (scm_i_string_chars (seed),
407 scm_i_string_length (seed)));
408 scm_remember_upto_here_1 (seed);
409 return res;
410
411 }
412 #undef FUNC_NAME
413
414 SCM_DEFINE (scm_datum_to_random_state, "datum->random-state", 1, 0, 0,
415 (SCM datum),
416 "Return a new random state using @var{datum}, which should have\n"
417 "been obtailed from @code{random-state->datum}.")
418 #define FUNC_NAME s_scm_datum_to_random_state
419 {
420 return make_rstate (scm_c_rstate_from_datum (datum));
421 }
422 #undef FUNC_NAME
423
424 SCM_DEFINE (scm_random_state_to_datum, "random-state->datum", 1, 0, 0,
425 (SCM state),
426 "Return a datum representation of @var{state} that may be\n"
427 "written out and read back with the Scheme reader.")
428 #define FUNC_NAME s_scm_random_state_to_datum
429 {
430 SCM_VALIDATE_RSTATE (1, state);
431 return scm_the_rng.to_datum (SCM_RSTATE (state));
432 }
433 #undef FUNC_NAME
434
435 SCM_DEFINE (scm_random_uniform, "random:uniform", 0, 1, 0,
436 (SCM state),
437 "Return a uniformly distributed inexact real random number in\n"
438 "[0,1).")
439 #define FUNC_NAME s_scm_random_uniform
440 {
441 if (SCM_UNBNDP (state))
442 state = SCM_VARIABLE_REF (scm_var_random_state);
443 SCM_VALIDATE_RSTATE (1, state);
444 return scm_from_double (scm_c_uniform01 (SCM_RSTATE (state)));
445 }
446 #undef FUNC_NAME
447
448 SCM_DEFINE (scm_random_normal, "random:normal", 0, 1, 0,
449 (SCM state),
450 "Return an inexact real in a normal distribution. The\n"
451 "distribution used has mean 0 and standard deviation 1. For a\n"
452 "normal distribution with mean m and standard deviation d use\n"
453 "@code{(+ m (* d (random:normal)))}.")
454 #define FUNC_NAME s_scm_random_normal
455 {
456 if (SCM_UNBNDP (state))
457 state = SCM_VARIABLE_REF (scm_var_random_state);
458 SCM_VALIDATE_RSTATE (1, state);
459 return scm_from_double (scm_c_normal01 (SCM_RSTATE (state)));
460 }
461 #undef FUNC_NAME
462
463 static void
464 vector_scale_x (SCM v, double c)
465 {
466 size_t n;
467 if (scm_is_simple_vector (v))
468 {
469 n = SCM_SIMPLE_VECTOR_LENGTH (v);
470 while (n-- > 0)
471 SCM_REAL_VALUE (SCM_SIMPLE_VECTOR_REF (v, n)) *= c;
472 }
473 else
474 {
475 /* must be a f64vector. */
476 scm_t_array_handle handle;
477 size_t i, len;
478 ssize_t inc;
479 double *elts;
480
481 elts = scm_f64vector_writable_elements (v, &handle, &len, &inc);
482
483 for (i = 0; i < len; i++, elts += inc)
484 *elts *= c;
485
486 scm_array_handle_release (&handle);
487 }
488 }
489
490 static double
491 vector_sum_squares (SCM v)
492 {
493 double x, sum = 0.0;
494 size_t n;
495 if (scm_is_simple_vector (v))
496 {
497 n = SCM_SIMPLE_VECTOR_LENGTH (v);
498 while (n-- > 0)
499 {
500 x = SCM_REAL_VALUE (SCM_SIMPLE_VECTOR_REF (v, n));
501 sum += x * x;
502 }
503 }
504 else
505 {
506 /* must be a f64vector. */
507 scm_t_array_handle handle;
508 size_t i, len;
509 ssize_t inc;
510 const double *elts;
511
512 elts = scm_f64vector_elements (v, &handle, &len, &inc);
513
514 for (i = 0; i < len; i++, elts += inc)
515 {
516 x = *elts;
517 sum += x * x;
518 }
519
520 scm_array_handle_release (&handle);
521 }
522 return sum;
523 }
524
525 /* For the uniform distribution on the solid sphere, note that in
526 * this distribution the length r of the vector has cumulative
527 * distribution r^n; i.e., u=r^n is uniform [0,1], so r can be
528 * generated as r=u^(1/n).
529 */
530 SCM_DEFINE (scm_random_solid_sphere_x, "random:solid-sphere!", 1, 1, 0,
531 (SCM v, SCM state),
532 "Fills @var{vect} with inexact real random numbers the sum of\n"
533 "whose squares is less than 1.0. Thinking of @var{vect} as\n"
534 "coordinates in space of dimension @var{n} @math{=}\n"
535 "@code{(vector-length @var{vect})}, the coordinates are\n"
536 "uniformly distributed within the unit @var{n}-sphere.")
537 #define FUNC_NAME s_scm_random_solid_sphere_x
538 {
539 if (SCM_UNBNDP (state))
540 state = SCM_VARIABLE_REF (scm_var_random_state);
541 SCM_VALIDATE_RSTATE (2, state);
542 scm_random_normal_vector_x (v, state);
543 vector_scale_x (v,
544 pow (scm_c_uniform01 (SCM_RSTATE (state)),
545 1.0 / scm_c_generalized_vector_length (v))
546 / sqrt (vector_sum_squares (v)));
547 return SCM_UNSPECIFIED;
548 }
549 #undef FUNC_NAME
550
551 SCM_DEFINE (scm_random_hollow_sphere_x, "random:hollow-sphere!", 1, 1, 0,
552 (SCM v, SCM state),
553 "Fills vect with inexact real random numbers\n"
554 "the sum of whose squares is equal to 1.0.\n"
555 "Thinking of vect as coordinates in space of\n"
556 "dimension n = (vector-length vect), the coordinates\n"
557 "are uniformly distributed over the surface of the\n"
558 "unit n-sphere.")
559 #define FUNC_NAME s_scm_random_hollow_sphere_x
560 {
561 if (SCM_UNBNDP (state))
562 state = SCM_VARIABLE_REF (scm_var_random_state);
563 SCM_VALIDATE_RSTATE (2, state);
564 scm_random_normal_vector_x (v, state);
565 vector_scale_x (v, 1 / sqrt (vector_sum_squares (v)));
566 return SCM_UNSPECIFIED;
567 }
568 #undef FUNC_NAME
569
570
571 SCM_DEFINE (scm_random_normal_vector_x, "random:normal-vector!", 1, 1, 0,
572 (SCM v, SCM state),
573 "Fills vect with inexact real random numbers that are\n"
574 "independent and standard normally distributed\n"
575 "(i.e., with mean 0 and variance 1).")
576 #define FUNC_NAME s_scm_random_normal_vector_x
577 {
578 long i;
579 scm_t_array_handle handle;
580 scm_t_array_dim *dim;
581
582 if (SCM_UNBNDP (state))
583 state = SCM_VARIABLE_REF (scm_var_random_state);
584 SCM_VALIDATE_RSTATE (2, state);
585
586 scm_generalized_vector_get_handle (v, &handle);
587 dim = scm_array_handle_dims (&handle);
588
589 if (scm_is_vector (v))
590 {
591 SCM *elts = scm_array_handle_writable_elements (&handle);
592 for (i = dim->lbnd; i <= dim->ubnd; i++, elts += dim->inc)
593 *elts = scm_from_double (scm_c_normal01 (SCM_RSTATE (state)));
594 }
595 else
596 {
597 /* must be a f64vector. */
598 double *elts = scm_array_handle_f64_writable_elements (&handle);
599 for (i = dim->lbnd; i <= dim->ubnd; i++, elts += dim->inc)
600 *elts = scm_c_normal01 (SCM_RSTATE (state));
601 }
602
603 scm_array_handle_release (&handle);
604
605 return SCM_UNSPECIFIED;
606 }
607 #undef FUNC_NAME
608
609 SCM_DEFINE (scm_random_exp, "random:exp", 0, 1, 0,
610 (SCM state),
611 "Return an inexact real in an exponential distribution with mean\n"
612 "1. For an exponential distribution with mean u use (* u\n"
613 "(random:exp)).")
614 #define FUNC_NAME s_scm_random_exp
615 {
616 if (SCM_UNBNDP (state))
617 state = SCM_VARIABLE_REF (scm_var_random_state);
618 SCM_VALIDATE_RSTATE (1, state);
619 return scm_from_double (scm_c_exp1 (SCM_RSTATE (state)));
620 }
621 #undef FUNC_NAME
622
623 void
624 scm_init_random ()
625 {
626 int i, m;
627 /* plug in default RNG */
628 scm_t_rng rng =
629 {
630 sizeof (scm_t_i_rstate),
631 scm_i_uniform32,
632 scm_i_init_rstate,
633 scm_i_copy_rstate,
634 scm_i_rstate_from_datum,
635 scm_i_rstate_to_datum
636 };
637 scm_the_rng = rng;
638
639 scm_tc16_rstate = scm_make_smob_type ("random-state", 0);
640
641 for (m = 1; m <= 0x100; m <<= 1)
642 for (i = m >> 1; i < m; ++i)
643 scm_masktab[i] = m - 1;
644
645 #include "libguile/random.x"
646
647 scm_add_feature ("random");
648 }
649
650 /*
651 Local Variables:
652 c-file-style: "gnu"
653 End:
654 */