mod normal;
use crate::Domain;
use nalgebra::{DefaultAllocator, Dim, Dyn, OMatrix, OVector, RealField, allocator::Allocator};
use rand::RngExt;
use rand_distr::{Uniform, uniform::SampleUniform};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(bound(
serialize = "D: Serialize, K: Serialize, OMatrix<T, D, Dyn>: Serialize, OVector<T, Dyn>: Serialize, Domain<T, D>: Serialize"
))]
#[serde(bound(
deserialize = "D: Deserialize<'de>, K: Deserialize<'de>, OMatrix<T, D, Dyn>: Deserialize<'de>, OVector<T, Dyn>: Deserialize<'de>, Domain<T, D>: Deserialize<'de>"
))]
pub struct ParticleDensity<T, D, K>
where
T: RealField,
D: Dim,
DefaultAllocator: Allocator<D> + Allocator<D, Dyn>,
{
particles: OMatrix<T, D, Dyn>,
opt_weights: Option<OVector<T, Dyn>>,
domain: Domain<T, D>,
kernel: K,
}
impl<T, D, K> ParticleDensity<T, D, K>
where
T: RealField + SampleUniform,
D: Dim,
DefaultAllocator: Allocator<D> + Allocator<D, Dyn>,
{
#[allow(clippy::len_without_is_empty)]
pub fn count(&self) -> usize {
self.particles.ncols()
}
pub fn kernel(&self) -> &K {
&self.kernel
}
pub fn particles(&self) -> &OMatrix<T, D, Dyn> {
&self.particles
}
pub fn sample_particle(&self, rng: &mut impl RngExt) -> OVector<T, D> {
let pdx = {
match &self.opt_weights {
Some(weights) =>
{
let uniform = Uniform::new(T::zero(), T::one()).unwrap();
let wdx = rng.sample(uniform);
match weights
.iter()
.enumerate()
.try_fold(T::zero(), |acc, (idx, weight)| {
let next_weight = acc + weight.clone();
if wdx < next_weight {
Err(idx)
} else {
Ok(next_weight)
}
}) {
Ok(_) => weights.len() - 1,
Err(idx) => idx,
}
}
None => {
let uniform = Uniform::new(0, self.particles.ncols()).unwrap();
rng.sample(uniform)
}
}
};
self.particles.column(pdx).clone_owned()
}
pub fn weights(&self) -> &Option<OVector<T, Dyn>> {
&self.opt_weights
}
}