d65def935fe4acd1aceff76056d4a91855c6c657
[bpt/guile.git] / libguile / random.c
1 /* Copyright (C) 1999 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 /* Author: Mikael Djurfeldt <djurfeldt@nada.kth.se> */
42
43 #include "_scm.h"
44
45 #include <math.h>
46 #include "genio.h"
47 #include "smob.h"
48 #include "numbers.h"
49 #include "feature.h"
50
51 #include "random.h"
52
53 \f
54 /*
55 * A plugin interface for RNGs
56 *
57 * Using this interface, it is possible for the application to tell
58 * libguile to use a different RNG. This is desirable if it is
59 * necessary to use the same RNG everywhere in the application in
60 * order to prevent interference, if the application uses RNG
61 * hardware, or if the application has special demands on the RNG.
62 *
63 * Look in random.h and how the default generator is "plugged in" in
64 * scm_init_random().
65 */
66
67 scm_rng scm_the_rng;
68
69 \f
70 /*
71 * The prepackaged RNG
72 *
73 * This is the MWC (Multiply With Carry) random number generator
74 * described by George Marsaglia at the Department of Statistics and
75 * Supercomputer Computations Research Institute, The Florida State
76 * University (http://stat.fsu.edu/~geo).
77 *
78 * It uses 64 bits, has a period of 4578426017172946943 (4.6e18), and
79 * passes all tests in the DIEHARD test suite
80 * (http://stat.fsu.edu/~geo/diehard.html)
81 */
82
83 #define A 2131995753UL
84
85 #if SIZEOF_LONG > 4
86 #if SIZEOF_INT > 4
87 #define LONG32 unsigned short
88 #else
89 #define LONG32 unsigned int
90 #endif
91 #define LONG64 unsigned long
92 #else
93 #define LONG32 unsigned long
94 #define LONG64 unsigned long long
95 #endif
96
97 #if SIZEOF_LONG > 4 || defined (HAVE_LONG_LONGS)
98
99 unsigned long
100 scm_i_uniform32 (scm_i_rstate *state)
101 {
102 LONG64 x = (LONG64) A * state->w + state->c;
103 LONG32 w = x & 0xffffffffUL;
104 state->w = w;
105 state->c = x >> 32L;
106 return w;
107 }
108
109 #else
110
111 /* ww This is a portable version of the same RNG without 64 bit
112 * * aa arithmetic.
113 * ----
114 * xx It is only intended to provide identical behaviour on
115 * xx platforms without 8 byte longs or long longs until
116 * xx someone has implemented the routine in assembler code.
117 * xxcc
118 * ----
119 * ccww
120 */
121
122 #define L(x) ((x) & 0xffff)
123 #define H(x) ((x) >> 16)
124
125 unsigned long
126 scm_i_uniform32 (scm_i_rstate *state)
127 {
128 LONG32 x1 = L (A) * L (state->w);
129 LONG32 x2 = L (A) * H (state->w);
130 LONG32 x3 = H (A) * L (state->w);
131 LONG32 w = L (x1) + L (state->c);
132 LONG32 m = H (x1) + L (x2) + L (x3) + H (state->c) + H (w);
133 LONG32 x4 = H (A) * H (state->w);
134 state->w = w = (L (m) << 16) + L (w);
135 state->c = H (x2) + H (x3) + x4 + H (m);
136 return w;
137 }
138
139 #endif
140
141 void
142 scm_i_init_rstate (scm_i_rstate *state, char *seed, int n)
143 {
144 LONG32 w = 0L;
145 LONG32 c = 0L;
146 int i, m;
147 for (i = 0; i < n; ++i)
148 {
149 m = i % 8;
150 if (m < 4)
151 w += seed[i] << (8 * m);
152 else
153 c += seed[i] << (8 * (m - 4));
154 }
155 if ((w == 0 && c == 0) || (w == 0xffffffffUL && c == A - 1))
156 ++c;
157 state->w = w;
158 state->c = c;
159 }
160
161 scm_i_rstate *
162 scm_i_copy_rstate (scm_i_rstate *state)
163 {
164 scm_rstate *new_state = malloc (scm_the_rng.rstate_size);
165 if (new_state == 0)
166 scm_wta (SCM_MAKINUM (scm_the_rng.rstate_size),
167 (char *) SCM_NALLOC, "rstate");
168 return memcpy (new_state, state, scm_the_rng.rstate_size);
169 }
170
171 \f
172 /*
173 * Random number library functions
174 */
175
176 inline double
177 scm_i_uniform01 (scm_rstate *state)
178 {
179 double x = (double) scm_the_rng.random_bits (state) / (double) 0xffffffffUL;
180 return ((x + (double) scm_the_rng.random_bits (state))
181 / (double) 0xffffffffUL);
182 }
183
184 double
185 scm_i_normal01 (scm_rstate *state)
186 {
187 if (state->reserved0)
188 {
189 state->reserved0 = 0;
190 return state->reserved1;
191 }
192 else
193 {
194 double r, a, n;
195
196 r = sqrt (-2.0 * log (scm_i_uniform01 (state)));
197 a = 2.0 * M_PI * scm_i_uniform01 (state);
198
199 n = r * sin (a);
200 state->reserved1 = r * cos (a);
201 state->reserved0 = 1;
202
203 return n;
204 }
205 }
206
207 double
208 scm_i_exp1 (scm_rstate *state)
209 {
210 return - log (scm_i_uniform01 (state));
211 }
212
213 unsigned char scm_masktab[256];
214
215 unsigned long
216 scm_i_random (unsigned long m, scm_rstate *state)
217 {
218 unsigned int r, mask;
219 mask = (m < 0x100
220 ? scm_masktab[m]
221 : (m < 0x10000
222 ? scm_masktab[m >> 8] << 8 | 0xff
223 : (m < 0x1000000
224 ? scm_masktab[m >> 16] << 16 | 0xffff
225 : scm_masktab[m >> 24] << 24 | 0xffffff)));
226 while ((r = scm_the_rng.random_bits (state) & mask) >= m);
227 return r;
228 }
229
230 SCM
231 scm_i_random_bignum (SCM m, scm_rstate *state)
232 {
233 SCM b;
234 int i, nd;
235 LONG32 *bits, mask, w;
236 nd = SCM_NUMDIGS (m);
237 /* calculate mask for most significant digit */
238 #if SIZEOF_INT == 4
239 /* 16 bit digits */
240 if (nd & 1)
241 {
242 /* fix most significant 16 bits */
243 unsigned short s = SCM_BDIGITS (m)[nd - 1];
244 mask = s < 0x100 ? scm_masktab[s] : scm_masktab[s >> 8] << 8 | 0xff;
245 }
246 else
247 #endif
248 {
249 /* fix most significant 32 bits */
250 #if SIZEOF_INT == 4 && defined (WORDS_BIGENDIAN)
251 w = SCM_BDIGITS (m)[nd - 1] << 16 | SCM_BDIGITS (m)[nd - 2];
252 #else
253 w = ((LONG32 *) SCM_BDIGITS (m))[nd / 2 - 1];
254 #endif
255 mask = (w < 0x10000
256 ? (w < 0x100
257 ? scm_masktab[w]
258 : scm_masktab[w >> 8] << 8 | 0xff)
259 : (w < 0x1000000
260 ? scm_masktab[w >> 16] << 16 | 0xffff
261 : scm_masktab[w >> 24] << 24 | 0xffffff));
262 }
263 b = scm_mkbig (nd, 0);
264 bits = (LONG32 *) SCM_BDIGITS (b);
265 do
266 {
267 i = nd;
268 /* treat most significant digit specially */
269 #if SIZEOF_INT == 4
270 /* 16 bit digits */
271 if (i & 1)
272 {
273 ((SCM_BIGDIG*) bits)[i - 1] = scm_the_rng.random_bits (state) & mask;
274 i /= 2;
275 }
276 else
277 #endif
278 {
279 /* fix most significant 32 bits */
280 i /= 2;
281 #if SIZEOF_INT == 4 && defined (WORDS_BIGENDIAN)
282 w = scm_the_rng.random_bits (state) & mask;
283 bits[--i] = (w & 0xffff) << 16 | w >> 16;
284 #else
285 bits[--i] = scm_the_rng.random_bits (state) & mask;
286 #endif
287 }
288 /* now fill up the rest of the bignum */
289 while (i)
290 bits[--i] = scm_the_rng.random_bits (state);
291 b = scm_normbig (b);
292 if (SCM_INUMP (b))
293 return b;
294 } while (scm_bigcomp (b, m) <= 0);
295 return b;
296 }
297
298 /*
299 * Scheme level representation of random states.
300 */
301
302 long scm_tc16_rstate;
303
304 static SCM
305 make_rstate (scm_rstate *state)
306 {
307 SCM cell;
308 SCM_NEWCELL (cell);
309 SCM_ENTER_A_SECTION;
310 SCM_SETCDR (cell, state);
311 SCM_SETCAR (cell, scm_tc16_rstate);
312 SCM_EXIT_A_SECTION;
313 return cell;
314 }
315
316 static int
317 print_rstate (SCM rstate, SCM port, scm_print_state *pstate)
318 {
319 scm_puts ("#<random-state ", port);
320 scm_intprint ((long) SCM_RSTATE (rstate), 16, port);
321 scm_putc ('>', port);
322 return 1;
323 }
324
325 static scm_sizet
326 free_rstate (SCM rstate)
327 {
328 free (SCM_RSTATE (rstate));
329 return scm_the_rng.rstate_size;
330 }
331
332 static scm_smobfuns rstate_smob = { 0, free_rstate, print_rstate, 0};
333
334 /*
335 * Scheme level interface.
336 */
337
338 SCM_GLOBAL_VCELL_INIT (scm_var_random_state, "*random-state*", scm_make_random_state (scm_makfrom0str ("URL:http://stat.fsu.edu/~geo/diehard.html")));
339
340 SCM_PROC (s_random, "random", 1, 1, 0, scm_random);
341
342 SCM
343 scm_random (SCM n, SCM state)
344 {
345 if (SCM_UNBNDP (state))
346 state = SCM_CDR (scm_var_random_state);
347 SCM_ASSERT (SCM_NIMP (state) && SCM_RSTATEP (state),
348 state, SCM_ARG2, s_random);
349 if (SCM_INUMP (n))
350 {
351 unsigned long m = SCM_INUM (n);
352 SCM_ASSERT (m > 0, n, SCM_ARG1, s_random);
353 return SCM_MAKINUM (scm_i_random (m, SCM_RSTATE (state)));
354 }
355 SCM_ASSERT (SCM_NIMP (n), n, SCM_ARG1, s_random);
356 if (SCM_REALP (n))
357 return scm_makdbl (SCM_REALPART (n) * scm_i_uniform01 (SCM_RSTATE (state)),
358 0.0);
359 SCM_ASSERT (SCM_TYP16 (n) == scm_tc16_bigpos, n, SCM_ARG1, s_random);
360 return scm_i_random_bignum (n, SCM_RSTATE (state));
361 }
362
363 SCM_PROC (s_make_random_state, "make-random-state", 0, 1, 0, scm_make_random_state);
364
365 SCM
366 scm_make_random_state (SCM state)
367 {
368 if (SCM_UNBNDP (state))
369 {
370 state = SCM_CDR (scm_var_random_state);
371 goto copy_state;
372 }
373 else if (SCM_NUMBERP (state))
374 {
375 state = scm_number_to_string (state, SCM_UNDEFINED);
376 goto seed;
377 }
378 else if (SCM_NIMP (state) && SCM_STRINGP (state))
379 seed:
380 {
381 scm_rstate *nstate = malloc (scm_the_rng.rstate_size);
382 if (nstate == 0)
383 scm_wta (SCM_MAKINUM (scm_the_rng.rstate_size),
384 (char *) SCM_NALLOC,
385 "rstate");
386 nstate->reserved0 = 0;
387 scm_the_rng.init_rstate (nstate, SCM_ROCHARS (state), SCM_LENGTH (state));
388 return make_rstate (nstate);
389 }
390 copy_state:
391 SCM_ASSERT (SCM_NIMP (state) && SCM_RSTATEP (state),
392 state,
393 SCM_ARG1,
394 s_make_random_state);
395 return make_rstate (scm_the_rng.copy_rstate (SCM_RSTATE (state)));
396 }
397
398 SCM_PROC (s_random_uniform, "random:uniform", 0, 1, 0, scm_random_uniform);
399
400 SCM
401 scm_random_uniform (SCM state)
402 {
403 if (SCM_UNBNDP (state))
404 state = SCM_CDR (scm_var_random_state);
405 SCM_ASSERT (SCM_NIMP (state) && SCM_RSTATEP (state),
406 state,
407 SCM_ARG1,
408 s_random_uniform);
409 return scm_makdbl (scm_i_uniform01 (SCM_RSTATE (state)), 0.0);
410 }
411
412 static void
413 vector_scale (SCM v, double c)
414 {
415 int n = SCM_LENGTH (v);
416 if (SCM_VECTORP (v))
417 while (--n >= 0)
418 SCM_REAL (SCM_VELTS (v)[n]) *= c;
419 else
420 while (--n >= 0)
421 ((double *) SCM_VELTS (v))[n] *= c;
422 }
423
424 static double
425 vector_sum_squares (SCM v)
426 {
427 double x, sum = 0.0;
428 int n = SCM_LENGTH (v);
429 if (SCM_VECTORP (v))
430 while (--n >= 0)
431 {
432 x = SCM_REAL (SCM_VELTS (v)[n]);
433 sum += x * x;
434 }
435 else
436 while (--n >= 0)
437 {
438 x = ((double *) SCM_VELTS (v))[n];
439 sum += x * x;
440 }
441 return sum;
442 }
443
444 SCM_PROC (s_random_solid_sphere_x, "random:solid-sphere!", 1, 1, 0, scm_random_solid_sphere_x);
445
446 /* For the uniform distribution on the solid sphere, note that in
447 * this distribution the length r of the vector has cumulative
448 * distribution r^n; i.e., u=r^n is uniform [0,1], so r can be
449 * generated as r=u^(1/n).
450 */
451 SCM
452 scm_random_solid_sphere_x (SCM v, SCM state)
453 {
454 SCM_ASSERT (SCM_NIMP (v)
455 && (SCM_VECTORP (v) || SCM_TYP7 (v) == scm_tc7_dvect),
456 v, SCM_ARG1, s_random_solid_sphere_x);
457 if (SCM_UNBNDP (state))
458 state = SCM_CDR (scm_var_random_state);
459 SCM_ASSERT (SCM_NIMP (state) && SCM_RSTATEP (state),
460 state,
461 SCM_ARG2,
462 s_random_solid_sphere_x);
463 scm_random_normal_vector_x (v, state);
464 vector_scale (v,
465 pow (scm_i_uniform01 (SCM_RSTATE (state)),
466 1.0 / SCM_LENGTH (v))
467 / sqrt (vector_sum_squares (v)));
468 return SCM_UNSPECIFIED;
469 }
470
471 SCM_PROC (s_random_hollow_sphere_x, "random:hollow-sphere!", 1, 1, 0, scm_random_hollow_sphere_x);
472
473 SCM
474 scm_random_hollow_sphere_x (SCM v, SCM state)
475 {
476 SCM_ASSERT (SCM_NIMP (v)
477 && (SCM_VECTORP (v) || SCM_TYP7 (v) == scm_tc7_dvect),
478 v, SCM_ARG1, s_random_solid_sphere_x);
479 if (SCM_UNBNDP (state))
480 state = SCM_CDR (scm_var_random_state);
481 SCM_ASSERT (SCM_NIMP (state) && SCM_RSTATEP (state),
482 state,
483 SCM_ARG2,
484 s_random_hollow_sphere_x);
485 scm_random_normal_vector_x (v, state);
486 vector_scale (v, 1 / sqrt (vector_sum_squares (v)));
487 return SCM_UNSPECIFIED;
488 }
489
490 SCM_PROC (s_random_normal, "random:normal", 0, 1, 0, scm_random_normal);
491
492 SCM
493 scm_random_normal (SCM state)
494 {
495 if (SCM_UNBNDP (state))
496 state = SCM_CDR (scm_var_random_state);
497 SCM_ASSERT (SCM_NIMP (state) && SCM_RSTATEP (state),
498 state,
499 SCM_ARG1,
500 s_random_normal);
501 return scm_makdbl (scm_i_normal01 (SCM_RSTATE (state)), 0.0);
502 }
503
504 SCM_PROC (s_random_normal_vector_x, "random:normal-vector!", 1, 1, 0, scm_random_normal_vector_x);
505
506 SCM
507 scm_random_normal_vector_x (SCM v, SCM state)
508 {
509 int n;
510 SCM_ASSERT (SCM_NIMP (v)
511 && (SCM_VECTORP (v) || SCM_TYP7 (v) == scm_tc7_dvect),
512 v, SCM_ARG1, s_random_solid_sphere_x);
513 if (SCM_UNBNDP (state))
514 state = SCM_CDR (scm_var_random_state);
515 SCM_ASSERT (SCM_NIMP (state) && SCM_RSTATEP (state),
516 state,
517 SCM_ARG2,
518 s_random_normal_vector_x);
519 n = SCM_LENGTH (v);
520 if (SCM_VECTORP (v))
521 while (--n >= 0)
522 SCM_VELTS (v)[n] = scm_makdbl (scm_i_normal01 (SCM_RSTATE (state)), 0.0);
523 else
524 while (--n >= 0)
525 ((double *) SCM_VELTS (v))[n] = scm_i_normal01 (SCM_RSTATE (state));
526 return SCM_UNSPECIFIED;
527 }
528
529 SCM_PROC (s_random_exp, "random:exp", 0, 1, 0, scm_random_exp);
530
531 SCM
532 scm_random_exp (SCM state)
533 {
534 if (SCM_UNBNDP (state))
535 state = SCM_CDR (scm_var_random_state);
536 SCM_ASSERT (SCM_NIMP (state) && SCM_RSTATEP (state),
537 state,
538 SCM_ARG1,
539 s_random_exp);
540 return scm_makdbl (scm_i_exp1 (SCM_RSTATE (state)), 0.0);
541 }
542
543 void
544 scm_init_random ()
545 {
546 int i, m;
547 /* plug in default RNG */
548 scm_rng rng =
549 {
550 sizeof (scm_i_rstate),
551 (unsigned long (*)()) scm_i_uniform32,
552 (void (*)()) scm_i_init_rstate,
553 (scm_rstate *(*)()) scm_i_copy_rstate
554 };
555 scm_the_rng = rng;
556
557 scm_tc16_rstate = scm_newsmob (&rstate_smob);
558
559 for (m = 1; m <= 0x100; m <<= 1)
560 for (i = m >> 1; i < m; ++i)
561 scm_masktab[i] = m - 1;
562
563 #include "random.x"
564
565 /* Check that the assumptions about bits per bignum digit are correct. */
566 #if SIZEOF_INT == 4
567 m = 16;
568 #else
569 m = 32;
570 #endif
571 if (m != SCM_BITSPERDIG)
572 {
573 fprintf (stderr, "Internal inconsistency: Confused about bignum digit size in random.c\n");
574 exit (1);
575 }
576
577 scm_add_feature ("random");
578 }