Wed, 30 Apr 2025 16:39:01 -0500
Use DynResult for Lipschitz factors and operator norm bounds
| src/discrete_gradient.rs | file | annotate | diff | comparison | revisions | |
| src/linops.rs | file | annotate | diff | comparison | revisions | |
| src/mapping.rs | file | annotate | diff | comparison | revisions | |
| src/mapping/dataterm.rs | file | annotate | diff | comparison | revisions | |
| src/mapping/quadratic.rs | file | annotate | diff | comparison | revisions |
--- a/src/discrete_gradient.rs Wed Apr 30 01:06:25 2025 -0500 +++ b/src/discrete_gradient.rs Wed Apr 30 16:39:01 2025 -0500 @@ -1,14 +1,13 @@ /*! Simple disrete gradient operators */ +use crate::error::DynResult; +use crate::instance::Instance; +use crate::linops::{Adjointable, BoundedLinear, Linear, Mapping, GEMV}; +use crate::norms::{Norm, L2}; +use crate::types::Float; +use nalgebra::{DVector, Dyn, Matrix, Storage, StorageMut, U1}; use numeric_literals::replace_float_literals; -use nalgebra::{ - DVector, Matrix, U1, Storage, StorageMut, Dyn -}; -use crate::types::Float; -use crate::instance::Instance; -use crate::linops::{Mapping, Linear, BoundedLinear, Adjointable, GEMV}; -use crate::norms::{Norm, L2}; #[derive(Copy, Clone, Debug)] /// Forward differences with Neumann boundary conditions @@ -27,26 +26,17 @@ pub struct BackwardNeumann; /// Finite differences gradient -pub struct Grad< - F : Float + nalgebra::RealField, - B : Discretisation<F>, - const N : usize -> { - dims : [usize; N], - h : F, // may be negative to implement adjoints! - discretisation : B, +pub struct Grad<F: Float + nalgebra::RealField, B: Discretisation<F>, const N: usize> { + dims: [usize; N], + h: F, // may be negative to implement adjoints! + discretisation: B, } - /// Finite differences divergence -pub struct Div< - F : Float + nalgebra::RealField, - B : Discretisation<F>, - const N : usize -> { - dims : [usize; N], - h : F, // may be negative to implement adjoints! - discretisation : B, +pub struct Div<F: Float + nalgebra::RealField, B: Discretisation<F>, const N: usize> { + dims: [usize; N], + h: F, // may be negative to implement adjoints! + discretisation: B, } /// Internal: classification of a point in a 1D discretisation @@ -62,53 +52,53 @@ use DiscretisationOrInterior::*; /// Trait for different discretisations -pub trait Discretisation<F : Float + nalgebra::RealField> : Copy { +pub trait Discretisation<F: Float + nalgebra::RealField>: Copy { /// Opposite discretisation, appropriate for adjoints with negated cell width. - type Opposite : Discretisation<F>; + type Opposite: Discretisation<F>; /// Add to appropiate index of `v` (as determined by `b`) the appropriate difference /// of `x` with cell width `h`. fn add_diff_mut<SMut, S>( &self, - v : &mut Matrix<F, Dyn, U1, SMut>, - x : &Matrix<F, Dyn, U1, S>, - α : F, - b : DiscretisationOrInterior, + v: &mut Matrix<F, Dyn, U1, SMut>, + x: &Matrix<F, Dyn, U1, S>, + α: F, + b: DiscretisationOrInterior, ) where - SMut : StorageMut<F, Dyn, U1>, - S : Storage<F, Dyn, U1>; + SMut: StorageMut<F, Dyn, U1>, + S: Storage<F, Dyn, U1>; /// Give the opposite discretisation, appropriate for adjoints with negated `h`. fn opposite(&self) -> Self::Opposite; /// Bound for the corresponding operator norm. #[replace_float_literals(F::cast_from(literal))] - fn opnorm_bound(&self, h : F) -> F { + fn opnorm_bound(&self, h: F) -> DynResult<F> { // See: Chambolle, “An Algorithm for Total Variation Minimization and Applications”. // Ok for forward and backward differences. // // Fuck nalgebra for polluting everything with its own shit. - num_traits::Float::sqrt(8.0) / h + Ok(num_traits::Float::sqrt(8.0) / h) } } -impl<F : Float + nalgebra::RealField> Discretisation<F> for ForwardNeumann { +impl<F: Float + nalgebra::RealField> Discretisation<F> for ForwardNeumann { type Opposite = BackwardDirichlet; #[inline] fn add_diff_mut<SMut, S>( &self, - v : &mut Matrix<F, Dyn, U1, SMut>, - x : &Matrix<F, Dyn, U1, S>, - α : F, - b : DiscretisationOrInterior, + v: &mut Matrix<F, Dyn, U1, SMut>, + x: &Matrix<F, Dyn, U1, S>, + α: F, + b: DiscretisationOrInterior, ) where - SMut : StorageMut<F, Dyn, U1>, - S : Storage<F, Dyn, U1> + SMut: StorageMut<F, Dyn, U1>, + S: Storage<F, Dyn, U1>, { match b { - Interior(c, (_, f)) | LeftBoundary(c, f) => { v[c] += (x[f] - x[c]) * α }, - RightBoundary(_c, _b) => { }, + Interior(c, (_, f)) | LeftBoundary(c, f) => v[c] += (x[f] - x[c]) * α, + RightBoundary(_c, _b) => {} } } @@ -118,23 +108,23 @@ } } -impl<F : Float + nalgebra::RealField> Discretisation<F> for BackwardNeumann { +impl<F: Float + nalgebra::RealField> Discretisation<F> for BackwardNeumann { type Opposite = ForwardDirichlet; #[inline] fn add_diff_mut<SMut, S>( &self, - v : &mut Matrix<F, Dyn, U1, SMut>, - x : &Matrix<F, Dyn, U1, S>, - α : F, - b : DiscretisationOrInterior, + v: &mut Matrix<F, Dyn, U1, SMut>, + x: &Matrix<F, Dyn, U1, S>, + α: F, + b: DiscretisationOrInterior, ) where - SMut : StorageMut<F, Dyn, U1>, - S : Storage<F, Dyn, U1> + SMut: StorageMut<F, Dyn, U1>, + S: Storage<F, Dyn, U1>, { match b { - Interior(c, (b, _)) | RightBoundary(c, b) => { v[c] += (x[c] - x[b]) * α }, - LeftBoundary(_c, _f) => { }, + Interior(c, (b, _)) | RightBoundary(c, b) => v[c] += (x[c] - x[b]) * α, + LeftBoundary(_c, _f) => {} } } @@ -144,24 +134,24 @@ } } -impl<F : Float + nalgebra::RealField> Discretisation<F> for BackwardDirichlet { +impl<F: Float + nalgebra::RealField> Discretisation<F> for BackwardDirichlet { type Opposite = ForwardNeumann; #[inline] fn add_diff_mut<SMut, S>( &self, - v : &mut Matrix<F, Dyn, U1, SMut>, - x : &Matrix<F, Dyn, U1, S>, - α : F, - b : DiscretisationOrInterior, + v: &mut Matrix<F, Dyn, U1, SMut>, + x: &Matrix<F, Dyn, U1, S>, + α: F, + b: DiscretisationOrInterior, ) where - SMut : StorageMut<F, Dyn, U1>, - S : Storage<F, Dyn, U1> + SMut: StorageMut<F, Dyn, U1>, + S: Storage<F, Dyn, U1>, { match b { - Interior(c, (b, _f)) => { v[c] += (x[c] - x[b]) * α }, - LeftBoundary(c, _f) => { v[c] += x[c] * α }, - RightBoundary(c, b) => { v[c] -= x[b] * α }, + Interior(c, (b, _f)) => v[c] += (x[c] - x[b]) * α, + LeftBoundary(c, _f) => v[c] += x[c] * α, + RightBoundary(c, b) => v[c] -= x[b] * α, } } @@ -171,24 +161,24 @@ } } -impl<F : Float + nalgebra::RealField> Discretisation<F> for ForwardDirichlet { +impl<F: Float + nalgebra::RealField> Discretisation<F> for ForwardDirichlet { type Opposite = BackwardNeumann; #[inline] fn add_diff_mut<SMut, S>( &self, - v : &mut Matrix<F, Dyn, U1, SMut>, - x : &Matrix<F, Dyn, U1, S>, - α : F, - b : DiscretisationOrInterior, + v: &mut Matrix<F, Dyn, U1, SMut>, + x: &Matrix<F, Dyn, U1, S>, + α: F, + b: DiscretisationOrInterior, ) where - SMut : StorageMut<F, Dyn, U1>, - S : Storage<F, Dyn, U1> + SMut: StorageMut<F, Dyn, U1>, + S: Storage<F, Dyn, U1>, { match b { - Interior(c, (_b, f)) => { v[c] += (x[f] - x[c]) * α }, - LeftBoundary(c, f) => { v[c] += x[f] * α }, - RightBoundary(c, _b) => { v[c] -= x[c] * α }, + Interior(c, (_b, f)) => v[c] += (x[f] - x[c]) * α, + LeftBoundary(c, f) => v[c] += x[f] * α, + RightBoundary(c, _b) => v[c] -= x[c] * α, } } @@ -198,30 +188,37 @@ } } -struct Iter<'a, const N : usize> { +struct Iter<'a, const N: usize> { /// Dimensions - dims : &'a [usize; N], + dims: &'a [usize; N], /// Dimension along which to calculate differences - d : usize, + d: usize, /// Stride along coordinate d - d_stride : usize, + d_stride: usize, /// Cartesian indices - i : [usize; N], + i: [usize; N], /// Linear index - k : usize, + k: usize, /// Maximal linear index - len : usize + len: usize, } -impl<'a, const N : usize> Iter<'a, N> { - fn new(dims : &'a [usize; N], d : usize) -> Self { +impl<'a, const N: usize> Iter<'a, N> { + fn new(dims: &'a [usize; N], d: usize) -> Self { let d_stride = dims[0..d].iter().product::<usize>(); let len = dims.iter().product::<usize>(); - Iter{ dims, d, d_stride, i : [0; N], k : 0, len } + Iter { + dims, + d, + d_stride, + i: [0; N], + k: 0, + len, + } } } -impl<'a, const N : usize> Iterator for Iter<'a, N> { +impl<'a, const N: usize> Iterator for Iter<'a, N> { type Item = DiscretisationOrInterior; fn next(&mut self) -> Option<Self::Item> { let res = if self.k >= self.len { @@ -243,7 +240,7 @@ for j in 0..N { if self.i[j] + 1 < self.dims[j] { self.i[j] += 1; - break + break; } self.i[j] = 0 } @@ -251,14 +248,13 @@ } } -impl<F, B, const N : usize> Mapping<DVector<F>> -for Grad<F, B, N> +impl<F, B, const N: usize> Mapping<DVector<F>> for Grad<F, B, N> where - B : Discretisation<F>, - F : Float + nalgebra::RealField, + B: Discretisation<F>, + F: Float + nalgebra::RealField, { type Codomain = DVector<F>; - fn apply<I : Instance<DVector<F>>>(&self, i : I) -> DVector<F> { + fn apply<I: Instance<DVector<F>>>(&self, i: I) -> DVector<F> { let mut y = DVector::zeros(N * self.len()); self.apply_add(&mut y, i); y @@ -266,15 +262,12 @@ } #[replace_float_literals(F::cast_from(literal))] -impl<F, B, const N : usize> GEMV<F, DVector<F>> -for Grad<F, B, N> +impl<F, B, const N: usize> GEMV<F, DVector<F>> for Grad<F, B, N> where - B : Discretisation<F>, - F : Float + nalgebra::RealField, + B: Discretisation<F>, + F: Float + nalgebra::RealField, { - fn gemv<I : Instance<DVector<F>>>( - &self, y : &mut DVector<F>, α : F, i : I, β : F - ) { + fn gemv<I: Instance<DVector<F>>>(&self, y: &mut DVector<F>, α: F, i: I, β: F) { if β == 0.0 { y.as_mut_slice().iter_mut().for_each(|x| *x = 0.0); } else if β != 1.0 { @@ -286,23 +279,22 @@ i.eval(|x| { assert_eq!(x.len(), m); for d in 0..N { - let mut v = y.generic_view_mut((d*m, 0), (Dyn(m), U1)); + let mut v = y.generic_view_mut((d * m, 0), (Dyn(m), U1)); for b in Iter::new(&self.dims, d) { - self.discretisation.add_diff_mut(&mut v, x, α/h, b) + self.discretisation.add_diff_mut(&mut v, x, α / h, b) } } }) } } -impl<F, B, const N : usize> Mapping<DVector<F>> -for Div<F, B, N> +impl<F, B, const N: usize> Mapping<DVector<F>> for Div<F, B, N> where - B : Discretisation<F>, - F : Float + nalgebra::RealField, + B: Discretisation<F>, + F: Float + nalgebra::RealField, { type Codomain = DVector<F>; - fn apply<I : Instance<DVector<F>>>(&self, i : I) -> DVector<F> { + fn apply<I: Instance<DVector<F>>>(&self, i: I) -> DVector<F> { let mut y = DVector::zeros(self.len()); self.apply_add(&mut y, i); y @@ -310,15 +302,12 @@ } #[replace_float_literals(F::cast_from(literal))] -impl<F, B, const N : usize> GEMV<F, DVector<F>> -for Div<F, B, N> +impl<F, B, const N: usize> GEMV<F, DVector<F>> for Div<F, B, N> where - B : Discretisation<F>, - F : Float + nalgebra::RealField, + B: Discretisation<F>, + F: Float + nalgebra::RealField, { - fn gemv<I : Instance<DVector<F>>>( - &self, y : &mut DVector<F>, α : F, i : I, β : F - ) { + fn gemv<I: Instance<DVector<F>>>(&self, y: &mut DVector<F>, α: F, i: I, β: F) { if β == 0.0 { y.as_mut_slice().iter_mut().for_each(|x| *x = 0.0); } else if β != 1.0 { @@ -330,26 +319,51 @@ i.eval(|x| { assert_eq!(x.len(), N * m); for d in 0..N { - let v = x.generic_view((d*m, 0), (Dyn(m), U1)); + let v = x.generic_view((d * m, 0), (Dyn(m), U1)); for b in Iter::new(&self.dims, d) { - self.discretisation.add_diff_mut(y, &v, α/h, b) + self.discretisation.add_diff_mut(y, &v, α / h, b) } } }) } } -impl<F, B, const N : usize> Grad<F, B, N> +impl<F, B, const N: usize> Grad<F, B, N> where - B : Discretisation<F>, - F : Float + nalgebra::RealField + B: Discretisation<F>, + F: Float + nalgebra::RealField, { /// Creates a new discrete gradient operator for the vector `u`, verifying dimensions. - pub fn new_for(u : &DVector<F>, h : F, dims : [usize; N], discretisation : B) - -> Option<Self> - { + pub fn new_for(u: &DVector<F>, h: F, dims: [usize; N], discretisation: B) -> Option<Self> { if u.len() == dims.iter().product::<usize>() { - Some(Grad { dims, h, discretisation } ) + Some(Grad { + dims, + h, + discretisation, + }) + } else { + None + } + } + + fn len(&self) -> usize { + self.dims.iter().product::<usize>() + } +} + +impl<F, B, const N: usize> Div<F, B, N> +where + B: Discretisation<F>, + F: Float + nalgebra::RealField, +{ + /// Creates a new discrete gradient operator for the vector `u`, verifying dimensions. + pub fn new_for(u: &DVector<F>, h: F, dims: [usize; N], discretisation: B) -> Option<Self> { + if u.len() == dims.iter().product::<usize>() * N { + Some(Div { + dims, + h, + discretisation, + }) } else { None } @@ -360,108 +374,84 @@ } } - -impl<F, B, const N : usize> Div<F, B, N> +impl<F, B, const N: usize> Linear<DVector<F>> for Grad<F, B, N> where - B : Discretisation<F>, - F : Float + nalgebra::RealField + B: Discretisation<F>, + F: Float + nalgebra::RealField, { - /// Creates a new discrete gradient operator for the vector `u`, verifying dimensions. - pub fn new_for(u : &DVector<F>, h : F, dims : [usize; N], discretisation : B) - -> Option<Self> - { - if u.len() == dims.iter().product::<usize>() * N { - Some(Div { dims, h, discretisation } ) - } else { - None - } - } - - fn len(&self) -> usize { - self.dims.iter().product::<usize>() - } } -impl<F, B, const N : usize> Linear<DVector<F>> -for Grad<F, B, N> +impl<F, B, const N: usize> Linear<DVector<F>> for Div<F, B, N> where - B : Discretisation<F>, - F : Float + nalgebra::RealField, + B: Discretisation<F>, + F: Float + nalgebra::RealField, { } -impl<F, B, const N : usize> Linear<DVector<F>> -for Div<F, B, N> +impl<F, B, const N: usize> BoundedLinear<DVector<F>, L2, L2, F> for Grad<F, B, N> where - B : Discretisation<F>, - F : Float + nalgebra::RealField, + B: Discretisation<F>, + F: Float + nalgebra::RealField, + DVector<F>: Norm<F, L2>, { -} - -impl<F, B, const N : usize> BoundedLinear<DVector<F>, L2, L2, F> -for Grad<F, B, N> -where - B : Discretisation<F>, - F : Float + nalgebra::RealField, - DVector<F> : Norm<F, L2>, -{ - fn opnorm_bound(&self, _ : L2, _ : L2) -> F { + fn opnorm_bound(&self, _: L2, _: L2) -> DynResult<F> { // Fuck nalgebra. - self.discretisation.opnorm_bound(num_traits::Float::abs(self.h)) + self.discretisation + .opnorm_bound(num_traits::Float::abs(self.h)) } } - -impl<F, B, const N : usize> BoundedLinear<DVector<F>, L2, L2, F> -for Div<F, B, N> +impl<F, B, const N: usize> BoundedLinear<DVector<F>, L2, L2, F> for Div<F, B, N> where - B : Discretisation<F>, - F : Float + nalgebra::RealField, - DVector<F> : Norm<F, L2>, + B: Discretisation<F>, + F: Float + nalgebra::RealField, + DVector<F>: Norm<F, L2>, { - fn opnorm_bound(&self, _ : L2, _ : L2) -> F { + fn opnorm_bound(&self, _: L2, _: L2) -> DynResult<F> { // Fuck nalgebra. - self.discretisation.opnorm_bound(num_traits::Float::abs(self.h)) + self.discretisation + .opnorm_bound(num_traits::Float::abs(self.h)) } } -impl<F, B, const N : usize> -Adjointable<DVector<F>, DVector<F>> -for Grad<F, B, N> +impl<F, B, const N: usize> Adjointable<DVector<F>, DVector<F>> for Grad<F, B, N> where - B : Discretisation<F>, - F : Float + nalgebra::RealField, + B: Discretisation<F>, + F: Float + nalgebra::RealField, { type AdjointCodomain = DVector<F>; - type Adjoint<'a> = Div<F, B::Opposite, N> where Self : 'a; + type Adjoint<'a> + = Div<F, B::Opposite, N> + where + Self: 'a; /// Form the adjoint operator of `self`. fn adjoint(&self) -> Self::Adjoint<'_> { Div { - dims : self.dims, - h : -self.h, - discretisation : self.discretisation.opposite(), + dims: self.dims, + h: -self.h, + discretisation: self.discretisation.opposite(), } } } - -impl<F, B, const N : usize> -Adjointable<DVector<F>, DVector<F>> -for Div<F, B, N> +impl<F, B, const N: usize> Adjointable<DVector<F>, DVector<F>> for Div<F, B, N> where - B : Discretisation<F>, - F : Float + nalgebra::RealField, + B: Discretisation<F>, + F: Float + nalgebra::RealField, { type AdjointCodomain = DVector<F>; - type Adjoint<'a> = Grad<F, B::Opposite, N> where Self : 'a; + type Adjoint<'a> + = Grad<F, B::Opposite, N> + where + Self: 'a; /// Form the adjoint operator of `self`. fn adjoint(&self) -> Self::Adjoint<'_> { Grad { - dims : self.dims, - h : -self.h, - discretisation : self.discretisation.opposite(), + dims: self.dims, + h: -self.h, + discretisation: self.discretisation.opposite(), } } } @@ -472,8 +462,8 @@ #[test] fn grad_adjoint() { - let im = DVector::from( (0..9).map(|t| t as f64).collect::<Vec<_>>()); - let v = DVector::from( (0..18).map(|t| t as f64).collect::<Vec<_>>()); + let im = DVector::from((0..9).map(|t| t as f64).collect::<Vec<_>>()); + let v = DVector::from((0..18).map(|t| t as f64).collect::<Vec<_>>()); let grad = Grad::new_for(&im, 1.0, [3, 3], ForwardNeumann).unwrap(); let a = grad.apply(&im).dot(&v); @@ -484,6 +474,5 @@ let a = grad.apply(&im).dot(&v); let b = grad.adjoint().apply(&v).dot(&im); assert_eq!(a, b); - } }
--- a/src/linops.rs Wed Apr 30 01:06:25 2025 -0500 +++ b/src/linops.rs Wed Apr 30 16:39:01 2025 -0500 @@ -3,6 +3,7 @@ */ use crate::direct_product::Pair; +use crate::error::DynResult; use crate::instance::Instance; pub use crate::mapping::{Composition, DifferentiableImpl, Mapping, Space}; use crate::norms::{Linfinity, Norm, NormExponent, PairNorm, L1, L2}; @@ -84,7 +85,9 @@ /// This is not expected to be the norm, just any bound on it that can be /// reasonably implemented. The [`NormExponent`] `xexp` indicates the norm /// in `X`, and `codexp` in the codomain. - fn opnorm_bound(&self, xexp: XExp, codexp: CodExp) -> F; + /// + /// This may fail with an error if the bound is for some reason incalculable. + fn opnorm_bound(&self, xexp: XExp, codexp: CodExp) -> DynResult<F>; } // Linear operator application into mutable target. The [`AsRef`] bound @@ -183,8 +186,8 @@ F: Num, E: NormExponent, { - fn opnorm_bound(&self, _xexp: E, _codexp: E) -> F { - F::ONE + fn opnorm_bound(&self, _xexp: E, _codexp: E) -> DynResult<F> { + Ok(F::ONE) } } @@ -267,8 +270,8 @@ E1: NormExponent, E2: NormExponent, { - fn opnorm_bound(&self, _xexp: E1, _codexp: E2) -> F { - F::ZERO + fn opnorm_bound(&self, _xexp: E1, _codexp: E2) -> DynResult<F> { + Ok(F::ZERO) } } @@ -351,9 +354,9 @@ T: BoundedLinear<X, Xexp, Zexp, F, Codomain = Z>, S: BoundedLinear<Z, Zexp, Yexp, F>, { - fn opnorm_bound(&self, xexp: Xexp, yexp: Yexp) -> F { + fn opnorm_bound(&self, xexp: Xexp, yexp: Yexp) -> DynResult<F> { let zexp = self.intermediate_norm_exponent; - self.outer.opnorm_bound(zexp, yexp) * self.inner.opnorm_bound(xexp, zexp) + Ok(self.outer.opnorm_bound(zexp, yexp)? * self.inner.opnorm_bound(xexp, zexp)?) } } @@ -691,12 +694,12 @@ &self, PairNorm(expa, expb, _): PairNorm<ExpA, ExpB, $expj>, expr: ExpR, - ) -> F { + ) -> DynResult<F> { // An application of the triangle inequality bounds the norm by the maximum // of the individual norms. A simple observation shows this to be exact. - let na = self.0.opnorm_bound(expa, expr); - let nb = self.1.opnorm_bound(expb, expr); - na.max(nb) + let na = self.0.opnorm_bound(expa, expr)?; + let nb = self.1.opnorm_bound(expb, expr)?; + Ok(na.max(nb)) } } @@ -715,12 +718,12 @@ &self, expa: ExpA, PairNorm(exps, expt, _): PairNorm<ExpS, ExpT, $expj>, - ) -> F { + ) -> DynResult<F> { // This is based on the rule for RowOp and ‖A^*‖ = ‖A‖, hence, // for A=[S; T], ‖A‖=‖[S^*, T^*]‖ ≤ max{‖S^*‖, ‖T^*‖} = max{‖S‖, ‖T‖} - let ns = self.0.opnorm_bound(expa, exps); - let nt = self.1.opnorm_bound(expa, expt); - ns.max(nt) + let ns = self.0.opnorm_bound(expa, exps)?; + let nt = self.1.opnorm_bound(expa, expt)?; + Ok(ns.max(nt)) } } };
--- a/src/mapping.rs Wed Apr 30 01:06:25 2025 -0500 +++ b/src/mapping.rs Wed Apr 30 16:39:01 2025 -0500 @@ -2,6 +2,7 @@ Traits for mathematical functions. */ +use crate::error::DynResult; pub use crate::instance::{BasicDecomposition, Decomposition, Instance, Space}; use crate::loc::Loc; use crate::norms::{Norm, NormExponent}; @@ -324,7 +325,7 @@ type FloatType: Float; /// Returns the Lipschitz factor of `self` with respect to the (semi)norm `D`. - fn lipschitz_factor(&self, seminorm: M) -> Option<Self::FloatType>; + fn lipschitz_factor(&self, seminorm: M) -> DynResult<Self::FloatType>; } /// Helper trait for implementing [`Lipschitz`] for mappings that implement [`DifferentiableImpl`]. @@ -332,7 +333,7 @@ type FloatType: Float; /// Compute the lipschitz factor of the derivative of `f`. - fn diff_lipschitz_factor(&self, seminorm: M) -> Option<Self::FloatType>; + fn diff_lipschitz_factor(&self, seminorm: M) -> DynResult<Self::FloatType>; } impl<'b, M, X, A> Lipschitz<M> for Differential<'b, X, A> @@ -342,7 +343,7 @@ { type FloatType = A::FloatType; - fn lipschitz_factor(&self, seminorm: M) -> Option<Self::FloatType> { + fn lipschitz_factor(&self, seminorm: M) -> DynResult<Self::FloatType> { (*self.g).diff_lipschitz_factor(seminorm) } }
--- a/src/mapping/dataterm.rs Wed Apr 30 01:06:25 2025 -0500 +++ b/src/mapping/dataterm.rs Wed Apr 30 16:39:01 2025 -0500 @@ -7,11 +7,13 @@ use super::{DifferentiableImpl, DifferentiableMapping, LipschitzDifferentiableImpl, Mapping}; use crate::convex::ConvexMapping; +use crate::error::DynResult; use crate::instance::{Instance, Space}; use crate::linops::{BoundedLinear, Linear, Preadjointable}; use crate::norms::{Normed, L2}; use crate::types::Float; use std::ops::Sub; + //use serde::{Deserialize, Serialize}; /// Functions of the form $\frac{1}{2}\|Ax-b\|_2^2$ for an operator $A$ @@ -114,7 +116,7 @@ { type FloatType = F; - fn diff_lipschitz_factor(&self, seminorm: X::NormExp) -> Option<F> { - Some(self.opA.opnorm_bound(seminorm, L2).powi(2)) + fn diff_lipschitz_factor(&self, seminorm: X::NormExp) -> DynResult<F> { + Ok(self.opA.opnorm_bound(seminorm, L2)?.powi(2)) } }
--- a/src/mapping/quadratic.rs Wed Apr 30 01:06:25 2025 -0500 +++ b/src/mapping/quadratic.rs Wed Apr 30 16:39:01 2025 -0500 @@ -7,6 +7,7 @@ use super::{DifferentiableImpl, LipschitzDifferentiableImpl, Mapping}; use crate::convex::ConvexMapping; +use crate::error::DynResult; use crate::euclidean::Euclidean; use crate::instance::{Instance, Space}; use crate::linops::{BoundedLinear, Linear, Preadjointable}; @@ -93,7 +94,7 @@ { type FloatType = F; - fn diff_lipschitz_factor(&self, seminorm: ExpX) -> Option<F> { - Some(self.opA.opnorm_bound(seminorm, L2).powi(2)) + fn diff_lipschitz_factor(&self, seminorm: ExpX) -> DynResult<F> { + Ok(self.opA.opnorm_bound(seminorm, L2)?.powi(2)) } }