| 1 | ;; Generators of random numbers. |
|---|
| 2 | ;; Liam Healy, Sat Jul 15 2006 - 14:43 |
|---|
| 3 | ;; Time-stamp: <2008-02-17 12:15:46EST generators.lisp> |
|---|
| 4 | ;; $Id$ |
|---|
| 5 | |
|---|
| 6 | (in-package :gsl) |
|---|
| 7 | |
|---|
| 8 | ;;; Would it be useful to have a make-data like macro? |
|---|
| 9 | |
|---|
| 10 | (export '(make-random-number-generator)) |
|---|
| 11 | |
|---|
| 12 | (defgeneric generator (object) |
|---|
| 13 | (:method ((object t)) |
|---|
| 14 | (if (cffi:pointerp object) |
|---|
| 15 | object |
|---|
| 16 | (call-next-method))) |
|---|
| 17 | (:documentation "The foreign pointer to the GSL generator function.")) |
|---|
| 18 | |
|---|
| 19 | (defclass gsl-random () |
|---|
| 20 | ((type :initarg :type :reader rng-type) |
|---|
| 21 | (generator :initarg :generator :accessor generator))) |
|---|
| 22 | |
|---|
| 23 | (defclass random-number-generator (gsl-random) |
|---|
| 24 | () |
|---|
| 25 | (:documentation "A generator of random numbers.")) |
|---|
| 26 | |
|---|
| 27 | (defmethod print-object ((object gsl-random) stream) |
|---|
| 28 | (print-unreadable-object (object stream :type t :identity t) |
|---|
| 29 | (format stream "~a" (when (generator object) (rng-name object))))) |
|---|
| 30 | |
|---|
| 31 | (defun make-random-number-generator |
|---|
| 32 | (&optional (type *default-type*) (generator t)) |
|---|
| 33 | "Make a random number generator; by default it is allocated on creation." |
|---|
| 34 | (let ((instance |
|---|
| 35 | (make-instance |
|---|
| 36 | 'random-number-generator |
|---|
| 37 | :type type |
|---|
| 38 | :generator generator))) |
|---|
| 39 | (if (eq generator t) (alloc instance) instance))) |
|---|
| 40 | |
|---|
| 41 | (defgo-s (random-number-generator type value) |
|---|
| 42 | make-random-number-generator free rng-set 1) |
|---|
| 43 | |
|---|
| 44 | ;;;;**************************************************************************** |
|---|
| 45 | ;;;; Initialization |
|---|
| 46 | ;;;;**************************************************************************** |
|---|
| 47 | |
|---|
| 48 | (defmfun alloc ((generator random-number-generator)) |
|---|
| 49 | "gsl_rng_alloc" (((rng-type generator) :pointer)) |
|---|
| 50 | :type :method |
|---|
| 51 | :c-return (ptr :pointer) |
|---|
| 52 | :return ((progn (setf (generator generator) ptr) generator)) |
|---|
| 53 | :documentation ; FDL |
|---|
| 54 | "Instatiate a random number generator of specified type. |
|---|
| 55 | For example, create an instance of the Tausworthe |
|---|
| 56 | generator: (rng-alloc *taus*). |
|---|
| 57 | The generator is automatically initialized with the default seed, |
|---|
| 58 | *default-seed*. This is zero by default but can be changed |
|---|
| 59 | either directly or by using the environment variable GSL_RNG_SEED.") |
|---|
| 60 | |
|---|
| 61 | (defmfun free ((generator random-number-generator)) |
|---|
| 62 | "gsl_rng_free" (((generator generator) :pointer)) |
|---|
| 63 | :type :method |
|---|
| 64 | :c-return :void |
|---|
| 65 | :after ((setf (generator generator) nil)) |
|---|
| 66 | :documentation ; FDL |
|---|
| 67 | "Free all the memory associated with the generator.") |
|---|
| 68 | |
|---|
| 69 | ;;;;**************************************************************************** |
|---|
| 70 | ;;;; Seed |
|---|
| 71 | ;;;;**************************************************************************** |
|---|
| 72 | |
|---|
| 73 | (cffi:defcvar ("gsl_rng_default_seed" *default-seed*) :ulong) |
|---|
| 74 | (setf (documentation '*default-seed* 'variable) |
|---|
| 75 | "The default seed for random number generators.") |
|---|
| 76 | (map-name '*default-seed* "gsl_rng_cmrg") |
|---|
| 77 | (export '*default-seed*) |
|---|
| 78 | |
|---|
| 79 | (defmfun rng-set (rng-instance value) |
|---|
| 80 | "gsl_rng_set" (((generator rng-instance) :pointer) (value :ulong)) |
|---|
| 81 | :c-return :void |
|---|
| 82 | :documentation ; FDL |
|---|
| 83 | "Initialize (or `seeds') the random number generator. If |
|---|
| 84 | the generator is seeded with the same value of s on two different |
|---|
| 85 | runs, the same stream of random numbers will be generated by successive |
|---|
| 86 | calls to the routines below. If different values of s are |
|---|
| 87 | supplied, then the generated streams of random numbers should be |
|---|
| 88 | completely different. If the seed s is zero then the standard seed |
|---|
| 89 | from the original implementation is used instead. For example, the |
|---|
| 90 | original Fortran source code for the *ranlux* generator used a seed |
|---|
| 91 | of 314159265, and so choosing s equal to zero reproduces this when |
|---|
| 92 | using *ranlux*.") |
|---|
| 93 | |
|---|
| 94 | ;;;;**************************************************************************** |
|---|
| 95 | ;;;; Sampling |
|---|
| 96 | ;;;;**************************************************************************** |
|---|
| 97 | |
|---|
| 98 | (defmfun get-random-number (generator) |
|---|
| 99 | "gsl_rng_get" (((generator generator) :pointer)) |
|---|
| 100 | :c-return :ulong |
|---|
| 101 | :documentation ; FDL |
|---|
| 102 | "Generate a random integer. The |
|---|
| 103 | minimum and maximum values depend on the algorithm used, but all |
|---|
| 104 | integers in the range [min, max] are equally likely. The |
|---|
| 105 | values of min and max can determined using the auxiliary |
|---|
| 106 | functions #'rng-max and #'rng-min.") |
|---|
| 107 | |
|---|
| 108 | (defmfun uniform (generator) |
|---|
| 109 | "gsl_rng_uniform" (((generator generator) :pointer)) |
|---|
| 110 | :c-return :double |
|---|
| 111 | :documentation ; FDL |
|---|
| 112 | "A double precision floating point number uniformly |
|---|
| 113 | distributed in the range [0,1). The range includes 0.0 but excludes 1.0. |
|---|
| 114 | The value is typically obtained by dividing the result of |
|---|
| 115 | #'rng-get by (+ (rng-max generator) 1.0) in double |
|---|
| 116 | precision. Some generators compute this ratio internally so that they |
|---|
| 117 | can provide floating point numbers with more than 32 bits of randomness |
|---|
| 118 | (the maximum number of bits that can be portably represented in a single |
|---|
| 119 | :ulong.") |
|---|
| 120 | |
|---|
| 121 | (defmfun uniform>0 (generator) |
|---|
| 122 | "gsl_rng_uniform_pos" (((generator generator) :pointer)) |
|---|
| 123 | :c-return :double |
|---|
| 124 | :documentation ; FDL |
|---|
| 125 | "Return a positive double precision floating point number |
|---|
| 126 | uniformly distributed in the range (0,1), excluding both 0.0 and 1.0. |
|---|
| 127 | The number is obtained by sampling the generator with the algorithm of |
|---|
| 128 | #'uniform until a non-zero value is obtained. You can use |
|---|
| 129 | this function if you need to avoid a singularity at 0.0.") |
|---|
| 130 | |
|---|
| 131 | (defmfun uniform-fixnum (generator upperbound) |
|---|
| 132 | "gsl_rng_uniform_int" (((generator generator) :pointer) (upperbound :ulong)) |
|---|
| 133 | :c-return :ulong |
|---|
| 134 | :documentation ; FDL |
|---|
| 135 | "Generate a random integer from 0 to upperbound-1 inclusive. |
|---|
| 136 | All integers in the range are equally likely, regardless |
|---|
| 137 | of the generator used. An offset correction is applied so that zero is |
|---|
| 138 | always returned with the correct probability, for any minimum value of |
|---|
| 139 | the underlying generator. If upperbound is larger than the range |
|---|
| 140 | of the generator then the function signals an error.") |
|---|
| 141 | |
|---|
| 142 | ;;;;**************************************************************************** |
|---|
| 143 | ;;;; Information functions about instances |
|---|
| 144 | ;;;;**************************************************************************** |
|---|
| 145 | |
|---|
| 146 | (export 'rng-name) |
|---|
| 147 | (defgeneric rng-name (rng-instance) |
|---|
| 148 | (:documentation ; FDL |
|---|
| 149 | "The name of the random number generator.")) |
|---|
| 150 | |
|---|
| 151 | (defmfun rng-name ((rng-instance random-number-generator)) |
|---|
| 152 | "gsl_rng_name" (((generator rng-instance) :pointer)) |
|---|
| 153 | :type :method |
|---|
| 154 | :c-return :string) |
|---|
| 155 | |
|---|
| 156 | (defmfun rng-max (rng-instance) |
|---|
| 157 | "gsl_rng_max" (((generator rng-instance) :pointer)) |
|---|
| 158 | :c-return :unsigned-long |
|---|
| 159 | :documentation "The largest value that #'get-random-number |
|---|
| 160 | can return.") |
|---|
| 161 | |
|---|
| 162 | (defmfun rng-min (rng-instance) |
|---|
| 163 | "gsl_rng_min" (((generator rng-instance) :pointer)) |
|---|
| 164 | :c-return :unsigned-long |
|---|
| 165 | :documentation ; FDL |
|---|
| 166 | "The smallest value that #'get-random-number |
|---|
| 167 | can return. Usually this value is zero. There are some generators with |
|---|
| 168 | algorithms that cannot return zero, and for these generators the minimum |
|---|
| 169 | value is 1.") |
|---|
| 170 | |
|---|
| 171 | (export 'rng-state) |
|---|
| 172 | (defgeneric rng-state (rng-instance) |
|---|
| 173 | (:documentation ; FDL |
|---|
| 174 | "A pointer to the state of generator.")) |
|---|
| 175 | |
|---|
| 176 | (defmfun rng-state ((rng-instance random-number-generator)) |
|---|
| 177 | "gsl_rng_state" (((generator rng-instance) :pointer)) |
|---|
| 178 | :c-return :pointer |
|---|
| 179 | :type :method |
|---|
| 180 | :index gsl-random-state) |
|---|
| 181 | |
|---|
| 182 | (export 'rng-size) |
|---|
| 183 | (defgeneric rng-size (rng-instance) |
|---|
| 184 | (:documentation ; FDL |
|---|
| 185 | "The size of the generator.")) |
|---|
| 186 | |
|---|
| 187 | (defmfun rng-size ((rng-instance random-number-generator)) |
|---|
| 188 | "gsl_rng_size" (((generator rng-instance) :pointer)) |
|---|
| 189 | :c-return size |
|---|
| 190 | :type :method |
|---|
| 191 | :index gsl-random-state) |
|---|
| 192 | |
|---|
| 193 | (export 'gsl-random-state) |
|---|
| 194 | (defun gsl-random-state (rng-instance) |
|---|
| 195 | "The complete state of a given random number generator, specified |
|---|
| 196 | as a vector of bytes." |
|---|
| 197 | (let* ((gen rng-instance) |
|---|
| 198 | (ans |
|---|
| 199 | (make-array (rng-size gen) |
|---|
| 200 | :element-type '(unsigned-byte 8)))) |
|---|
| 201 | (loop for i from 0 below (length ans) |
|---|
| 202 | do |
|---|
| 203 | (setf (aref ans i) |
|---|
| 204 | (mem-aref (rng-state gen) :uint8 i))) |
|---|
| 205 | ans)) |
|---|
| 206 | |
|---|
| 207 | ;;;;**************************************************************************** |
|---|
| 208 | ;;;; Copying state |
|---|
| 209 | ;;;;**************************************************************************** |
|---|
| 210 | |
|---|
| 211 | (defmfun copy |
|---|
| 212 | ((destination random-number-generator) (source random-number-generator)) |
|---|
| 213 | "gsl_rng_memcpy" |
|---|
| 214 | (((generator destination) :pointer) ((generator source) :pointer)) |
|---|
| 215 | :type :method |
|---|
| 216 | :documentation ; FDL |
|---|
| 217 | "Copy the random number generator source into the |
|---|
| 218 | pre-existing generator destination, |
|---|
| 219 | making destination into an exact copy |
|---|
| 220 | of source. The two generators must be of the same type.") |
|---|
| 221 | |
|---|
| 222 | (export 'clone-generator) |
|---|
| 223 | (defgeneric clone-generator (instance) |
|---|
| 224 | (:documentation ; FDL |
|---|
| 225 | "Create a new generator which is an exact copy of the original. |
|---|
| 226 | Don't use; use #'make-random-number-generator, #'copy instead.")) |
|---|
| 227 | |
|---|
| 228 | (defmfun clone-generator ((instance random-number-generator)) |
|---|
| 229 | "gsl_rng_clone" (((generator instance) :pointer)) |
|---|
| 230 | :c-return :pointer |
|---|
| 231 | :type :method) |
|---|
| 232 | |
|---|
| 233 | (defmfun write-binary |
|---|
| 234 | ((object random-number-generator) stream) |
|---|
| 235 | "gsl_rng_fwrite" |
|---|
| 236 | ((stream :pointer) ((generator object) :pointer)) |
|---|
| 237 | :type :method) |
|---|
| 238 | |
|---|
| 239 | (defmfun read-binary |
|---|
| 240 | ((object random-number-generator) stream) |
|---|
| 241 | "gsl_block_fread" |
|---|
| 242 | ((stream :pointer) ((pointer object) :pointer)) |
|---|
| 243 | :type :method) |
|---|
| 244 | |
|---|
| 245 | ;;;;**************************************************************************** |
|---|
| 246 | ;;;; Examples and unit test |
|---|
| 247 | ;;;;**************************************************************************** |
|---|
| 248 | |
|---|
| 249 | #| |
|---|
| 250 | (make-tests random-number-generators |
|---|
| 251 | (letm ((rng (random-number-generator *mt19937* 0))) |
|---|
| 252 | (loop for i from 0 to 10 |
|---|
| 253 | collect |
|---|
| 254 | (uniform-fixnum rng 1000))) |
|---|
| 255 | (letm ((rng (random-number-generator *cmrg* 0))) |
|---|
| 256 | (loop for i from 0 to 10 collect (uniform rng)))) |
|---|
| 257 | |# |
|---|
| 258 | |
|---|
| 259 | (LISP-UNIT:DEFINE-TEST RANDOM-NUMBER-GENERATORS |
|---|
| 260 | (LISP-UNIT::ASSERT-NUMERICAL-EQUAL |
|---|
| 261 | (LIST |
|---|
| 262 | (LIST 999 162 282 947 231 484 957 744 540 739 759)) |
|---|
| 263 | (MULTIPLE-VALUE-LIST |
|---|
| 264 | (LETM ((RNG (RANDOM-NUMBER-GENERATOR *MT19937* 0))) |
|---|
| 265 | (LOOP FOR I FROM 0 TO 10 COLLECT |
|---|
| 266 | (UNIFORM-FIXNUM RNG 1000))))) |
|---|
| 267 | (LISP-UNIT::ASSERT-NUMERICAL-EQUAL |
|---|
| 268 | (LIST |
|---|
| 269 | (LIST 0.11177622997750353d0 0.9591667949963206d0 |
|---|
| 270 | 0.8415268011584537d0 0.9254037136795947d0 |
|---|
| 271 | 0.27540698474059205d0 0.7093040573919677d0 |
|---|
| 272 | 0.5541333041871588d0 0.8806957769583426d0 |
|---|
| 273 | 0.597139396982798d0 0.7518741133398722d0 |
|---|
| 274 | 0.9311084621265104d0)) |
|---|
| 275 | (MULTIPLE-VALUE-LIST |
|---|
| 276 | (LETM ((RNG (RANDOM-NUMBER-GENERATOR *CMRG* 0))) |
|---|
| 277 | (LOOP FOR I FROM 0 TO 10 COLLECT |
|---|
| 278 | (UNIFORM RNG)))))) |
|---|