diff -r 6105b5cd8d89 -r f0e8704d3f0e src/measures/discrete.rs --- a/src/measures/discrete.rs Tue Aug 01 10:25:09 2023 +0300 +++ b/src/measures/discrete.rs Mon Feb 17 13:54:53 2025 -0500 @@ -11,9 +11,11 @@ use alg_tools::norms::Norm; use alg_tools::tabledump::TableDump; -use alg_tools::linops::{Apply, Linear}; +use alg_tools::linops::{Mapping, Linear}; use alg_tools::iter::{MapF,Mappable}; use alg_tools::nalgebra_support::ToNalgebraRealField; +use alg_tools::collection::Collection; +use alg_tools::instance::{Instance, Decomposition, MyCow, EitherDecomp, Space}; use crate::types::*; use super::base::*; @@ -29,6 +31,8 @@ pub(super) spikes : Vec>, } +pub type RNDM = DiscreteMeasure, F>; + /// Iterator over the [`DeltaMeasure`] spikes of a [`DiscreteMeasure`]. pub type SpikeIter<'a, Domain, F> = std::slice::Iter<'a, DeltaMeasure>; @@ -59,6 +63,20 @@ self.spikes.len() } + /// Replace with the zero measure. + #[inline] + pub fn clear(&mut self) { + self.spikes.clear() + } + + /// Remove `i`:th spike, not maintaining order. + /// + /// Panics if indiex is out of bounds. + #[inline] + pub fn swap_remove(&mut self, i : usize) -> DeltaMeasure{ + self.spikes.swap_remove(i) + } + /// Iterate over (references to) the [`DeltaMeasure`] spikes in this measure #[inline] pub fn iter_spikes(&self) -> SpikeIter<'_, Domain, F> { @@ -95,6 +113,13 @@ self.spikes.iter_mut().zip(iter).for_each(|(δ, α)| δ.set_mass(α)); } + /// Update the locations of all the spikes to those produced by an iterator. + #[inline] + pub fn set_locations<'a, I : Iterator>(&mut self, iter : I) + where Domain : 'static + Clone { + self.spikes.iter_mut().zip(iter.cloned()).for_each(|(δ, α)| δ.set_location(α)); + } + // /// Map the masses of all the spikes using a function and an iterator // #[inline] // pub fn zipmap_masses< @@ -107,50 +132,162 @@ /// Prune all spikes with zero mass. #[inline] pub fn prune(&mut self) { - self.spikes.retain(|δ| δ.α != F::ZERO); + self.prune_by(|δ| δ.α != F::ZERO); + } + + /// Prune spikes by the predicate `g`. + #[inline] + pub fn prune_by) -> bool>(&mut self, g : G) { + self.spikes.retain(g); + } + + /// Add the spikes produced by `iter` to this measure. + #[inline] + pub fn extend>>( + &mut self, + iter : I + ) { + self.spikes.extend(iter); + } + + /// Add a spike to the measure + #[inline] + pub fn push(&mut self, δ : DeltaMeasure) { + self.spikes.push(δ); + } + + /// Iterate over triples of masses and locations of two discrete measures, which are assumed + /// to have equal locations of same spike indices. + pub fn both_matching<'a>(&'a self, other : &'a DiscreteMeasure) -> + impl Iterator { + let m = self.len().max(other.len()); + self.iter_spikes().map(Some).chain(std::iter::repeat(None)) + .zip(other.iter_spikes().map(Some).chain(std::iter::repeat(None))) + .take(m) + .map(|(oδ, orδ)| { + match (oδ, orδ) { + (Some(δ), Some(rδ)) => (δ.α, rδ.α, &δ.x), // Assumed δ.x=rδ.x + (Some(δ), None) => (δ.α, F::ZERO, &δ.x), + (None, Some(rδ)) => (F::ZERO, rδ.α, &rδ.x), + (None, None) => panic!("This cannot happen!"), + } + }) + } + + /// Subtract `other` from `self`, assuming equal locations of same spike indices + pub fn sub_matching(&self, other : &DiscreteMeasure) -> DiscreteMeasure + where Domain : Clone { + self.both_matching(other) + .map(|(α, β, x)| (x.clone(), α - β)) + .collect() + } + + /// Add `other` to `self`, assuming equal locations of same spike indices + pub fn add_matching(&self, other : &DiscreteMeasure) -> DiscreteMeasure + where Domain : Clone { + self.both_matching(other) + .map(|(α, β, x)| (x.clone(), α + β)) + .collect() + } + + /// Calculate the Radon-norm distance of `self` to `other`, + /// assuming equal locations of same spike indices. + pub fn dist_matching(&self, other : &DiscreteMeasure) -> F where F : Float { + self.both_matching(other) + .map(|(α, β, _)| (α-β).abs()) + .sum() + } +} + +impl IntoIterator for DiscreteMeasure { + type Item = DeltaMeasure; + type IntoIter = std::vec::IntoIter>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.spikes.into_iter() + } +} + +impl<'a, Domain, F : Num> IntoIterator for &'a DiscreteMeasure { + type Item = &'a DeltaMeasure; + type IntoIter = SpikeIter<'a, Domain, F>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.spikes.iter() + } +} + +impl Sum> for DiscreteMeasure { + // Required method + fn sum(iter: I) -> Self + where + I : Iterator> + { + Self::from_iter(iter) + } +} + +impl<'a, Domain : Clone, F : Num> Sum<&'a DeltaMeasure> + for DiscreteMeasure +{ + // Required method + fn sum(iter: I) -> Self + where + I : Iterator> + { + Self::from_iter(iter.cloned()) + } +} + +impl Sum> for DiscreteMeasure { + // Required method + fn sum(iter: I) -> Self + where + I : Iterator> + { + Self::from_iter(iter.map(|μ| μ.into_iter()).flatten()) + } +} + +impl<'a, Domain : Clone, F : Num> Sum<&'a DiscreteMeasure> + for DiscreteMeasure +{ + // Required method + fn sum(iter: I) -> Self + where + I : Iterator> + { + Self::from_iter(iter.map(|μ| μ.iter_spikes()).flatten().cloned()) } } impl DiscreteMeasure { /// Computes `μ1 ← θ * μ1 - ζ * μ2`, pruning entries where both `μ1` (`self`) and `μ2` have - // zero weight. `μ2` will contain copy of pruned original `μ1` without arithmetic performed. - /// **This expects `self` and `μ2` to have matching coordinates in each index**. + // zero weight. `μ2` will contain a pruned copy of pruned original `μ1` without arithmetic + /// performed. **This expects `self` and `μ2` to have matching coordinates in each index**. // `μ2` can be than `self`, but not longer. pub fn pruning_sub(&mut self, θ : F, ζ : F, μ2 : &mut Self) { - let mut μ2_get = 0; - let mut μ2_insert = 0; - self.spikes.drain_filter(|&mut DeltaMeasure{ α : ref mut α_ref, ref x }| { - // Get weight of spike in μ2, zero if out of bounds. - let β = μ2.spikes.get(μ2_get).map_or(F::ZERO, DeltaMeasure::get_mass); - μ2_get += 1; - - if *α_ref == F::ZERO && β == F::ZERO { - // Prune - true + for δ in &self[μ2.len()..] { + μ2.push(DeltaMeasure{ x : δ.x.clone(), α : F::ZERO}); + } + debug_assert_eq!(self.len(), μ2.len()); + let mut dest = 0; + for i in 0..self.len() { + let α = self[i].α; + let α_new = θ * α - ζ * μ2[i].α; + if dest < i { + μ2[dest] = DeltaMeasure{ x : self[i].x.clone(), α }; + self[dest] = DeltaMeasure{ x : self[i].x.clone(), α : α_new }; } else { - // Save self weight - let α = *α_ref; - // Modify self - *α_ref = θ * α - ζ * β; - // Make copy of old self weight in μ2 - let δ = DeltaMeasure{ α, x : x.clone() }; - match μ2.spikes.get_mut(μ2_insert) { - Some(replace) => { - *replace = δ; - }, - None => { - debug_assert_eq!(μ2.len(), μ2_insert); - μ2.spikes.push(δ); - }, - } - μ2_insert += 1; - // Keep - false + μ2[i].α = α; + self[i].α = α_new; } - }); - // Truncate μ2 to same length as self. - μ2.spikes.truncate(μ2_insert); - debug_assert_eq!(μ2.len(), self.len()); + dest += 1; + } + self.spikes.truncate(dest); + μ2.spikes.truncate(dest); } } @@ -174,23 +311,61 @@ pub fn set_masses_dvector(&mut self, x : &DVector) { self.set_masses(x.iter().map(|&α| F::from_nalgebra_mixed(α))); } + + // /// Extracts the masses of the spikes as a [`Vec`]. + // pub fn masses_vec(&self) -> Vec { + // self.iter_masses() + // .map(|α| α.to_nalgebra_mixed()) + // .collect() + // } + + // /// Sets the masses of the spikes from the values of a [`Vec`]. + // pub fn set_masses_vec(&mut self, x : &Vec) { + // self.set_masses(x.iter().map(|&α| F::from_nalgebra_mixed(α))); + // } } -impl Index for DiscreteMeasure { - type Output = DeltaMeasure; +// impl Index for DiscreteMeasure { +// type Output = DeltaMeasure; +// #[inline] +// fn index(&self, i : usize) -> &Self::Output { +// self.spikes.index(i) +// } +// } + +// impl IndexMut for DiscreteMeasure { +// #[inline] +// fn index_mut(&mut self, i : usize) -> &mut Self::Output { +// self.spikes.index_mut(i) +// } +// } + +impl< + Domain, + F : Num, + I : std::slice::SliceIndex<[DeltaMeasure]> +> Index +for DiscreteMeasure { + type Output = ]>>::Output; #[inline] - fn index(&self, i : usize) -> &Self::Output { + fn index(&self, i : I) -> &Self::Output { self.spikes.index(i) } } -impl IndexMut for DiscreteMeasure { +impl< + Domain, + F : Num, + I : std::slice::SliceIndex<[DeltaMeasure]> +> IndexMut +for DiscreteMeasure { #[inline] - fn index_mut(&mut self, i : usize) -> &mut Self::Output { + fn index_mut(&mut self, i : I) -> &mut Self::Output { self.spikes.index_mut(i) } } + impl>, const K : usize> From<[D; K]> for DiscreteMeasure { #[inline] @@ -199,6 +374,45 @@ } } +impl From>> +for DiscreteMeasure { + #[inline] + fn from(spikes : Vec>) -> Self { + DiscreteMeasure{ spikes } + } +} + +impl<'a, Domain, F : Num, D> From<&'a [D]> +for DiscreteMeasure +where &'a D : Into> { + #[inline] + fn from(list : &'a [D]) -> Self { + list.into_iter().map(|d| d.into()).collect() + } +} + + +impl From> +for DiscreteMeasure { + #[inline] + fn from(δ : DeltaMeasure) -> Self { + DiscreteMeasure{ + spikes : vec!(δ) + } + } +} + +impl<'a, Domain : Clone, F : Num> From<&'a DeltaMeasure> +for DiscreteMeasure { + #[inline] + fn from(δ : &'a DeltaMeasure) -> Self { + DiscreteMeasure{ + spikes : vec!(δ.clone()) + } + } +} + + impl>> FromIterator for DiscreteMeasure { #[inline] @@ -258,19 +472,28 @@ } } -impl> Apply for DiscreteMeasure -where G: for<'a> Apply<&'a Domain, Output = Y> { - type Output = Y; +impl Mapping for DiscreteMeasure +where + Domain : Space, + G::Codomain : Sum + Mul, + G : Mapping + Clone + Space, + for<'b> &'b Domain : Instance, +{ + type Codomain = G::Codomain; + #[inline] - fn apply(&self, g : G) -> Y { - self.spikes.iter().map(|m| g.apply(&m.x) * m.α).sum() + fn apply>(&self, g : I) -> Self::Codomain { + g.eval(|g| self.spikes.iter().map(|m| g.apply(&m.x) * m.α).sum()) } } -impl> Linear for DiscreteMeasure -where G : for<'a> Apply<&'a Domain, Output = Y> { - type Codomain = Y; -} +impl Linear for DiscreteMeasure +where + Domain : Space, + G::Codomain : Sum + Mul, + G : Mapping + Clone + Space, + for<'b> &'b Domain : Instance, +{ } /// Helper trait for constructing arithmetic operations for combinations @@ -278,6 +501,7 @@ trait Lift { type Producer : Iterator>; + #[allow(dead_code)] /// Lifts `self` into a [`DiscreteMeasure`]. fn lift(self) -> DiscreteMeasure; @@ -574,3 +798,217 @@ make_discrete_scalarop_lhs!(Mul, mul; f32 f64 i8 i16 i32 i64 isize u8 u16 u32 u64 usize); make_discrete_scalarop_lhs!(Div, div; f32 f64 i8 i16 i32 i64 isize u8 u16 u32 u64 usize); + +impl Collection for DiscreteMeasure { + type Element = DeltaMeasure; + type RefsIter<'a> = std::slice::Iter<'a, Self::Element> where Self : 'a; + + #[inline] + fn iter_refs(&self) -> Self::RefsIter<'_> { + self.iter_spikes() + } +} + +impl Space for DiscreteMeasure { + type Decomp = MeasureDecomp; +} + +pub type SpikeSlice<'b, Domain, F> = &'b [DeltaMeasure]; + +pub type EitherSlice<'b, Domain, F> = EitherDecomp< + Vec>, + SpikeSlice<'b, Domain, F> +>; + +impl Decomposition> for MeasureDecomp { + type Decomposition<'b> = EitherSlice<'b, Domain, F> where DiscreteMeasure : 'b; + type Reference<'b> = SpikeSlice<'b, Domain, F> where DiscreteMeasure : 'b; + + /// Left the lightweight reference type into a full decomposition type. + fn lift<'b>(r : Self::Reference<'b>) -> Self::Decomposition<'b> { + EitherDecomp::Borrowed(r) + } +} + +impl Instance, MeasureDecomp> +for DiscreteMeasure +{ + fn decompose<'b>(self) + -> >>::Decomposition<'b> + where Self : 'b, DiscreteMeasure : 'b { + EitherDecomp::Owned(self.spikes) + } + + fn ref_instance(&self) + -> >>::Reference<'_> + { + self.spikes.as_slice() + } + + fn cow<'b>(self) -> MyCow<'b, DiscreteMeasure> where Self : 'b { + MyCow::Owned(self) + } + + fn own(self) -> DiscreteMeasure { + self + } +} + +impl<'a, F : Num, Domain : Clone> Instance, MeasureDecomp> +for &'a DiscreteMeasure +{ + fn decompose<'b>(self) + -> >>::Decomposition<'b> + where Self : 'b, DiscreteMeasure : 'b { + EitherDecomp::Borrowed(self.spikes.as_slice()) + } + + fn ref_instance(&self) + -> >>::Reference<'_> + { + self.spikes.as_slice() + } + + fn cow<'b>(self) -> MyCow<'b, DiscreteMeasure> where Self : 'b { + MyCow::Borrowed(self) + } + + fn own(self) -> DiscreteMeasure { + self.clone() + } +} + +impl<'a, F : Num, Domain : Clone> Instance, MeasureDecomp> +for EitherSlice<'a, Domain, F> +{ + fn decompose<'b>(self) + -> >>::Decomposition<'b> + where Self : 'b, DiscreteMeasure : 'b { + self + } + + fn ref_instance(&self) + -> >>::Reference<'_> + { + match self { + EitherDecomp::Owned(v) => v.as_slice(), + EitherDecomp::Borrowed(s) => s, + } + } + + fn own(self) -> DiscreteMeasure { + match self { + EitherDecomp::Owned(v) => v.into(), + EitherDecomp::Borrowed(s) => s.into(), + } + } +} + +impl<'a, F : Num, Domain : Clone> Instance, MeasureDecomp> +for &'a EitherSlice<'a, Domain, F> +{ + fn decompose<'b>(self) + -> >>::Decomposition<'b> + where Self : 'b, DiscreteMeasure : 'b { + match self { + EitherDecomp::Owned(v) => EitherDecomp::Borrowed(v.as_slice()), + EitherDecomp::Borrowed(s) => EitherDecomp::Borrowed(s), + } + } + + fn ref_instance(&self) + -> >>::Reference<'_> + { + match self { + EitherDecomp::Owned(v) => v.as_slice(), + EitherDecomp::Borrowed(s) => s, + } + } + + fn own(self) -> DiscreteMeasure { + match self { + EitherDecomp::Owned(v) => v.as_slice(), + EitherDecomp::Borrowed(s) => s + }.into() + } +} + +impl<'a, F : Num, Domain : Clone> Instance, MeasureDecomp> +for SpikeSlice<'a, Domain, F> +{ + fn decompose<'b>(self) + -> >>::Decomposition<'b> + where Self : 'b, DiscreteMeasure : 'b { + EitherDecomp::Borrowed(self) + } + + fn ref_instance(&self) + -> >>::Reference<'_> + { + self + } + + fn own(self) -> DiscreteMeasure { + self.into() + } +} + +impl<'a, F : Num, Domain : Clone> Instance, MeasureDecomp> +for &'a SpikeSlice<'a, Domain, F> +{ + fn decompose<'b>(self) + -> >>::Decomposition<'b> + where Self : 'b, DiscreteMeasure : 'b { + EitherDecomp::Borrowed(*self) + } + + fn ref_instance(&self) + -> >>::Reference<'_> + { + *self + } + + fn own(self) -> DiscreteMeasure { + (*self).into() + } +} + +impl Instance, MeasureDecomp> +for DeltaMeasure +{ + fn decompose<'b>(self) + -> >>::Decomposition<'b> + where Self : 'b, DiscreteMeasure : 'b { + EitherDecomp::Owned(vec![self]) + } + + fn ref_instance(&self) + -> >>::Reference<'_> + { + std::slice::from_ref(self) + } + + fn own(self) -> DiscreteMeasure { + self.into() + } +} + +impl<'a, F : Num, Domain : Clone> Instance, MeasureDecomp> +for &'a DeltaMeasure +{ + fn decompose<'b>(self) + -> >>::Decomposition<'b> + where Self : 'b, DiscreteMeasure : 'b { + EitherDecomp::Borrowed(std::slice::from_ref(self)) + } + + fn ref_instance(&self) + -> >>::Reference<'_> + { + std::slice::from_ref(*self) + } + + fn own(self) -> DiscreteMeasure { + self.into() + } +}