prodef 0.1.0

A simple Rust crate for handling probability distributions.
Documentation
use crate::{Domain, domain::SDomain};
use nalgebra::{
    DefaultAllocator, Dim, OVector, RealField, SVector, Scalar, U1, VectorView,
    allocator::Allocator,
};
use serde::{Deserialize, Serialize};

/// A simple, D-dimensional, function domain.
///
/// This function domain represents a Cartesian product of [`SDomain`]s.
#[allow(missing_docs)]
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(bound(serialize = "OVector<SDomain<T>, D>: Serialize"))]
#[serde(bound(deserialize = "OVector<SDomain<T>, D>: Deserialize<'de>"))]
pub struct MDomain<T, D>(OVector<SDomain<T>, D>)
where
    T: Scalar,
    D: Dim,
    DefaultAllocator: Allocator<D>;

impl<T, D> MDomain<T, D>
where
    T: Scalar,
    D: Dim,
    DefaultAllocator: Allocator<D>,
{
    /// Create a new [`MDomain`] from a vector of [`SDomain`]s.
    pub fn new(domains: OVector<SDomain<T>, D>) -> Self {
        Self(domains)
    }

    /// Create an unbounded [`MDomain`] of the given dimensionality.
    pub fn unbounded(ndim: usize) -> Self {
        Self(OVector::from_element_generic(
            D::from_usize(ndim),
            U1,
            SDomain::Unbounded,
        ))
    }
}

impl<T, D> Domain<T, D> for MDomain<T, D>
where
    T: PartialOrd + Scalar + Send + Sync,
    D: Dim,
    DefaultAllocator: Allocator<D>,
    <DefaultAllocator as Allocator<D>>::Buffer<SDomain<T>>: Send + Sync,
{
    fn clip<RStride: Dim, CStride: Dim>(
        &self,
        sample: &VectorView<T, D, RStride, CStride>,
    ) -> OVector<T, D> {
        (&self).clip(sample)
    }

    fn contains<RStride: Dim, CStride: Dim>(
        &self,
        sample: &VectorView<T, D, RStride, CStride>,
    ) -> bool {
        (&self).contains(sample)
    }

    fn maximum_values(&self) -> Option<OVector<T, D>> {
        (&self).maximum_values()
    }

    fn minimum_values(&self) -> Option<OVector<T, D>> {
        (&self).minimum_values()
    }

    fn size(&self) -> OVector<Option<T>, D>
    where
        T: RealField,
    {
        (&self).size()
    }
}

impl<T, D> Domain<T, D> for &MDomain<T, D>
where
    T: PartialOrd + Scalar + Send + Sync,
    D: Dim,
    DefaultAllocator: Allocator<D>,
    <DefaultAllocator as Allocator<D>>::Buffer<SDomain<T>>: Send + Sync,
{
    fn clip<RStride: Dim, CStride: Dim>(
        &self,
        sample: &VectorView<T, D, RStride, CStride>,
    ) -> OVector<T, D> {
        OVector::from_iterator_generic(
            sample.shape_generic().0,
            U1,
            self.0.iter().enumerate().map(|(i, sdom)| {
                Domain::<T, U1>::clip::<U1, U1>(sdom, &SVector::from([sample[i].clone()]).as_view())
                    [0]
                .clone()
            }),
        )
    }

    fn contains<RStride: Dim, CStride: Dim>(
        &self,
        sample: &VectorView<T, D, RStride, CStride>,
    ) -> bool {
        self.0.iter().zip(sample).fold(true, |acc, (sdom, value)| {
            acc & Domain::<T, U1>::contains::<U1, U1>(
                sdom,
                &SVector::from([value.clone()]).as_view(),
            )
        })
    }

    fn maximum_values(&self) -> Option<OVector<T, D>> {
        Some(OVector::from_iterator_generic(
            self.0.shape_generic().0,
            U1,
            self.0
                .iter()
                .map(|sdom| Domain::<T, U1>::maximum_values(sdom).unwrap()[0].clone()),
        ))
    }

    fn minimum_values(&self) -> Option<OVector<T, D>> {
        Some(OVector::from_iterator_generic(
            self.0.shape_generic().0,
            U1,
            self.0
                .iter()
                .map(|sdom| Domain::<T, U1>::minimum_values(sdom).unwrap()[0].clone()),
        ))
    }

    fn size(&self) -> OVector<Option<T>, D>
    where
        T: RealField,
    {
        OVector::from_iterator_generic(
            self.0.shape_generic().0,
            U1,
            self.0
                .iter()
                .map(|sdom| Domain::<T, U1>::size(sdom)[0].clone()),
        )
    }
}