comparison src/clojure/contrib/probabilities/monte_carlo.clj @ 10:ef7dbbd6452c

added clojure source goodness
author Robert McIntyre <rlm@mit.edu>
date Sat, 21 Aug 2010 06:25:44 -0400
parents
children
comparison
equal deleted inserted replaced
9:35cf337adfcf 10:ef7dbbd6452c
1 ;; Monte-Carlo algorithms
2
3 ;; by Konrad Hinsen
4 ;; last updated May 3, 2009
5
6 ;; Copyright (c) Konrad Hinsen, 2009. All rights reserved. The use
7 ;; and distribution terms for this software are covered by the Eclipse
8 ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
9 ;; which can be found in the file epl-v10.html at the root of this
10 ;; distribution. By using this software in any fashion, you are
11 ;; agreeing to be bound by the terms of this license. You must not
12 ;; remove this notice, or any other, from this software.
13
14 (ns
15 ^{:author "Konrad Hinsen"
16 :doc "Monte-Carlo method support
17
18 Monte-Carlo methods transform an input random number stream
19 (usually having a continuous uniform distribution in the
20 interval [0, 1)) into a random number stream whose distribution
21 satisfies certain conditions (usually the expectation value
22 is equal to some desired quantity). They are thus
23 transformations from one probability distribution to another one.
24
25 This library represents a Monte-Carlo method by a function that
26 takes as input the state of a random number stream with
27 uniform distribution (see
28 clojure.contrib.probabilities.random-numbers) and returns a
29 vector containing one sample value of the desired output
30 distribution and the final state of the input random number
31 stream. Such functions are state monad values and can be
32 composed using operations defined in clojure.contrib.monads."}
33 clojure.contrib.probabilities.monte-carlo
34 (:refer-clojure :exclude (deftype))
35 (:use [clojure.contrib.macros :only (const)])
36 (:use [clojure.contrib.types :only (deftype)])
37 (:use [clojure.contrib.stream-utils :only (defstream stream-next)])
38 (:use [clojure.contrib.monads
39 :only (with-monad state-m m-lift m-seq m-fmap)])
40 (:require [clojure.contrib.generic.arithmetic :as ga])
41 (:require [clojure.contrib.accumulators :as acc]))
42
43 ;; Random number transformers and random streams
44 ;;
45 ;; A random number transformer is a function that takes a random stream
46 ;; state as input and returns the next value from the transformed stream
47 ;; plus the new state of the input stream. Random number transformers
48 ;; are thus state monad values.
49 ;;
50 ;; Distributions are implemented as random number transformers that
51 ;; transform a uniform distribution in the interval [0, 1) to the
52 ;; desired distribution. Composition of such distributions allows
53 ;; the realization of any kind of Monte-Carlo algorithm. The result
54 ;; of such a composition is always again a distribution.
55 ;;
56 ;; Random streams are defined by a random number transformer and an
57 ;; input random number stream. If the randon number transformer represents
58 ;; a distribution, the input stream must have a uniform distribution
59 ;; in the interval [0, 1).
60
61 ; Random stream definition
62 (deftype ::random-stream random-stream
63 "Define a random stream by a distribution and the state of a
64 random number stream with uniform distribution in [0, 1)."
65 {:arglists '([distribution random-stream-state])}
66 (fn [d rs] (list d rs)))
67
68 (defstream ::random-stream
69 [[d rs]]
70 (let [[r nrs] (d rs)]
71 [r (random-stream d nrs)]))
72
73 ; Rejection of values is used in the construction of distributions
74 (defn reject
75 "Return the distribution that results from rejecting the values from
76 dist that do not satisfy predicate p."
77 [p dist]
78 (fn [rs]
79 (let [[r nrs] (dist rs)]
80 (if (p r)
81 (recur nrs)
82 [r nrs]))))
83
84 ; Draw a value from a discrete distribution given as a map from
85 ; values to probabilities.
86 ; (see clojure.contrib.probabilities.finite-distributions)
87 (with-monad state-m
88 (defn discrete
89 "A discrete distribution, defined by a map dist mapping values
90 to probabilities. The sum of probabilities must be one."
91 [dist]
92 (letfn [(pick-at-level [l dist-items]
93 (let [[[x p] & rest-dist] dist-items]
94 (if (> p l)
95 x
96 (recur (- l p) rest-dist))))]
97 (m-fmap #(pick-at-level % (seq dist)) stream-next))))
98
99 ; Uniform distribution in an finite half-open interval
100 (with-monad state-m
101 (defn interval
102 [a b]
103 "Transform a sequence of uniform random numbers in the interval [0, 1)
104 into a sequence of uniform random numbers in the interval [a, b)."
105 (let [d (- b a)
106 f (if (zero? a)
107 (if (= d 1)
108 identity
109 (fn [r] (* d r)))
110 (if (= d 1)
111 (fn [r] (+ a r))
112 (fn [r] (+ a (* d r)))))]
113 (m-fmap f stream-next))))
114
115 ; Normal (Gaussian) distribution
116 (defn normal
117 "Transform a sequence urs of uniform random number in the interval [0, 1)
118 into a sequence of normal random numbers with mean mu and standard
119 deviation sigma."
120 [mu sigma]
121 ; This function implements the Kinderman-Monahan ratio method:
122 ; A.J. Kinderman & J.F. Monahan
123 ; Computer Generation of Random Variables Using the Ratio of Uniform Deviates
124 ; ACM Transactions on Mathematical Software 3(3) 257-260, 1977
125 (fn [rs]
126 (let [[u1 rs] (stream-next rs)
127 [u2* rs] (stream-next rs)
128 u2 (- 1. u2*)
129 s (const (* 4 (/ (. Math exp (- 0.5)) (. Math sqrt 2.))))
130 z (* s (/ (- u1 0.5) u2))
131 zz (+ (* 0.25 z z) (. Math log u2))]
132 (if (> zz 0)
133 (recur rs)
134 [(+ mu (* sigma z)) rs]))))
135
136 ; Lognormal distribution
137 (with-monad state-m
138 (defn lognormal
139 "Transform a sequence of uniform random numbesr in the interval [0, 1)
140 into a sequence of lognormal random numbers with mean mu and standard
141 deviation sigma."
142 [mu sigma]
143 (m-fmap #(. Math exp %) (normal mu sigma))))
144
145 ; Exponential distribution
146 (with-monad state-m
147 (defn exponential
148 "Transform a sequence of uniform random numbers in the interval [0, 1)
149 into a sequence of exponential random numbers with parameter lambda."
150 [lambda]
151 (when (<= lambda 0)
152 (throw (IllegalArgumentException.
153 "exponential distribution requires a positive argument")))
154 (let [neg-inv-lambda (- (/ lambda))
155 ; remove very small numbers to prevent log from returning -Infinity
156 not-too-small (reject #(< % 1e-323) stream-next)]
157 (m-fmap #(* (. Math log %) neg-inv-lambda) not-too-small))))
158
159 ; Another implementation of the normal distribution. It uses the
160 ; Box-Muller transform, but discards one of the two result values
161 ; at each cycle because the random number transformer interface cannot
162 ; handle two outputs at the same time.
163 (defn normal-box-muller
164 "Transform a sequence of uniform random numbers in the interval [0, 1)
165 into a sequence of normal random numbers with mean mu and standard
166 deviation sigma."
167 [mu sigma]
168 (fn [rs]
169 (let [[u1 rs] (stream-next rs)
170 [u2 rs] (stream-next rs)
171 v1 (- (* 2.0 u1) 1.0)
172 v2 (- (* 2.0 u2) 1.0)
173 s (+ (* v1 v1) (* v2 v2))
174 ls (. Math sqrt (/ (* -2.0 (. Math log s)) s))
175 x1 (* v1 ls)
176 x2 (* v2 ls)]
177 (if (or (>= s 1) (= s 0))
178 (recur rs)
179 [x1 rs]))))
180
181 ; Finite samples from a distribution
182 (with-monad state-m
183
184 (defn sample
185 "Return the distribution of samples of length n from the
186 distribution dist"
187 [n dist]
188 (m-seq (replicate n dist)))
189
190 (defn sample-reduce
191 "Returns the distribution of the reduction of f over n samples from the
192 distribution dist."
193 ([f n dist]
194 (if (zero? n)
195 (m-result (f))
196 (let [m-f (m-lift 2 f)
197 sample (replicate n dist)]
198 (reduce m-f sample))))
199 ([f val n dist]
200 (let [m-f (m-lift 2 f)
201 m-val (m-result val)
202 sample (replicate n dist)]
203 (reduce m-f m-val sample))))
204
205 (defn sample-sum
206 "Return the distribution of the sum over n samples from the
207 distribution dist."
208 [n dist]
209 (sample-reduce ga/+ n dist))
210
211 (defn sample-mean
212 "Return the distribution of the mean over n samples from the
213 distribution dist"
214 [n dist]
215 (let [div-by-n (m-lift 1 #(ga/* % (/ n)))]
216 (div-by-n (sample-sum n dist))))
217
218 (defn sample-mean-variance
219 "Return the distribution of the mean-and-variance (a vector containing
220 the mean and the variance) over n samples from the distribution dist"
221 [n dist]
222 (let [extract (m-lift 1 (fn [mv] [(:mean mv) (:variance mv)]))]
223 (extract (sample-reduce acc/add acc/empty-mean-variance n dist))))
224
225 )
226
227 ; Uniform distribution inside an n-sphere
228 (with-monad state-m
229 (defn n-sphere
230 "Return a uniform distribution of n-dimensional vectors inside an
231 n-sphere of radius r."
232 [n r]
233 (let [box-dist (sample n (interval (- r) r))
234 sq #(* % %)
235 r-sq (sq r)
236 vec-sq #(apply + (map sq %))
237 sphere-dist (reject #(> (vec-sq %) r-sq) box-dist)
238 as-vectors (m-lift 1 vec)]
239 (as-vectors sphere-dist))))
240