about summary refs log tree commit diff
path: root/src/nd-random.sc
diff options
context:
space:
mode:
Diffstat (limited to 'src/nd-random.sc')
-rw-r--r--src/nd-random.sc98
1 files changed, 98 insertions, 0 deletions
diff --git a/src/nd-random.sc b/src/nd-random.sc
new file mode 100644
index 0000000..3d96113
--- /dev/null
+++ b/src/nd-random.sc
@@ -0,0 +1,98 @@
+(pre-include "math.h")
+(pre-include "gsl/gsl_blas.h")
+(pre-include "gsl/gsl_randist.h")
+(pre-include "gsl/gsl_sf_gamma.h")
+(pre-include "gsl/gsl_vector.h")
+(pre-include "utils.h")
+
+(define (random-direction-vector r x)
+  (void (const gsl-rng*) gsl-vector*)
+  "Generate a random vector distributed uniformly on the unit
+sphere. Write the result to X."
+  (gsl-ran-dir-nd r (: x size) (: x data)))
+
+(define (rotate-from-nth-canonical x orient)
+  ((static void) gsl-vector* (const gsl-vector*))
+  (let* ((n size-t (: x size))
+         (xn double (gsl-vector-get x (- n 1)))
+         (mun double (gsl-vector-get orient (- n 1)))
+         (orient-sub gsl-vector-const-view
+                     (gsl-vector-const-subvector orient 0 (- n 1)))
+         (b double (gsl-blas-dnrm2 (address-of (struct-get orient-sub vector))))
+         (a double (/ (- (dot-product orient x)
+                         (* xn mun))
+                      b))
+         (s double (sqrt (- 1 (gsl-pow-2 mun)))))
+    (gsl-blas-daxpy (/ (+ (* xn s)
+                          (* a (- mun 1)))
+                       b)
+                    orient
+                    x)
+    (gsl-vector-set x
+                    (- n 1)
+                    (+ (gsl-vector-get x (- n 1))
+                       (* xn (- mun 1))
+                       (- (* a s))
+                       (- (/ (* mun (+ (* xn s)
+                                       (* a (- mun 1))))
+                             b))))))
+
+(define (beta-inc-unnormalized a b x) ((static double) double double double)
+  (return (* (gsl-sf-beta-inc a b x)
+             (gsl-sf-beta a b))))
+
+(define (incomplete-wallis-integral theta m) ((static double) double (unsigned int))
+  "Return the incomplete Wallis integral \\int_0^\\theta \\sin^m x
+dx. THETA should be in [0,pi]."
+  (cond
+   ((or (< theta 0) (> theta M-PI))
+    (GSL-ERROR "Incomplete Wallis integral only allows theta in [0,pi]" GSL-EDOM))
+   ((< theta M-PI-2)
+    (return (/ (beta-inc-unnormalized (* 0.5 (+ m 1)) 0.5 (gsl-pow-2 (sin theta)))
+               2)))
+   (else
+    (return (/ (+ (gsl-sf-beta (* 0.5 (+ m 1)) 0.5)
+                  (beta-inc-unnormalized 0.5 (* 0.5 (+ m 1)) (gsl-pow-2 (cos theta))))
+               2)))))
+
+(define (planar-angle->solid-angle planar-angle dimension) ((static double) double (unsigned int))
+  (return (/ (* 2
+                (pow M-PI (* 0.5 (- dimension 1)))
+                (incomplete-wallis-integral planar-angle (- dimension 2)))
+             (gsl-sf-gamma (* 0.5 (- dimension 1))))))
+
+(define (solid-angle->planar-angle solid-angle dimension) ((static double) double (unsigned int))
+  (define (f planar-angle params) (double double void*)
+    (return (- (planar-angle->solid-angle planar-angle dimension)
+               solid-angle)))
+  
+  (declare gsl-f (struct-variable gsl-function (function (address-of f))))
+  ;; TODO: Equality comparisons to floating point values may be
+  ;; problematic. Fix it.
+  (cond
+   ((= solid-angle 0) (return 0))
+   ((= solid-angle (surface-area-of-ball dimension)) (return M-PI))
+   (else (return (bisection (address-of gsl-f) 0 M-PI)))))
+
+;; TODO: There is an edge case when mean is the (n-1)th canonical
+;; basis vector. Fix it.
+(define (hollow-cone-random-vector r mean theta-min theta-max x)
+  (void (const gsl-rng*) (const gsl-vector*) double double gsl-vector*)
+  ;; Generate random vector around the nth canonical basis vector.
+  (let* ((n size-t (: x size)))
+    (gsl-ran-dir-nd r (- n 1) (: x data))
+    (gsl-vector-scale x (* (cos theta-max)
+                           (tan (solid-angle->planar-angle
+                                 (gsl-ran-flat r
+                                               (planar-angle->solid-angle theta-min n)
+                                               (planar-angle->solid-angle theta-max n))
+                                 n))))
+    (gsl-vector-set x (- n 1) (cos theta-max)))
+  (gsl-vector-scale x (/ 1 (gsl-blas-dnrm2 x)))
+
+  ;; Rotate to arbitrary basis.
+  (rotate-from-nth-canonical x mean))
+
+(define (subsampling-random-vector r mean theta-max x)
+  (void (const gsl-rng*) (const gsl-vector*) double gsl-vector*)
+  (hollow-cone-random-vector r mean 0 theta-max x))