--- 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<'_>; } -