use libm::{erff, lgammaf};
use rand::Rng;
use rand::rngs::ThreadRng;
use rand_distr::{Binomial, Distribution, StandardNormal};
use std::f32;
pub fn logistic(x: f32) -> f32 {
1.0 / (1.0 + (-x).exp())
}
pub fn odds_to_prob(q: f32) -> f32 {
q / (1.0 + q)
}
const LN_SQRT_TWO_PI: f32 = 0.918_938_5_f32;
pub fn rand_crt(rng: &mut ThreadRng, n: u32, r: f32) -> u32 {
(0..n)
.map(|t| rng.random_bool(r as f64 / (r as f64 + t as f64)) as u32)
.sum()
}
pub fn negbin_logpmf(r: f32, lgamma_r: f32, p: f32, k: u32) -> f32 {
const MINP: f32 = 0.999999_f32;
let p = p.min(MINP);
if k == 0 {
r * (-p).ln_1p()
} else {
let k_ln_factorial = lgammaf(k as f32 + 1.0);
let lgamma_rpk = lgammaf(r + k as f32);
lgamma_rpk - lgamma_r - k_ln_factorial + (k as f32) * p.ln() + r * (-p).ln_1p()
}
}
pub fn normal_logpdf(μ: f32, σ: f32, x: f32) -> f32 {
-LN_SQRT_TWO_PI - σ.ln() - ((x - μ) / σ).powi(2) / 2.0
}
pub fn randn(rng: &mut ThreadRng) -> f32 {
rng.sample::<f32, StandardNormal>(StandardNormal)
}
pub fn halfnormal_logpdf(σ: f32, x: f32) -> f32 {
-LN_SQRT_TWO_PI - σ.ln() - x.powi(2) / (2.0 * σ.powi(2))
}
fn erfint(span: f32, σ: f32) -> f32 {
-span * erff(span / (f32::consts::SQRT_2 * σ))
- (f32::consts::SQRT_2 * f32::consts::FRAC_2_SQRT_PI / 2.0)
* σ
* (-span.powi(2) / (2.0 * σ.powi(2))).exp()
}
pub fn uniformly_imprecise_normal_prob(a: f32, b: f32, a0: f32, b0: f32, σ: f32) -> f32 {
0.5 * (b0 - a0).recip()
* (b - a).recip()
* (erfint(b - b0, σ) + erfint(a - a0, σ) - erfint(b - a0, σ) - erfint(a - b0, σ))
}
pub struct MultinomialSampler<'a> {
rng: &'a mut ThreadRng,
probs: &'a [f64],
ρ: f64, s: u32, i: usize,
}
impl<'a> MultinomialSampler<'a> {
pub fn new(rng: &'a mut ThreadRng, probs: &'a [f64], n: u32) -> Self {
MultinomialSampler {
rng,
probs,
ρ: probs.iter().sum(),
s: n,
i: 0,
}
}
}
impl<'a> Iterator for MultinomialSampler<'a> {
type Item = u32;
fn next(&mut self) -> Option<u32> {
if self.i == self.probs.len() {
return None;
}
if self.s == 0 {
self.i += 1;
return Some(0);
}
if self.i == self.probs.len() - 1 {
self.i += 1;
return Some(self.s);
}
let pi = self.probs[self.i];
let r = (pi / self.ρ).min(1.0);
let x = Binomial::new(self.s as u64, r).unwrap().sample(self.rng) as u32;
self.ρ -= pi;
self.s -= x;
self.i += 1;
Some(x)
}
}