mod normal;
use nalgebra::{
DefaultAllocator, Dim, Dyn, OMatrix, OVector, RealField, Scalar, allocator::Allocator,
};
use rand::Rng;
use rand_distr::{Uniform, uniform::SampleUniform};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(bound(
serialize = "D: Serialize, B: Serialize, K: Serialize, OMatrix<T, D, Dyn>: Serialize, OVector<T, Dyn>: Serialize"
))]
#[serde(bound(
deserialize = "D: Deserialize<'de>, B: Deserialize<'de>, K: Deserialize<'de>, OMatrix<T, D, Dyn>: Deserialize<'de>, OVector<T, Dyn>: Deserialize<'de>"
))]
pub struct ParticleDensity<T, D, B, K>
where
T: Scalar,
D: Dim,
DefaultAllocator: Allocator<D> + Allocator<D, Dyn>,
{
particles: OMatrix<T, D, Dyn>,
opt_weights: Option<OVector<T, Dyn>>,
domain: B,
kernel: K,
}
impl<T, D, B, K> ParticleDensity<T, D, B, K>
where
T: RealField + SampleUniform,
D: Dim,
DefaultAllocator: Allocator<D> + Allocator<D, Dyn>,
{
pub fn kernel(&self) -> &K {
&self.kernel
}
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.particles.ncols()
}
pub fn particles(&self) -> &OMatrix<T, D, Dyn> {
&self.particles
}
pub fn sample_particle(&self, rng: &mut impl Rng) -> 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
}
}