src/delta.rs

changeset 0
e8f3b6c55ce7
child 4
f248f9848c87
--- /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);

mercurial