src/seminorms.rs

branch
dev
changeset 61
4f468d35fa29
parent 54
b3312eee105c
--- a/src/seminorms.rs	Sun Apr 27 15:03:51 2025 -0500
+++ b/src/seminorms.rs	Thu Feb 26 11:38:43 2026 -0500
@@ -6,13 +6,15 @@
 
 use crate::measures::{DeltaMeasure, DiscreteMeasure, Radon, SpikeIter, RNDM};
 use alg_tools::bisection_tree::*;
+use alg_tools::bounds::Bounded;
+use alg_tools::error::DynResult;
 use alg_tools::instance::Instance;
 use alg_tools::iter::{FilterMapX, Mappable};
 use alg_tools::linops::{BoundedLinear, Linear, Mapping};
 use alg_tools::loc::Loc;
 use alg_tools::mapping::RealMapping;
 use alg_tools::nalgebra_support::ToNalgebraRealField;
-use alg_tools::norms::Linfinity;
+use alg_tools::norms::{Linfinity, Norm, NormExponent};
 use alg_tools::sets::Cube;
 use alg_tools::types::*;
 use itertools::Itertools;
@@ -68,37 +70,37 @@
 //
 
 /// A trait alias for simple convolution kernels.
-pub trait SimpleConvolutionKernel<F: Float, const N: usize>:
-    RealMapping<F, N> + Support<F, N> + Bounded<F> + Clone + 'static
+pub trait SimpleConvolutionKernel<const N: usize, F: Float = f64>:
+    RealMapping<N, F> + Support<N, F> + Bounded<F> + Clone + 'static
 {
 }
 
-impl<T, F: Float, const N: usize> SimpleConvolutionKernel<F, N> for T where
-    T: RealMapping<F, N> + Support<F, N> + Bounded<F> + Clone + 'static
+impl<T, F: Float, const N: usize> SimpleConvolutionKernel<N, F> for T where
+    T: RealMapping<N, F> + Support<N, F> + Bounded<F> + Clone + 'static
 {
 }
 
 /// [`SupportGenerator`] for [`ConvolutionOp`].
 #[derive(Clone, Debug)]
-pub struct ConvolutionSupportGenerator<F: Float, K, const N: usize>
+pub struct ConvolutionSupportGenerator<K, const N: usize, F: Float = f64>
 where
-    K: SimpleConvolutionKernel<F, N>,
+    K: SimpleConvolutionKernel<N, F>,
 {
     kernel: K,
-    centres: RNDM<F, N>,
+    centres: RNDM<N, F>,
 }
 
-impl<F: Float, K, const N: usize> ConvolutionSupportGenerator<F, K, N>
+impl<F: Float, K, const N: usize> ConvolutionSupportGenerator<K, N, F>
 where
-    K: SimpleConvolutionKernel<F, N>,
+    K: SimpleConvolutionKernel<N, F>,
 {
     /// Construct the convolution kernel corresponding to `δ`, i.e., one centered at `δ.x` and
     /// weighted by `δ.α`.
     #[inline]
     fn construct_kernel<'a>(
         &'a self,
-        δ: &'a DeltaMeasure<Loc<F, N>, F>,
-    ) -> Weighted<Shift<K, F, N>, F> {
+        δ: &'a DeltaMeasure<Loc<N, F>, F>,
+    ) -> Weighted<Shift<K, N, F>, F> {
         self.kernel.clone().shift(δ.x).weigh(δ.α)
     }
 
@@ -108,21 +110,21 @@
     #[inline]
     fn construct_kernel_and_id_filtered<'a>(
         &'a self,
-        (id, δ): (usize, &'a DeltaMeasure<Loc<F, N>, F>),
-    ) -> Option<(usize, Weighted<Shift<K, F, N>, F>)> {
+        (id, δ): (usize, &'a DeltaMeasure<Loc<N, F>, F>),
+    ) -> Option<(usize, Weighted<Shift<K, N, F>, F>)> {
         (δ.α != F::ZERO).then(|| (id.into(), self.construct_kernel(δ)))
     }
 }
 
-impl<F: Float, K, const N: usize> SupportGenerator<F, N> for ConvolutionSupportGenerator<F, K, N>
+impl<F: Float, K, const N: usize> SupportGenerator<N, F> for ConvolutionSupportGenerator<K, N, F>
 where
-    K: SimpleConvolutionKernel<F, N>,
+    K: SimpleConvolutionKernel<N, F>,
 {
     type Id = usize;
-    type SupportType = Weighted<Shift<K, F, N>, F>;
+    type SupportType = Weighted<Shift<K, N, F>, F>;
     type AllDataIter<'a> = FilterMapX<
         'a,
-        Zip<RangeFrom<usize>, SpikeIter<'a, Loc<F, N>, F>>,
+        Zip<RangeFrom<usize>, SpikeIter<'a, Loc<N, F>, F>>,
         Self,
         (Self::Id, Self::SupportType),
     >;
@@ -150,13 +152,13 @@
 pub struct ConvolutionOp<F, K, BT, const N: usize>
 where
     F: Float + ToNalgebraRealField,
-    BT: BTImpl<F, N, Data = usize>,
-    K: SimpleConvolutionKernel<F, N>,
+    BT: BTImpl<N, F, Data = usize>,
+    K: SimpleConvolutionKernel<N, F>,
 {
     /// Depth of the [`BT`] bisection tree for the outputs [`Mapping::apply`].
     depth: BT::Depth,
     /// Domain of the [`BT`] bisection tree for the outputs [`Mapping::apply`].
-    domain: Cube<F, N>,
+    domain: Cube<N, F>,
     /// The convolution kernel
     kernel: K,
     _phantoms: PhantomData<(F, BT)>,
@@ -165,13 +167,13 @@
 impl<F, K, BT, const N: usize> ConvolutionOp<F, K, BT, N>
 where
     F: Float + ToNalgebraRealField,
-    BT: BTImpl<F, N, Data = usize>,
-    K: SimpleConvolutionKernel<F, N>,
+    BT: BTImpl<N, F, Data = usize>,
+    K: SimpleConvolutionKernel<N, F>,
 {
     /// Creates a new convolution operator $𝒟$ with `kernel` on `domain`.
     ///
     /// The output of [`Mapping::apply`] is a [`BT`] of given `depth`.
-    pub fn new(depth: BT::Depth, domain: Cube<F, N>, kernel: K) -> Self {
+    pub fn new(depth: BT::Depth, domain: Cube<N, F>, kernel: K) -> Self {
         ConvolutionOp {
             depth: depth,
             domain: domain,
@@ -181,7 +183,7 @@
     }
 
     /// Returns the support generator for this convolution operator.
-    fn support_generator(&self, μ: RNDM<F, N>) -> ConvolutionSupportGenerator<F, K, N> {
+    fn support_generator(&self, μ: RNDM<N, F>) -> ConvolutionSupportGenerator<K, N, F> {
         // TODO: can we avoid cloning μ?
         ConvolutionSupportGenerator {
             kernel: self.kernel.clone(),
@@ -195,18 +197,18 @@
     }
 }
 
-impl<F, K, BT, const N: usize> Mapping<RNDM<F, N>> for ConvolutionOp<F, K, BT, N>
+impl<F, K, BT, const N: usize> Mapping<RNDM<N, F>> for ConvolutionOp<F, K, BT, N>
 where
     F: Float + ToNalgebraRealField,
-    BT: BTImpl<F, N, Data = usize>,
-    K: SimpleConvolutionKernel<F, N>,
-    Weighted<Shift<K, F, N>, F>: LocalAnalysis<F, BT::Agg, N>,
+    BT: BTImpl<N, F, Data = usize>,
+    K: SimpleConvolutionKernel<N, F>,
+    Weighted<Shift<K, N, F>, F>: LocalAnalysis<F, BT::Agg, N>,
 {
-    type Codomain = BTFN<F, ConvolutionSupportGenerator<F, K, N>, BT, N>;
+    type Codomain = BTFN<F, ConvolutionSupportGenerator<K, N, F>, BT, N>;
 
     fn apply<I>(&self, μ: I) -> Self::Codomain
     where
-        I: Instance<RNDM<F, N>>,
+        I: Instance<RNDM<N, F>>,
     {
         let g = self.support_generator(μ.own());
         BTFN::construct(self.domain.clone(), self.depth, g)
@@ -214,46 +216,67 @@
 }
 
 /// [`ConvolutionOp`]s as linear operators over [`DiscreteMeasure`]s.
-impl<F, K, BT, const N: usize> Linear<RNDM<F, N>> for ConvolutionOp<F, K, BT, N>
+impl<F, K, BT, const N: usize> Linear<RNDM<N, F>> for ConvolutionOp<F, K, BT, N>
 where
     F: Float + ToNalgebraRealField,
-    BT: BTImpl<F, N, Data = usize>,
-    K: SimpleConvolutionKernel<F, N>,
-    Weighted<Shift<K, F, N>, F>: LocalAnalysis<F, BT::Agg, N>,
+    BT: BTImpl<N, F, Data = usize>,
+    K: SimpleConvolutionKernel<N, F>,
+    Weighted<Shift<K, N, F>, F>: LocalAnalysis<F, BT::Agg, N>,
 {
 }
 
-impl<F, K, BT, const N: usize> BoundedLinear<RNDM<F, N>, Radon, Linfinity, F>
+impl<F, K, BT, const N: usize> BoundedLinear<RNDM<N, F>, Radon, Linfinity, F>
     for ConvolutionOp<F, K, BT, N>
 where
     F: Float + ToNalgebraRealField,
-    BT: BTImpl<F, N, Data = usize>,
-    K: SimpleConvolutionKernel<F, N>,
-    Weighted<Shift<K, F, N>, F>: LocalAnalysis<F, BT::Agg, N>,
+    BT: BTImpl<N, F, Data = usize>,
+    K: SimpleConvolutionKernel<N, F>,
+    Weighted<Shift<K, N, F>, F>: LocalAnalysis<F, BT::Agg, N>,
 {
-    fn opnorm_bound(&self, _: Radon, _: Linfinity) -> F {
+    fn opnorm_bound(&self, _: Radon, _: Linfinity) -> DynResult<F> {
         // With μ = ∑_i α_i δ_{x_i}, we have
         // |𝒟μ|_∞
         // = sup_z |∑_i α_i φ(z - x_i)|
         // ≤ sup_z ∑_i |α_i| |φ(z - x_i)|
         // ≤ ∑_i |α_i| |φ|_∞
         // = |μ|_ℳ |φ|_∞
-        self.kernel.bounds().uniform()
+        Ok(self.kernel.bounds().uniform())
     }
 }
 
-impl<F, K, BT, const N: usize> DiscreteMeasureOp<Loc<F, N>, F> for ConvolutionOp<F, K, BT, N>
+impl<'a, F, K, BT, const N: usize> NormExponent for &'a ConvolutionOp<F, K, BT, N>
+where
+    F: Float + ToNalgebraRealField,
+    BT: BTImpl<N, F, Data = usize>,
+    K: SimpleConvolutionKernel<N, F>,
+    Weighted<Shift<K, N, F>, F>: LocalAnalysis<F, BT::Agg, N>,
+{
+}
+
+impl<'a, F, K, BT, const N: usize> Norm<&'a ConvolutionOp<F, K, BT, N>, F> for RNDM<N, F>
 where
     F: Float + ToNalgebraRealField,
-    BT: BTImpl<F, N, Data = usize>,
-    K: SimpleConvolutionKernel<F, N>,
-    Weighted<Shift<K, F, N>, F>: LocalAnalysis<F, BT::Agg, N>,
+    BT: BTImpl<N, F, Data = usize>,
+    K: SimpleConvolutionKernel<N, F>,
+    Weighted<Shift<K, N, F>, F>: LocalAnalysis<F, BT::Agg, N>,
 {
-    type PreCodomain = PreBTFN<F, ConvolutionSupportGenerator<F, K, N>, N>;
+    fn norm(&self, op𝒟: &'a ConvolutionOp<F, K, BT, N>) -> F {
+        self.apply(op𝒟.apply(self)).sqrt()
+    }
+}
+
+impl<F, K, BT, const N: usize> DiscreteMeasureOp<Loc<N, F>, F> for ConvolutionOp<F, K, BT, N>
+where
+    F: Float + ToNalgebraRealField,
+    BT: BTImpl<N, F, Data = usize>,
+    K: SimpleConvolutionKernel<N, F>,
+    Weighted<Shift<K, N, F>, F>: LocalAnalysis<F, BT::Agg, N>,
+{
+    type PreCodomain = PreBTFN<F, ConvolutionSupportGenerator<K, N, F>, N>;
 
     fn findim_matrix<'a, I>(&self, points: I) -> DMatrix<F::MixedType>
     where
-        I: ExactSizeIterator<Item = &'a Loc<F, N>> + Clone,
+        I: ExactSizeIterator<Item = &'a Loc<N, F>> + Clone,
     {
         // TODO: Preliminary implementation. It be best to use sparse matrices or
         // possibly explicit operators without matrices
@@ -268,7 +291,7 @@
     /// A version of [`Mapping::apply`] that does not instantiate the [`BTFN`] codomain with
     /// a bisection tree, instead returning a [`PreBTFN`]. This can improve performance when
     /// the output is to be added as the right-hand-side operand to a proper BTFN.
-    fn preapply(&self, μ: RNDM<F, N>) -> Self::PreCodomain {
+    fn preapply(&self, μ: RNDM<N, F>) -> Self::PreCodomain {
         BTFN::new_pre(self.support_generator(μ))
     }
 }
@@ -277,27 +300,27 @@
 /// for [`ConvolutionSupportGenerator`].
 macro_rules! make_convolutionsupportgenerator_scalarop_rhs {
     ($trait:ident, $fn:ident, $trait_assign:ident, $fn_assign:ident) => {
-        impl<F: Float, K: SimpleConvolutionKernel<F, N>, const N: usize> std::ops::$trait_assign<F>
-            for ConvolutionSupportGenerator<F, K, N>
+        impl<F: Float, K: SimpleConvolutionKernel<N, F>, const N: usize> std::ops::$trait_assign<F>
+            for ConvolutionSupportGenerator<K, N, F>
         {
             fn $fn_assign(&mut self, t: F) {
                 self.centres.$fn_assign(t);
             }
         }
 
-        impl<F: Float, K: SimpleConvolutionKernel<F, N>, const N: usize> std::ops::$trait<F>
-            for ConvolutionSupportGenerator<F, K, N>
+        impl<F: Float, K: SimpleConvolutionKernel<N, F>, const N: usize> std::ops::$trait<F>
+            for ConvolutionSupportGenerator<K, N, F>
         {
-            type Output = ConvolutionSupportGenerator<F, K, N>;
+            type Output = ConvolutionSupportGenerator<K, N, F>;
             fn $fn(mut self, t: F) -> Self::Output {
                 std::ops::$trait_assign::$fn_assign(&mut self.centres, t);
                 self
             }
         }
-        impl<'a, F: Float, K: SimpleConvolutionKernel<F, N>, const N: usize> std::ops::$trait<F>
-            for &'a ConvolutionSupportGenerator<F, K, N>
+        impl<'a, F: Float, K: SimpleConvolutionKernel<N, F>, const N: usize> std::ops::$trait<F>
+            for &'a ConvolutionSupportGenerator<K, N, F>
         {
-            type Output = ConvolutionSupportGenerator<F, K, N>;
+            type Output = ConvolutionSupportGenerator<K, N, F>;
             fn $fn(self, t: F) -> Self::Output {
                 ConvolutionSupportGenerator {
                     kernel: self.kernel.clone(),
@@ -314,20 +337,20 @@
 /// Generates an unary operation (e.g. [`std::ops::Neg`]) for [`ConvolutionSupportGenerator`].
 macro_rules! make_convolutionsupportgenerator_unaryop {
     ($trait:ident, $fn:ident) => {
-        impl<F: Float, K: SimpleConvolutionKernel<F, N>, const N: usize> std::ops::$trait
-            for ConvolutionSupportGenerator<F, K, N>
+        impl<F: Float, K: SimpleConvolutionKernel<N, F>, const N: usize> std::ops::$trait
+            for ConvolutionSupportGenerator<K, N, F>
         {
-            type Output = ConvolutionSupportGenerator<F, K, N>;
+            type Output = ConvolutionSupportGenerator<K, N, F>;
             fn $fn(mut self) -> Self::Output {
                 self.centres = self.centres.$fn();
                 self
             }
         }
 
-        impl<'a, F: Float, K: SimpleConvolutionKernel<F, N>, const N: usize> std::ops::$trait
-            for &'a ConvolutionSupportGenerator<F, K, N>
+        impl<'a, F: Float, K: SimpleConvolutionKernel<N, F>, const N: usize> std::ops::$trait
+            for &'a ConvolutionSupportGenerator<K, N, F>
         {
-            type Output = ConvolutionSupportGenerator<F, K, N>;
+            type Output = ConvolutionSupportGenerator<K, N, F>;
             fn $fn(self) -> Self::Output {
                 ConvolutionSupportGenerator {
                     kernel: self.kernel.clone(),

mercurial