use std::fmt::Debug;
use fastrand::Rng;
use num_traits::{FromPrimitive, Zero};
use crate::{
traits::{SelfAdd, SelfDiv},
Density,
Sample,
};
#[derive(Copy, Clone, Debug)]
pub struct KernelDensityEstimator<C>(pub C);
impl<Ks> Density for KernelDensityEstimator<Ks>
where
Ks: Iterator + Clone,
Ks::Item: Density,
<<Ks as Iterator>::Item as Density>::Param: Copy,
<<Ks as Iterator>::Item as Density>::Output: SelfAdd + SelfDiv + FromPrimitive + Zero,
{
type Param = <<Ks as Iterator>::Item as Density>::Param;
type Output = <<Ks as Iterator>::Item as Density>::Output;
#[allow(clippy::cast_precision_loss)]
fn density(&self, at: Self::Param) -> Self::Output {
let (n_points, sum) = self
.0
.clone()
.fold((0_usize, Self::Output::zero()), |(n, sum), component| {
(n + 1, sum + component.density(at))
});
if n_points == 0 {
Self::Output::zero()
} else {
sum / Self::Output::from_usize(n_points).unwrap()
}
}
}
impl<Ks> Sample for KernelDensityEstimator<Ks>
where
Ks: Iterator + Clone,
Ks::Item: Sample,
{
type Param = Option<<<Ks as Iterator>::Item as Sample>::Param>;
fn sample(&self, rng: &mut Rng) -> Self::Param {
let sample = self
.0
.clone()
.enumerate()
.filter(|(i, _)| rng.usize(0..=*i) == 0)
.last()?
.1
.sample(rng);
Some(sample)
}
}
#[cfg(test)]
mod tests {
use std::iter;
use super::*;
use crate::kernel::universal::Uniform;
#[test]
fn sample_single_component_ok() {
let kernel: Uniform<_, ()> = Uniform::with_bounds(-1.0..=1.0);
let kde = KernelDensityEstimator(iter::once(kernel));
let mut rng = Rng::new();
let sample = kde.sample(&mut rng).unwrap();
assert!((-1.0..=1.0).contains(&sample));
let _ = kde.sample(&mut rng).unwrap();
}
}