use rand::prelude::*;
use crate::{
models::{BN, CatBN},
random::{Random, RngCatCPD, RngDag},
set,
types::{Error, Labels, Result, States},
};
pub struct RngCatBN<'a, R>
where
R: Rng,
{
rng: &'a mut R,
states: &'a States,
alpha: f64,
p: f64,
}
impl<'a, R> RngCatBN<'a, R>
where
R: Rng,
{
pub fn new(rng: &'a mut R, states: &'a States, alpha: f64, p: f64) -> Result<Self> {
if alpha <= 0.0 {
return Err(Error::InvalidParameter("alpha", "must be positive"));
}
if !(0.0..=1.0).contains(&p) {
return Err(Error::InvalidParameter("p", "must be in [0, 1]"));
}
Ok(Self {
rng,
states,
alpha,
p,
})
}
}
impl<R> Random for RngCatBN<'_, R>
where
R: Rng,
{
type Output = Result<CatBN>;
fn random(&mut self) -> Self::Output {
let labels: Labels = self.states.keys().cloned().collect();
let graph = RngDag::new(self.rng, &labels, self.p)?.random()?;
let cpds = labels
.iter()
.enumerate()
.map(|(i, x)| {
let pa_i = graph.parents(&set![i])?;
let mut states = States::default();
states.insert(x.clone(), self.states[x].clone());
let conditioning_states = pa_i
.iter()
.map(|&j| {
let y = &labels[j];
(y.clone(), self.states[y].clone())
})
.collect();
RngCatCPD::new(self.rng, &states, &conditioning_states, self.alpha)?.random()
})
.collect::<Result<Vec<_>>>()?;
CatBN::new(graph, cpds)
}
}