use crate::api::expr::Ex;
use crate::base::errors::SymplexError;
use super::rv::{Distribution, RandomVariable, Support};
#[derive(Clone, Debug)]
pub struct Rng(u64);
impl Rng {
pub fn new(seed: u64) -> Self {
Rng(seed)
}
pub fn next_u64(&mut self) -> u64 {
self.0 = self.0.wrapping_add(0x9E37_79B9_7F4A_7C15);
let mut z = self.0;
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
z ^ (z >> 31)
}
pub fn next_f64(&mut self) -> f64 {
(self.next_u64() >> 11) as f64 / (1u64 << 53) as f64
}
}
impl RandomVariable {
pub fn sample(&self, n: usize, rng: &mut Rng) -> Result<Vec<f64>, SymplexError> {
let ctx = self.context();
let x = ctx.symbol("_sample_x");
match (self.distribution(), self.support()) {
(Distribution::Continuous(_), _) => {
let p = ctx.symbol("_sample_p");
let q = self.quantile(&p).ok_or_else(|| {
SymplexError::NotImplemented(format!(
"sampling {}: no closed-form quantile function",
self.distribution().name()
))
})?;
let q = q.compile(&["_sample_p"])?;
Ok((0..n).map(|_| q.call(&[rng.next_f64()])).collect())
}
(
Distribution::Discrete(_),
Support::Discrete {
lo: Some(lo),
hi: Some(hi),
},
) => {
let lo_v = lo.eval_f64()?;
let hi_v = hi.eval_f64()?;
if !(lo_v.is_finite() && hi_v.is_finite() && hi_v >= lo_v) {
return Err(SymplexError::NotImplemented(format!(
"sampling {}: the support is not a finite integer range",
self.distribution().name()
)));
}
let pmf = self.density(&x).compile(&["_sample_x"])?;
let values: Vec<f64> = {
let mut v = Vec::new();
let mut k = lo_v;
while k <= hi_v && v.len() < 1_000_000 {
v.push(k);
k += 1.0;
}
v
};
let mut cumulative = Vec::with_capacity(values.len());
let mut acc = 0.0;
for &k in &values {
acc += pmf.call(&[k]);
cumulative.push(acc);
}
Ok((0..n)
.map(|_| {
let u = rng.next_f64() * acc;
let idx = cumulative.partition_point(|c| *c < u);
values[idx.min(values.len().saturating_sub(1))]
})
.collect())
}
(Distribution::Discrete(_), Support::Finite(values)) => {
let mut cumulative = Vec::with_capacity(values.len());
let mut points = Vec::with_capacity(values.len());
let mut acc = 0.0;
for v in &values {
acc += self.density(v).eval_f64()?;
cumulative.push(acc);
points.push(v.eval_f64()?);
}
Ok((0..n)
.map(|_| {
let u = rng.next_f64() * acc;
let idx = cumulative.partition_point(|c| *c < u);
points[idx.min(points.len().saturating_sub(1))]
})
.collect())
}
_ => Err(SymplexError::NotImplemented(format!(
"sampling {}: no sampling route for an infinite discrete support",
self.distribution().name()
))),
}
}
pub fn sample_one(&self, rng: &mut Rng) -> Result<f64, SymplexError> {
Ok(self.sample(1, rng)?.first().copied().unwrap_or(f64::NAN))
}
#[doc(hidden)]
pub fn sampling_symbol(&self) -> Ex {
self.context().symbol("_sample_x")
}
}