use probability; use probability::distribution::Sample;
use probability::prelude::*;
use probability::source::Xorshift128Plus;
use rand::distributions::Distribution;
use statrs::distribution::Binomial;
pub fn multinomial_sample_statrs(n: u64, pvec: Vec<f64>) -> Vec<f64> {
let mut r = rand::thread_rng();
let mut x: Vec<f64> = Vec::new();
let _sum: f64 = pvec.iter().sum();
let pvec_norm: Vec<f64> = pvec.iter().map(|x| x / _sum).collect();
let dim = pvec_norm.len();
let mut remaining_p = 1.0;
let mut remaining_n = n;
for (counter, p) in pvec_norm.iter().enumerate() {
if remaining_n == 0 {
x.push(0.0);
} else if counter == dim - 1 {
x.push(remaining_n as f64);
} else {
let _ptmp = p / remaining_p;
if !(0.0 < _ptmp && _ptmp < 1.0) {
println!("{:?}", &pvec_norm[counter..]);
panic!("0<{}<1, counter {}", _ptmp, counter);
}
let bin = Binomial::new(_ptmp, remaining_n).unwrap_or_else(|_| {
panic!(
"p={} remaining_p={} ptmp={} n={}",
p, remaining_p, _ptmp, remaining_n
)
});
let b = bin.sample(&mut r);
x.push(b);
remaining_n -= b as u64;
remaining_p -= p;
}
}
x
}
pub fn multinomial_sample(n: u64, pvec: &[f64], source: &mut Xorshift128Plus) -> Vec<f64> {
let dim = pvec.len();
let mut x: Vec<f64> = Vec::with_capacity(dim);
let _sum: f64 = pvec.iter().sum();
let pvec_norm: Vec<f64> = pvec.iter().map(|x| x / _sum).collect();
let mut remaining_p = 1.0;
let mut remaining_n = n;
for (counter, p) in pvec_norm.iter().enumerate() {
if remaining_n == 0 {
x.push(0.0);
} else if counter == dim - 1 {
x.push(remaining_n as f64);
} else {
let _ptmp = p / remaining_p;
if !(0.0 < _ptmp && _ptmp < 1.0) {
println!("{:?}", &pvec_norm[counter..]);
panic!("0<{}<1, counter {}", _ptmp, counter);
}
let btmp = probability::distribution::Binomial::new(remaining_n as usize, _ptmp)
.sample(source); let b = btmp as f64;
x.push(b);
remaining_n -= b as u64;
remaining_p -= p;
}
}
x
}
pub fn multinomial_sample_binary_search(
n: u64,
pvec: &[f64],
source: &mut Xorshift128Plus,
) -> Vec<f64> {
let mut x: Vec<f64> = std::iter::repeat(0.0).take(pvec.len()).collect();
let _sum: f64 = pvec.iter().sum();
let pvec_norm: Vec<f64> = pvec.iter().map(|x| x / _sum).collect();
let mut _acc = 0.0;
let cdf: Vec<f64> = pvec_norm
.iter()
.map(|x| {
_acc += x;
_acc
})
.collect();
let rv_uniform = Uniform::new(0.0, 1.0);
for _i in 0..n {
let r = rv_uniform.sample(source);
let res = cdf.binary_search_by(|v| v.partial_cmp(&r).unwrap());
let interval = match res {
Ok(index_exact_match) => {
println!("Should not happen {}", index_exact_match);
index_exact_match
}
Err(first_index_larger_than_r) => first_index_larger_than_r,
};
x[interval] += 1.0;
}
x
}
#[cfg(test)]
mod test {
use statrs::distribution::Multinomial;
use super::*;
#[allow(dead_code)]
fn test_multinomial_binary() {
use std::time::Instant;
let mut random_source = source::default(4);
let dim = 50_000_000;
let n = 100_000_000;
let p: Vec<_> = (1..dim).map(|x| x as f64).collect();
let now = Instant::now();
let x = multinomial_sample_binary_search(n, &p, &mut random_source);
let elapsed_time = now.elapsed();
println!(
"Time for binary search N={} dim={}: {} secs",
n,
dim,
elapsed_time.as_secs()
);
let s: f64 = x.iter().fold(0.0, |acc, el| acc + *el);
assert_eq!(n as f64, s)
}
#[allow(dead_code)]
fn test_multinomial(dim: i32) {
let mut random_source = source::default(42);
let n = 1000;
let p: Vec<_> = (1..dim).map(|x| x as f64).collect();
let x = multinomial_sample(n, &p, &mut random_source);
let _n2: f64 = x.iter().sum();
}
#[allow(dead_code)]
fn multinomial_speed_eval() {
use core::ops::{Add, Div, Sub};
use std::fmt::Debug;
use std::fs::File;
use std::time::Instant;
pub fn linspace<T>(x0: T, xend: T, n: u16) -> Vec<T>
where
T: Sub<Output = T> + Add<Output = T> + Div<Output = T> + Clone + Debug,
u16: TryInto<T> + TryInto<usize>,
<u16 as TryInto<T>>::Error: Debug,
{
let segments: T = (n - 1)
.try_into()
.expect("requested number of elements did not fit into T");
let n_size: usize = n
.try_into()
.expect("requested number of elements exceeds usize");
let dx = (xend - x0.clone()) / segments;
let mut x = vec![x0; n_size];
for i in 1..n_size {
x[i] = x[i - 1].clone() + dx.clone();
}
x
}
let dims: Vec<usize> = linspace(4.0, 7.0, 20)
.iter()
.map(|x| 10_f64.powf(*x) as usize)
.collect();
let nvector: Vec<usize> = linspace(4.0, 7.0, 20)
.iter()
.map(|x| 10_f64.powf(*x) as usize)
.collect();
println!("{:?}", dims);
let mut random_source = source::default(42);
struct Res {
n: usize,
d: usize,
time: f32,
}
let mut results: Vec<Res> = Vec::new();
for n in nvector {
for d in dims.clone() {
println!("n={} d={}", n, d);
let pvec: Vec<_> = (1..d).map(|x| x as f64).collect();
let now = Instant::now();
let _x = multinomial_sample_binary_search(n as u64, &pvec, &mut random_source);
let elapsed_time = now.elapsed().as_secs_f32();
let r = Res { n, d, time: elapsed_time };
results.push(r)
}
}
let mut fh = File::create("/tmp/multinom_speed.csv").unwrap();
use std::io::Write;
writeln!(fh, "N,d,time").unwrap();
for r in results {
writeln!(fh, "{},{},{}", r.n, r.d, r.time).unwrap();
}
}
}