src/bisection_tree/support.rs

changeset 198
3868555d135c
parent 151
402d717bb5c0
--- a/src/bisection_tree/support.rs	Sun Apr 27 20:29:43 2025 -0500
+++ b/src/bisection_tree/support.rs	Fri May 15 14:46:30 2026 -0500
@@ -1,31 +1,29 @@
-
 /*!
 Traits for representing the support of a [`Mapping`], and analysing the mapping on a [`Cube`].
 */
-use serde::Serialize;
-use std::ops::{MulAssign,DivAssign,Neg};
-use crate::types::{Float, Num};
+use super::aggregator::Bounds;
+pub use crate::bounds::{GlobalAnalysis, LocalAnalysis};
+use crate::loc::Loc;
+use crate::mapping::{ClosedSpace, DifferentiableImpl, DifferentiableMapping, Instance, Mapping};
 use crate::maputil::map2;
-use crate::mapping::{
-    Instance, Mapping, DifferentiableImpl, DifferentiableMapping, Space
-};
+use crate::norms::{Linfinity, Norm, L1, L2};
+pub use crate::operator_arithmetic::{Constant, Weighted};
 use crate::sets::Cube;
-use crate::loc::Loc;
-use super::aggregator::Bounds;
-use crate::norms::{Norm, L1, L2, Linfinity};
-pub use crate::operator_arithmetic::{Weighted, Constant};
+use crate::types::{Float, Num};
+use serde::Serialize;
+use std::ops::{DivAssign, MulAssign, Neg};
 
 /// A trait for working with the supports of [`Mapping`]s.
 ///
 /// `Mapping` is not a super-trait to allow more general use.
-pub trait Support<F : Num, const N : usize> : Sized + Sync + Send + 'static {
+pub trait Support<const N: usize, F: Num>: Sized + Sync + Send + 'static {
     /// Return a cube containing the support of the function represented by `self`.
     ///
     /// The hint may be larger than the actual support, but must contain it.
-    fn support_hint(&self) -> Cube<F,N>;
+    fn support_hint(&self) -> Cube<N, F>;
 
     /// Indicate whether `x` is in the support of the function represented by `self`.
-    fn in_support(&self, x : &Loc<F,N>) -> bool;
+    fn in_support(&self, x: &Loc<N, F>) -> bool;
 
     // Indicate whether `cube` is fully in the support of the function represented by `self`.
     //fn fully_in_support(&self, cube : &Cube<F,N>) -> bool;
@@ -41,139 +39,99 @@
     /// The default implementation returns `[None; N]`.
     #[inline]
     #[allow(unused_variables)]
-    fn bisection_hint(&self, cube : &Cube<F, N>) -> [Option<F>; N] {
+    fn bisection_hint(&self, cube: &Cube<N, F>) -> [Option<F>; N] {
         [None; N]
     }
 
     /// Translate `self` by `x`.
     #[inline]
-    fn shift(self, x : Loc<F, N>) -> Shift<Self, F, N> {
-        Shift { shift : x, base_fn : self }
+    fn shift(self, x: Loc<N, F>) -> Shift<Self, N, F> {
+        Shift { shift: x, base_fn: self }
     }
 }
 
-/// Trait for globally analysing a property `A` of a [`Mapping`].
-///
-/// Typically `A` is an [`Aggregator`][super::aggregator::Aggregator] such as
-/// [`Bounds`][super::aggregator::Bounds].
-pub trait GlobalAnalysis<F : Num, A> {
-    /// Perform global analysis of the property `A` of `Self`.
-    ///
-    /// As an example, in the case of `A` being [`Bounds`][super::aggregator::Bounds],
-    /// this function will return global upper and lower bounds for the mapping
-    /// represented by `self`.
-    fn global_analysis(&self) -> A;
+/// Shift of [`Support`] and [`Mapping`]; output of [`Support::shift`].
+#[derive(Copy, Clone, Debug, Serialize)] // Serialize! but not implemented by Loc.
+pub struct Shift<T, const N: usize, F = f64> {
+    shift: Loc<N, F>,
+    base_fn: T,
 }
 
-// default impl<F, A, N, L> GlobalAnalysis<F, A, N> for L
-// where L : LocalAnalysis<F, A, N> {
-//     #[inline]
-//     fn global_analysis(&self) -> Bounds<F> {
-//         self.local_analysis(&self.support_hint())
-//     }
-// }
-
-/// Trait for locally analysing a property `A` of a [`Mapping`] (implementing [`Support`])
-/// within a [`Cube`].
-///
-/// Typically `A` is an [`Aggregator`][super::aggregator::Aggregator] such as
-/// [`Bounds`][super::aggregator::Bounds].
-pub trait LocalAnalysis<F : Num, A, const N : usize> : GlobalAnalysis<F, A> + Support<F, N> {
-    /// Perform local analysis of the property `A` of `Self`.
-    ///
-    /// As an example, in the case of `A` being [`Bounds`][super::aggregator::Bounds],
-    /// this function will return upper and lower bounds within `cube` for the mapping
-    /// represented by `self`.
-    fn local_analysis(&self, cube : &Cube<F, N>) -> A;
-}
-
-/// Trait for determining the upper and lower bounds of an float-valued [`Mapping`].
-///
-/// This is a blanket-implemented alias for [`GlobalAnalysis`]`<F, Bounds<F>>`
-/// [`Mapping`] is not a supertrait to allow flexibility in the implementation of either
-/// reference or non-reference arguments.
-pub trait Bounded<F : Float> : GlobalAnalysis<F, Bounds<F>> {
-    /// Return lower and upper bounds for the values of of `self`.
-    #[inline]
-    fn bounds(&self) -> Bounds<F> {
-        self.global_analysis()
-    }
-}
-
-impl<F : Float, T : GlobalAnalysis<F, Bounds<F>>> Bounded<F> for T { }
-
-/// Shift of [`Support`] and [`Mapping`]; output of [`Support::shift`].
-#[derive(Copy,Clone,Debug,Serialize)] // Serialize! but not implemented by Loc.
-pub struct Shift<T, F, const N : usize> {
-    shift : Loc<F, N>,
-    base_fn : T,
-}
-
-impl<'a, T, V : Space, F : Float, const N : usize> Mapping<Loc<F, N>> for Shift<T,F,N>
-where T : Mapping<Loc<F, N>, Codomain=V> {
+impl<'a, T, V: ClosedSpace, F: Float, const N: usize> Mapping<Loc<N, F>> for Shift<T, N, F>
+where
+    T: Mapping<Loc<N, F>, Codomain = V>,
+{
     type Codomain = V;
 
     #[inline]
-    fn apply<I : Instance<Loc<F, N>>>(&self, x : I) -> Self::Codomain {
+    fn apply<I: Instance<Loc<N, F>>>(&self, x: I) -> Self::Codomain {
         self.base_fn.apply(x.own() - &self.shift)
     }
 }
 
-impl<'a, T, V : Space, F : Float, const N : usize> DifferentiableImpl<Loc<F, N>> for Shift<T,F,N>
-where T : DifferentiableMapping<Loc<F, N>, DerivativeDomain=V> {
+impl<'a, T, V: ClosedSpace, F: Float, const N: usize> DifferentiableImpl<Loc<N, F>>
+    for Shift<T, N, F>
+where
+    T: DifferentiableMapping<Loc<N, F>, DerivativeDomain = V>,
+{
     type Derivative = V;
 
     #[inline]
-    fn differential_impl<I : Instance<Loc<F, N>>>(&self, x : I) -> Self::Derivative {
+    fn differential_impl<I: Instance<Loc<N, F>>>(&self, x: I) -> Self::Derivative {
         self.base_fn.differential(x.own() - &self.shift)
     }
 }
 
-impl<'a, T, F : Float, const N : usize> Support<F,N> for Shift<T,F,N>
-where T : Support<F, N> {
+impl<'a, T, F: Float, const N: usize> Support<N, F> for Shift<T, N, F>
+where
+    T: Support<N, F>,
+{
     #[inline]
-    fn support_hint(&self) -> Cube<F,N> {
+    fn support_hint(&self) -> Cube<N, F> {
         self.base_fn.support_hint().shift(&self.shift)
     }
 
     #[inline]
-    fn in_support(&self, x : &Loc<F,N>) -> bool {
+    fn in_support(&self, x: &Loc<N, F>) -> bool {
         self.base_fn.in_support(&(x - &self.shift))
     }
-    
+
     // fn fully_in_support(&self, _cube : &Cube<F,N>) -> bool {
     //     //self.base_fn.fully_in_support(cube.shift(&vectorneg(self.shift)))
     //     todo!("Not implemented, but not used at the moment")
     // }
 
     #[inline]
-    fn bisection_hint(&self, cube : &Cube<F,N>) -> [Option<F>; N] {
+    fn bisection_hint(&self, cube: &Cube<N, F>) -> [Option<F>; N] {
         let base_hint = self.base_fn.bisection_hint(cube);
         map2(base_hint, &self.shift, |h, s| h.map(|z| z + *s))
     }
-
 }
 
-impl<'a, T, F : Float, const N : usize> GlobalAnalysis<F, Bounds<F>> for Shift<T,F,N>
-where T : LocalAnalysis<F, Bounds<F>, N> {
+impl<'a, T, F: Float, const N: usize> GlobalAnalysis<F, Bounds<F>> for Shift<T, N, F>
+where
+    T: LocalAnalysis<F, Bounds<F>, N>,
+{
     #[inline]
     fn global_analysis(&self) -> Bounds<F> {
         self.base_fn.global_analysis()
     }
 }
 
-impl<'a, T, F : Float, const N : usize> LocalAnalysis<F, Bounds<F>, N> for Shift<T,F,N>
-where T : LocalAnalysis<F, Bounds<F>, N> {
+impl<'a, T, F: Float, const N: usize> LocalAnalysis<F, Bounds<F>, N> for Shift<T, N, F>
+where
+    T: LocalAnalysis<F, Bounds<F>, N>,
+{
     #[inline]
-    fn local_analysis(&self, cube : &Cube<F, N>) -> Bounds<F> {
+    fn local_analysis(&self, cube: &Cube<N, F>) -> Bounds<F> {
         self.base_fn.local_analysis(&cube.shift(&(-self.shift)))
     }
 }
 
 macro_rules! impl_shift_norm {
     ($($norm:ident)*) => { $(
-        impl<'a, T, F : Float, const N : usize> Norm<F, $norm> for Shift<T,F,N>
-        where T : Norm<F, $norm> {
+        impl<'a, T, F : Float, const N : usize> Norm<$norm, F> for Shift<T, N, F>
+        where T : Norm<$norm, F> {
             #[inline]
             fn norm(&self, n : $norm) -> F {
                 self.base_fn.norm(n)
@@ -184,33 +142,36 @@
 
 impl_shift_norm!(L1 L2 Linfinity);
 
-impl<'a, T, F : Float, C, const N : usize> Support<F,N> for Weighted<T, C>
-where T : Support<F, N>,
-      C : Constant<Type=F> {
-
+impl<'a, T, F: Float, C, const N: usize> Support<N, F> for Weighted<T, C>
+where
+    T: Support<N, F>,
+    C: Constant<Type = F>,
+{
     #[inline]
-    fn support_hint(&self) -> Cube<F,N> {
+    fn support_hint(&self) -> Cube<N, F> {
         self.base_fn.support_hint()
     }
 
     #[inline]
-    fn in_support(&self, x : &Loc<F,N>) -> bool {
+    fn in_support(&self, x: &Loc<N, F>) -> bool {
         self.base_fn.in_support(x)
     }
-    
+
     // fn fully_in_support(&self, cube : &Cube<F,N>) -> bool {
     //     self.base_fn.fully_in_support(cube)
     // }
 
     #[inline]
-    fn bisection_hint(&self, cube : &Cube<F,N>) -> [Option<F>; N] {
+    fn bisection_hint(&self, cube: &Cube<N, F>) -> [Option<F>; N] {
         self.base_fn.bisection_hint(cube)
     }
 }
 
-impl<'a, T, F : Float, C> GlobalAnalysis<F, Bounds<F>> for Weighted<T, C>
-where T : GlobalAnalysis<F, Bounds<F>>,
-      C : Constant<Type=F> {
+impl<'a, T, F: Float, C> GlobalAnalysis<F, Bounds<F>> for Weighted<T, C>
+where
+    T: GlobalAnalysis<F, Bounds<F>>,
+    C: Constant<Type = F>,
+{
     #[inline]
     fn global_analysis(&self) -> Bounds<F> {
         let Bounds(lower, upper) = self.base_fn.global_analysis();
@@ -222,11 +183,13 @@
     }
 }
 
-impl<'a, T, F : Float, C, const N : usize> LocalAnalysis<F, Bounds<F>, N> for Weighted<T, C>
-where T : LocalAnalysis<F, Bounds<F>, N>,
-      C : Constant<Type=F> {
+impl<'a, T, F: Float, C, const N: usize> LocalAnalysis<F, Bounds<F>, N> for Weighted<T, C>
+where
+    T: LocalAnalysis<F, Bounds<F>, N>,
+    C: Constant<Type = F>,
+{
     #[inline]
-    fn local_analysis(&self, cube : &Cube<F, N>) -> Bounds<F> {
+    fn local_analysis(&self, cube: &Cube<N, F>) -> Bounds<F> {
         let Bounds(lower, upper) = self.base_fn.local_analysis(cube);
         debug_assert!(lower <= upper);
         match self.weight.value() {
@@ -238,31 +201,33 @@
 
 macro_rules! make_weighted_scalarop_rhs {
     ($trait:ident, $fn:ident, $trait_assign:ident, $fn_assign:ident) => {
-        impl<F : Float, T> std::ops::$trait_assign<F> for Weighted<T, F> {
+        impl<F: Float, T> std::ops::$trait_assign<F> for Weighted<T, F> {
             #[inline]
-            fn $fn_assign(&mut self, t : F) {
+            fn $fn_assign(&mut self, t: F) {
                 self.weight.$fn_assign(t);
             }
         }
 
-        impl<'a, F : Float, T> std::ops::$trait<F> for Weighted<T, F> {
+        impl<'a, F: Float, T> std::ops::$trait<F> for Weighted<T, F> {
             type Output = Self;
             #[inline]
-            fn $fn(mut self, t : F) -> Self {
+            fn $fn(mut self, t: F) -> Self {
                 self.weight.$fn_assign(t);
                 self
             }
         }
 
-        impl<'a, F : Float, T> std::ops::$trait<F> for &'a Weighted<T, F>
-        where T : Clone {
+        impl<'a, F: Float, T> std::ops::$trait<F> for &'a Weighted<T, F>
+        where
+            T: Clone,
+        {
             type Output = Weighted<T, F>;
             #[inline]
-            fn $fn(self, t : F) -> Self::Output {
-                Weighted { weight : self.weight.$fn(t), base_fn : self.base_fn.clone() }
+            fn $fn(self, t: F) -> Self::Output {
+                Weighted { weight: self.weight.$fn(t), base_fn: self.base_fn.clone() }
             }
         }
-    }
+    };
 }
 
 make_weighted_scalarop_rhs!(Mul, mul, MulAssign, mul_assign);
@@ -270,8 +235,8 @@
 
 macro_rules! impl_weighted_norm {
     ($($norm:ident)*) => { $(
-        impl<'a, T, F : Float> Norm<F, $norm> for Weighted<T,F>
-        where T : Norm<F, $norm> {
+        impl<'a, T, F : Float> Norm<$norm, F> for Weighted<T,F>
+        where T : Norm<$norm, F> {
             #[inline]
             fn norm(&self, n : $norm) -> F {
                 self.base_fn.norm(n) * self.weight.abs()
@@ -282,52 +247,60 @@
 
 impl_weighted_norm!(L1 L2 Linfinity);
 
-
 /// Normalisation of [`Support`] and [`Mapping`] to L¹ norm 1.
 ///
 /// Currently only scalar-valued functions are supported.
 #[derive(Copy, Clone, Debug, Serialize, PartialEq)]
 pub struct Normalised<T>(
     /// The base [`Support`] or [`Mapping`].
-    pub T
+    pub T,
 );
 
-impl<'a, T, F : Float, const N : usize> Mapping<Loc<F, N>> for Normalised<T>
-where T : Norm<F, L1> + Mapping<Loc<F,N>, Codomain=F> {
+impl<'a, T, F: Float, const N: usize> Mapping<Loc<N, F>> for Normalised<T>
+where
+    T: Norm<L1, F> + Mapping<Loc<N, F>, Codomain = F>,
+{
     type Codomain = F;
 
     #[inline]
-    fn apply<I : Instance<Loc<F, N>>>(&self, x : I) -> Self::Codomain {
+    fn apply<I: Instance<Loc<N, F>>>(&self, x: I) -> Self::Codomain {
         let w = self.0.norm(L1);
-        if w == F::ZERO { F::ZERO } else { self.0.apply(x) / w }
+        if w == F::ZERO {
+            F::ZERO
+        } else {
+            self.0.apply(x) / w
+        }
     }
 }
 
-impl<'a, T, F : Float, const N : usize> Support<F,N> for Normalised<T>
-where T : Norm<F, L1> + Support<F, N> {
-
+impl<'a, T, F: Float, const N: usize> Support<N, F> for Normalised<T>
+where
+    T: Norm<L1, F> + Support<N, F>,
+{
     #[inline]
-    fn support_hint(&self) -> Cube<F,N> {
+    fn support_hint(&self) -> Cube<N, F> {
         self.0.support_hint()
     }
 
     #[inline]
-    fn in_support(&self, x : &Loc<F,N>) -> bool {
+    fn in_support(&self, x: &Loc<N, F>) -> bool {
         self.0.in_support(x)
     }
-    
+
     // fn fully_in_support(&self, cube : &Cube<F,N>) -> bool {
     //     self.0.fully_in_support(cube)
     // }
 
     #[inline]
-    fn bisection_hint(&self, cube : &Cube<F,N>) -> [Option<F>; N] {
+    fn bisection_hint(&self, cube: &Cube<N, F>) -> [Option<F>; N] {
         self.0.bisection_hint(cube)
     }
 }
 
-impl<'a, T, F : Float> GlobalAnalysis<F, Bounds<F>> for Normalised<T>
-where T : Norm<F, L1> + GlobalAnalysis<F, Bounds<F>> {
+impl<'a, T, F: Float> GlobalAnalysis<F, Bounds<F>> for Normalised<T>
+where
+    T: Norm<L1, F> + GlobalAnalysis<F, Bounds<F>>,
+{
     #[inline]
     fn global_analysis(&self) -> Bounds<F> {
         let Bounds(lower, upper) = self.0.global_analysis();
@@ -338,10 +311,12 @@
     }
 }
 
-impl<'a, T, F : Float, const N : usize> LocalAnalysis<F, Bounds<F>, N> for Normalised<T>
-where T : Norm<F, L1> + LocalAnalysis<F, Bounds<F>, N> {
+impl<'a, T, F: Float, const N: usize> LocalAnalysis<F, Bounds<F>, N> for Normalised<T>
+where
+    T: Norm<L1, F> + LocalAnalysis<F, Bounds<F>, N>,
+{
     #[inline]
-    fn local_analysis(&self, cube : &Cube<F, N>) -> Bounds<F> {
+    fn local_analysis(&self, cube: &Cube<N, F>) -> Bounds<F> {
         let Bounds(lower, upper) = self.0.local_analysis(cube);
         debug_assert!(lower <= upper);
         let w = self.0.norm(L1);
@@ -350,19 +325,25 @@
     }
 }
 
-impl<'a, T, F : Float> Norm<F, L1> for Normalised<T>
-where T : Norm<F, L1> {
+impl<'a, T, F: Float> Norm<L1, F> for Normalised<T>
+where
+    T: Norm<L1, F>,
+{
     #[inline]
-    fn norm(&self, _ : L1) -> F {
+    fn norm(&self, _: L1) -> F {
         let w = self.0.norm(L1);
-        if w == F::ZERO { F::ZERO } else { F::ONE }
+        if w == F::ZERO {
+            F::ZERO
+        } else {
+            F::ONE
+        }
     }
 }
 
 macro_rules! impl_normalised_norm {
     ($($norm:ident)*) => { $(
-        impl<'a, T, F : Float> Norm<F, $norm> for Normalised<T>
-        where T : Norm<F, $norm> + Norm<F, L1> {
+        impl<'a, T, F : Float> Norm<$norm, F> for Normalised<T>
+        where T : Norm<$norm, F> + Norm<L1, F> {
             #[inline]
             fn norm(&self, n : $norm) -> F {
                 let w = self.0.norm(L1);
@@ -375,37 +356,39 @@
 impl_normalised_norm!(L2 Linfinity);
 
 /*
-impl<F : Num, S : Support<F, N>, const N : usize> LocalAnalysis<F, NullAggregator, N> for S {
-    fn local_analysis(&self, _cube : &Cube<F, N>) -> NullAggregator { NullAggregator }
+impl<F : Num, S : Support< N, F>, const N : usize> LocalAnalysis<F, NullAggregator, N> for S {
+    fn local_analysis(&self, _cube : &Cube<N, F>) -> NullAggregator { NullAggregator }
 }
 
 impl<F : Float, S : Bounded<F>, const N : usize> LocalAnalysis<F, Bounds<F>, N> for S {
     #[inline]
-    fn local_analysis(&self, cube : &Cube<F, N>) -> Bounds<F> {
+    fn local_analysis(&self, cube : &Cube<N, F>) -> Bounds<F> {
         self.bounds(cube)
     }
 }*/
 
 /// Generator of [`Support`]-implementing component functions based on low storage requirement
 /// [ids][`Self::Id`].
-pub trait SupportGenerator<F : Float, const N : usize>
-: MulAssign<F> + DivAssign<F> + Neg<Output=Self> + Clone + Sync + Send + 'static {
+pub trait SupportGenerator<const N: usize, F: Float = f64>:
+    MulAssign<F> + DivAssign<F> + Neg<Output = Self> + Clone + Sync + Send + 'static
+{
     /// The identification type
-    type Id : 'static + Copy;
+    type Id: 'static + Copy;
     /// The type of the [`Support`] (often also a [`Mapping`]).
-    type SupportType : 'static + Support<F, N>;
+    type SupportType: 'static + Support<N, F>;
     /// An iterator over all the [`Support`]s of the generator.
-    type AllDataIter<'a> : Iterator<Item=(Self::Id, Self::SupportType)> where Self : 'a;
+    type AllDataIter<'a>: Iterator<Item = (Self::Id, Self::SupportType)>
+    where
+        Self: 'a;
 
     /// Returns the component identified by `id`.
     ///
     /// Panics if `id` is an invalid identifier.
-    fn support_for(&self, id : Self::Id) -> Self::SupportType;
-    
+    fn support_for(&self, id: Self::Id) -> Self::SupportType;
+
     /// Returns the number of different components in this generator.
     fn support_count(&self) -> usize;
 
     /// Returns an iterator over all pairs of `(id, support)`.
     fn all_data(&self) -> Self::AllDataIter<'_>;
 }
-

mercurial