123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488 |
- ; GENERIC PART OF MRG32k3a-GENERATOR FOR SRFI-27
- ; ==============================================
- ;
- ; Sebastian.Egner@philips.com, 2002.
- ;
- ; This is the generic R5RS-part of the implementation of the MRG32k3a
- ; generator to be used in SRFI-27. It is based on a separate implementation
- ; of the core generator (presumably in native code) and on code to
- ; provide essential functionality not available in R5RS (see below).
- ;
- ; compliance:
- ; Scheme R5RS with integer covering at least {-2^53..2^53-1}.
- ; In addition,
- ; SRFI-23: error
- ;
- ; history of this file:
- ; SE, 22-Mar-2002: refactored from earlier versions
- ; SE, 25-Mar-2002: pack/unpack need not allocate
- ; SE, 27-Mar-2002: changed interface to core generator
- ; SE, 10-Apr-2002: updated spec of mrg32k3a-random-integer
- ; Generator
- ; =========
- ;
- ; Pierre L'Ecuyer's MRG32k3a generator is a Combined Multiple Recursive
- ; Generator. It produces the sequence {(x[1,n] - x[2,n]) mod m1 : n}
- ; defined by the two recursive generators
- ;
- ; x[1,n] = ( a12 x[1,n-2] + a13 x[1,n-3]) mod m1,
- ; x[2,n] = (a21 x[2,n-1] + a23 x[2,n-3]) mod m2,
- ;
- ; where the constants are
- ; m1 = 4294967087 = 2^32 - 209 modulus of 1st component
- ; m2 = 4294944443 = 2^32 - 22853 modulus of 2nd component
- ; a12 = 1403580 recursion coefficients
- ; a13 = -810728
- ; a21 = 527612
- ; a23 = -1370589
- ;
- ; The generator passes all tests of G. Marsaglia's Diehard testsuite.
- ; Its period is (m1^3 - 1)(m2^3 - 1)/2 which is nearly 2^191.
- ; L'Ecuyer reports: "This generator is well-behaved in all dimensions
- ; up to at least 45: ..." [with respect to the spectral test, SE].
- ;
- ; The period is maximal for all values of the seed as long as the
- ; state of both recursive generators is not entirely zero.
- ;
- ; As the successor state is a linear combination of previous
- ; states, it is possible to advance the generator by more than one
- ; iteration by applying a linear transformation. The following
- ; publication provides detailed information on how to do that:
- ;
- ; [1] P. L'Ecuyer, R. Simard, E. J. Chen, W. D. Kelton:
- ; An Object-Oriented Random-Number Package With Many Long
- ; Streams and Substreams. 2001.
- ; To appear in Operations Research.
- ;
- ; Arithmetics
- ; ===========
- ;
- ; The MRG32k3a generator produces values in {0..2^32-209-1}. All
- ; subexpressions of the actual generator fit into {-2^53..2^53-1}.
- ; The code below assumes that Scheme's "integer" covers this range.
- ; In addition, it is assumed that floating point literals can be
- ; read and there is some arithmetics with inexact numbers.
- ;
- ; However, for advancing the state of the generator by more than
- ; one step at a time, the full range {0..2^32-209-1} is needed.
- ; Required: Backbone Generator
- ; ============================
- ;
- ; At this point in the code, the following procedures are assumed
- ; to be defined to execute the core generator:
- ;
- ; (mrg32k3a-pack-state unpacked-state) -> packed-state
- ; (mrg32k3a-unpack-state packed-state) -> unpacked-state
- ; pack/unpack a state of the generator. The core generator works
- ; on packed states, passed as an explicit argument, only. This
- ; allows native code implementations to store their state in a
- ; suitable form. Unpacked states are #(x10 x11 x12 x20 x21 x22)
- ; with integer x_ij. Pack/unpack need not allocate new objects
- ; in case packed and unpacked states are identical.
- ;
- ; (mrg32k3a-random-range) -> m-max
- ; (mrg32k3a-random-integer packed-state range) -> x in {0..range-1}
- ; advance the state of the generator and return the next random
- ; range-limited integer.
- ; Note that the state is not necessarily advanced by just one
- ; step because we use the rejection method to avoid any problems
- ; with distribution anomalies.
- ; The range argument must be an exact integer in {1..m-max}.
- ; It can be assumed that range is a fixnum if the Scheme system
- ; has such a number representation.
- ;
- ; (mrg32k3a-random-real packed-state) -> x in (0,1)
- ; advance the state of the generator and return the next random
- ; real number between zero and one (both excluded). The type of
- ; the result should be a flonum if possible.
- ; Required: Record Data Type
- ; ==========================
- ;
- ; At this point in the code, the following procedures are assumed
- ; to be defined to create and access a new record data type:
- ;
- ; (:random-source-make a0 a1 a2 a3 a4 a5) -> s
- ; constructs a new random source object s consisting of the
- ; objects a0 .. a5 in this order.
- ;
- ; (:random-source? obj) -> bool
- ; tests if a Scheme object is a :random-source.
- ;
- ; (:random-source-state-ref s) -> a0
- ; (:random-source-state-set! s) -> a1
- ; (:random-source-randomize! s) -> a2
- ; (:random-source-pseudo-randomize! s) -> a3
- ; (:random-source-make-integers s) -> a4
- ; (:random-source-make-reals s) -> a5
- ; retrieve the values in the fields of the object s.
- ; Required: Current Time as an Integer
- ; ====================================
- ;
- ; At this point in the code, the following procedure is assumed
- ; to be defined to obtain a value that is likely to be different
- ; for each invokation of the Scheme system:
- ;
- ; (:random-source-current-time) -> x
- ; an integer that depends on the system clock. It is desired
- ; that the integer changes as fast as possible.
- ; Accessing the State
- ; ===================
- (define (mrg32k3a-state-ref packed-state)
- (cons 'lecuyer-mrg32k3a
- (vector->list (mrg32k3a-unpack-state packed-state))))
- (define (mrg32k3a-state-set external-state)
- (define (check-value x m)
- (if (and (integer? x)
- (exact? x)
- (<= 0 x (- m 1)))
- #t
- (error "illegal value" x)))
- (if (and (list? external-state)
- (= (length external-state) 7)
- (eq? (car external-state) 'lecuyer-mrg32k3a))
- (let ((s (cdr external-state)))
- (check-value (list-ref s 0) mrg32k3a-m1)
- (check-value (list-ref s 1) mrg32k3a-m1)
- (check-value (list-ref s 2) mrg32k3a-m1)
- (check-value (list-ref s 3) mrg32k3a-m2)
- (check-value (list-ref s 4) mrg32k3a-m2)
- (check-value (list-ref s 5) mrg32k3a-m2)
- (if (or (zero? (+ (list-ref s 0) (list-ref s 1) (list-ref s 2)))
- (zero? (+ (list-ref s 3) (list-ref s 4) (list-ref s 5))))
- (error "illegal degenerate state" external-state))
- (mrg32k3a-pack-state (list->vector s)))
- (error "malformed state" external-state)))
- ; Pseudo-Randomization
- ; ====================
- ;
- ; Reference [1] above shows how to obtain many long streams and
- ; substream from the backbone generator.
- ;
- ; The idea is that the generator is a linear operation on the state.
- ; Hence, we can express this operation as a 3x3-matrix acting on the
- ; three most recent states. Raising the matrix to the k-th power, we
- ; obtain the operation to advance the state by k steps at once. The
- ; virtual streams and substreams are now simply parts of the entire
- ; periodic sequence (which has period around 2^191).
- ;
- ; For the implementation it is necessary to compute with matrices in
- ; the ring (Z/(m1*m1)*Z)^(3x3). By the Chinese-Remainder Theorem, this
- ; is isomorphic to ((Z/m1*Z) x (Z/m2*Z))^(3x3). We represent such a pair
- ; of matrices
- ; [ [[x00 x01 x02],
- ; [x10 x11 x12],
- ; [x20 x21 x22]], mod m1
- ; [[y00 y01 y02],
- ; [y10 y11 y12],
- ; [y20 y21 y22]] mod m2]
- ; as a vector of length 18 of the integers as writen above:
- ; #(x00 x01 x02 x10 x11 x12 x20 x21 x22
- ; y00 y01 y02 y10 y11 y12 y20 y21 y22)
- ;
- ; As the implementation should only use the range {-2^53..2^53-1}, the
- ; fundamental operation (x*y) mod m, where x, y, m are nearly 2^32,
- ; is computed by breaking up x and y as x = x1*w + x0 and y = y1*w + y0
- ; where w = 2^16. In this case, all operations fit the range because
- ; w^2 mod m is a small number. If proper multiprecision integers are
- ; available this is not necessary, but pseudo-randomize! is an expected
- ; to be called only occasionally so we do not provide this implementation.
- (define mrg32k3a-m1 4294967087) ; modulus of component 1
- (define mrg32k3a-m2 4294944443) ; modulus of component 2
- (define mrg32k3a-initial-state ; 0 3 6 9 12 15 of A^16, see below
- '#( 1062452522
- 2961816100
- 342112271
- 2854655037
- 3321940838
- 3542344109))
- (define mrg32k3a-generators #f) ; computed when needed
- (define (mrg32k3a-pseudo-randomize-state i j)
- (define (product A B) ; A*B in ((Z/m1*Z) x (Z/m2*Z))^(3x3)
- (define w 65536) ; wordsize to split {0..2^32-1}
- (define w-sqr1 209) ; w^2 mod m1
- (define w-sqr2 22853) ; w^2 mod m2
- (define (lc i0 i1 i2 j0 j1 j2 m w-sqr) ; linear combination
- (let ((a0h (quotient (vector-ref A i0) w))
- (a0l (modulo (vector-ref A i0) w))
- (a1h (quotient (vector-ref A i1) w))
- (a1l (modulo (vector-ref A i1) w))
- (a2h (quotient (vector-ref A i2) w))
- (a2l (modulo (vector-ref A i2) w))
- (b0h (quotient (vector-ref B j0) w))
- (b0l (modulo (vector-ref B j0) w))
- (b1h (quotient (vector-ref B j1) w))
- (b1l (modulo (vector-ref B j1) w))
- (b2h (quotient (vector-ref B j2) w))
- (b2l (modulo (vector-ref B j2) w)))
- (modulo
- (+ (* (+ (* a0h b0h)
- (* a1h b1h)
- (* a2h b2h))
- w-sqr)
- (* (+ (* a0h b0l)
- (* a0l b0h)
- (* a1h b1l)
- (* a1l b1h)
- (* a2h b2l)
- (* a2l b2h))
- w)
- (* a0l b0l)
- (* a1l b1l)
- (* a2l b2l))
- m)))
-
- (vector
- (lc 0 1 2 0 3 6 mrg32k3a-m1 w-sqr1) ; (A*B)_00 mod m1
- (lc 0 1 2 1 4 7 mrg32k3a-m1 w-sqr1) ; (A*B)_01
- (lc 0 1 2 2 5 8 mrg32k3a-m1 w-sqr1)
- (lc 3 4 5 0 3 6 mrg32k3a-m1 w-sqr1) ; (A*B)_10
- (lc 3 4 5 1 4 7 mrg32k3a-m1 w-sqr1)
- (lc 3 4 5 2 5 8 mrg32k3a-m1 w-sqr1)
- (lc 6 7 8 0 3 6 mrg32k3a-m1 w-sqr1)
- (lc 6 7 8 1 4 7 mrg32k3a-m1 w-sqr1)
- (lc 6 7 8 2 5 8 mrg32k3a-m1 w-sqr1)
- (lc 9 10 11 9 12 15 mrg32k3a-m2 w-sqr2) ; (A*B)_00 mod m2
- (lc 9 10 11 10 13 16 mrg32k3a-m2 w-sqr2)
- (lc 9 10 11 11 14 17 mrg32k3a-m2 w-sqr2)
- (lc 12 13 14 9 12 15 mrg32k3a-m2 w-sqr2)
- (lc 12 13 14 10 13 16 mrg32k3a-m2 w-sqr2)
- (lc 12 13 14 11 14 17 mrg32k3a-m2 w-sqr2)
- (lc 15 16 17 9 12 15 mrg32k3a-m2 w-sqr2)
- (lc 15 16 17 10 13 16 mrg32k3a-m2 w-sqr2)
- (lc 15 16 17 11 14 17 mrg32k3a-m2 w-sqr2)))
- (define (power A e) ; A^e
- (cond
- ((zero? e)
- '#(1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 1))
- ((= e 1)
- A)
- ((even? e)
- (power (product A A) (quotient e 2)))
- (else
- (product (power A (- e 1)) A))))
- (define (power-power A b) ; A^(2^b)
- (if (zero? b)
- A
- (power-power (product A A) (- b 1))))
- (define A ; the MRG32k3a recursion
- '#( 0 1403580 4294156359
- 1 0 0
- 0 1 0
- 527612 0 4293573854
- 1 0 0
- 0 1 0))
- ; check arguments
- (if (not (and (integer? i)
- (exact? i)
- (integer? j)
- (exact? j)))
- (error "i j must be exact integer" i j))
- ; precompute A^(2^127) and A^(2^76) only once
- (if (not mrg32k3a-generators)
- (set! mrg32k3a-generators
- (list (power-power A 127)
- (power-power A 76)
- (power A 16))))
- ; compute M = A^(16 + i*2^127 + j*2^76)
- (let ((M (product
- (list-ref mrg32k3a-generators 2)
- (product
- (power (list-ref mrg32k3a-generators 0)
- (modulo i (expt 2 28)))
- (power (list-ref mrg32k3a-generators 1)
- (modulo j (expt 2 28)))))))
- (mrg32k3a-pack-state
- (vector
- (vector-ref M 0)
- (vector-ref M 3)
- (vector-ref M 6)
- (vector-ref M 9)
- (vector-ref M 12)
- (vector-ref M 15)))))
- ; True Randomization
- ; ==================
- ;
- ; The value obtained from the system time is feed into a very
- ; simple pseudo random number generator. This in turn is used
- ; to obtain numbers to randomize the state of the MRG32k3a
- ; generator, avoiding period degeneration.
- (define (mrg32k3a-randomize-state state)
- ;; G. Marsaglia's simple 16-bit generator with carry
- (let* ((m 65536)
- (x (modulo (:random-source-current-time) m)))
- (define (random-m)
- (let ((y (modulo x m)))
- (set! x (+ (* 30903 y) (quotient x m)))
- y))
- (define (random n) ; m < n < m^2
- (modulo (+ (* (random-m) m) (random-m)) n))
- ; modify the state
- (let ((m1 mrg32k3a-m1)
- (m2 mrg32k3a-m2)
- (s (mrg32k3a-unpack-state state)))
- (mrg32k3a-pack-state
- (vector
- (+ 1 (modulo (+ (vector-ref s 0) (random (- m1 1))) (- m1 1)))
- (modulo (+ (vector-ref s 1) (random m1)) m1)
- (modulo (+ (vector-ref s 2) (random m1)) m1)
- (+ 1 (modulo (+ (vector-ref s 3) (random (- m2 1))) (- m2 1)))
- (modulo (+ (vector-ref s 4) (random m2)) m2)
- (modulo (+ (vector-ref s 5) (random m2)) m2))))))
- ; Large Integers
- ; ==============
- ;
- ; To produce large integer random deviates, for n > m-max, we first
- ; construct large random numbers in the range {0..m-max^k-1} for some
- ; k such that m-max^k >= n and then use the rejection method to choose
- ; uniformly from the range {0..n-1}.
- (define mrg32k3a-m-max
- (mrg32k3a-random-range))
- (define (mrg32k3a-random-power state k) ; n = m-max^k, k >= 1
- (if (= k 1)
- (mrg32k3a-random-integer state mrg32k3a-m-max)
- (+ (* (mrg32k3a-random-power state (- k 1)) mrg32k3a-m-max)
- (mrg32k3a-random-integer state mrg32k3a-m-max))))
- (define (mrg32k3a-random-large state n) ; n > m-max
- (do ((k 2 (+ k 1))
- (mk (* mrg32k3a-m-max mrg32k3a-m-max) (* mk mrg32k3a-m-max)))
- ((>= mk n)
- (let* ((mk-by-n (quotient mk n))
- (a (* mk-by-n n)))
- (do ((x (mrg32k3a-random-power state k)
- (mrg32k3a-random-power state k)))
- ((< x a) (quotient x mk-by-n)))))))
- ; Multiple Precision Reals
- ; ========================
- ;
- ; To produce multiple precision reals we produce a large integer value
- ; and convert it into a real value. This value is then normalized.
- ; The precision goal is unit <= 1/(m^k + 1), or 1/unit - 1 <= m^k.
- ; If you know more about the floating point number types of the
- ; Scheme system, this can be improved.
- (define (mrg32k3a-random-real-mp state unit)
- (do ((k 1 (+ k 1))
- (u (- (/ 1 unit) 1) (/ u mrg32k3a-m1)))
- ((<= u 1)
- (/ (exact->inexact (+ (mrg32k3a-random-power state k) 1))
- (exact->inexact (+ (expt mrg32k3a-m-max k) 1))))))
- ; Provide the Interface as Specified in the SRFI
- ; ==============================================
- ;
- ; An object of type random-source is a record containing the procedures
- ; as components. The actual state of the generator is stored in the
- ; binding-time environment of make-random-source.
- (define (make-random-source)
- (let ((state (mrg32k3a-pack-state ; make a new copy
- (list->vector (vector->list mrg32k3a-initial-state)))))
- (:random-source-make
- (lambda ()
- (mrg32k3a-state-ref state))
- (lambda (new-state)
- (set! state (mrg32k3a-state-set new-state)))
- (lambda ()
- (set! state (mrg32k3a-randomize-state state)))
- (lambda (i j)
- (set! state (mrg32k3a-pseudo-randomize-state i j)))
- (lambda ()
- (lambda (n)
- (cond
- ((not (and (integer? n) (exact? n) (positive? n)))
- (error "range must be exact positive integer" n))
- ((<= n mrg32k3a-m-max)
- (mrg32k3a-random-integer state n))
- (else
- (mrg32k3a-random-large state n)))))
- (lambda args
- (cond
- ((null? args)
- (lambda ()
- (mrg32k3a-random-real state)))
- ((null? (cdr args))
- (let ((unit (car args)))
- (cond
- ((not (and (real? unit) (< 0 unit 1)))
- (error "unit must be real in (0,1)" unit))
- ((<= (- (/ 1 unit) 1) mrg32k3a-m1)
- (lambda ()
- (mrg32k3a-random-real state)))
- (else
- (lambda ()
- (mrg32k3a-random-real-mp state unit))))))
- (else
- (error "illegal arguments" args)))))))
- (define random-source?
- :random-source?)
- (define (random-source-state-ref s)
- ((:random-source-state-ref s)))
- (define (random-source-state-set! s state)
- ((:random-source-state-set! s) state))
- (define (random-source-randomize! s)
- ((:random-source-randomize! s)))
- (define (random-source-pseudo-randomize! s i j)
- ((:random-source-pseudo-randomize! s) i j))
- ; ---
- (define (random-source-make-integers s)
- ((:random-source-make-integers s)))
- (define (random-source-make-reals s . unit)
- (apply (:random-source-make-reals s) unit))
- ; ---
- (define default-random-source
- (make-random-source))
- (define random-integer
- (random-source-make-integers default-random-source))
- (define random-real
- (random-source-make-reals default-random-source))
|