486f9b2d6055
Implement PCGs
author | Steve Losh <steve@stevelosh.com> |
---|---|
date | Tue, 31 Jan 2017 18:54:39 +0000 |
parents | 02235ffe8b98 |
children | 03240af4df9b |
branches/tags | (none) |
files | src/names.lisp src/random-numbers.lisp |
Changes
--- a/src/names.lisp Thu Jan 26 23:53:51 2017 +0000 +++ b/src/names.lisp Tue Jan 31 18:54:39 2017 +0000 @@ -85,3 +85,4 @@ (Lu cia nia) (Da niela) (Ga brie la)) +
--- a/src/random-numbers.lisp Thu Jan 26 23:53:51 2017 +0000 +++ b/src/random-numbers.lisp Tue Jan 31 18:54:39 2017 +0000 @@ -154,5 +154,147 @@ (first (random-weighted-list weights 1))) +;; from cl-utilities + +;; If we're using the SB-ROTATE-BYTE extension, we should inline our +;; call and let SBCL handle optimization from there. +#+sbcl (declaim (inline rotate-byte)) + +(defun rotate-byte (count bytespec integer) + "Rotates a field of bits within INTEGER; specifically, returns an +integer that contains the bits of INTEGER rotated COUNT times +leftwards within the byte specified by BYTESPEC, and elsewhere +contains the bits of INTEGER. See http://www.cliki.net/ROTATE-BYTE" + (declare (optimize (speed 3) (safety 0) (space 0) (debug 1))) + #-sbcl + (let ((size (byte-size bytespec))) + (when (= size 0) + (return-from rotate-byte integer)) + (let ((count (mod count size))) + (flet ((rotate-byte-from-0 (count size integer) + (let ((bytespec (byte size 0))) + (if (> count 0) + (logior (ldb bytespec (ash integer count)) + (ldb bytespec (ash integer (- count size)))) + (logior (ldb bytespec (ash integer count)) + (ldb bytespec (ash integer (+ count size)))))))) + (dpb (rotate-byte-from-0 count size (ldb bytespec integer)) + bytespec + integer)))) + ;; On SBCL, we use the SB-ROTATE-BYTE extension. + #+sbcl (sb-rotate-byte:rotate-byte count bytespec integer)) + + +;;;; PCG +(defstruct (pcg (:constructor make-pcg%)) + (state 0 :type (unsigned-byte 64)) + (increment 0 :type (unsigned-byte 64))) + +(declaim (inline permute-xor-shift permute-rotate advance-state)) + +(defun permute-xor-shift (data) + (declare (optimize (speed 3) (debug 0) (safety 1)) + (type (unsigned-byte 37) data)) + (-<> data + (ash <> -18) + (logxor data <>) + (ldb (byte 32 0) <>))) + +(defun permute-rotate (data selector) + (declare (optimize (speed 3) (debug 0) (safety 1)) + (type (unsigned-byte 32) data) + (type (unsigned-byte 5) selector)) + (sb-rotate-byte:rotate-byte selector + (byte 32 0) + data)) + +(defun advance-state (pcg) + (declare (optimize (speed 3) (debug 0) (safety 1)) + (type pcg pcg)) + (setf (pcg-state pcg) + (mod (+ (* (pcg-state pcg) 6364136223846793005) + (pcg-increment pcg)) + (expt 2 64))) + nil) + + +; uint64_t oldstate = rng->state; +; rng->state = oldstate * 6364136223846793005ULL + rng->inc; +; bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +; >>27 +; bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb........................... +; assign to uint32 fuckin lol +; .....bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb........................... + +; uint32_t xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u; +; uint32_t rot = oldstate >> 59u; +; return (xorshifted >> rot) | (xorshifted << ((-rot) & 31)); +(declaim (ftype (function (pcg) (unsigned-byte 32)) pcg)) + +(defun-inlineable pcg-random (pcg) + (declare (optimize (speed 3) (debug 0) (safety 1)) + (type pcg pcg)) + (let* ((state (pcg-state pcg)) + (data (ldb (byte 37 (- 64 37)) state)) + (selector (ldb (byte 5 (- 64 5)) state))) + (advance-state pcg) + (-<> data + (permute-xor-shift <>) + (permute-rotate <> selector)))) + +(defun make-pcg (seed &optional (stream-id 0)) + (let* ((increment (logior 1 (ash stream-id 1))) + (pcg (make-pcg% :state 0 :increment increment))) + (pcg-random pcg) + (incf (pcg-state pcg) seed) + (modf (pcg-state pcg) 64) + (pcg-random pcg) + pcg)) + +(defun pcg-random-bounded (pcg bound) + (declare + (optimize (speed 3) (debug 0) (safety 1)) + (type pcg pcg) + (type (and (unsigned-byte 32) + (integer 1)) + bound) + (inline pcg-random)) + (loop + :with threshold = (mod (expt 2 32) bound) + :for n = (pcg-random pcg) + :when (>= n threshold) + :do (return (values (mod n bound))))) + +(declaim (ftype (function (pcg (integer 1 32)) + (unsigned-byte 32)) pcg-random-bits)) + +(defun-inline pcg-random-bits (pcg count) + (declare (optimize (speed 3) (debug 0) (safety 1)) + (inline pcg-random)) + (ldb (byte count 0) (pcg-random pcg))) + +(defun pcg-random-float (pcg) + (declare (optimize (speed 3) (debug 0) (safety 1)) + (type pcg pcg)) + ; https://en.wikipedia.org/wiki/Single-precision_floating-point_format + ; Singles have 24 bits of precision + (/ (pcg-random-bits pcg 24) + (coerce (expt 2 24) 'single-float))) + +; https://en.wikipedia.org/wiki/Double-precision_floating-point_format +; Doubles have 53 bits of precision +(defun pcg-random-double (pcg) + (declare (optimize (speed 3) (debug 0) (safety 1)) + (type pcg pcg)) + (/ (logior (ash (pcg-random-bits pcg 26) 27) + (pcg-random-bits pcg 27)) + (coerce (expt 2 53) 'double-float))) + +(defun pcg-random-range (pcg min max) + (+ min (pcg-random-bounded pcg (- max min)))) + +(defun pcg-random-range-inclusive (pcg min max) + (+ min (pcg-random-bounded pcg (1+ (- max min))))) + ;;;; Scratch ; (spectral)