Raymond Toy pushed to branch rtoy-xoro at cmucl / cmucl
Commits:
-
192fe3b6
by Raymond Toy at 2017-12-16T08:16:46-08:00
-
c62e3467
by Raymond Toy at 2017-12-16T08:17:24-08:00
2 changed files:
Changes:
| ... | ... | @@ -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
|
| 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*))))))
|