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

Commits:

2 changed files:

Changes:

  • src/code/rand-xoroshiro.lisp
    ... ... @@ -18,7 +18,7 @@
    18 18
     	  make-xoro-random-state))
    
    19 19
     
    
    20 20
     (in-package "KERNEL")
    
    21
    -(export '(%xorohiro-single-float %xorohiro-double-float xoroshiro-chunk init-random-state))
    
    21
    +(export '(%xorohiro-single-float %xorohiro-double-float xoroshiro-chunk init-xoro-state))
    
    22 22
     
    
    23 23
     (sys:register-lisp-feature :random-xoroshiro)
    
    24 24
     
    
    ... ... @@ -80,10 +80,19 @@
    80 80
     (defstruct (xoro-random-state
    
    81 81
     	     (:constructor make-xoroshiro-object)
    
    82 82
     	     (:make-load-form-fun :just-dump-it-normally))
    
    83
    +  ;; The state of the RNG.  The actual algorithm uses 2 64-bit words
    
    84
    +  ;; of state.  To reduce consing, we use an array of double-float's
    
    85
    +  ;; since a double-float is 64 bits long.  At no point do we operate
    
    86
    +  ;; on these as floats; they're just convenient objects to hold the
    
    87
    +  ;; state we need.
    
    83 88
       (state (init-xoro-state)
    
    84 89
        :type (simple-array double-float (2)))
    
    85
    -  (rand (make-array 1 :element-type '(unsigned-byte 32) :initial-element 0)
    
    86
    -   :type (simple-array (unsigned-byte 32) (1)))
    
    90
    +  ;; The generator produces 64-bit results.  We separate the 64-bit
    
    91
    +  ;; result into two parts.  One is returned and the other is cached
    
    92
    +  ;; here for later use.
    
    93
    +  (rand 0 :type (unsigned-byte 32))
    
    94
    +  ;; Indicates if RAND holds a valid value.  If NIL, we need to
    
    95
    +  ;; generate a new 64-bit result.
    
    87 96
       (cached-p nil :type (member t nil)))
    
    88 97
     
    
    89 98
     (defvar *xoro-random-state*)
    
    ... ... @@ -92,13 +101,11 @@
    92 101
       (flet ((copy-random-state (state)
    
    93 102
     	   (let ((old-state (xoro-random-state-state state))
    
    94 103
     		 (new-state
    
    95
    -		  (make-array 2 :element-type 'double-float))
    
    96
    -		 (new-rand (make-array 1 :element-type '(unsigned-byte 32))))
    
    104
    +		  (make-array 2 :element-type 'double-float)))
    
    97 105
     	     (setf (aref new-state 0) (aref old-state 0))
    
    98 106
     	     (setf (aref new-state 1) (aref old-state 1))
    
    99
    -	     (setf (aref new-rand 0) (aref (xoro-random-state-rand state) 0))
    
    100 107
     	     (make-xoroshiro-object :state new-state
    
    101
    -				    :rand new-rand
    
    108
    +				    :rand (xoro-random-state-rand state)
    
    102 109
     				    :cached-p (xoro-random-state-cached-p state)))))
    
    103 110
         (cond ((not state)
    
    104 111
     	   (copy-random-state *xoro-random-state*))
    
    ... ... @@ -106,7 +113,7 @@
    106 113
     	   (copy-random-state state))
    
    107 114
     	  ((eq state t)
    
    108 115
     	   (make-xoroshiro-object :state (init-xoro-state (generate-seed 4))
    
    109
    -				  :rand (make-array 1 :element-type '(unsigned-byte 32) :initial-element 0)
    
    116
    +				  :rand 0
    
    110 117
     				  :cached-p nil))
    
    111 118
     	  (t
    
    112 119
     	   (error "Argument is not a RANDOM-STATE, T, or NIL: ~S" state)))))
    
    ... ... @@ -130,15 +137,15 @@
    130 137
       (let ((cached (xoro-random-state-cached-p rng-state)))
    
    131 138
         (cond (cached
    
    132 139
     	   (setf (xoro-random-state-cached-p rng-state) nil)
    
    133
    -	   (aref (xoro-random-state-rand rng-state) 0))
    
    140
    +	   (xoro-random-state-rand rng-state))
    
    134 141
     	  (t
    
    135 142
     	   (let ((s (xoro-random-state-state rng-state)))
    
    136 143
     	     (declare (type (simple-array double-float (2)) s))
    
    137 144
     	     (multiple-value-bind (r1 r0)
    
    138 145
     		 (vm::xoroshiro-next s)
    
    139
    -	       (setf (aref (xoro-random-state-rand rng-state) 0) r1)
    
    146
    +	       (setf (xoro-random-state-rand rng-state) r0)
    
    140 147
     	       (setf (xoro-random-state-cached-p rng-state) t)
    
    141
    -	       r0))))))
    
    148
    +	       r1))))))
    
    142 149
     
    
    143 150
     #-x86
    
    144 151
     (defun xoroshiro-next (state)
    
    ... ... @@ -267,7 +274,7 @@
    267 274
     	1d0)))
    
    268 275
     
    
    269 276
     #+double-double
    
    270
    -(defun %random-double-double-float (arg state)
    
    277
    +(defun %xoroshiro-double-double-float (arg state)
    
    271 278
       (declare (type (double-double-float (0w0)) arg)
    
    272 279
     	   (type xoro-random-state state))
    
    273 280
       ;; Generate a 31-bit integer, scale it and sum them up
    

  • tests/rng.lisp
    1
    +;; Tests for RNG
    
    2
    +
    
    3
    +(defpackage :rng-tests
    
    4
    +  (:use :cl :lisp-unit))
    
    5
    +
    
    6
    +(in-package "RNG-TESTS")
    
    7
    +
    
    8
    +(defun 64-bit-rng-state (rng)
    
    9
    +  (let ((state (kernel::xoro-random-state-state rng)))
    
    10
    +    (flet ((convert (x)
    
    11
    +	     (multiple-value-bind (hi lo)
    
    12
    +		 (kernel:double-float-bits x)
    
    13
    +	       (logior (ash (ldb (byte 32 0) hi) 32)
    
    14
    +		       lo))))
    
    15
    +      (values (convert (aref state 0)) (convert (aref state 1))))))
    
    16
    +
    
    17
    +(defun 64-bit-value (rng)
    
    18
    +  (logior (ash (kernel::xoroshiro-chunk rng) 32)
    
    19
    +	  (kernel::xoroshiro-chunk rng)))
    
    20
    +
    
    21
    +(defvar *test-state*)
    
    22
    +  
    
    23
    +(define-test rng.initial-state
    
    24
    +  (setf *test-state*
    
    25
    +	(kernel::make-xoroshiro-object :state (kernel::init-xoro-state #x12345678)
    
    26
    +				       :rand 0
    
    27
    +				       :cached-p nil))
    
    28
    +  (multiple-value-bind (s0 s1)
    
    29
    +      (64-bit-rng-state *test-state*)
    
    30
    +    (assert-equal #x38f1dc39d1906b6f s0)
    
    31
    +    (assert-equal #xdfe4142236dd9517 s1)
    
    32
    +    (assert-equal 0 (kernel::xoro-random-state-rand *test-state*))
    
    33
    +    (assert-equal nil (kernel::xoro-random-state-cached-p *test-state*))))
    
    34
    +
    
    35
    +
    
    36
    +(define-test rng.values-test
    
    37
    +  (assert-equal (list #x38f1dc39d1906b6f #xdfe4142236dd9517)
    
    38
    +		(multiple-value-list (64-bit-rng-state *test-state*)))
    
    39
    +  (assert-equal 0 (kernel::xoro-random-state-rand *test-state*))
    
    40
    +  (assert-equal nil (kernel::xoro-random-state-cached-p *test-state*))
    
    41
    +
    
    42
    +  (dolist (item '((#x18d5f05c086e0086 (#x228f4926843b364d #x74dfe78e715c81be))
    
    43
    +		  (#x976f30b4f597b80b (#x5b6bd4558bd96a68 #x567b7f35650aea8f))
    
    44
    +		  (#xb1e7538af0e454f7 (#x13e5253e242fac52 #xed380e70d10ab60e))
    
    45
    +		  (#x011d33aef53a6260 (#x9d0764952ca00d8a #x5251a5cfedd2b4ef))
    
    46
    +		  (#xef590a651a72c279 (#xba4ef2b425bda963 #x172b965cf56c15ac))
    
    47
    +		  (#xd17a89111b29bf0f (#x458277a5e5f0a21b #xd1bccfad6564e8d))
    
    48
    +		  (#x529e44a0bc46f0a8 (#x2becb68d5a7194c7 #x3a6ec964899bb5f3))
    
    49
    +		  (#x665b7ff1e40d4aba (#xededfd481d0a19fe #x3ea213411827fe9d))
    
    50
    +		  (#x2c9010893532189b (#xd7bb59bcd8fba26f #x52de763d34fee090))
    
    51
    +		  (#x2a99cffa0dfa82ff (#xf96e892c62d6ff2e #xc0542ff85652f81e))))
    
    52
    +    (destructuring-bind (value state)
    
    53
    +	item
    
    54
    +      (assert-equal value (64-bit-value *test-state*))
    
    55
    +      (assert-equal state (multiple-value-list (64-bit-rng-state *test-state*))))))