4bb1c58b0d34b3e523a621ea070ba639aa40d48d
[bpt/guile.git] / libguile / random.c
1 /* Copyright (C) 1999, 2000 Free Software Foundation, Inc.
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2, or (at your option)
5 * any later version.
6 *
7 * This program 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
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this software; see the file COPYING. If not, write to
14 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
15 * Boston, MA 02111-1307 USA
16 *
17 * As a special exception, the Free Software Foundation gives permission
18 * for additional uses of the text contained in its release of GUILE.
19 *
20 * The exception is that, if you link the GUILE library with other files
21 * to produce an executable, this does not by itself cause the
22 * resulting executable to be covered by the GNU General Public License.
23 * Your use of that executable is in no way restricted on account of
24 * linking the GUILE library code into it.
25 *
26 * This exception does not however invalidate any other reasons why
27 * the executable file might be covered by the GNU General Public License.
28 *
29 * This exception applies only to the code released by the
30 * Free Software Foundation under the name GUILE. If you copy
31 * code from other Free Software Foundation releases into a copy of
32 * GUILE, as the General Public License permits, the exception does
33 * not apply to the code that you add in this way. To avoid misleading
34 * anyone as to the status of such modified files, you must delete
35 * this exception notice from them.
36 *
37 * If you write modifications of your own for GUILE, it is your choice
38 * whether to permit this exception to apply to your modifications.
39 * If you do not wish that, delete this exception notice. */
40
41 /* Software engineering face-lift by Greg J. Badros, 11-Dec-1999,
42 gjb@cs.washington.edu, http://www.cs.washington.edu/homes/gjb */
43
44
45 /* Author: Mikael Djurfeldt <djurfeldt@nada.kth.se> */
46
47 #include "_scm.h"
48
49 #include <stdio.h>
50 #include <math.h>
51 #include "genio.h"
52 #include "smob.h"
53 #include "numbers.h"
54 #include "feature.h"
55
56 #include "validate.h"
57 #include "random.h"
58
59 \f
60 /*
61 * A plugin interface for RNGs
62 *
63 * Using this interface, it is possible for the application to tell
64 * libguile to use a different RNG. This is desirable if it is
65 * necessary to use the same RNG everywhere in the application in
66 * order to prevent interference, if the application uses RNG
67 * hardware, or if the application has special demands on the RNG.
68 *
69 * Look in random.h and how the default generator is "plugged in" in
70 * scm_init_random().
71 */
72
73 scm_rng scm_the_rng;
74
75 \f
76 /*
77 * The prepackaged RNG
78 *
79 * This is the MWC (Multiply With Carry) random number generator
80 * described by George Marsaglia at the Department of Statistics and
81 * Supercomputer Computations Research Institute, The Florida State
82 * University (http://stat.fsu.edu/~geo).
83 *
84 * It uses 64 bits, has a period of 4578426017172946943 (4.6e18), and
85 * passes all tests in the DIEHARD test suite
86 * (http://stat.fsu.edu/~geo/diehard.html)
87 */
88
89 #define A 2131995753UL
90
91 #if SIZEOF_LONG > 4
92 #if SIZEOF_INT > 4
93 #define LONG32 unsigned short
94 #else
95 #define LONG32 unsigned int
96 #endif
97 #define LONG64 unsigned long
98 #else
99 #define LONG32 unsigned long
100 #define LONG64 unsigned long long
101 #endif
102
103 #if SIZEOF_LONG > 4 || defined (HAVE_LONG_LONGS)
104
105 unsigned long
106 scm_i_uniform32 (scm_i_rstate *state)
107 {
108 LONG64 x = (LONG64) A * state->w + state->c;
109 LONG32 w = x & 0xffffffffUL;
110 state->w = w;
111 state->c = x >> 32L;
112 return w;
113 }
114
115 #else
116
117 /* ww This is a portable version of the same RNG without 64 bit
118 * * aa arithmetic.
119 * ----
120 * xx It is only intended to provide identical behaviour on
121 * xx platforms without 8 byte longs or long longs until
122 * xx someone has implemented the routine in assembler code.
123 * xxcc
124 * ----
125 * ccww
126 */
127
128 #define L(x) ((x) & 0xffff)
129 #define H(x) ((x) >> 16)
130
131 unsigned long
132 scm_i_uniform32 (scm_i_rstate *state)
133 {
134 LONG32 x1 = L (A) * L (state->w);
135 LONG32 x2 = L (A) * H (state->w);
136 LONG32 x3 = H (A) * L (state->w);
137 LONG32 w = L (x1) + L (state->c);
138 LONG32 m = H (x1) + L (x2) + L (x3) + H (state->c) + H (w);
139 LONG32 x4 = H (A) * H (state->w);
140 state->w = w = (L (m) << 16) + L (w);
141 state->c = H (x2) + H (x3) + x4 + H (m);
142 return w;
143 }
144
145 #endif
146
147 void
148 scm_i_init_rstate (scm_i_rstate *state, char *seed, int n)
149 {
150 LONG32 w = 0L;
151 LONG32 c = 0L;
152 int i, m;
153 for (i = 0; i < n; ++i)
154 {
155 m = i % 8;
156 if (m < 4)
157 w += seed[i] << (8 * m);
158 else
159 c += seed[i] << (8 * (m - 4));
160 }
161 if ((w == 0 && c == 0) || (w == 0xffffffffUL && c == A - 1))
162 ++c;
163 state->w = w;
164 state->c = c;
165 }
166
167 scm_i_rstate *
168 scm_i_copy_rstate (scm_i_rstate *state)
169 {
170 scm_rstate *new_state = malloc (scm_the_rng.rstate_size);
171 if (new_state == 0)
172 scm_wta (SCM_MAKINUM (scm_the_rng.rstate_size),
173 (char *) SCM_NALLOC, "rstate");
174 return memcpy (new_state, state, scm_the_rng.rstate_size);
175 }
176
177 \f
178 /*
179 * Random number library functions
180 */
181
182 scm_rstate *
183 scm_c_make_rstate (char *seed, int n)
184 {
185 scm_rstate *state = malloc (scm_the_rng.rstate_size);
186 if (state == 0)
187 scm_wta (SCM_MAKINUM (scm_the_rng.rstate_size),
188 (char *) SCM_NALLOC,
189 "rstate");
190 state->reserved0 = 0;
191 scm_the_rng.init_rstate (state, seed, n);
192 return state;
193 }
194
195 scm_rstate *
196 scm_c_default_rstate ()
197 {
198 SCM state = SCM_CDR (scm_var_random_state);
199 SCM_ASSERT (SCM_RSTATEP (state),
200 state, "*random-state* contains bogus random state", 0);
201 return SCM_RSTATE (state);
202 }
203
204 inline double
205 scm_c_uniform01 (scm_rstate *state)
206 {
207 double x = (double) scm_the_rng.random_bits (state) / (double) 0xffffffffUL;
208 return ((x + (double) scm_the_rng.random_bits (state))
209 / (double) 0xffffffffUL);
210 }
211
212 double
213 scm_c_normal01 (scm_rstate *state)
214 {
215 if (state->reserved0)
216 {
217 state->reserved0 = 0;
218 return state->reserved1;
219 }
220 else
221 {
222 double r, a, n;
223
224 r = sqrt (-2.0 * log (scm_c_uniform01 (state)));
225 a = 2.0 * M_PI * scm_c_uniform01 (state);
226
227 n = r * sin (a);
228 state->reserved1 = r * cos (a);
229 state->reserved0 = 1;
230
231 return n;
232 }
233 }
234
235 double
236 scm_c_exp1 (scm_rstate *state)
237 {
238 return - log (scm_c_uniform01 (state));
239 }
240
241 unsigned char scm_masktab[256];
242
243 unsigned long
244 scm_c_random (scm_rstate *state, unsigned long m)
245 {
246 unsigned int r, mask;
247 mask = (m < 0x100
248 ? scm_masktab[m]
249 : (m < 0x10000
250 ? scm_masktab[m >> 8] << 8 | 0xff
251 : (m < 0x1000000
252 ? scm_masktab[m >> 16] << 16 | 0xffff
253 : scm_masktab[m >> 24] << 24 | 0xffffff)));
254 while ((r = scm_the_rng.random_bits (state) & mask) >= m);
255 return r;
256 }
257
258 SCM
259 scm_c_random_bignum (scm_rstate *state, SCM m)
260 {
261 SCM b;
262 int i, nd;
263 LONG32 *bits, mask, w;
264 nd = SCM_NUMDIGS (m);
265 /* calculate mask for most significant digit */
266 #if SIZEOF_INT == 4
267 /* 16 bit digits */
268 if (nd & 1)
269 {
270 /* fix most significant 16 bits */
271 unsigned short s = SCM_BDIGITS (m)[nd - 1];
272 mask = s < 0x100 ? scm_masktab[s] : scm_masktab[s >> 8] << 8 | 0xff;
273 }
274 else
275 #endif
276 {
277 /* fix most significant 32 bits */
278 #if SIZEOF_INT == 4
279 w = SCM_BDIGITS (m)[nd - 1] << 16 | SCM_BDIGITS (m)[nd - 2];
280 #else
281 w = SCM_BDIGITS (m)[nd - 1];
282 #endif
283 mask = (w < 0x10000
284 ? (w < 0x100
285 ? scm_masktab[w]
286 : scm_masktab[w >> 8] << 8 | 0xff)
287 : (w < 0x1000000
288 ? scm_masktab[w >> 16] << 16 | 0xffff
289 : scm_masktab[w >> 24] << 24 | 0xffffff));
290 }
291 b = scm_mkbig (nd, 0);
292 bits = (LONG32 *) SCM_BDIGITS (b);
293 do
294 {
295 i = nd;
296 /* treat most significant digit specially */
297 #if SIZEOF_INT == 4
298 /* 16 bit digits */
299 if (i & 1)
300 {
301 ((SCM_BIGDIG*) bits)[i - 1] = scm_the_rng.random_bits (state) & mask;
302 i /= 2;
303 }
304 else
305 #endif
306 {
307 /* fix most significant 32 bits */
308 #if SIZEOF_INT == 4
309 w = scm_the_rng.random_bits (state) & mask;
310 ((SCM_BIGDIG*) bits)[i - 2] = w & 0xffff;
311 ((SCM_BIGDIG*) bits)[i - 1] = w >> 16;
312 i = i / 2 - 1;
313 #else
314 i /= 2;
315 bits[--i] = scm_the_rng.random_bits (state) & mask;
316 #endif
317 }
318 /* now fill up the rest of the bignum */
319 while (i)
320 bits[--i] = scm_the_rng.random_bits (state);
321 b = scm_normbig (b);
322 if (SCM_INUMP (b))
323 return b;
324 } while (scm_bigcomp (b, m) <= 0);
325 return b;
326 }
327
328 /*
329 * Scheme level representation of random states.
330 */
331
332 long scm_tc16_rstate;
333
334 static SCM
335 make_rstate (scm_rstate *state)
336 {
337 SCM_RETURN_NEWSMOB (scm_tc16_rstate, state);
338 }
339
340 static scm_sizet
341 free_rstate (SCM rstate)
342 {
343 free (SCM_RSTATE (rstate));
344 return scm_the_rng.rstate_size;
345 }
346
347 /*
348 * Scheme level interface.
349 */
350
351 SCM_GLOBAL_VCELL_INIT (scm_var_random_state, "*random-state*", scm_seed_to_random_state (scm_makfrom0str ("URL:http://stat.fsu.edu/~geo/diehard.html")));
352
353 SCM_DEFINE (scm_random, "random", 1, 1, 0,
354 (SCM n, SCM state),
355 "Return a number in [0,N).\n"
356 "\n"
357 "Accepts a positive integer or real n and returns a \n"
358 "number of the same type between zero (inclusive) and \n"
359 "N (exclusive). The values returned have a uniform \n"
360 "distribution.\n"
361 "\n"
362 "The optional argument STATE must be of the type produced by\n"
363 "`seed->andom-state'. It defaults to the value of the variable\n"
364 "*random-state*. This object is used to maintain the state of\n"
365 "the pseudo-random-number generator and is altered as a side\n"
366 "effect of the random operation.\n"
367 "")
368 #define FUNC_NAME s_scm_random
369 {
370 if (SCM_UNBNDP (state))
371 state = SCM_CDR (scm_var_random_state);
372 SCM_VALIDATE_RSTATE (2,state);
373 if (SCM_INUMP (n))
374 {
375 unsigned long m = SCM_INUM (n);
376 SCM_ASSERT_RANGE (1,n,m > 0);
377 return SCM_MAKINUM (scm_c_random (SCM_RSTATE (state), m));
378 }
379 SCM_VALIDATE_NIM (1,n);
380 if (SCM_REALP (n))
381 return scm_makdbl (SCM_REALPART (n) * scm_c_uniform01 (SCM_RSTATE (state)),
382 0.0);
383 SCM_VALIDATE_SMOB (1, n, big);
384 return scm_c_random_bignum (SCM_RSTATE (state), n);
385 }
386 #undef FUNC_NAME
387
388 SCM_DEFINE (scm_copy_random_state, "copy-random-state", 0, 1, 0,
389 (SCM state),
390 "Return a copy of the random state STATE.")
391 #define FUNC_NAME s_scm_copy_random_state
392 {
393 if (SCM_UNBNDP (state))
394 state = SCM_CDR (scm_var_random_state);
395 SCM_VALIDATE_RSTATE (1,state);
396 return make_rstate (scm_the_rng.copy_rstate (SCM_RSTATE (state)));
397 }
398 #undef FUNC_NAME
399
400 SCM_DEFINE (scm_seed_to_random_state, "seed->random-state", 1, 0, 0,
401 (SCM seed),
402 "Return a new random state using SEED.")
403 #define FUNC_NAME s_scm_seed_to_random_state
404 {
405 if (SCM_NUMBERP (seed))
406 seed = scm_number_to_string (seed, SCM_UNDEFINED);
407 SCM_VALIDATE_STRING (1,seed);
408 return make_rstate (scm_c_make_rstate (SCM_ROCHARS (seed),
409 SCM_LENGTH (seed)));
410 }
411 #undef FUNC_NAME
412
413 SCM_DEFINE (scm_random_uniform, "random:uniform", 0, 1, 0,
414 (SCM state),
415 "Returns a uniformly distributed inexact real random number in [0,1).")
416 #define FUNC_NAME s_scm_random_uniform
417 {
418 if (SCM_UNBNDP (state))
419 state = SCM_CDR (scm_var_random_state);
420 SCM_VALIDATE_RSTATE (1,state);
421 return scm_makdbl (scm_c_uniform01 (SCM_RSTATE (state)), 0.0);
422 }
423 #undef FUNC_NAME
424
425 SCM_DEFINE (scm_random_normal, "random:normal", 0, 1, 0,
426 (SCM state),
427 "Returns an inexact real in a normal distribution.\n"
428 "The distribution used has mean 0 and standard deviation 1.\n"
429 "For a normal distribution with mean m and standard deviation\n"
430 "d use @code{(+ m (* d (random:normal)))}.\n"
431 "")
432 #define FUNC_NAME s_scm_random_normal
433 {
434 if (SCM_UNBNDP (state))
435 state = SCM_CDR (scm_var_random_state);
436 SCM_VALIDATE_RSTATE (1,state);
437 return scm_makdbl (scm_c_normal01 (SCM_RSTATE (state)), 0.0);
438 }
439 #undef FUNC_NAME
440
441 #ifdef HAVE_ARRAYS
442
443 static void
444 vector_scale (SCM v, double c)
445 {
446 int n = SCM_LENGTH (v);
447 if (SCM_VECTORP (v))
448 while (--n >= 0)
449 SCM_REAL (SCM_VELTS (v)[n]) *= c;
450 else
451 while (--n >= 0)
452 ((double *) SCM_VELTS (v))[n] *= c;
453 }
454
455 static double
456 vector_sum_squares (SCM v)
457 {
458 double x, sum = 0.0;
459 int n = SCM_LENGTH (v);
460 if (SCM_VECTORP (v))
461 while (--n >= 0)
462 {
463 x = SCM_REAL (SCM_VELTS (v)[n]);
464 sum += x * x;
465 }
466 else
467 while (--n >= 0)
468 {
469 x = ((double *) SCM_VELTS (v))[n];
470 sum += x * x;
471 }
472 return sum;
473 }
474
475 /* For the uniform distribution on the solid sphere, note that in
476 * this distribution the length r of the vector has cumulative
477 * distribution r^n; i.e., u=r^n is uniform [0,1], so r can be
478 * generated as r=u^(1/n).
479 */
480 SCM_DEFINE (scm_random_solid_sphere_x, "random:solid-sphere!", 1, 1, 0,
481 (SCM v, SCM state),
482 "Fills vect with inexact real random numbers\n"
483 "the sum of whose squares is less than 1.0.\n"
484 "Thinking of vect as coordinates in space of \n"
485 "dimension n = (vector-length vect), the coordinates \n"
486 "are uniformly distributed within the unit n-shere.\n"
487 "The sum of the squares of the numbers is returned.\n"
488 "")
489 #define FUNC_NAME s_scm_random_solid_sphere_x
490 {
491 SCM_VALIDATE_VECTOR_OR_DVECTOR (1,v);
492 if (SCM_UNBNDP (state))
493 state = SCM_CDR (scm_var_random_state);
494 SCM_VALIDATE_RSTATE (2,state);
495 scm_random_normal_vector_x (v, state);
496 vector_scale (v,
497 pow (scm_c_uniform01 (SCM_RSTATE (state)),
498 1.0 / SCM_LENGTH (v))
499 / sqrt (vector_sum_squares (v)));
500 return SCM_UNSPECIFIED;
501 }
502 #undef FUNC_NAME
503
504 SCM_DEFINE (scm_random_hollow_sphere_x, "random:hollow-sphere!", 1, 1, 0,
505 (SCM v, SCM state),
506 "Fills vect with inexact real random numbers\n"
507 "the sum of whose squares is equal to 1.0.\n"
508 "Thinking of vect as coordinates in space of \n"
509 "dimension n = (vector-length vect), the coordinates\n"
510 "are uniformly distributed over the surface of the \n"
511 "unit n-shere.\n"
512 "")
513 #define FUNC_NAME s_scm_random_hollow_sphere_x
514 {
515 SCM_VALIDATE_VECTOR_OR_DVECTOR (1,v);
516 if (SCM_UNBNDP (state))
517 state = SCM_CDR (scm_var_random_state);
518 SCM_VALIDATE_RSTATE (2,state);
519 scm_random_normal_vector_x (v, state);
520 vector_scale (v, 1 / sqrt (vector_sum_squares (v)));
521 return SCM_UNSPECIFIED;
522 }
523 #undef FUNC_NAME
524
525
526 SCM_DEFINE (scm_random_normal_vector_x, "random:normal-vector!", 1, 1, 0,
527 (SCM v, SCM state),
528 "Fills vect with inexact real random numbers that are\n"
529 "independent and standard normally distributed\n"
530 "(i.e., with mean 0 and variance 1).\n"
531 "")
532 #define FUNC_NAME s_scm_random_normal_vector_x
533 {
534 int n;
535 SCM_VALIDATE_VECTOR_OR_DVECTOR (1,v);
536 if (SCM_UNBNDP (state))
537 state = SCM_CDR (scm_var_random_state);
538 SCM_VALIDATE_RSTATE (2,state);
539 n = SCM_LENGTH (v);
540 if (SCM_VECTORP (v))
541 while (--n >= 0)
542 SCM_VELTS (v)[n] = scm_makdbl (scm_c_normal01 (SCM_RSTATE (state)), 0.0);
543 else
544 while (--n >= 0)
545 ((double *) SCM_VELTS (v))[n] = scm_c_normal01 (SCM_RSTATE (state));
546 return SCM_UNSPECIFIED;
547 }
548 #undef FUNC_NAME
549
550 #endif /* HAVE_ARRAYS */
551
552 SCM_DEFINE (scm_random_exp, "random:exp", 0, 1, 0,
553 (SCM state),
554 "Returns an inexact real in an exponential distribution with mean 1.\n"
555 "For an exponential distribution with mean u use (* u (random:exp)).\n"
556 "")
557 #define FUNC_NAME s_scm_random_exp
558 {
559 if (SCM_UNBNDP (state))
560 state = SCM_CDR (scm_var_random_state);
561 SCM_VALIDATE_RSTATE (1,state);
562 return scm_makdbl (scm_c_exp1 (SCM_RSTATE (state)), 0.0);
563 }
564 #undef FUNC_NAME
565
566 void
567 scm_init_random ()
568 {
569 int i, m;
570 /* plug in default RNG */
571 scm_rng rng =
572 {
573 sizeof (scm_i_rstate),
574 (unsigned long (*)()) scm_i_uniform32,
575 (void (*)()) scm_i_init_rstate,
576 (scm_rstate *(*)()) scm_i_copy_rstate
577 };
578 scm_the_rng = rng;
579
580 scm_tc16_rstate = scm_make_smob_type_mfpe ("random-state", 0,
581 NULL, free_rstate, NULL, NULL);
582
583 for (m = 1; m <= 0x100; m <<= 1)
584 for (i = m >> 1; i < m; ++i)
585 scm_masktab[i] = m - 1;
586
587 #include "random.x"
588
589 /* Check that the assumptions about bits per bignum digit are correct. */
590 #if SIZEOF_INT == 4
591 m = 16;
592 #else
593 m = 32;
594 #endif
595 if (m != SCM_BITSPERDIG)
596 {
597 fprintf (stderr, "Internal inconsistency: Confused about bignum digit size in random.c\n");
598 exit (1);
599 }
600
601 scm_add_feature ("random");
602 }