diff -r 9738b51d90d7 -r 4f468d35fa29 src/kernels/ball_indicator.rs --- a/src/kernels/ball_indicator.rs Sun Apr 27 15:03:51 2025 -0500 +++ b/src/kernels/ball_indicator.rs Thu Feb 26 11:38:43 2026 -0500 @@ -1,56 +1,44 @@ - //! Implementation of the indicator function of a ball with respect to various norms. +use super::base::*; +use crate::types::*; +use alg_tools::bisection_tree::{Bounds, Constant, GlobalAnalysis, LocalAnalysis, Support}; +use alg_tools::coefficients::factorial; +use alg_tools::euclidean::StaticEuclidean; +use alg_tools::instance::Instance; +use alg_tools::loc::Loc; +use alg_tools::mapping::{DifferentiableImpl, Differential, LipschitzDifferentiableImpl, Mapping}; +use alg_tools::maputil::array_init; +use alg_tools::norms::*; +use alg_tools::sets::Cube; +use anyhow::anyhow; use float_extras::f64::tgamma as gamma; use numeric_literals::replace_float_literals; use serde::Serialize; -use alg_tools::types::*; -use alg_tools::norms::*; -use alg_tools::loc::Loc; -use alg_tools::sets::Cube; -use alg_tools::bisection_tree::{ - Support, - Constant, - Bounds, - LocalAnalysis, - GlobalAnalysis, -}; -use alg_tools::mapping::{ - Mapping, - Differential, - DifferentiableImpl, -}; -use alg_tools::instance::Instance; -use alg_tools::euclidean::StaticEuclidean; -use alg_tools::maputil::array_init; -use alg_tools::coefficients::factorial; -use crate::types::*; -use super::base::*; /// Representation of the indicator of the ball $𝔹_q = \\{ x ∈ ℝ^N \mid \\|x\\|\_q ≤ r \\}$, /// where $q$ is the `Exponent`, and $r$ is the radius [`Constant`] `C`. -#[derive(Copy,Clone,Serialize,Debug,Eq,PartialEq)] -pub struct BallIndicator { +#[derive(Copy, Clone, Serialize, Debug, Eq, PartialEq)] +pub struct BallIndicator { /// The radius of the ball. - pub r : C, + pub r: C, /// The exponent $q$ of the norm creating the ball - pub exponent : Exponent, + pub exponent: Exponent, } /// Alias for the representation of the indicator of the $∞$-norm-ball /// $𝔹_∞ = \\{ x ∈ ℝ^N \mid \\|x\\|\_∞ ≤ c \\}$. -pub type CubeIndicator = BallIndicator; +pub type CubeIndicator = BallIndicator; #[replace_float_literals(C::Type::cast_from(literal))] -impl<'a, F : Float, C : Constant, Exponent : NormExponent, const N : usize> -Mapping> -for BallIndicator +impl<'a, F: Float, C: Constant, Exponent: NormExponent, const N: usize> + Mapping> for BallIndicator where - Loc : Norm + Loc: Norm, { type Codomain = C::Type; #[inline] - fn apply>>(&self, x : I) -> Self::Codomain { + fn apply>>(&self, x: I) -> Self::Codomain { let r = self.r.value(); let n = x.eval(|x| x.norm(self.exponent)); if n <= r { @@ -61,114 +49,105 @@ } } -impl<'a, F : Float, C : Constant, Exponent : NormExponent, const N : usize> -DifferentiableImpl> -for BallIndicator +impl<'a, F: Float, C: Constant, Exponent: NormExponent, const N: usize> + DifferentiableImpl> for BallIndicator where - C : Constant, - Loc : Norm + C: Constant, + Loc: Norm, { - type Derivative = Loc; + type Derivative = Loc; #[inline] - fn differential_impl>>(&self, _x : I) -> Self::Derivative { + fn differential_impl>>(&self, _x: I) -> Self::Derivative { Self::Derivative::origin() } } -impl, Exponent : NormExponent, const N : usize> -Lipschitz -for BallIndicator -where C : Constant, - Loc : Norm { +impl, Exponent: NormExponent, const N: usize> Lipschitz + for BallIndicator +where + C: Constant, + Loc: Norm, +{ type FloatType = C::Type; - fn lipschitz_factor(&self, _l2 : L2) -> Option { - None + fn lipschitz_factor(&self, _l2: L2) -> DynResult { + Err(anyhow!("Not a Lipschitz function")) } } -impl<'b, F : Float, C : Constant, Exponent : NormExponent, const N : usize> -Lipschitz -for Differential<'b, Loc, BallIndicator> -where C : Constant, - Loc : Norm { +impl<'b, F: Float, C: Constant, Exponent: NormExponent, const N: usize> + LipschitzDifferentiableImpl, L2> for BallIndicator +where + C: Constant, + Loc: Norm, +{ type FloatType = C::Type; - fn lipschitz_factor(&self, _l2 : L2) -> Option { - None + fn diff_lipschitz_factor(&self, _l2: L2) -> DynResult { + Err(anyhow!("Not a Lipschitz-differentiable function")) } } -impl<'a, 'b, F : Float, C : Constant, Exponent : NormExponent, const N : usize> -Lipschitz -for Differential<'b, Loc, &'a BallIndicator> -where C : Constant, - Loc : Norm { +impl<'b, F: Float, C: Constant, Exponent: NormExponent, const N: usize> NormBounded + for Differential<'b, Loc, BallIndicator> +where + C: Constant, + Loc: Norm, +{ type FloatType = C::Type; - fn lipschitz_factor(&self, _l2 : L2) -> Option { - None - } -} - - -impl<'b, F : Float, C : Constant, Exponent : NormExponent, const N : usize> -NormBounded -for Differential<'b, Loc, BallIndicator> -where C : Constant, - Loc : Norm { - type FloatType = C::Type; - - fn norm_bound(&self, _l2 : L2) -> C::Type { + fn norm_bound(&self, _l2: L2) -> C::Type { F::INFINITY } } -impl<'a, 'b, F : Float, C : Constant, Exponent : NormExponent, const N : usize> -NormBounded -for Differential<'b, Loc, &'a BallIndicator> -where C : Constant, - Loc : Norm { +impl<'a, 'b, F: Float, C: Constant, Exponent: NormExponent, const N: usize> + NormBounded for Differential<'b, Loc, &'a BallIndicator> +where + C: Constant, + Loc: Norm, +{ type FloatType = C::Type; - fn norm_bound(&self, _l2 : L2) -> C::Type { + fn norm_bound(&self, _l2: L2) -> C::Type { F::INFINITY } } - -impl<'a, F : Float, C : Constant, Exponent : NormExponent, const N : usize> -Support -for BallIndicator -where Loc : Norm, - Linfinity : Dominated> { - +impl<'a, F: Float, C: Constant, Exponent, const N: usize> Support + for BallIndicator +where + Exponent: NormExponent + Sync + Send + 'static, + Loc: Norm, + Linfinity: Dominated>, +{ #[inline] - fn support_hint(&self) -> Cube { + fn support_hint(&self) -> Cube { let r = Linfinity.from_norm(self.r.value(), self.exponent); array_init(|| [-r, r]).into() } #[inline] - fn in_support(&self, x : &Loc) -> bool { + fn in_support(&self, x: &Loc) -> bool { let r = Linfinity.from_norm(self.r.value(), self.exponent); x.norm(self.exponent) <= r } /// This can only really work in a reasonable fashion for N=1. #[inline] - fn bisection_hint(&self, cube : &Cube) -> [Option; N] { + fn bisection_hint(&self, cube: &Cube) -> [Option; N] { let r = Linfinity.from_norm(self.r.value(), self.exponent); cube.map(|a, b| symmetric_interval_hint(r, a, b)) } } #[replace_float_literals(F::cast_from(literal))] -impl<'a, F : Float, C : Constant, Exponent : NormExponent, const N : usize> -GlobalAnalysis> -for BallIndicator -where Loc : Norm { +impl<'a, F: Float, C: Constant, Exponent: NormExponent, const N: usize> + GlobalAnalysis> for BallIndicator +where + Loc: Norm, +{ #[inline] fn global_analysis(&self) -> Bounds { Bounds(0.0, 1.0) @@ -176,29 +155,28 @@ } #[replace_float_literals(F::cast_from(literal))] -impl<'a, F : Float, C : Constant, Exponent : NormExponent, const N : usize> -Norm -for BallIndicator -where Loc : Norm { +impl<'a, F: Float, C: Constant, Exponent: NormExponent, const N: usize> Norm + for BallIndicator +where + Loc: Norm, +{ #[inline] - fn norm(&self, _ : Linfinity) -> F { + fn norm(&self, _: Linfinity) -> F { 1.0 } } #[replace_float_literals(F::cast_from(literal))] -impl<'a, F : Float, C : Constant, const N : usize> -Norm -for BallIndicator { +impl<'a, F: Float, C: Constant, const N: usize> Norm for BallIndicator { #[inline] - fn norm(&self, _ : L1) -> F { + fn norm(&self, _: L1) -> F { // Using https://en.wikipedia.org/wiki/Volume_of_an_n-ball#Balls_in_Lp_norms, // we have V_N^1(r) = (2r)^N / N! let r = self.r.value(); - if N==1 { + if N == 1 { 2.0 * r - } else if N==2 { - r*r + } else if N == 2 { + r * r } else { (2.0 * r).powi(N as i32) * F::cast_from(factorial(N)) } @@ -206,17 +184,15 @@ } #[replace_float_literals(F::cast_from(literal))] -impl<'a, F : Float, C : Constant, const N : usize> -Norm -for BallIndicator { +impl<'a, F: Float, C: Constant, const N: usize> Norm for BallIndicator { #[inline] - fn norm(&self, _ : L1) -> F { + fn norm(&self, _: L1) -> F { // See https://en.wikipedia.org/wiki/Volume_of_an_n-ball#The_volume. let r = self.r.value(); let π = F::PI; - if N==1 { + if N == 1 { 2.0 * r - } else if N==2 { + } else if N == 2 { π * (r * r) } else { let ndiv2 = F::cast_from(N) / 2.0; @@ -227,94 +203,100 @@ } #[replace_float_literals(F::cast_from(literal))] -impl<'a, F : Float, C : Constant, const N : usize> -Norm -for BallIndicator { +impl<'a, F: Float, C: Constant, const N: usize> Norm + for BallIndicator +{ #[inline] - fn norm(&self, _ : L1) -> F { + fn norm(&self, _: L1) -> F { let two_r = 2.0 * self.r.value(); two_r.powi(N as i32) } } - macro_rules! indicator_local_analysis { ($exponent:ident) => { - impl<'a, F : Float, C : Constant, const N : usize> - LocalAnalysis, N> - for BallIndicator - where Loc : Norm, - Linfinity : Dominated> { + impl<'a, F: Float, C: Constant, const N: usize> LocalAnalysis, N> + for BallIndicator + where + Loc: Norm<$exponent, F>, + Linfinity: Dominated>, + { #[inline] - fn local_analysis(&self, cube : &Cube) -> Bounds { + fn local_analysis(&self, cube: &Cube) -> Bounds { // The function is maximised/minimised where the 2-norm is minimised/maximised. let lower = self.apply(cube.maxnorm_point()); let upper = self.apply(cube.minnorm_point()); Bounds(lower, upper) } } - } + }; } indicator_local_analysis!(L1); indicator_local_analysis!(L2); indicator_local_analysis!(Linfinity); - #[replace_float_literals(F::cast_from(literal))] -impl<'a, F : Float, R, const N : usize> Mapping> -for AutoConvolution> -where R : Constant { +impl<'a, F: Float, R, const N: usize> Mapping> for AutoConvolution> +where + R: Constant, +{ type Codomain = F; #[inline] - fn apply>>(&self, y : I) -> F { + fn apply>>(&self, y: I) -> F { let two_r = 2.0 * self.0.r.value(); // This is just a product of one-dimensional versions - y.cow().iter().map(|&x| { - 0.0.max(two_r - x.abs()) - }).product() + y.decompose() + .iter() + .map(|&x| 0.0.max(two_r - x.abs())) + .product() } } #[replace_float_literals(F::cast_from(literal))] -impl Support -for AutoConvolution> -where R : Constant { +impl Support for AutoConvolution> +where + R: Constant, +{ #[inline] - fn support_hint(&self) -> Cube { + fn support_hint(&self) -> Cube { let two_r = 2.0 * self.0.r.value(); array_init(|| [-two_r, two_r]).into() } #[inline] - fn in_support(&self, y : &Loc) -> bool { + fn in_support(&self, y: &Loc) -> bool { let two_r = 2.0 * self.0.r.value(); y.iter().all(|x| x.abs() <= two_r) } #[inline] - fn bisection_hint(&self, cube : &Cube) -> [Option; N] { + fn bisection_hint(&self, cube: &Cube) -> [Option; N] { let two_r = 2.0 * self.0.r.value(); cube.map(|c, d| symmetric_interval_hint(two_r, c, d)) } } #[replace_float_literals(F::cast_from(literal))] -impl GlobalAnalysis> -for AutoConvolution> -where R : Constant { +impl GlobalAnalysis> + for AutoConvolution> +where + R: Constant, +{ #[inline] fn global_analysis(&self) -> Bounds { Bounds(0.0, self.apply(Loc::ORIGIN)) } } -impl LocalAnalysis, N> -for AutoConvolution> -where R : Constant { +impl LocalAnalysis, N> + for AutoConvolution> +where + R: Constant, +{ #[inline] - fn local_analysis(&self, cube : &Cube) -> Bounds { + fn local_analysis(&self, cube: &Cube) -> Bounds { // The function is maximised/minimised where the absolute value is minimised/maximised. let lower = self.apply(cube.maxnorm_point()); let upper = self.apply(cube.minnorm_point());