Remove some code duplication in TRANSLATE-PREDICATE
[maxima.git] / src / rand-mt19937.lisp
blob9928a22de116c6f9a5b6f5ef7fbc7a5bbd2a8306
1 ;;; Mersenne Twister MT19937, adapted from CMUCL rand-mt19937.lisp -r1.11 (2003/03/06)
3 ;;; CMUCL version by Douglas T. Crosher and Raymond Toy based
4 ;;; on public domain code from Carnegie Mellon University.
5 ;;; Modified for Maxima by Robert Dodier.
6 ;;; (1) Construct floating point numbers using portable operations.
7 ;;; (2) Construct large integers using all bits of each chunk.
9 ;;; Begin MT19937 implementation.
10 ;;; **********************************************************************
11 ;;;
12 ;;; Support for the Mersenne Twister, MT19937, random number generator
13 ;;; due to Matsumoto and Nishimura. This implementation has been
14 ;;; placed in the public domain with permission from M. Matsumoto.
15 ;;;
16 ;;; Makoto Matsumoto and T. Nishimura, "Mersenne twister: A
17 ;;; 623-dimensionally equidistributed uniform pseudorandom number
18 ;;; generator.", ACM Transactions on Modeling and Computer Simulation,
19 ;;; 1997, to appear.
21 (in-package :mt19937)
23 (defconstant mt19937-n 624)
24 (defconstant mt19937-m 397)
25 (defconstant mt19937-upper-mask #x80000000)
26 (defconstant mt19937-lower-mask #x7fffffff)
27 (defconstant mt19937-b #x9D2C5680)
28 (defconstant mt19937-c #xEFC60000)
29 ;;;
30 ;;;; Random state hackery:
32 ;;; The state is stored in a (simple-array (unsigned-byte 32) (627))
33 ;;; wrapped in a random-state structure:
34 ;;;
35 ;;; 0-1: Constant matrix A. [0, #x9908b0df]
36 ;;; 2: Index k.
37 ;;; 3-626: State.
39 ;; GENERATE-SEED
41 ;; Generate a random seed that can be used for seeding the generator.
42 ;; The current time is used as the seed.
44 (defun generate-seed ()
45 (logand (get-universal-time) #xffffffff))
47 ;; New initializer proposed by Takuji Nishimura and Makota Matsumoto.
48 ;; (See http://www.math.keio.ac.jp/~matumoto/MT2002/emt19937ar.html)
50 ;; This corrects a deficiency in the original initializer wherein the
51 ;; MSB of the seed was not well represented in the state.
53 ;; The initialization routine is described below. Let s be the seed,
54 ;; mt[] be the state vector. Then the algorithm is
56 ;; mt[0] = s & 0xffffffffUL
58 ;; for (k = 1; k < N; k++) {
59 ;; mt[k] = 1812433253 * (mt[k-1] ^ (mt[k-1] >> 30)) + k
60 ;; mt[k] &= 0xffffffffUL
61 ;; }
63 ;; The multiplier is from Knuth TAOCP Vol2, 3rd Ed., p. 106.
66 (defun int-init-random-state (&optional (seed 5489) state)
67 (declare (type (integer 0 #xffffffff) seed))
68 (let ((state (or state (make-array 627 :element-type '(unsigned-byte 32)))))
69 (declare (type (simple-array (unsigned-byte 32) (627)) state))
70 (setf (aref state 0) 0)
71 (setf (aref state 1) #x9908b0df)
72 (setf (aref state 2) mt19937-n)
73 (setf (aref state 3) seed)
74 (do ((k 1 (1+ k)))
75 ((>= k 624))
76 (declare (type (mod 625) k))
77 (let ((prev (aref state (+ 3 (1- k)))))
78 (setf (aref state (+ 3 k))
79 (logand (+ (* 1812433253 (logxor prev (ash prev -30)))
81 #xffffffff))))
82 state))
84 ;; Initialize from an array.
86 ;; Here is the algorithm, in C. init_genrand is the initalizer above,
87 ;; init_key is the seed vector of length key_length.
89 ;; init_genrand(19650218UL);
90 ;; i=1; j=0;
91 ;; k = (N>key_length ? N : key_length);
92 ;; for (; k; k--) {
93 ;; mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL))
94 ;; + init_key[j] + j; /* non linear */
95 ;; mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
96 ;; i++; j++;
97 ;; if (i>=N) {
98 ;; mt[0] = mt[N-1]; i=1;
99 ;; }
100 ;; if (j>=key_length) {
101 ;; j=0;
102 ;; }
103 ;; }
104 ;; for (k=N-1; k; k--) {
105 ;; mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL))
106 ;; - i; /* non linear */
107 ;; mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
108 ;; i++;
109 ;; if (i>=N) { mt[0] = mt[N-1]; i=1; }
110 ;; }
112 ;; mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */
115 (defun vec-init-random-state (key &optional state)
116 (declare (type (array (unsigned-byte 32) (*)) key))
117 (let ((key-len (length key))
118 (state (init-random-state 19650218 state))
119 (i 1)
120 (j 0))
121 (loop for k from (max key-len mt19937-n) above 0 do
122 (let ((prev (aref state (+ 3 (1- i)))))
123 (setf (aref state (+ 3 i))
124 (ldb (byte 32 0)
125 (+ (aref key j) j
126 (logxor (aref state (+ 3 i))
127 (ldb (byte 32 0)
128 (* 1664525
129 (logxor prev (ash prev -30))))))))
130 (incf i)
131 (incf j)
132 (when (>= i mt19937-n)
133 (setf (aref state 3)
134 (aref state (+ 3 (- mt19937-n 1))))
135 (setf i 1))
136 (when (>= j key-len)
137 (setf j 0))))
139 (loop for k from (1- mt19937-n) above 0 do
140 (let ((prev (aref state (+ 3 (1- i)))))
141 (setf (aref state (+ 3 i))
142 (ldb (byte 32 0)
143 (- (logxor (aref state (+ 3 i))
144 (* 1566083941
145 (logxor prev (ash prev -30))))
146 i)))
147 (incf i)
148 (when (>= i mt19937-n)
149 (setf (aref state 3)
150 (aref state (+ 3 (- mt19937-n 1))))
151 (setf i 1))))
152 (setf (aref state 3) #x80000000)
153 state))
156 (defun init-random-state (&optional (seed 5489) state)
157 "Generate an random state vector from the given SEED. The seed can be
158 either an integer or a vector of (unsigned-byte 32)"
159 (declare (type (or null integer
160 (array (unsigned-byte 32) (*)))
161 seed))
162 (etypecase seed
163 (integer
164 (int-init-random-state (ldb (byte 32 0) seed) state))
165 ((array (unsigned-byte 32) (*))
166 (vec-init-random-state seed state))))
168 (defstruct (random-state
169 (:constructor make-random-object))
170 (state (init-random-state) :type (simple-array (unsigned-byte 32) (627))))
172 (defvar *random-state* (make-random-object))
174 (defun make-random-state (&optional state)
175 "Make a random state object. If STATE is not supplied, return a copy
176 of the default random state. If STATE is a random state, then return a
177 copy of STATE. If STATE is T then return a random state generated from
178 the universal time. To make a random state from an integer seed, try
179 ``(make-random-object :state (init-random-state <seed>))''."
180 (flet ((copy-random-state (state)
181 (let ((state (random-state-state state))
182 (new-state
183 (make-array 627 :element-type '(unsigned-byte 32))))
184 (dotimes (i 627)
185 (setf (aref new-state i) (aref state i)))
186 (make-random-object :state new-state))))
187 (cond ((not state) (copy-random-state *random-state*))
188 ((random-state-p state) (copy-random-state state))
189 ((eq state t)
190 (make-random-object :state (init-random-state (generate-seed))))
191 (t (error (intl:gettext "make_random_state: argument must be a random state object, or true, or false; found: ~S") state)))))
193 ;;;; Random entries:
195 ;;; Size of the chunks returned by random-chunk.
197 (defconstant random-chunk-length 32)
199 ;;; random-chunk -- Internal
201 ;;; This function generaters a 32bit integer between 0 and #xffffffff
202 ;;; inclusive.
204 (declaim (inline random-chunk))
206 ;;; Portable implementation.
207 (defun random-mt19937-update (state)
208 (declare (type (simple-array (unsigned-byte 32) (627)) state)
209 (optimize (speed 3) (safety 0)))
210 (let ((y 0))
211 (declare (type (unsigned-byte 32) y))
212 (do ((kk 3 (1+ kk)))
213 ((>= kk (+ 3 (- mt19937-n mt19937-m))))
214 (declare (type (mod 628) kk))
215 (setf y (logior (logand (aref state kk) mt19937-upper-mask)
216 (logand (aref state (1+ kk)) mt19937-lower-mask)))
217 (setf (aref state kk) (logxor (aref state (+ kk mt19937-m))
218 (ash y -1) (aref state (logand y 1)))))
219 (do ((kk (+ (- mt19937-n mt19937-m) 3) (1+ kk)))
220 ((>= kk (+ (1- mt19937-n) 3)))
221 (declare (type (mod 628) kk))
222 (setf y (logior (logand (aref state kk) mt19937-upper-mask)
223 (logand (aref state (1+ kk)) mt19937-lower-mask)))
224 (setf (aref state kk) (logxor (aref state (+ kk (- mt19937-m mt19937-n)))
225 (ash y -1) (aref state (logand y 1)))))
226 (setf y (logior (logand (aref state (+ 3 (1- mt19937-n)))
227 mt19937-upper-mask)
228 (logand (aref state 3) mt19937-lower-mask)))
229 (setf (aref state (+ 3 (1- mt19937-n)))
230 (logxor (aref state (+ 3 (1- mt19937-m)))
231 (ash y -1) (aref state (logand y 1)))))
232 (values))
234 (defun random-chunk (state)
235 (declare (type random-state state)
236 (optimize (speed 3) (safety 0)))
237 (let* ((state (random-state-state state))
238 (k (aref state 2)))
239 (declare (type (mod 628) k))
240 (when (= k mt19937-n)
241 (random-mt19937-update state)
242 (setf k 0))
243 (setf (aref state 2) (1+ k))
244 (let ((y (aref state (+ 3 k))))
245 (declare (type (unsigned-byte 32) y))
246 (setf y (logxor y (ash y -11)))
247 (setf y (logxor y (ash (logand y (ash mt19937-b -7)) 7)))
248 (setf y (logxor y (ash (logand y (ash mt19937-c -15)) 15)))
249 (setf y (logxor y (ash y -18)))
250 y)))
252 ;;; %RANDOM-SINGLE-FLOAT, %RANDOM-DOUBLE-FLOAT -- Interface
254 (declaim (inline %random-single-float %random-double-float
255 #+(or scl clisp) %random-long-float
256 #+(and cmu double-double) %random-double-double-float))
258 (declaim (ftype (function ((single-float (0f0)) random-state)
259 (single-float 0f0))
260 %random-single-float))
262 (declaim (ftype (function ((double-float (0d0)) random-state)
263 (double-float 0d0))
264 %random-double-float))
266 #+(or scl clisp)
267 (declaim (ftype (function ((long-float (0l0)) random-state)
268 (long-float 0l0))
269 %random-long-float))
271 #+(and cmu double-double)
272 (declaim (ftype (function ((kernel:double-double-float (0w0)) random-state)
273 (kernel:double-double-float 0w0))
274 %random-double-double-float))
277 (defun %random-single-float (arg state)
278 "Handle the single or double float case of RANDOM. We generate a float
279 in [0f0, 1f0) by clobbering the mantissa of 1f0 with random bits (23 bits);
280 this yields a number in [1f0, 2f0). Then 1f0 is subtracted."
281 (let*
282 ((random-mantissa-bits (%random-integer (expt 2 23) state))
283 (random-unit-float (- (scale-float (float (+ (expt 2 23) random-mantissa-bits) 1f0) -23) 1f0)))
284 (* arg random-unit-float)))
286 (defun %random-double-float (arg state)
287 "Handle the single or double float case of RANDOM. We generate a float
288 in [0d0, 1d0) by clobbering the mantissa of 1d0 with random bits (52 bits);
289 this yields a number in [1d0, 2d0). Then 1d0 is subtracted."
290 (let*
291 ((random-mantissa-bits (%random-integer (expt 2 52) state))
292 (random-unit-double (- (scale-float (float (+ (expt 2 52) random-mantissa-bits) 1d0) -52) 1d0)))
293 (* arg random-unit-double)))
295 #+(or scl clisp)
296 (defun %random-long-float (arg state)
297 "Handle the long float case of RANDOM. We generate a float in [0l0, 1l0) by
298 clobbering the mantissa of 1l0 with random bits; this yields a number in
299 [1l0, 2l0). Then 1l0 is subtracted."
300 (let* ((d (1- (float-digits 1l0)))
301 (m (expt 2 d))
302 (random-mantissa-bits (%random-integer m state))
303 (random-unit-double (- (scale-float (float (+ m random-mantissa-bits) 1l0) (- d)) 1l0)))
304 (* arg random-unit-double)))
306 #+(and cmu double-double)
307 (defun %random-double-double-float (arg state)
308 "Handle the double-double float case of RANDOM. We generate a float in [0w0, 1w0) by
309 clobbering the mantissa of 1w0 with random bits; this yields a number in
310 [1w0, 2w0). Then 1w0 is subtracted."
311 (let* ((d (1- (float-digits 1w0)))
312 (m (expt 2 d))
313 (random-mantissa-bits (%random-integer m state))
314 (random-unit-double (- (scale-float (float (+ m random-mantissa-bits) 1w0) (- d)) 1w0)))
315 (* arg random-unit-double)))
317 ;;;; Random integers:
319 ;;; %RANDOM-INTEGER -- Internal
321 (defun %random-integer (arg state)
322 "Generates an integer greater than or equal to zero and less than Arg.
323 Successive chunks are concatenated without overlap to construct integers
324 larger than a single chunk. The return value has this property:
325 If two integers are generated from the same state with Arg equal to 2^m and 2^n,
326 respectively, then bit k is the same in both integers for 0 <= k < min(m,n).
327 Each call to %RANDOM-INTEGER consumes at least one chunk; bits left over
328 from previous chunks are not re-used."
329 (declare (type (integer 1) arg) (type random-state state))
330 (do*
331 ((nchunks (ceiling (integer-length (1- arg)) random-chunk-length) (1- nchunks))
332 (new-bits 0 (random-chunk state))
333 (bits 0 (logior bits (ash new-bits shift)))
334 (shift 0 (+ shift random-chunk-length)))
335 ((= 0 nchunks)
336 (rem bits arg))))
338 (defun random (arg &optional (state *random-state*))
339 "Generates a uniformly distributed pseudo-random number greater than or equal to zero
340 and less than Arg. State, if supplied, is the random state to use."
341 (declare (inline %random-single-float %random-double-float))
342 (cond
343 #-gcl ; GCL's single and double floats are the same; route all floats through %random-double-float
344 ((and (typep arg 'single-float) (> arg 0.0F0))
345 (%random-single-float arg state))
346 ((and (typep arg 'double-float) (> arg 0.0D0))
347 (%random-double-float arg state))
348 #+(or scl clisp)
349 ((and (typep arg 'long-float) (> arg 0.0L0))
350 (%random-long-float arg state))
351 #+(and cmu double-double)
352 ((and (typep arg 'kernel:double-double-float) (> arg 0.0W0))
353 (%random-double-double-float arg state))
354 ((and (integerp arg) (> arg 0))
355 (%random-integer arg state))
357 (error 'simple-type-error
358 :expected-type '(or (integer 1) (float (0))) :datum arg
359 :format-control (intl:gettext "random: argument must be a positive integer or a positive float; found: ~S")
360 :format-arguments (list arg)))))
362 ;;; begin Maxima-specific stuff
364 (in-package :maxima)
366 (defmfun $set_random_state (x)
367 "Copy the argument, and assign the copy to MT19937::*RANDOM-STATE*.
368 Returns '$done."
369 (setq mt19937::*random-state* (mt19937::make-random-state x))
370 '$done)
372 (defmfun $make_random_state (x)
373 "Returns a new random state object. If argument is an integer or array,
374 use argument to initialize random state. Otherwise punt to MT19937::MAKE-RANDOM-STATE."
375 (cond
376 ((or (integerp x) (arrayp x))
377 (mt19937::make-random-object :state (mt19937::init-random-state x)))
379 (mt19937::make-random-state x))))
381 (defmfun $random (x)
382 "Returns the next number from this generator.
383 Punt to MT19937::RANDOM."
384 (if (and (or (integerp x) (floatp x))
385 (> x 0))
386 (mt19937::random x)
387 (merror (intl:gettext "random: argument must be a positive integer or positive float; found: ~M") x)))
389 ;;; end Maxima-specific stuff