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