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