aboutsummaryrefslogtreecommitdiffstats
path: root/194-impl.scm
diff options
context:
space:
mode:
authorGravatar Peter McGoron 2025-02-13 17:35:22 -0500
committerGravatar Peter McGoron 2025-02-13 17:35:22 -0500
commit58ef077b9eefebc7ccacb13159047f038ab847bc (patch)
tree4581e700dc9d03f3298c4ed2b6621dc80a272c36 /194-impl.scm
parentchange parameter to CHICKENs SRFI-27 parameter (diff)
use CHICKEN SRFI-27 generators whenever possible
Diffstat (limited to '194-impl.scm')
-rw-r--r--194-impl.scm340
1 files changed, 20 insertions, 320 deletions
diff --git a/194-impl.scm b/194-impl.scm
index 8936d35..ae98046 100644
--- a/194-impl.scm
+++ b/194-impl.scm
@@ -40,18 +40,8 @@
;;
(define (make-random-integer-generator low-bound up-bound)
- (unless (and (integer? low-bound)
- (exact? low-bound))
- (error "expected exact integer for lower bound"))
- (unless (and (integer? up-bound)
- (exact? up-bound))
- (error "expected exact integer for upper bound"))
- (unless (< low-bound up-bound)
- (error "upper bound should be greater than lower bound"))
- (let ((rand-int-proc (random-source-make-integers (current-random-source)))
- (range (- up-bound low-bound)))
- (lambda ()
- (+ low-bound (rand-int-proc range)))))
+ (make-uniform-random-integers high: (- up-bound 1)
+ low: low-bound))
(define (make-random-u1-generator)
(make-random-integer-generator 0 2))
@@ -142,10 +132,7 @@
(r (sqrt (+ (* m t) b))))
(+ origin (make-polar r phi))))))))
-(define (make-random-boolean-generator)
- (define u1 (make-random-u1-generator))
- (lambda ()
- (zero? (u1))))
+(define (make-random-boolean-generator) (make-random-bernoullis))
(define (make-random-char-generator str)
(when (not (string? str))
@@ -168,16 +155,17 @@
(define PI (* 4 (atan 1.0)))
-(define (make-bernoulli-generator p)
- (unless (real? p)
- (error "expected p to be real"))
- (unless (<= 0 p 1)
- (error "expected 0 <= p <= 1"))
- (let ((rand-real-proc (random-source-make-reals (current-random-source))))
- (lambda ()
- (if (<= (rand-real-proc) p)
- 1
- 0))))
+(define make-bernoulli-generator
+ (case-lambda
+ (() (make-bernoulli-generator 1/2 (current-random-real)))
+ ((p) (make-bernoulli-generator p (current-random-real)))
+ ((p source)
+ (gmap (lambda (x) (if x 1 0))
+ (make-random-bernoullis p: p randoms: source)))))
+
+(define (make-binomial-generator n p)
+ ;; Already in CHICKEN's SRFI-27
+ (make-random-binomials t: n p: p randoms: (current-random-real)))
(define (make-categorical-generator weights-vec)
(define weight-sum
@@ -203,40 +191,13 @@
(it newsum
(+ i 1)))))))
-;; Normal distribution (continuous - generates real numbers)
-;; Box-Muller algorithm
-;; NB: We tested Ziggurat method, too,
-;; only to find out Box-Muller is faster about 12% - presumably
-;; the overhead of each ops is larger in Gauche than C/C++, and
-;; so the difference of cost of log or sin from the primitive
-;; addition/multiplication are negligible.
-
-;; NOTE: this implementation is not thread safe
(define make-normal-generator
(case-lambda
- (()
- (make-normal-generator 0.0 1.0))
- ((mean)
- (make-normal-generator mean 1.0))
+ (() (make-normal-generator #i0 #i1))
+ ((mean) (make-normal-generator mean #i1))
((mean deviation)
- (let ((rand-real-proc (random-source-make-reals (current-random-source)))
- (state #f))
- (unless (and (real? mean)
- (finite? mean))
- (error "expected mean to be finite real number"))
- (unless (and (real? deviation)
- (finite? deviation)
- (> deviation 0))
- (error "expected deviation to be positive finite real number"))
- (lambda ()
- (if state
- (let ((result state))
- (set! state #f)
- result)
- (let* ((r (sqrt (* -2 (log (rand-real-proc)))))
- (theta (* 2 PI (rand-real-proc))))
- (set! state (+ mean (* deviation r (cos theta))))
- (+ mean (* deviation r (sin theta))))))))))
+ (make-random-normals mu: mean sigma: deviation
+ randoms: (current-random-real)))))
(define (make-exponential-generator mean)
(unless (and (real? mean)
@@ -248,114 +209,10 @@
(- (* mean (log (rand-real-proc)))))))
(define (make-geometric-generator p)
+ (make-random-geometrics p: p randoms: (current-random-real)))
- (define (log1p x)
- ;; Adapted from SRFI 144
- (let ((u (+ 1.0 x)))
- (cond ((= u 1.0)
- x) ;; gets sign of zero result correct
- ((= u x)
- (log u)) ;; large arguments and infinities
- (else
- (* (log u) (/ x (- u 1.0)))))))
-
- (unless (and (real? p)
- (> p 0)
- (<= p 1))
- (error "expected p to be real number, 0 < p <= 1"))
- (if (zero? (- p 1.))
- ;; p is indistinguishable from 1.
- (lambda () 1)
- (let ((c (/ (log1p (- p))))
- (rand-real-proc (random-source-make-reals (current-random-source))))
- (lambda ()
- (exact (ceiling (* c (log (rand-real-proc)))))))))
-
-;; Draw from poisson distribution with mean L, variance L.
-;; For small L, we use Knuth's method. For larger L, we use rejection
-;; method by Atkinson, The Computer Generation of Poisson Random Variables,
-;; J. of the Royal Statistical Society Series C (Applied Statistics), 28(1),
-;; pp29-35, 1979. The code here is a port by John D Cook's C++ implementation
-;; (http://www.johndcook.com/stand_alone_code.html )
-
-;; NOTE: this implementation calculates and stores a table of log(n!) on first invocation of L >= 36
-;; and therefore is not entirely thread safe (should still produce correct result, but with performance hit if table
-;; is recalculated multiple times)
(define (make-poisson-generator L)
- (unless (and (real? L)
- (finite? L)
- (> L 0))
- (error "expected L to be finite positive real number"))
- (let ((rand-real-proc (random-source-make-reals (current-random-source))))
- (if (< L 30)
- (make-poisson/small rand-real-proc L)
- (make-poisson/large rand-real-proc L))))
-
-;; private
-(define (make-poisson/small rand-real-proc L)
- (lambda ()
- (do ((exp-L (exp (- L)))
- (k 0 (+ k 1))
- (p 1.0 (* p (rand-real-proc))))
- ((<= p exp-L) (- k 1)))))
-
-;; private
-(define (make-poisson/large rand-real-proc L)
- (let* ((c (- 0.767 (/ 3.36 L)))
- (beta (/ PI (sqrt (* 3 L))))
- (alpha (* beta L))
- (k (- (log c) L (log beta))))
- (define (loop)
- (let* ((u (rand-real-proc))
- (x (/ (- alpha (log (/ (- 1.0 u) u))) beta))
- (n (exact (floor (+ x 0.5)))))
- (if (< n 0)
- (loop)
- (let* ((v (rand-real-proc))
- (y (- alpha (* beta x)))
- (t (+ 1.0 (exp y)))
- (lhs (+ y (log (/ v (* t t)))))
- (rhs (+ k (* n (log L)) (- (log-of-fact n)))))
- (if (<= lhs rhs)
- n
- (loop))))))
- loop))
-
-;; private
-;; log(n!) table for n 1 to 256. Vector, where nth index corresponds to log((n+1)!)
-;; Computed on first invocation of `log-of-fact`
-(define log-fact-table #f)
-
-;; private
-;; computes log-fact-table
-;; log(n!) = log((n-1)!) + log(n)
-(define (make-log-fact-table!)
- (define table (make-vector 256))
- (vector-set! table 0 0)
- (do ((i 1 (+ i 1)))
- ((> i 255) #t)
- (vector-set! table i (+ (vector-ref table (- i 1))
- (log (+ i 1)))))
- (set! log-fact-table table))
-
-;; private
-;; returns log(n!)
-;; adapted from https://www.johndcook.com/blog/2010/08/16/how-to-compute-log-factorial/
-(define (log-of-fact n)
- (when (not log-fact-table)
- (make-log-fact-table!))
- (cond
- ((<= n 1) 0)
- ((<= n 256) (vector-ref log-fact-table (- n 1)))
- (else (let ((x (+ n 1)))
- (+ (* (- x 0.5)
- (log x))
- (- x)
- (* 0.5
- (log (* 2 PI)))
- (/ 1.0 (* x 12.0)))))))
-
-
+ (make-random-poissons mu: L random: (current-random-real)))
(define (gsampling . generators-lst)
(let ((gen-vec (list->vector generators-lst))
@@ -392,160 +249,3 @@
(pick)))))
-;;; Code for binomial random variable generation.
-;;; Written by Brad Lucier, lucier@math.purdue.edu
-
-;;; binomial-geometric is somewhat classical, the
-;;; "First waiting time algorithm" from page 525 of
-;;; Devroye, L. (1986), Non-Uniform Random Variate
-;;; Generation, Springer-Verlag, New York.
-
-;;; binomial-rejection is algorithm BTRS from
-;;; Hormann, W. (1993), The generation of binomial
-;;; random variates, Journal of Statistical Computation
-;;; and Simulation, 46:1-2, 101-110,
-;;; DOI: https://doi.org/10.1080/00949659308811496
-;;; stirling-tail and BTRD (mentioned in the comments)
-;;; are also from that paper.
-
-;;; Another implementation of the same algorithm is at
-;;; https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/kernels/random_binomial_op.cc
-;;; That implementation pointed out at least two bugs in the
-;;; BTRS paper.
-
-(define (stirling-tail k)
- ;; Computes
- ;;
- ;; \log(k!)-[\log(\sqrt{2\pi})+(k+\frac12)\log(k+1)-(k+1)]
- ;;
- (let ((small-k-table
- ;; Computed using computable reals package
- ;; Matches values in paper, which are given
- ;; for 0\leq k < 10
- '#(.08106146679532726
- .0413406959554093
- .02767792568499834
- .020790672103765093
- .016644691189821193
- .013876128823070748
- .01189670994589177
- .010411265261972096
- .009255462182712733
- .00833056343336287
- .007573675487951841
- .00694284010720953
- .006408994188004207
- .0059513701127588475
- .005554733551962801
- .0052076559196096404
- .004901395948434738
- .004629153749334028
- .004385560249232324
- .004166319691996922)))
- (if (< k 20)
- (vector-ref small-k-table k)
- ;; the correction term (+ (/ (* 12 (+ k 1))) ...)
- ;; in Stirling's approximation to log(k!)
- (let* ((inexact-k+1 (inexact (+ k 1)))
- (inexact-k+1^2 (square inexact-k+1)))
- (/ (- #i1/12
- (/ (- #i1/360
- (/ #i1/1260 inexact-k+1^2))
- inexact-k+1^2))
- inexact-k+1)))))
-
-(define (make-binomial-generator n p)
- (if (not (and (real? p)
- (<= 0 p 1)
- (exact-integer? n)
- (positive? n)))
- (error "make-binomial-generator: Bad parameters: " n p)
- (cond ((< 1/2 p)
- (let ((complement (make-binomial-generator n (- 1 p))))
- (lambda ()
- (- n (complement)))))
- ((zero? p)
- (lambda () 0))
- ((< (* n p) 10)
- (binomial-geometric n p))
- (else
- (binomial-rejection n p)))))
-
-(define (binomial-geometric n p)
- (let ((geom (make-geometric-generator p)))
- (lambda ()
- (let loop ((X -1)
- (sum 0))
- (if (< n sum)
- X
- (loop (+ X 1)
- (+ sum (geom))))))))
-
-(define (binomial-rejection n p)
- ;; call when p <= 1/2 and np >= 10
- ;; Use notation from the paper
- (let* ((spq
- (inexact (sqrt (* n p (- 1 p)))))
- (b
- (+ 1.15 (* 2.53 spq)))
- (a
- (+ -0.0873
- (* 0.0248 b)
- (* 0.01 p)))
- (c
- (+ (* n p) 0.5))
- (v_r
- (- 0.92
- (/ 4.2 b)))
- (alpha
- ;; The formula in BTRS has 1.5 instead of 5.1;
- ;; The formula for alpha in algorithm BTRD and Table 1
- ;; and the tensorflow code uses 5.1, so we use 5.1
- (* (+ 2.83 (/ 5.1 b)) spq))
- (lpq
- (log (/ p (- 1 p))))
- (m
- (exact (floor (* (+ n 1) p))))
- (rand-real-proc
- (random-source-make-reals (current-random-source))))
- (lambda ()
- (let loop ()
- (let* ((u (rand-real-proc))
- (v (rand-real-proc))
- (u
- (- u 0.5))
- (us
- (- 0.5 (abs u)))
- (k
- (exact
- (floor
- (+ (* (+ (* 2. (/ a us)) b) u) c)))))
- (cond ((or (< k 0)
- (< n k))
- (loop))
- ((and (<= 0.07 us)
- (<= v v_r))
- k)
- (else
- (let ((v
- ;; The tensorflow code notes that BTRS doesn't have
- ;; this logarithm; BTRS is incorrect (see BTRD, step 3.2)
- (log (* v (/ alpha
- (+ (/ a (square us)) b))))))
- (if (<= v
- (+ (* (+ m 0.5)
- (log (* (/ (+ m 1.)
- (- n m -1.)))))
- (* (+ n 1.)
- (log (/ (- n m -1.)
- (- n k -1.))))
- (* (+ k 0.5)
- (log (* (/ (- n k -1.)
- (+ k 1.)))))
- (* (- k m) lpq)
- (- (+ (stirling-tail m)
- (stirling-tail (- n m)))
- (+ (stirling-tail k)
- (stirling-tail (- n k))))))
- k
- (loop))))))))))