use core::f64;
use std::cell::RefCell;
use ndarray::prelude::*;
use ndarray_stats::QuantileExt;
use rand::{
Rng, SeedableRng,
distr::{Distribution, weighted::WeightedIndex},
};
use rand_distr::Exp;
use rayon::prelude::*;
use crate::{
datasets::{CatSample, CatTable, CatTrj, CatType, GaussTable},
models::{BN, CIM, CPD, CTBN, CatBN, CatCTBN, GaussBN, Labelled},
samplers::{BNSampler, CTBNSampler, ParBNSampler, ParCTBNSampler},
set,
types::EPSILON,
};
#[derive(Debug)]
pub struct ForwardSampler<'a, R, M> {
rng: RefCell<&'a mut R>,
model: &'a M,
}
impl<'a, R, M> ForwardSampler<'a, R, M> {
#[inline]
pub const fn new(rng: &'a mut R, model: &'a M) -> Self {
let rng = RefCell::new(rng);
Self { rng, model }
}
}
impl<R: Rng> BNSampler<CatBN> for ForwardSampler<'_, R, CatBN> {
type Sample = <CatBN as BN>::Sample;
type Samples = <CatBN as BN>::Samples;
fn sample(&self) -> Self::Sample {
let mut rng = self.rng.borrow_mut();
let mut sample = Array::zeros(self.model.labels().len());
self.model.topological_order().iter().for_each(|&i| {
let cpd_i = &self.model.cpds()[i];
let pa_i = self.model.graph().parents(&set![i]);
let pa_i = pa_i.iter().map(|&z| sample[z]).collect();
sample[i] = cpd_i.sample(&mut rng, &pa_i)[0];
});
sample
}
fn sample_n(&self, n: usize) -> Self::Samples {
let mut dataset = Array::zeros((n, self.model.labels().len()));
dataset.rows_mut().into_iter().for_each(|mut row| {
row.assign(&self.sample());
});
CatTable::new(self.model.states().clone(), dataset)
}
}
impl<R: Rng + SeedableRng> ParBNSampler<CatBN> for ForwardSampler<'_, R, CatBN> {
type Samples = <CatBN as BN>::Samples;
fn par_sample_n(&self, n: usize) -> Self::Samples {
let rng = self.rng.borrow_mut();
let seeds: Vec<_> = rng.random_iter().take(n).collect();
let mut samples = Array::zeros((n, self.model.labels().len()));
seeds
.into_par_iter()
.zip(samples.axis_iter_mut(Axis(0)))
.for_each(|(seed, mut row)| {
let mut rng = R::seed_from_u64(seed);
let sampler = ForwardSampler::new(&mut rng, self.model);
row.assign(&sampler.sample());
});
CatTable::new(self.model.states().clone(), samples)
}
}
impl<R: Rng> BNSampler<GaussBN> for ForwardSampler<'_, R, GaussBN> {
type Sample = <GaussBN as BN>::Sample;
type Samples = <GaussBN as BN>::Samples;
fn sample(&self) -> Self::Sample {
let mut rng = self.rng.borrow_mut();
let mut sample = Array::zeros(self.model.labels().len());
self.model.topological_order().iter().for_each(|&i| {
let cpd_i = &self.model.cpds()[i];
let pa_i = self.model.graph().parents(&set![i]);
let pa_i = pa_i.iter().map(|&z| sample[z]).collect();
sample[i] = cpd_i.sample(&mut rng, &pa_i)[0];
});
sample
}
fn sample_n(&self, n: usize) -> Self::Samples {
let mut samples = Array::zeros((n, self.model.labels().len()));
samples.rows_mut().into_iter().for_each(|mut row| {
row.assign(&self.sample());
});
GaussTable::new(self.model.labels().clone(), samples)
}
}
impl<R: Rng + SeedableRng> ParBNSampler<GaussBN> for ForwardSampler<'_, R, GaussBN> {
type Samples = <GaussBN as BN>::Samples;
fn par_sample_n(&self, n: usize) -> Self::Samples {
let rng = self.rng.borrow_mut();
let seeds: Vec<_> = rng.random_iter().take(n).collect();
let mut samples = Array::zeros((n, self.model.labels().len()));
seeds
.into_par_iter()
.zip(samples.axis_iter_mut(Axis(0)))
.for_each(|(seed, mut row)| {
let mut rng = R::seed_from_u64(seed);
let sampler = ForwardSampler::new(&mut rng, self.model);
row.assign(&sampler.sample());
});
GaussTable::new(self.model.labels().clone(), samples)
}
}
impl<R: Rng> ForwardSampler<'_, R, CatCTBN> {
fn sample_time(&self, event: &CatSample, i: usize) -> f64 {
let x = event[i] as usize;
let cim_i = &self.model.cims()[i];
let pa_i = self.model.graph().parents(&set![i]);
let pa_i = pa_i.iter().map(|&z| event[z] as usize);
let pa_i = cim_i.conditioning_multi_index().ravel(pa_i);
let q_i_x = -cim_i.parameters()[[pa_i, x, x]];
let exp_i_x = Exp::new(q_i_x).unwrap();
exp_i_x.sample(&mut self.rng.borrow_mut())
}
}
impl<R: Rng> CTBNSampler<CatCTBN> for ForwardSampler<'_, R, CatCTBN> {
type Sample = <CatCTBN as CTBN>::Trajectory;
type Samples = <CatCTBN as CTBN>::Trajectories;
#[inline]
fn sample_by_length(&self, max_length: usize) -> Self::Sample {
self.sample_by_length_or_time(max_length, f64::MAX)
}
#[inline]
fn sample_by_time(&self, max_time: f64) -> Self::Sample {
self.sample_by_length_or_time(usize::MAX, max_time)
}
fn sample_by_length_or_time(&self, max_length: usize, max_time: f64) -> Self::Sample {
assert!(
max_length > 0,
"The maximum length of the trajectory must be strictly positive."
);
assert!(max_time > 0., "The maximum time must be positive.");
let mut sample_events = Vec::new();
let mut sample_times = Vec::new();
let mut event = {
let mut rng = self.rng.borrow_mut();
let initial = self.model.initial_distribution();
let initial = ForwardSampler::new(&mut rng, initial);
initial.sample()
};
sample_events.push(event.clone());
sample_times.push(0.);
let mut times: Array1<_> = (0..event.len())
.map(|i| self.sample_time(&event, i))
.collect();
let mut i = times.argmin().unwrap();
let mut time = times[i];
while sample_events.len() < max_length && time < max_time {
let x = event[i] as usize;
let cim_i = &self.model.cims()[i];
let pa_i = self.model.graph().parents(&set![i]);
let pa_i = pa_i.iter().map(|&z| event[z] as usize);
let pa_i = cim_i.conditioning_multi_index().ravel(pa_i);
let mut q_i_zx = cim_i.parameters().slice(s![pa_i, x, ..]).to_owned();
q_i_zx[x] = 0.;
q_i_zx /= q_i_zx.sum();
let s_i_zx = WeightedIndex::new(&q_i_zx).unwrap();
event[i] = s_i_zx.sample(&mut self.rng.borrow_mut()) as CatType;
sample_events.push(event.clone());
sample_times.push(time);
std::iter::once(i)
.chain(self.model.graph().children(&set![i]))
.for_each(|j| {
times[j] = time + self.sample_time(&event, j);
});
times += EPSILON;
i = times.argmin().unwrap();
time = times[i];
}
let states = self.model.states().clone();
let shape = (sample_events.len(), sample_events[0].len());
let sample_events = Array::from_iter(sample_events.into_iter().flatten())
.into_shape_with_order(shape)
.expect("Failed to convert events to 2D array.");
let sample_times = Array::from_iter(sample_times);
CatTrj::new(states, sample_events, sample_times)
}
#[inline]
fn sample_n_by_length(&self, max_length: usize, n: usize) -> Self::Samples {
(0..n).map(|_| self.sample_by_length(max_length)).collect()
}
#[inline]
fn sample_n_by_time(&self, max_time: f64, n: usize) -> Self::Samples {
(0..n).map(|_| self.sample_by_time(max_time)).collect()
}
#[inline]
fn sample_n_by_length_or_time(
&self,
max_length: usize,
max_time: f64,
n: usize,
) -> Self::Samples {
(0..n)
.map(|_| self.sample_by_length_or_time(max_length, max_time))
.collect()
}
}
impl<R: Rng + SeedableRng> ParCTBNSampler<CatCTBN> for ForwardSampler<'_, R, CatCTBN> {
type Samples = <CatCTBN as CTBN>::Trajectories;
#[inline]
fn par_sample_n_by_length(&self, max_length: usize, n: usize) -> Self::Samples {
self.par_sample_n_by_length_or_time(max_length, f64::MAX, n)
}
#[inline]
fn par_sample_n_by_time(&self, max_time: f64, n: usize) -> Self::Samples {
self.par_sample_n_by_length_or_time(usize::MAX, max_time, n)
}
fn par_sample_n_by_length_or_time(
&self,
max_length: usize,
max_time: f64,
n: usize,
) -> Self::Samples {
let rng = self.rng.borrow_mut();
let seeds: Vec<_> = rng.random_iter().take(n).collect();
seeds
.into_par_iter()
.map(|seed| {
let mut rng = R::seed_from_u64(seed);
let sampler = ForwardSampler::new(&mut rng, self.model);
sampler.sample_by_length_or_time(max_length, max_time)
})
.collect()
}
}