use dry::macro_for;
use ndarray::prelude::*;
use crate::{
datasets::{CatIncTable, CatTable, CatWtdTable},
estimators::{BE, CPDEstimator, CSSEstimator, ParCPDEstimator, ParCSSEstimator, SSE},
models::{CatCPD, CatCPDS},
types::{Error, Result, Set, States},
};
impl BE<'_, CatTable, usize> {
fn fit(
states: &States,
shape: &Array1<usize>,
x: &Set<usize>,
z: &Set<usize>,
sample_statistics: CatCPDS,
prior: usize,
) -> Result<CatCPD> {
let n_xz = sample_statistics.fitted_conditional_counts();
let n_z = n_xz.sum_axis(Axis(1)).insert_axis(Axis(1));
let alpha = prior;
if alpha == 0 {
return Err(Error::InvalidParameter("alpha", "must be positive"));
}
let alpha = alpha as f64;
let n_xz = n_xz + alpha;
let n_z = n_z + alpha * x.iter().map(|&i| shape[i]).product::<usize>() as f64;
let parameters = &n_xz / &n_z;
let sample_log_likelihood = Some((&n_xz * parameters.ln()).sum());
let conditioning_states = z
.iter()
.map(|&i| {
let (k, v) = states
.get_index(i)
.ok_or_else(|| Error::IndexOutOfBounds(i))?;
Ok((k.clone(), v.clone()))
})
.collect::<Result<_>>()?;
let states = x
.iter()
.map(|&i| {
let (k, v) = states
.get_index(i)
.ok_or_else(|| Error::IndexOutOfBounds(i))?;
Ok((k.clone(), v.clone()))
})
.collect::<Result<_>>()?;
let sample_statistics = Some(sample_statistics);
CatCPD::with_optionals(
states,
conditioning_states,
parameters,
sample_statistics,
sample_log_likelihood,
)
}
}
macro_for!($type in [CatTable, CatIncTable, CatWtdTable] {
impl CPDEstimator<CatCPD> for BE<'_, $type, ()> {
#[inline]
fn fit(&self, x: &Set<usize>, z: &Set<usize>) -> Result<CatCPD> {
self.clone().with_prior(1).fit(x, z)
}
}
impl CPDEstimator<CatCPD> for BE<'_, $type, usize> {
#[inline]
fn fit(&self, x: &Set<usize>, z: &Set<usize>) -> Result<CatCPD> {
let (states, shape, prior) = (self.dataset.states(), self.dataset.shape(), self.prior);
let sample_statistics = SSE::new(self.dataset);
let sample_statistics = sample_statistics.with_missing_method(
self.missing_method,
self.missing_mechanism.clone()
)?;
let sample_statistics = sample_statistics.fit(x, z)?;
BE::<'_, CatTable, _>::fit(states, shape, x, z, sample_statistics, prior)
}
}
impl ParCPDEstimator<CatCPD> for BE<'_, $type, ()> {
#[inline]
fn par_fit(&self, x: &Set<usize>, z: &Set<usize>) -> Result<CatCPD> {
self.clone().with_prior(1).par_fit(x, z)
}
}
impl ParCPDEstimator<CatCPD> for BE<'_, $type, usize> {
#[inline]
fn par_fit(&self, x: &Set<usize>, z: &Set<usize>) -> Result<CatCPD> {
let (states, shape, prior) = (self.dataset.states(), self.dataset.shape(), self.prior);
let sample_statistics = SSE::new(self.dataset);
let sample_statistics = sample_statistics.with_missing_method(
self.missing_method,
self.missing_mechanism.clone()
)?;
let sample_statistics = sample_statistics.par_fit(x, z)?;
BE::<'_, CatTable, _>::fit(states, shape, x, z, sample_statistics, prior)
}
}
});