--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/delta.rs Fri Nov 28 12:48:17 2025 -0500 @@ -0,0 +1,320 @@ +/*! +This module implementes delta measures, i.e., single spikes $\alpha \delta_x$ for some +location $x$ and mass $\alpha$. +*/ + +use super::base::*; +use alg_tools::instance::{ClosedSpace, Instance, Space}; +use alg_tools::linops::{Linear, Mapping}; +use alg_tools::loc::Loc; +use alg_tools::norms::Norm; +use alg_tools::self_ownable; +use alg_tools::types::*; +use serde::ser::{Serialize, SerializeStruct, Serializer}; +use std::ops::{Div, DivAssign, Mul, MulAssign, Neg}; + +/// Representation of a delta measure. +/// +/// This is a single spike $\alpha \delta\_x$ for some location $x$ in `Domain` and +/// a mass $\alpha$ in `F`. +#[derive(Clone, Copy, Debug)] +pub struct DeltaMeasure<Domain, F: Num> { + // This causes [`csv`] to crash. + //#[serde(flatten)] + /// Location of the spike + pub x: Domain, + /// Mass of the spike + pub α: F, +} + +const COORDINATE_NAMES: &'static [&'static str] = &["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7"]; + +// Need to manually implement serialisation as [`csv`] writer fails on +// structs with nested arrays as well as with #[serde(flatten)]. +impl<F: Num, const N: usize> Serialize for DeltaMeasure<Loc<N, F>, F> +where + F: Serialize, +{ + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + assert!(N <= COORDINATE_NAMES.len()); + + let mut s = serializer.serialize_struct("DeltaMeasure", N + 1)?; + for (i, e) in (0..).zip(self.x.iter()) { + s.serialize_field(COORDINATE_NAMES[i], e)?; + } + s.serialize_field("weight", &self.α)?; + s.end() + } +} + +impl<Domain, F: Float> Measure<F> for DeltaMeasure<Domain, F> { + type Domain = Domain; +} + +impl<Domain, F: Float> Norm<Radon, F> for DeltaMeasure<Domain, F> { + #[inline] + fn norm(&self, _: Radon) -> F { + self.α.abs() + } +} + +// impl<Domain : PartialEq, F : Float> Dist<Radon, F> for DeltaMeasure<Domain, F> { +// #[inline] +// fn dist(&self, other : &Self, _ : Radon) -> F { +// if self.x == other. x { +// (self.α - other.α).abs() +// } else { +// self.α.abs() + other.α.abs() +// } +// } +// } + +impl<Domain, G, F: Num> Mapping<G> for DeltaMeasure<Domain, F> +where + Domain: Space, + G::Codomain: Mul<F, Output = G::Codomain>, + G: Mapping<Domain> + Clone + ClosedSpace, + for<'b> &'b Domain: Instance<Domain>, +{ + type Codomain = G::Codomain; + + #[inline] + fn apply<I: Instance<G>>(&self, g: I) -> Self::Codomain { + g.eval(|g̃| g̃.apply(&self.x) * self.α) + } +} + +impl<Domain, G, F: Num> Linear<G> for DeltaMeasure<Domain, F> +where + Domain: Space, + G::Codomain: Mul<F, Output = G::Codomain>, + G: Mapping<Domain> + Clone + ClosedSpace, + for<'b> &'b Domain: Instance<Domain>, +{ +} + +// /// Partial blanket implementation of [`DeltaMeasure`] as a linear functional of [`Mapping`]s. +// /// A full blanket implementation is not possible due to annoying Rust limitations: only [`Apply`] +// /// on a reference is implemented, but a consuming [`Apply`] has to be implemented on a case-by-case +// /// basis, not because an implementation could not be written, but because the Rust trait system +// /// chokes up. +// impl<Domain, G, F : Num, V> Linear<G> for DeltaMeasure<Domain, F> +// where G: for<'a> Apply<&'a Domain, Output = V>, +// V : Mul<F>, +// Self: Apply<G, Output = <V as Mul<F>>::Output> { +// type Codomain = <V as Mul<F>>::Output; +// } + +// impl<'b, Domain, G, F : Num, V> Apply<&'b G> for DeltaMeasure<Domain, F> +// where G: for<'a> Apply<&'a Domain, Output = V>, +// V : Mul<F> { +// type Output = <V as Mul<F>>::Output; + +// #[inline] +// fn apply(&self, g : &'b G) -> Self::Output { +// g.apply(&self.x) * self.α +// } +// } + +// /// Implementation of the necessary apply for BTFNs +// mod btfn_apply { +// use super::*; +// use alg_tools::bisection_tree::{BTFN, BTImpl, SupportGenerator, LocalAnalysis}; + +// impl<F : Float, BT, G, V, const N : usize> Apply<BTFN<F, G, BT, N>> +// for DeltaMeasure<Loc<N, F>, F> +// where BT : BTImpl< N, F>, +// G : SupportGenerator< N, F, Id=BT::Data>, +// G::SupportType : LocalAnalysis<F, BT::Agg, N> + for<'a> Apply<&'a Loc<N, F>, Output = V>, +// V : std::iter::Sum + Mul<F> { + +// type Output = <V as Mul<F>>::Output; + +// #[inline] +// fn apply(&self, g : BTFN<F, G, BT, N>) -> Self::Output { +// g.apply(&self.x) * self.α +// } +// } +// } + +impl<D, Domain, F: Num> From<(D, F)> for DeltaMeasure<Domain, F> +where + D: Into<Domain>, +{ + #[inline] + fn from((x, α): (D, F)) -> Self { + DeltaMeasure { x: x.into(), α: α } + } +} + +impl<'a, Domain: Clone, F: Num> From<&'a DeltaMeasure<Domain, F>> for DeltaMeasure<Domain, F> { + #[inline] + fn from(d: &'a DeltaMeasure<Domain, F>) -> Self { + d.clone() + } +} + +impl<Domain, F: Num> DeltaMeasure<Domain, F> { + /// Set the mass of the spike. + #[inline] + pub fn set_mass(&mut self, α: F) { + self.α = α + } + + /// Set the location of the spike. + #[inline] + pub fn set_location(&mut self, x: Domain) { + self.x = x + } + + /// Get the mass of the spike. + #[inline] + pub fn get_mass(&self) -> F { + self.α + } + + /// Get a mutable reference to the mass of the spike. + #[inline] + pub fn get_mass_mut(&mut self) -> &mut F { + &mut self.α + } + + /// Get a reference to the location of the spike. + #[inline] + pub fn get_location(&self) -> &Domain { + &self.x + } + + /// Get a mutable reference to the location of the spike. + #[inline] + pub fn get_location_mut(&mut self) -> &mut Domain { + &mut self.x + } +} + +impl<Domain, F: Num> IntoIterator for DeltaMeasure<Domain, F> { + type Item = Self; + type IntoIter = std::iter::Once<Self>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + std::iter::once(self) + } +} + +impl<'a, Domain, F: Num> IntoIterator for &'a DeltaMeasure<Domain, F> { + type Item = Self; + type IntoIter = std::iter::Once<Self>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + std::iter::once(self) + } +} + +macro_rules! make_delta_scalarop_rhs { + ($trait:ident, $fn:ident, $trait_assign:ident, $fn_assign:ident) => { + impl<F: Num, Domain> $trait<F> for DeltaMeasure<Domain, F> { + type Output = Self; + fn $fn(mut self, b: F) -> Self { + self.α.$fn_assign(b); + self + } + } + + impl<'a, F: Num, Domain> $trait<&'a F> for DeltaMeasure<Domain, F> { + type Output = Self; + fn $fn(mut self, b: &'a F) -> Self { + self.α.$fn_assign(*b); + self + } + } + + impl<'b, F: Num, Domain: Clone> $trait<F> for &'b DeltaMeasure<Domain, F> { + type Output = DeltaMeasure<Domain, F>; + fn $fn(self, b: F) -> Self::Output { + DeltaMeasure { α: self.α.$fn(b), x: self.x.clone() } + } + } + + impl<'a, 'b, F: Num, Domain: Clone> $trait<&'a F> for &'b DeltaMeasure<Domain, F> { + type Output = DeltaMeasure<Domain, F>; + fn $fn(self, b: &'a F) -> Self::Output { + DeltaMeasure { α: self.α.$fn(*b), x: self.x.clone() } + } + } + + impl<F: Num, Domain> $trait_assign<F> for DeltaMeasure<Domain, F> { + fn $fn_assign(&mut self, b: F) { + self.α.$fn_assign(b) + } + } + + impl<'a, F: Num, Domain> $trait_assign<&'a F> for DeltaMeasure<Domain, F> { + fn $fn_assign(&mut self, b: &'a F) { + self.α.$fn_assign(*b) + } + } + }; +} + +make_delta_scalarop_rhs!(Mul, mul, MulAssign, mul_assign); +make_delta_scalarop_rhs!(Div, div, DivAssign, div_assign); + +macro_rules! make_delta_scalarop_lhs { + ($trait:ident, $fn:ident; $($f:ident)+) => { $( + impl<Domain> $trait<DeltaMeasure<Domain, $f>> for $f { + type Output = DeltaMeasure<Domain, $f>; + fn $fn(self, mut δ : DeltaMeasure<Domain, $f>) -> Self::Output { + δ.α = self.$fn(δ.α); + δ + } + } + + impl<'a, Domain : Clone> $trait<&'a DeltaMeasure<Domain, $f>> for $f { + type Output = DeltaMeasure<Domain, $f>; + fn $fn(self, δ : &'a DeltaMeasure<Domain, $f>) -> Self::Output { + DeltaMeasure{ x : δ.x.clone(), α : self.$fn(δ.α) } + } + } + + impl<'b, Domain> $trait<DeltaMeasure<Domain, $f>> for &'b $f { + type Output = DeltaMeasure<Domain, $f>; + fn $fn(self, mut δ : DeltaMeasure<Domain, $f>) -> Self::Output { + δ.α = self.$fn(δ.α); + δ + } + } + + impl<'a, 'b, Domain : Clone> $trait<&'a DeltaMeasure<Domain, $f>> for &'b $f { + type Output = DeltaMeasure<Domain, $f>; + fn $fn(self, δ : &'a DeltaMeasure<Domain, $f>) -> Self::Output { + DeltaMeasure{ x : δ.x.clone(), α : self.$fn(δ.α) } + } + } + )+ } +} + +make_delta_scalarop_lhs!(Mul, mul; f32 f64 i8 i16 i32 i64 isize u8 u16 u32 u64 usize); +make_delta_scalarop_lhs!(Div, div; f32 f64 i8 i16 i32 i64 isize u8 u16 u32 u64 usize); + +macro_rules! make_delta_unary { + ($trait:ident, $fn:ident, $type:ty) => { + impl<'a, F: Num + Neg<Output = F>, Domain: Clone> Neg for $type { + type Output = DeltaMeasure<Domain, F>; + fn $fn(self) -> Self::Output { + let mut tmp = self.clone(); + tmp.α = tmp.α.$fn(); + tmp + } + } + }; +} + +make_delta_unary!(Neg, neg, DeltaMeasure<Domain, F>); +make_delta_unary!(Neg, neg, &'a DeltaMeasure<Domain, F>); + +self_ownable!(DeltaMeasure<Domain, F> where Domain: Clone, F: Num);