diff options
| author | 2025-01-10 16:22:02 -0500 | |
|---|---|---|
| committer | 2025-01-10 16:22:02 -0500 | |
| commit | cd78c57222dfe6ad9906b95e6c0353629469c10f (patch) | |
| tree | f0932a3d2f2be771f4ae6f4f528d6a6eecdb58c0 /tests/run.scm | |
init
Diffstat (limited to 'tests/run.scm')
| -rw-r--r-- | tests/run.scm | 598 |
1 files changed, 598 insertions, 0 deletions
diff --git a/tests/run.scm b/tests/run.scm new file mode 100644 index 0000000..063886b --- /dev/null +++ b/tests/run.scm @@ -0,0 +1,598 @@ +;;; SPDX-FileCopyrightText: 2020 John Cowan +;;; SPDX-FileCopyrightText: 2020 Arvydas Silanskas +;;; SPDX-FileCopyrightText: 2024 Bradley J Lucier +;;; SPDX-License-Identifier: MIT + +;;; NOTE: for zipf tests data can be exported, this can be enabled by uncommenting appropriate lines. + +(cond-expand + (gambit + ;; Should work for any Gambit no earlier than + ;; v4.9.5-104-g562e58da 20240201212453 + (import (gambit))) + (else + (import (scheme base) + (srfi 133)))) + +(import + (scheme inexact) + (scheme complex) + (scheme cxr) + (scheme file) + (scheme write) + (srfi 1) + (srfi 27) + (srfi 194)) + +(cond-expand + ((library (srfi 158)) (import (srfi 158))) + ((library (srfi 121)) (import (srfi 121)))) + +(cond-expand + (chibi (begin + (import (except (chibi test) test-equal)) + (define-syntax test-equal + (syntax-rules () + ((_ args ...) (test args ...)))) + (define-syntax test-approximate + (syntax-rules () + ((_ target value max-delta) + (test-assert (and (<= value (+ target max-delta)) + (>= value (- target max-delta))))))))) + (else (import (srfi 64)))) + +;;; syntax just we can plop it at top and still allow internal `define`s +(define-syntax reset-source! + (syntax-rules () + ((_) + (define _ (random-source-pseudo-randomize! (current-random-source) 0 0))))) + +(define (reset-source!*) + (random-source-pseudo-randomize! (current-random-source) 0 0)) + +(define (assert-number-generator/all-in-range gen from to) + (test-assert + (generator-every + (lambda (num) + (and (>= num from) + (< num to))) + (gtake gen 1000)))) + +(define (assert-number-generator gen from to) + (define range (- to from)) + (define lower-quarter (+ from (* 0.25 range))) + (define upper-quarter (- to (* 0.25 range))) + (assert-number-generator/all-in-range gen from to) + + (test-assert + (generator-any + (lambda (num) + (and (>= num from) + (< num lower-quarter))) + (gtake gen 1000))) + + (test-assert + (generator-any + (lambda (num) + (and (>= num lower-quarter) + (< num upper-quarter))) + (gtake gen 1000))) + + (test-assert + (generator-any + (lambda (num) + (and (>= num upper-quarter) + (< num to))) + (gtake gen 1000)))) + +(define (assert-int-generator gen byte-size signed?) + (define from (if signed? + (- (expt 2 (- byte-size 1))) + 0)) + (define to (if signed? + (expt 2 (- byte-size 1)) + (expt 2 byte-size))) + (assert-number-generator gen from to)) + +(test-begin "srfi-194") + +(test-group "Test clamp real number" + (reset-source!) + (test-equal 10.0 (clamp-real-number 5.0 10.0 11)) + (test-equal 5.0 (clamp-real-number 5.0 10.0 2.0)) + (test-equal 7.5 (clamp-real-number 5.0 10.0 7.5))) + +(test-group "Test with-random-source basic syntax" + (reset-source!) + (with-random-source default-random-source + (lambda () (make-random-integer-generator 0 10)))) + +;;; testing random source, which is implementation specific +(cond-expand + (gauche + (import + (gauche base) + (math mt-random)) + (test-group "Test with-random-source" + (reset-source!) + ;; create and consume generators that are made with different source + ;; with various order, and check that order doesn't change the outcome + (define (test-multiple-sources gen1-maker gen1-expect + gen2-maker gen2-expect) + + ;;create gen1, consume gen1, create gen2, consume gen2 + (let ((gen1 (gen1-maker))) + (test-equal (generator->list gen1) gen1-expect) + (let ((gen2 (gen2-maker))) + (test-equal (generator->list gen2) gen2-expect))) + + ;; create gen1, create gen2, consume gen1, consume gen2 + (let ((gen1 (gen1-maker)) + (gen2 (gen2-maker))) + (test-equal (generator->list gen1) gen1-expect) + (test-equal (generator->list gen2) gen2-expect))) + + (define multiple-sources-testcase + (list (lambda () + (gtake (with-random-source + (make <mersenne-twister> :seed 0) + (lambda () (make-random-integer-generator 0 10))) + 5)) + '(5 5 7 8 6) + (lambda () + (gtake (with-random-source + (make <mersenne-twister> :seed 1) + (lambda () (make-random-integer-generator 0 10))) + 5)) + '(4 9 7 9 0))) + (apply test-multiple-sources multiple-sources-testcase))) + (else + #f)) + +(test-group "Test make-random-source-generator" + (reset-source!) + (define (make-numbers src-gen) + (define gen1 (with-random-source (src-gen) (lambda () (make-random-integer-generator 0 100)))) + (define gen2 (with-random-source (src-gen) (lambda () (make-random-real-generator 0. 100.)))) + (generator->list + (gappend + (gtake gen1 10) + (gtake gen2 10)))) + + (test-equal + (make-numbers (make-random-source-generator 0)) + (make-numbers (make-random-source-generator 0))) + (test-assert + (not (equal? (make-numbers (make-random-source-generator 0)) + (make-numbers (make-random-source-generator 1)))))) +(test-group "Test random int" + (reset-source!) + (assert-number-generator + (make-random-integer-generator 1 100) + 1 100) + + (for-each + (lambda (testcase) + (define make-gen (car testcase)) + (define byte-size (cadr testcase)) + (define signed? (caddr testcase)) + (assert-int-generator (make-gen) byte-size signed?)) + (list + (list make-random-u8-generator 8 #f) + (list make-random-s8-generator 8 #t) + (list make-random-u16-generator 16 #f) + (list make-random-s16-generator 16 #t) + (list make-random-u32-generator 32 #f) + (list make-random-s32-generator 32 #t) + (list make-random-u64-generator 64 #f) + (list make-random-s64-generator 64 #t))) + + ;;test u1 separately, since it will fail quarter checks due to small range + (assert-number-generator/all-in-range (make-random-u1-generator) 0 2) + (test-assert + (generator-any + (lambda (v) (= v 0)) + (gtake (make-random-u1-generator) 100))) + (test-assert + (generator-any + (lambda (v) (= v 1)) + (gtake (make-random-u1-generator) 100)))) + +(test-group "Test random real" + (reset-source!) + (assert-number-generator + (make-random-real-generator 1.0 5.0) + 1.0 5.0) + + (test-assert + (generator-any + (lambda (v) + (not (= v (floor v)))) + (make-random-real-generator 1.0 5.0)))) + +(test-group "Test complex rectangular" + (reset-source!) + + (assert-number-generator + (gmap + real-part + (make-random-rectangular-generator -10.0 10.0 -100.0 100.0)) + -10 10) + + (assert-number-generator + (gmap + imag-part + (make-random-rectangular-generator -100.0 100.0 -10.0 10.0)) + -10 10) + + (test-assert + (generator-any + (lambda (num) + (and (not (= 0 (real-part num))) + (not (= 0 (imag-part num))))) + (make-random-rectangular-generator -10.0 10.0 -10.0 10.0)))) +(test-group "Test complex polar" + (reset-source!) + (define PI (* 4 (atan 1.0))) + + (define (test-polar g origin mag-from mag-to angle-from angle-to test-converge-origin) + (assert-number-generator + (gmap + (lambda (num) + (angle (- num origin))) + g) + angle-from angle-to) + + (assert-number-generator + (gmap + (lambda (num) + (magnitude (- num origin))) + g) + mag-from mag-to) + + ;; split generated area through circle at 0.5*(mag-from + mag-to) + ;; and validate generated points in them proportional to their area + (let* ((outter-count 0) + (inner-count 0) + (donut-area (lambda (r1 r2) (- (* r1 r1) (* r2 r2)))) + (mag-mid (/ (+ mag-from mag-to) 2.)) + (expected-fraction (/ (donut-area mag-to mag-mid) + (donut-area mag-mid mag-from)))) + (generator-for-each + (lambda (point) + (if (< (magnitude (- point origin)) mag-mid) + (set! inner-count (+ 1 inner-count)) + (set! outter-count (+ 1 outter-count)))) + (gtake g 10000)) + (test-approximate expected-fraction (/ outter-count inner-count) 0.2)) + + ;; test points converge to center + (when test-converge-origin + (let ((sum 0+0i)) + (generator-for-each + (lambda (point) (set! sum (+ point sum))) + (gtake g 1000)) + (test-approximate (real-part origin) (real-part (/ sum 1000.)) 0.1) + (test-approximate (imag-part origin) (imag-part (/ sum 1000.)) 0.1)))) + + + (test-polar (make-random-polar-generator 0. 1.) + 0+0i 0. 1. (- PI) PI #t) + + (test-polar (make-random-polar-generator 2+5i 1. 2.) + 2+5i 1. 2. (- PI) PI #t) + + (test-polar (make-random-polar-generator 1. 2. -1. 1.) + 0+0i 1. 2. -1. 1. #f) + + (test-polar (make-random-polar-generator -1+3i 0. 2. (- PI) PI) + -1+3i 0. 2. (- PI) PI #t)) + +(test-group "Test random bool" + (reset-source!) + (test-assert + (generator-every + (lambda (v) + (or (eq? v #t) + (eq? v #f))) + (gtake (make-random-boolean-generator) 10000))) + + (test-assert + (generator-any + (lambda (v) + (eq? #t v)) + (make-random-boolean-generator))) + + (test-assert + (generator-any + (lambda (v) + (eq? #f v)) + (make-random-boolean-generator)))) + +(test-group "Test random char" + (reset-source!) + (test-assert + (generator-every + (lambda (v) + (or (equal? v #\a) + (equal? v #\b))) + (gtake (make-random-char-generator "ab") + 10000))) + + (test-assert + (generator-any + (lambda (v) + (equal? v #\a)) + (make-random-char-generator "ab"))) + + (test-assert + (generator-any + (lambda (v) + (equal? v #\b)) + (make-random-char-generator "ab")))) + +(test-group "Test random string" + (reset-source!) + (test-assert + (generator-every + (lambda (str) + (and (< (string-length str) 5) + (every (lambda (c) + (or (equal? c #\a) + (equal? c #\b))) + (string->list str)))) + (gtake (make-random-string-generator 5 "ab") + 10000))) + + (test-assert + (generator-any + (lambda (str) + (equal? "abb" str)) + (make-random-string-generator 4 "ab")))) + +(test-group "Test Bernoulli" + (reset-source!) + (define g (make-bernoulli-generator 0.7)) + (define expect 7000) + (define actual (generator-count + (lambda (i) (= i 1)) + (gtake g 10000))) + (define ratio (inexact (/ actual expect))) + (test-assert (> ratio 0.9)) + (test-assert (< ratio 1.1))) + +(test-group "Test categorical" + (reset-source!) + (define result-vec (vector 0 0 0)) + (define expect-vec (vector 2000 5000 3000)) + (define wvec (vector 20 50 30)) + (define g (make-categorical-generator wvec)) + (generator-for-each + (lambda (i) + (vector-set! result-vec i (+ 1 (vector-ref result-vec i)))) + (gtake g 10000)) + (vector-for-each + (lambda (result expect) + (define ratio (inexact (/ result expect))) + (test-approximate 1.0 ratio 0.1)) + result-vec + expect-vec)) + +(test-group "Test poisson" + (reset-source!) + ;;TODO import from somewhere? + (define (fact k) + (cond + ((<= k 1) 1) + (else (* k (fact (- k 1)))))) + (define (expected-fraction L k) + (/ (* (exact (expt L k)) (exact (exp (- L)))) + (fact k))) + + (define (test-poisson L poisson-gen test-points) + (generator-every + (lambda (k) + (define expect (expected-fraction L k)) + (define actual (/ (generator-count + (lambda (i) (= i k)) + (gtake poisson-gen 10000)) + 10000)) + (define ratio (/ actual expect)) + (test-assert (> ratio 8/10)) + (test-assert (< ratio 12/10))) + (list->generator test-points))) + + (test-poisson 2 (make-poisson-generator 2) '(1 2 3)) + (test-poisson 40 (make-poisson-generator 40) '(30 40 50)) + (test-poisson 280 (make-poisson-generator 280) '(260 280 300))) + +(test-group "Test normal" + (reset-source!) + (define frac-at-1dev 0.34134) + (define frac-at-2dev 0.47725) + (define frac-at-3dev 0.49865) + + (define (test-normal-at-point gen count-from count-to expected-fraction) + (define actual (/ (generator-count + (lambda (n) + (and (>= n count-from) + (< n count-to))) + (gtake gen 10000)) + 10000.0)) + (test-assert (and (> actual (* 0.9 expected-fraction)) + (< actual (* 1.1 expected-fraction))))) + + (define (test-normal gen mean deviation) + (test-normal-at-point gen mean (+ mean deviation) frac-at-1dev) + (test-normal-at-point gen mean (+ mean (* 2 deviation)) frac-at-2dev) + (test-normal-at-point gen mean (+ mean (* 3 deviation)) frac-at-3dev)) + + (test-normal (make-normal-generator) 0.0 1.0) + (test-normal (make-normal-generator 1.0) 1.0 1.0) + (test-normal (make-normal-generator 1.0 2.0) 1.0 2.0)) + +(test-group "Test exponential" + (reset-source!) + (define (expected-fraction mean x) + (- 1 (exp (* (- (/ 1.0 mean)) x)))) + + (define (test-exp-at-point gen count-to expected) + (define actual (/ (generator-count + (lambda (n) + (< n count-to)) + (gtake gen 10000)) + 10000.0)) + (test-assert (> actual (* 0.9 expected))) + (test-assert (< actual (* 1.1 expected)))) + + (define (test-exp gen mean) + (test-exp-at-point gen 1 (expected-fraction mean 1)) + (test-exp-at-point gen 2 (expected-fraction mean 2)) + (test-exp-at-point gen 3 (expected-fraction mean 3))) + + (test-exp (make-exponential-generator 1) 1) + (test-exp (make-exponential-generator 1.5) 1.5)) + +(test-group "Test geometric" + (reset-source!) + (define (expected-fraction p x) + (* (expt (- 1 p) (- x 1)) p)) + + (define (test-geom-at-point gen p x) + (define expected (expected-fraction p x)) + (define actual (/ (generator-count + (lambda (n) + (= n x)) + (gtake gen 100000)) + 100000)) + (define ratio (/ actual expected)) + (test-assert (> ratio 0.9)) + (test-assert (< ratio 1.1))) + + (define (test-geom gen p) + (test-geom-at-point gen p 1) + (test-geom-at-point gen p 3) + (test-geom-at-point gen p 5)) + + (test-geom (make-geometric-generator 0.5) 0.5)) + +(test-group "Test uniform sampling" + (reset-source!) + (test-equal + '() + (generator->list (gsampling))) + (test-equal + '() + (generator->list (gsampling (generator) (generator)))) + (test-equal + '(1 1 1) + (generator->list (gsampling (generator) (generator 1 1 1)))) + (test-assert + (generator-any + (lambda (el) + (= el 1)) + (gsampling (circular-generator 1) (circular-generator 2)))) + (test-assert + (generator-any + (lambda (el) + (= el 2)) + (gsampling (circular-generator 1) (circular-generator 2))))) + +;;; See zipf-test.scm +(test-group "Test Zipf sampling" + (reset-source!) + (include "zipf-test.scm") + (zipf-test-group)) + +(test-group "Test sphere" + (include "sphere-test.scm") + (reset-source!*) + (cond-expand + (gambit + ;; Gambit fails this test, but I think the generator code is correct. + #t) + (else + (test-sphere (make-sphere-generator 1) (vector 1.0 1.0) 200 #t))) + (test-sphere (make-sphere-generator 2) (vector 1.0 1.0 1.0) 200 #t) + (test-sphere (make-sphere-generator 3) (vector 1.0 1.0 1.0 1.0) 200 #t) + + (reset-source!*) + (cond-expand + (gambit + ;; Gambit fails this test, but I think the generator code is correct. + #t) + (else + (test-sphere (make-ellipsoid-generator (vector 1.0 1.0)) (vector 1.0 1.0) 200 #t))) + (test-sphere (make-ellipsoid-generator (vector 1.0 1.0 1.0)) (vector 1.0 1.0 1.0) 200 #t) + (test-sphere (make-ellipsoid-generator (vector 1.0 1.0 1.0 1.0)) (vector 1.0 1.0 1.0 1.0) 200 #t) + + (reset-source!*) + (test-sphere (make-ellipsoid-generator (vector 1.0 3.0)) (vector 1.0 3.0) 200 #f) + (test-sphere (make-ellipsoid-generator (vector 1.0 3.0 5.0)) (vector 1.0 3.0 5.0) 200 #f) + (test-sphere (make-ellipsoid-generator (vector 1.0 3.0 5.0 7.0)) (vector 1.0 3.0 5.0 7.0) 200 #f) + + (reset-source!*) + (test-ball (make-ball-generator 2) (vector 1.0 1.0)) + (test-ball (make-ball-generator 3) (vector 1.0 1.0 1.0)) + (test-ball (make-ball-generator (vector 1.0 3.0)) (vector 1.0 3.0)) + (test-ball (make-ball-generator (vector 1.0 3.0 5.0)) (vector 1.0 3.0 5.0)) + + (reset-source!*) + + (test-ellipsoid 1 1 10000) + (test-ellipsoid 5 5 10000) + (test-ellipsoid 10 2 10000) + + ;; test the unit ball + (test-ellipse 1 1 10000) + + ;; test the scaled unit ball + (test-ellipse 5 5 10000) + + ;; test an eccentric ellipse + (test-ellipse 10 2 10000)) + +(test-group "Test binomial" + (reset-source!) + (define (factorial n) + (if (<= n 1) + 1 + (* n (factorial (- n 1))))) + (define (C n k) + (/ (factorial n) + (* (factorial k) (factorial (- n k))))) + (define (expected-frac n p k) + (* (C n k) (expt p k) (expt (- 1 p) (- n k)))) + + (define (test-binomial n p count) + (define g (make-binomial-generator n p)) + (define counts (make-vector (+ n 1) 0)) + (generator-for-each + (lambda (x) + (vector-set! counts x (+ 1 (vector-ref counts x)))) + (gtake g count)) + (for-each + (lambda (k) + (define expected (* count (expected-frac n p k) )) + (define actual (vector-ref counts k)) + (cond + ((= expected 0) + (test-equal 0 actual)) + ;;hacky.. testing values with very low probability fails + ((> expected (* 1/1000 count)) + (test-approximate 1.0 (/ actual expected) 0.2)))) + (iota (+ n 1)))) + + (test-binomial 1 0 100) + (test-binomial 1 1 100) + (test-binomial 1 0. 100) + (test-binomial 1 1. 100) + (test-binomial 10 0 100) + (test-binomial 10 1 100) + (test-binomial 10 0. 100) + (test-binomial 10 1. 100) + (test-binomial 10 0.25 100000) + (test-binomial 40 0.375 1000000)) + + +(test-end "srfi-194") |
