2023-05-21 01:13:51 +00:00
|
|
|
import std/math
|
2023-05-21 04:46:10 +00:00
|
|
|
import std/sugar
|
2023-05-21 02:24:30 +00:00
|
|
|
import std/random
|
2023-05-21 04:46:10 +00:00
|
|
|
import std/sequtils
|
2023-05-21 02:24:30 +00:00
|
|
|
|
2023-05-21 03:06:34 +00:00
|
|
|
randomize()
|
2023-05-21 01:13:51 +00:00
|
|
|
|
2023-05-21 02:24:30 +00:00
|
|
|
## Distribution functions
|
2023-05-21 03:06:34 +00:00
|
|
|
|
|
|
|
## Normal
|
|
|
|
## <https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform#Basic_form>
|
|
|
|
proc ur_normal(): float =
|
2023-05-21 01:13:51 +00:00
|
|
|
let u1 = rand(1.0)
|
|
|
|
let u2 = rand(1.0)
|
2023-05-21 05:29:57 +00:00
|
|
|
let z = sqrt(-2.0 * ln(u1)) * sin(2 * PI * u2)
|
2023-05-21 03:06:34 +00:00
|
|
|
return z
|
|
|
|
|
|
|
|
proc normal(mean: float, sigma: float): float =
|
|
|
|
return (mean + sigma * ur_normal())
|
|
|
|
|
|
|
|
proc lognormal(logmean: float, logsigma: float): float =
|
|
|
|
let answer = pow(E, normal(logmean, logsigma))
|
|
|
|
return answer
|
|
|
|
|
|
|
|
proc to(low: float, high: float): float =
|
|
|
|
let normal95confidencePoint = 1.6448536269514722
|
2023-05-21 05:29:57 +00:00
|
|
|
let loglow = ln(low)
|
|
|
|
let loghigh = ln(high)
|
2023-05-21 03:06:34 +00:00
|
|
|
let logmean = (loglow + loghigh)/2
|
2023-05-25 05:39:16 +00:00
|
|
|
let logsigma = (loghigh - loglow) / (2.0 * normal95confidencePoint)
|
2023-05-21 03:06:34 +00:00
|
|
|
return lognormal(logmean, logsigma)
|
|
|
|
|
2023-05-21 04:46:10 +00:00
|
|
|
## echo ur_normal()
|
|
|
|
## echo normal(10, 20)
|
|
|
|
## echo lognormal(2, 4)
|
|
|
|
## echo to(10, 90)
|
|
|
|
|
|
|
|
## Manipulate samples
|
|
|
|
|
2023-05-21 16:05:15 +00:00
|
|
|
proc mixture(fs: seq[proc (): float{.nimcall.}], ps: seq[float], n: int): seq[float] =
|
2023-05-21 04:46:10 +00:00
|
|
|
|
2023-05-21 16:05:15 +00:00
|
|
|
assert fs.len == ps.len
|
2023-05-21 04:46:10 +00:00
|
|
|
|
|
|
|
var ws: seq[float]
|
|
|
|
var sum = 0.0
|
|
|
|
for i, p in ps:
|
|
|
|
sum = sum + p
|
|
|
|
ws.add(sum)
|
|
|
|
ws = ws.map(w => w/sum)
|
|
|
|
|
2023-05-21 16:05:15 +00:00
|
|
|
var samples: seq[float]
|
|
|
|
let rs = toSeq(1..n).map(_=>rand(1.0))
|
|
|
|
for i in 0..(n-1):
|
|
|
|
let r = rs[i]
|
|
|
|
var j = ws.len - 1
|
|
|
|
for k, w in ws:
|
2023-05-21 04:46:10 +00:00
|
|
|
if r < w:
|
2023-05-21 16:05:15 +00:00
|
|
|
j = k
|
2023-05-21 04:46:10 +00:00
|
|
|
break
|
2023-05-21 16:05:15 +00:00
|
|
|
## only occasion when ^ doesn't assign j
|
2023-05-21 04:46:10 +00:00
|
|
|
## is when r is exactly 1
|
|
|
|
## which would correspond to choosing the last item in ws
|
2023-05-21 16:05:15 +00:00
|
|
|
## which is why j is initialized to ws.len - 1
|
|
|
|
let f = fs[j]
|
|
|
|
samples.add(f())
|
|
|
|
return samples
|
|
|
|
|
2023-05-21 04:46:10 +00:00
|
|
|
|
2023-05-21 05:22:02 +00:00
|
|
|
## Actual model
|
|
|
|
|
|
|
|
let n = 1000000
|
|
|
|
|
|
|
|
let p_a = 0.8
|
|
|
|
let p_b = 0.5
|
|
|
|
let p_c = p_a * p_b
|
|
|
|
|
|
|
|
let weights = @[ 1.0 - p_c, p_c/2.0, p_c/4.0, p_c/4.0 ]
|
|
|
|
|
2023-05-21 16:05:15 +00:00
|
|
|
let fs = @[ proc (): float = 0.0, proc (): float = 1.0, proc (): float = to(1.0, 3.0), proc (): float = to(2.0, 10.0)]
|
|
|
|
let result = mixture(fs, weights, n)
|
2023-05-21 05:22:02 +00:00
|
|
|
let mean_result = foldl(result, a + b, 0.0) / float(result.len)
|
|
|
|
|
|
|
|
# echo result
|
|
|
|
echo mean_result
|