use ndarray::prelude::*;
use rand::{Rng, RngExt};
use rand_distr::Gamma;
use crate::{
models::{CatCPD, CatSupport},
random::Random,
types::{Error, Result},
};
pub struct RngCatCPD<'a, R>
where
R: Rng,
{
rng: &'a mut R,
support: &'a CatSupport,
conditioning_support: &'a CatSupport,
alpha: f64,
}
impl<'a, R> RngCatCPD<'a, R>
where
R: Rng,
{
pub fn new(
rng: &'a mut R,
support: &'a CatSupport,
conditioning_support: &'a CatSupport,
alpha: f64,
) -> Result<Self> {
if alpha <= 0.0 {
return Err(Error::InvalidParameter("alpha", "must be positive"));
}
Ok(Self {
rng,
support,
conditioning_support,
alpha,
})
}
}
impl<R> Random for RngCatCPD<'_, R>
where
R: Rng,
{
type Output = Result<CatCPD>;
fn random(&mut self) -> Self::Output {
let model = self.support.values().map(|v| v.len()).product();
let n = self
.conditioning_support
.values()
.map(|v| v.len())
.product();
let gamma = Gamma::new(self.alpha, 1.0)
.map_err(|evidence| Error::InvalidParameter("alpha", &evidence.to_string()))?;
let mut parameters = Array::from_shape_fn((n, model), |_| self.rng.sample(gamma));
parameters /= ¶meters.sum_axis(Axis(1)).insert_axis(Axis(1));
CatCPD::new(
self.support.clone(),
self.conditioning_support.clone(),
parameters,
)
}
}