Raymond Toy pushed to branch rtoy-xoro at cmucl / cmucl

Commits:

1 changed file:

Changes:

  • src/code/rand-xoroshiro.lisp
    1
    +;;; -*- Mode: Lisp; Package: Kernel -*-
    
    2
    +;;;
    
    3
    +;;; **********************************************************************
    
    4
    +(ext:file-comment
    
    5
    +  "$Header: src/code/rand-xoroshiro.lisp $")
    
    6
    +
    
    7
    +;;;
    
    8
    +;;; **********************************************************************
    
    9
    +;;;
    
    10
    +;;; Support for the xoroshiro128+ random number generator by David
    
    11
    +;;; Blackman and Sebastiano Vigna (vigna@acm.org)
    
    12
    +
    
    13
    +(in-package "LISP")
    
    14
    +(intl:textdomain "cmucl")
    
    15
    +
    
    16
    +(export '(random-state random-state-p random *random-state*
    
    17
    +	  make-random-state))
    
    18
    +
    
    19
    +(in-package "KERNEL")
    
    20
    +(export '(%random-single-float %random-double-float random-chunk init-random-state))
    
    21
    +
    
    22
    +(sys:register-lisp-feature :random-xoroshiro)
    
    23
    +
    
    24
    +(defun int-init-random-state (&optional (seed 5772156649015328606) state)
    
    25
    +  (let ((state (or state (make-array 2 :element-type 'double-float)))
    
    26
    +	(splitmix-state (ldb (byte 64 0) seed)))
    
    27
    +    (flet ((splitmix64 ()
    
    28
    +	     (let ((z (setf splitmix-state
    
    29
    +			    (ldb (byte 64 0) (+ splitmix-state #x9e3779b97f4a7c15)))))
    
    30
    +	       (declare (type (unsigned-byte 64) z))
    
    31
    +	       (setf z (ldb (byte 64 0)
    
    32
    +			    (* (logxor z (ash z -30))
    
    33
    +			       #xbf58476d1ce4e5b9)))
    
    34
    +	       (setf z (ldb (byte 64 0)
    
    35
    +			    (* (logxor z (ash z -27))
    
    36
    +			       #x94d049bb133111eb)))
    
    37
    +	       (logxor z (ash z -31))))
    
    38
    +	   (make-double (x)
    
    39
    +	     (let ((lo (ldb (byte 32 0) x))
    
    40
    +		   (hi (ldb (byte 32 32) x)))
    
    41
    +	       (kernel:make-double-float
    
    42
    +		(if (< hi #x80000000)
    
    43
    +		    hi
    
    44
    +		    (- hi #x100000000))
    
    45
    +		lo))))
    
    46
    +      (let* ((s0 (splitmix64))
    
    47
    +	     (s1 (splitmix64)))
    
    48
    +	   (setf (aref state 0) (make-double s0)
    
    49
    +		 (aref state 1) (make-double s1))))))
    
    50
    +
    
    51
    +(defun vec-init-random-state (key &optional state)
    
    52
    +  (declare (type (array (unsigned-byte 32) (4)) key)
    
    53
    +	   (type (simple-array double-float (2)) state))
    
    54
    +  (flet ((make-double (hi lo)
    
    55
    +	   (kernel:make-double-float
    
    56
    +		(if (< hi #x80000000)
    
    57
    +		    hi
    
    58
    +		    (- hi #x100000000))
    
    59
    +		lo)))
    
    60
    +    (setf (aref state 0) (make-double (aref key 0) (aref key 1))
    
    61
    +	  (aref state 1) (make-double (aref key 2) (aref key 3)))))
    
    62
    +  
    
    63
    +  
    
    64
    +(defun init-random-state (&optional (seed 5772156649015328606) state)
    
    65
    +  "Generate an random state vector from the given SEED.  The seed can be
    
    66
    +  either an integer or a vector of (unsigned-byte 32)"
    
    67
    +  (declare (type (or null integer
    
    68
    +		     (array (unsigned-byte 32) (*)))
    
    69
    +		 seed))
    
    70
    +  (etypecase seed
    
    71
    +    (integer
    
    72
    +     (int-init-random-state (ldb (byte 64 0) seed) state))
    
    73
    +    ((array (unsigned-byte 32) (4))
    
    74
    +     (vec-init-random-state seed state))))
    
    75
    +
    
    76
    +(defstruct (xoro-random-state
    
    77
    +	     (:constructor make-xoroshiro-object)
    
    78
    +	     (:make-load-form-fun :just-dump-it-normally))
    
    79
    +  (state (init-random-state)
    
    80
    +   :type (simple-array double-float (2)))
    
    81
    +  (rand (make-array 1 :element-type '(unsigned-byte 32) :initial-element 0)
    
    82
    +   :type (simple-array (unsigned-byte 32) (1)))
    
    83
    +  (cached-p nil :type (member t nil)))
    
    84
    +
    
    85
    +
    
    86
    +
    
    87
    +;;;; Random entries:
    
    88
    +
    
    89
    +;;; Size of the chunks returned by random-chunk.
    
    90
    +;;;
    
    91
    +(defconstant random-chunk-length 32)
    
    92
    +
    
    93
    +;;; random-chunk -- Internal
    
    94
    +;;;
    
    95
    +;;; This function generaters a 32bit integer between 0 and #xffffffff
    
    96
    +;;; inclusive.
    
    97
    +;;;
    
    98
    +(declaim (inline random-chunk))
    
    99
    +
    
    100
    +(defun random-chunk (rng-state)
    
    101
    +  (declare (type xoro-state rng-state)
    
    102
    +	   (optimize (speed 3) (safety 0)))
    
    103
    +  (let ((cached (xoro-state-cached-p rng-state)))
    
    104
    +    (cond (cached
    
    105
    +	   (setf (xoro-state-cached-p rng-state) nil)
    
    106
    +	   (aref (xoro-state-rand rng-state) 0))
    
    107
    +	  (t
    
    108
    +	   (let ((s (xoro-state-state rng-state)))
    
    109
    +	     (declare (type (simple-array double-float (2)) s))
    
    110
    +	     (multiple-value-bind (r1 r0)
    
    111
    +		 (vm::xoroshiro-next s)
    
    112
    +	       (setf (aref (xoro-state-rand rng-state) 0) r1)
    
    113
    +	       (setf (xoro-state-cached-p rng-state) t)
    
    114
    +	       r0))))))
    
    115
    +
    
    116
    +#-x86
    
    117
    +(defun xoroshiro-next (state)
    
    118
    +  (declare (type (simple-array double-float (2)) state))
    
    119
    +  (flet ((rotl-55 (x1 x0)
    
    120
    +	   (declare (type (unsigned-byte 32) x0 x1)
    
    121
    +		    (optimize (speed 3) (safety 0)))
    
    122
    +	   ;; x << 55
    
    123
    +	   (let ((sl55-h (ldb (byte 32 0) (ash x0 (- 55 32))))
    
    124
    +		 (sl55-l 0))
    
    125
    +	     ;; x >> 9
    
    126
    +	     (let ((sr9-h (ash x1 -9))
    
    127
    +		   (sr9-l (ldb (byte 32 0)
    
    128
    +			       (logior (ash x0 -9)
    
    129
    +				       (ash x1 23)))))
    
    130
    +	       (values (logior sl55-h sr9-h)
    
    131
    +		       (logior sl55-l sr9-l)))))
    
    132
    +	 (rotl-36 (x1 x0)
    
    133
    +	   (declare (type (unsigned-byte 32) x0 x1)
    
    134
    +		    (optimize (speed 3) (safety 0)))
    
    135
    +	   ;; x << 36
    
    136
    +	   (let ((sl36-h (ldb (byte 32 0) (ash x0 4))))
    
    137
    +	     ;; x >> 28
    
    138
    +	     (let ((sr28-l (ldb (byte 32 0)
    
    139
    +				(logior (ash x0 -28)
    
    140
    +					(ash x1 4))))
    
    141
    +		   (sr28-h (ash x1 -28)))
    
    142
    +	       (values (logior sl36-h sr28-h)
    
    143
    +		       sr28-l))))
    
    144
    +	 (shl-14 (x1 x0)
    
    145
    +	   (declare (type (unsigned-byte 32) x1 x0)
    
    146
    +		    (optimize (speed 3) (safety 0)))
    
    147
    +	   (values (ldb (byte 32 0)
    
    148
    +			(logior (ash x1 14)
    
    149
    +				(ash x0 (- 14 32))))
    
    150
    +		   (ldb (byte 32 0)
    
    151
    +			(ash x0 14))))
    
    152
    +	 (make-double (hi lo)
    
    153
    +	   (kernel:make-double-float
    
    154
    +	    (if (< hi #x80000000)
    
    155
    +		hi
    
    156
    +		(- hi #x100000000))
    
    157
    +	    lo)))
    
    158
    +    (let ((s0-1 0)
    
    159
    +	  (s0-0 0)
    
    160
    +	  (s1-1 0)
    
    161
    +	  (s1-0 0)
    
    162
    +	  (r1 0)
    
    163
    +	  (r0 0))
    
    164
    +      (declare (type (unsigned-byte 32)) s0-1 s0-0 s1-1 s1-0 r1 r0)
    
    165
    +      (multiple-value-bind (x1 x0)
    
    166
    +	  (kernel:double-float-bits (aref state 0))
    
    167
    +	(setf s0-1 (ldb (byte 32 0) x1)
    
    168
    +	      s0-0 x0))
    
    169
    +      (multiple-value-bind (x1 x0)
    
    170
    +	  (kernel:double-float-bits (aref state 1))
    
    171
    +	(setf s1-1 (ldb (byte 32 0) x1)
    
    172
    +	      s1-0 x0))
    
    173
    +
    
    174
    +      (multiple-value-prog1
    
    175
    +	  (multiple-value-bind (sum-0 c)
    
    176
    +	      (bignum::%add-with-carry s0-0 s1-0 0)
    
    177
    +	    (values (bignum::%add-with-carry s0-1 s1-1 c)
    
    178
    +		    sum-0))
    
    179
    +      ;; s1 ^= s0
    
    180
    +      (setf s1-1 (logxor s1-1 s0-1)
    
    181
    +	    s1-0 (logxor s1-0 s0-0))
    
    182
    +      ;; s[0] = rotl(s0,55) ^ s1 ^ (s1 << 14)
    
    183
    +      (multiple-value-setq (s0-1 s0-0)
    
    184
    +	(rotl-55 s0-1 s0-0))
    
    185
    +      (setf s0-1 (logxor s0-1 s1-1)
    
    186
    +	    s0-0 (logxor s0-0 s1-0))
    
    187
    +      (multiple-value-bind (s14-1 s14-0)
    
    188
    +	  (shl-14 s1-1 s1-0)
    
    189
    +	(setf s0-1 (logxor s0-1 s14-1)
    
    190
    +	      s0-0 (logxor s0-0 s14-0)))
    
    191
    +      (setf (aref s 0) s0-0)
    
    192
    +      (setf (aref s 1) s0-1)
    
    193
    +
    
    194
    +      (multiple-value-bind (r1 r0)
    
    195
    +	  (rotl-36 s1-1 s1-0)
    
    196
    +	(setf (aref s 2) r0
    
    197
    +	      (aref s 3) r1))
    
    198
    +      (setf (aref state 0) (make-double s0-1 s0-0)
    
    199
    +	    (aref state 1) (make-double s1-1 s1-0))))))
    
    200
    +
    
    201
    +;;; %RANDOM-SINGLE-FLOAT, %RANDOM-DOUBLE-FLOAT  --  Interface
    
    202
    +;;;
    
    203
    +;;;    Handle the single or double float case of RANDOM.  We generate a float
    
    204
    +;;; between 0.0 and 1.0 by clobbering the significand of 1.0 with random bits,
    
    205
    +;;; then subtracting 1.0.  This hides the fact that we have a hidden bit.
    
    206
    +;;;
    
    207
    +(declaim (inline %random-single-float %random-double-float))
    
    208
    +(declaim (ftype (function ((single-float (0f0)) random-state)
    
    209
    +			  (single-float 0f0))
    
    210
    +		%random-single-float))
    
    211
    +;;;
    
    212
    +(defun %random-single-float (arg state)
    
    213
    +  (declare (type (single-float (0f0)) arg)
    
    214
    +	   (type random-state state))
    
    215
    +  (* arg
    
    216
    +     (- (make-single-float
    
    217
    +	 (dpb (ash (random-chunk state)
    
    218
    +		   (- vm:single-float-digits random-chunk-length))
    
    219
    +	      vm:single-float-significand-byte
    
    220
    +	      (single-float-bits 1.0)))
    
    221
    +	1.0)))
    
    222
    +;;;
    
    223
    +(declaim (ftype (function ((double-float (0d0)) random-state)
    
    224
    +			  (double-float 0d0))
    
    225
    +		%random-double-float))
    
    226
    +;;;
    
    227
    +;;; 53bit version.
    
    228
    +;;;
    
    229
    +#-x86
    
    230
    +(defun %random-double-float (arg state)
    
    231
    +  (declare (type (double-float (0d0)) arg)
    
    232
    +	   (type random-state state))
    
    233
    +  (* arg
    
    234
    +     (- (lisp::make-double-float
    
    235
    +	 (dpb (ash (random-chunk state)
    
    236
    +		   (- vm:double-float-digits random-chunk-length
    
    237
    +		      vm:word-bits))
    
    238
    +	      vm:double-float-significand-byte
    
    239
    +	      (lisp::double-float-high-bits 1d0))
    
    240
    +	 (random-chunk state))
    
    241
    +	1d0)))
    
    242
    +
    
    243
    +;;; Using a faster inline VOP.
    
    244
    +#+x86
    
    245
    +(defun %random-double-float (arg state)
    
    246
    +  (declare (type (double-float (0d0)) arg)
    
    247
    +	   (type random-state state))
    
    248
    +  (let ((state-vector (random-state-state state)))
    
    249
    +    (* arg
    
    250
    +       (- (lisp::make-double-float
    
    251
    +	   (dpb (ash (vm::random-mt19937 state-vector)
    
    252
    +		     (- vm:double-float-digits random-chunk-length
    
    253
    +			vm:word-bits))
    
    254
    +		vm:double-float-significand-byte
    
    255
    +		(lisp::double-float-high-bits 1d0))
    
    256
    +	   (vm::random-mt19937 state-vector))
    
    257
    +	  1d0))))
    
    258
    +
    
    259
    +#+long-float
    
    260
    +(declaim (inline %random-long-float))
    
    261
    +#+long-float
    
    262
    +(declaim (ftype (function ((long-float (0l0)) random-state) (long-float 0l0))
    
    263
    +		%random-long-float))
    
    264
    +
    
    265
    +;;; Using a faster inline VOP.
    
    266
    +#+(and long-float x86)
    
    267
    +(defun %random-long-float (arg state)
    
    268
    +  (declare (type (long-float (0l0)) arg)
    
    269
    +	   (type random-state state))
    
    270
    +  (let ((state-vector (random-state-state state)))
    
    271
    +    (* arg
    
    272
    +       (- (lisp::make-long-float
    
    273
    +	   (lisp::long-float-exp-bits 1l0)
    
    274
    +	   (logior (vm::random-mt19937 state-vector) vm:long-float-hidden-bit)
    
    275
    +	   (vm::random-mt19937 state-vector))
    
    276
    +	  1l0))))
    
    277
    +
    
    278
    +#+(and long-float sparc)
    
    279
    +(defun %random-long-float (arg state)
    
    280
    +  (declare (type (long-float (0l0)) arg)
    
    281
    +	   (type random-state state))
    
    282
    +  (* arg
    
    283
    +     (- (lisp::make-long-float
    
    284
    +	 (lisp::long-float-exp-bits 1l0)	; X needs more work
    
    285
    +	 (random-chunk state) (random-chunk state) (random-chunk state))
    
    286
    +	1l0)))
    
    287
    +#+double-double
    
    288
    +(defun %random-double-double-float (arg state)
    
    289
    +  (declare (type (double-double-float (0w0)) arg)
    
    290
    +	   (type random-state state))
    
    291
    +  ;; Generate a 31-bit integer, scale it and sum them up
    
    292
    +  (let* ((r 0w0)
    
    293
    +	 (scale (scale-float 1d0 -31))
    
    294
    +	 (mult scale))
    
    295
    +    (declare (double-float mult)
    
    296
    +	     (type double-double-float r)
    
    297
    +	     (optimize (speed 3) (inhibit-warnings 3)))
    
    298
    +    (dotimes (k 4)
    
    299
    +      (setf r (+ r (* mult (ldb (byte 31 0) (random-chunk state)))))
    
    300
    +      (setf mult (* mult scale)))
    
    301
    +    (* arg r)))
    
    302
    +
    
    303
    +
    
    304
    +;;;; Random integers:
    
    305
    +
    
    306
    +;;; Amount we overlap chunks by when building a large integer to make up for
    
    307
    +;;; the loss of randomness in the low bits.
    
    308
    +;;;
    
    309
    +(defconstant random-integer-overlap 3)
    
    310
    +
    
    311
    +;;; Extra bits of randomness that we generate before taking the value MOD the
    
    312
    +;;; limit, to avoid loss of randomness near the limit.
    
    313
    +;;;
    
    314
    +(defconstant random-integer-extra-bits 10)
    
    315
    +
    
    316
    +;;; Largest fixnum we can compute from one chunk of bits.
    
    317
    +;;;
    
    318
    +(defconstant random-fixnum-max
    
    319
    +  (1- (ash 1 (- random-chunk-length random-integer-extra-bits))))
    
    320
    +
    
    321
    +
    
    322
    +;;; %RANDOM-INTEGER  --  Internal
    
    323
    +;;;
    
    324
    +(defun %random-integer (arg state)
    
    325
    +  (declare (type (integer 1) arg) (type random-state state))
    
    326
    +  (let ((shift (- random-chunk-length random-integer-overlap)))
    
    327
    +    (do ((bits (random-chunk state)
    
    328
    +	       (logxor (ash bits shift) (random-chunk state)))
    
    329
    +	 (count (+ (integer-length arg)
    
    330
    +		   (- random-integer-extra-bits shift))
    
    331
    +		(- count shift)))
    
    332
    +	((minusp count)
    
    333
    +	 (rem bits arg))
    
    334
    +      (declare (fixnum count)))))
    
    335
    +
    
    336
    +(defun random (arg &optional (state *random-state*))
    
    337
    +  "Generate a uniformly distributed pseudo-random number between zero
    
    338
    +  and Arg.  State, if supplied, is the random state to use."
    
    339
    +  (declare (inline %random-single-float %random-double-float
    
    340
    +		   #+long-float %long-float))
    
    341
    +  (cond
    
    342
    +    ((typep arg '(integer 1 #x100000000))
    
    343
    +     ;; Let the compiler deftransform take care of this case.
    
    344
    +     (random arg state))
    
    345
    +    ((and (typep arg 'single-float) (> arg 0.0F0))
    
    346
    +     (%random-single-float arg state))
    
    347
    +    ((and (typep arg 'double-float) (> arg 0.0D0))
    
    348
    +     (%random-double-float arg state))
    
    349
    +    #+long-float
    
    350
    +    ((and (typep arg 'long-float) (> arg 0.0L0))
    
    351
    +     (%random-long-float arg state))
    
    352
    +    #+double-double
    
    353
    +    ((and (typep arg 'double-double-float) (> arg 0.0w0))
    
    354
    +     (%random-double-double-float arg state))
    
    355
    +    ((and (integerp arg) (> arg 0))
    
    356
    +     (%random-integer arg state))
    
    357
    +    (t
    
    358
    +     (error 'simple-type-error
    
    359
    +	    :expected-type '(or (integer 1) (float (0.0))) :datum arg
    
    360
    +	    :format-control (intl:gettext "Argument is not a positive integer or a positive float: ~S")
    
    361
    +	    :format-arguments (list arg)))))
    
    362
    +