diff -r 1a38447a89fa -r 9226980e45a7 src/linops.rs --- a/src/linops.rs Sat Dec 14 09:31:27 2024 -0500 +++ b/src/linops.rs Tue Dec 31 08:30:02 2024 -0500 @@ -4,59 +4,69 @@ use numeric_literals::replace_float_literals; use std::marker::PhantomData; +use serde::Serialize; use crate::types::*; -use serde::Serialize; -pub use crate::mapping::Apply; +pub use crate::mapping::{Mapping, Space}; use crate::direct_product::Pair; +use crate::instance::Instance; +use crate::norms::{NormExponent, PairNorm, L1, L2, Linfinity}; /// Trait for linear operators on `X`. -pub trait Linear : Apply - + for<'a> Apply<&'a X, Output=Self::Codomain> { - type Codomain; -} +pub trait Linear : Mapping +{ } /// Efficient in-place summation. #[replace_float_literals(F::cast_from(literal))] -pub trait AXPY { +pub trait AXPY : Space +where + F : Num, + X : Space, +{ /// Computes `y = βy + αx`, where `y` is `Self`. - fn axpy(&mut self, α : F, x : &X, β : F); + fn axpy>(&mut self, α : F, x : I, β : F); /// Copies `x` to `self`. - fn copy_from(&mut self, x : &X) { + fn copy_from>(&mut self, x : I) { self.axpy(1.0, x, 0.0) } /// Computes `y = αx`, where `y` is `Self`. - fn scale_from(&mut self, α : F, x : &X) { + fn scale_from>(&mut self, α : F, x : I) { self.axpy(α, x, 0.0) } } /// Efficient in-place application for [`Linear`] operators. #[replace_float_literals(F::cast_from(literal))] -pub trait GEMV>::Codomain> : Linear { +pub trait GEMV>::Codomain> : Linear { /// Computes `y = αAx + βy`, where `A` is `Self`. - fn gemv(&self, y : &mut Y, α : F, x : &X, β : F); + fn gemv>(&self, y : &mut Y, α : F, x : I, β : F); /// Computes `y = Ax`, where `A` is `Self` - fn apply_mut(&self, y : &mut Y, x : &X){ + fn apply_mut>(&self, y : &mut Y, x : I){ self.gemv(y, 1.0, x, 0.0) } /// Computes `y += Ax`, where `A` is `Self` - fn apply_add(&self, y : &mut Y, x : &X){ + fn apply_add>(&self, y : &mut Y, x : I){ self.gemv(y, 1.0, x, 1.0) } } /// Bounded linear operators -pub trait BoundedLinear : Linear { - type FloatType : Float; +pub trait BoundedLinear : Linear +where + F : Num, + X : Space, + XExp : NormExponent, + CodExp : NormExponent +{ /// A bound on the operator norm $\|A\|$ for the linear operator $A$=`self`. /// This is not expected to be the norm, just any bound on it that can be - /// reasonably implemented. - fn opnorm_bound(&self) -> Self::FloatType; + /// reasonably implemented. The [`NormExponent`] `xexp` indicates the norm + /// in `X`, and `codexp` in the codomain. + fn opnorm_bound(&self, xexp : XExp, codexp : CodExp) -> F; } // Linear operator application into mutable target. The [`AsRef`] bound @@ -70,16 +80,16 @@ }*/ /// Trait for forming the adjoint operator of `Self`. -pub trait Adjointable : Linear { - type AdjointCodomain; +pub trait Adjointable : Linear +where + X : Space, + Yʹ : Space, +{ + type AdjointCodomain : Space; type Adjoint<'a> : Linear where Self : 'a; /// Form the adjoint operator of `self`. fn adjoint(&self) -> Self::Adjoint<'_>; - - /*fn adjoint_apply(&self, y : &Yʹ) -> Self::AdjointCodomain { - self.adjoint().apply(y) - }*/ } /// Trait for forming a preadjoint of an operator. @@ -89,189 +99,206 @@ /// operator. The space `Ypre` is the predual of its codomain, and should be the /// domain of the adjointed operator. `Self::Preadjoint` should be /// [`Adjointable`]`<'a,Ypre,X>`. -pub trait Preadjointable : Linear { - type PreadjointCodomain; - type Preadjoint<'a> : Adjointable where Self : 'a; - /// Form the preadjoint operator of `self`. +pub trait Preadjointable : Linear { + type PreadjointCodomain : Space; + type Preadjoint<'a> : Adjointable< + Ypre, X, AdjointCodomain=Self::Codomain, Codomain=Self::PreadjointCodomain + > where Self : 'a; + + /// Form the adjoint operator of `self`. fn preadjoint(&self) -> Self::Preadjoint<'_>; } /// Adjointable operators $A: X → Y$ between reflexive spaces $X$ and $Y$. -pub trait SimplyAdjointable : Adjointable>::Codomain> {} -impl<'a,X,T> SimplyAdjointable for T where T : Adjointable>::Codomain> {} +pub trait SimplyAdjointable : Adjointable>::Codomain> {} +impl<'a,X : Space, T> SimplyAdjointable for T +where T : Adjointable>::Codomain> {} /// The identity operator #[derive(Clone,Copy,Debug,Serialize,Eq,PartialEq)] pub struct IdOp (PhantomData); impl IdOp { - fn new() -> IdOp { IdOp(PhantomData) } + pub fn new() -> IdOp { IdOp(PhantomData) } } -impl Apply for IdOp { - type Output = X; +impl Mapping for IdOp { + type Codomain = X; - fn apply(&self, x : X) -> X { - x + fn apply>(&self, x : I) -> X { + x.own() } } -impl<'a, X> Apply<&'a X> for IdOp where X : Clone { - type Output = X; - - fn apply(&self, x : &'a X) -> X { - x.clone() - } -} - -impl Linear for IdOp where X : Clone { - type Codomain = X; -} - +impl Linear for IdOp +{ } #[replace_float_literals(F::cast_from(literal))] -impl GEMV for IdOp where Y : AXPY, X : Clone { +impl GEMV for IdOp +where + Y : AXPY, + X : Clone + Space +{ // Computes `y = αAx + βy`, where `A` is `Self`. - fn gemv(&self, y : &mut Y, α : F, x : &X, β : F) { + fn gemv>(&self, y : &mut Y, α : F, x : I, β : F) { y.axpy(α, x, β) } - fn apply_mut(&self, y : &mut Y, x : &X){ + fn apply_mut>(&self, y : &mut Y, x : I){ y.copy_from(x); } } -impl BoundedLinear for IdOp where X : Clone { - type FloatType = float; - fn opnorm_bound(&self) -> float { 1.0 } +impl BoundedLinear for IdOp +where + X : Space + Clone, + F : Num, + E : NormExponent +{ + fn opnorm_bound(&self, _xexp : E, _codexp : E) -> F { F::ONE } } -impl Adjointable for IdOp where X : Clone { +impl Adjointable for IdOp { type AdjointCodomain=X; type Adjoint<'a> = IdOp where X : 'a; + fn adjoint(&self) -> Self::Adjoint<'_> { IdOp::new() } } +impl Preadjointable for IdOp { + type PreadjointCodomain=X; + type Preadjoint<'a> = IdOp where X : 'a; + + fn preadjoint(&self) -> Self::Preadjoint<'_> { IdOp::new() } +} + /// “Row operator” $(S, T)$; $(S, T)(x, y)=Sx + Ty$. pub struct RowOp(pub S, pub T); use std::ops::Add; -impl Apply> for RowOp +impl Mapping> for RowOp where - S : Apply, - T : Apply, - S::Output : Add + A : Space, + B : Space, + S : Mapping, + T : Mapping, + S::Codomain : Add, + >::Output : Space, + { - type Output = >::Output; + type Codomain = >::Output; - fn apply(&self, Pair(a, b) : Pair) -> Self::Output { + fn apply>>(&self, x : I) -> Self::Codomain { + let Pair(a, b) = x.decompose(); self.0.apply(a) + self.1.apply(b) } } -impl<'a, A, B, S, T> Apply<&'a Pair> for RowOp +impl Linear> for RowOp where - S : Apply<&'a A>, - T : Apply<&'a B>, - S::Output : Add -{ - type Output = >::Output; + A : Space, + B : Space, + S : Linear, + T : Linear, + S::Codomain : Add, + >::Output : Space, +{ } - fn apply(&self, Pair(ref a, ref b) : &'a Pair) -> Self::Output { - self.0.apply(a) + self.1.apply(b) - } -} -impl Linear> for RowOp +impl<'b, F, S, T, Y, U, V> GEMV, Y> for RowOp where - RowOp : Apply, Output=D> + for<'a> Apply<&'a Pair, Output=D>, -{ - type Codomain = D; -} - -impl GEMV, Y> for RowOp -where + U : Space, + V : Space, S : GEMV, T : GEMV, F : Num, Self : Linear, Codomain=Y> { - fn gemv(&self, y : &mut Y, α : F, x : &Pair, β : F) { - self.0.gemv(y, α, &x.0, β); - self.1.gemv(y, α, &x.1, β); + fn gemv>>(&self, y : &mut Y, α : F, x : I, β : F) { + let Pair(u, v) = x.decompose(); + self.0.gemv(y, α, u, β); + self.1.gemv(y, α, v, F::ONE); } - fn apply_mut(&self, y : &mut Y, x : &Pair){ - self.0.apply_mut(y, &x.0); - self.1.apply_mut(y, &x.1); + fn apply_mut>>(&self, y : &mut Y, x : I) { + let Pair(u, v) = x.decompose(); + self.0.apply_mut(y, u); + self.1.apply_mut(y, v); } /// Computes `y += Ax`, where `A` is `Self` - fn apply_add(&self, y : &mut Y, x : &Pair){ - self.0.apply_add(y, &x.0); - self.1.apply_add(y, &x.1); + fn apply_add>>(&self, y : &mut Y, x : I) { + let Pair(u, v) = x.decompose(); + self.0.apply_add(y, u); + self.1.apply_add(y, v); } } - /// “Column operator” $(S; T)$; $(S; T)x=(Sx, Tx)$. pub struct ColOp(pub S, pub T); -impl Apply for ColOp +impl Mapping for ColOp where - S : for<'a> Apply<&'a A, Output=O>, - T : Apply, - A : std::borrow::Borrow, + A : Space, + S : Mapping, + T : Mapping, { - type Output = Pair; + type Codomain = Pair; - fn apply(&self, a : A) -> Self::Output { - Pair(self.0.apply(a.borrow()), self.1.apply(a)) + fn apply>(&self, a : I) -> Self::Codomain { + Pair(self.0.apply(a.ref_instance()), self.1.apply(a)) } } -impl Linear for ColOp +impl Linear for ColOp where - ColOp : Apply + for<'a> Apply<&'a A, Output=D>, -{ - type Codomain = D; -} + A : Space, + S : Mapping, + T : Mapping, +{ } impl GEMV> for ColOp where + X : Space, S : GEMV, T : GEMV, F : Num, Self : Linear> { - fn gemv(&self, y : &mut Pair, α : F, x : &X, β : F) { - self.0.gemv(&mut y.0, α, x, β); + fn gemv>(&self, y : &mut Pair, α : F, x : I, β : F) { + self.0.gemv(&mut y.0, α, x.ref_instance(), β); self.1.gemv(&mut y.1, α, x, β); } - fn apply_mut(&self, y : &mut Pair, x : &X){ - self.0.apply_mut(&mut y.0, x); + fn apply_mut>(&self, y : &mut Pair, x : I){ + self.0.apply_mut(&mut y.0, x.ref_instance()); self.1.apply_mut(&mut y.1, x); } /// Computes `y += Ax`, where `A` is `Self` - fn apply_add(&self, y : &mut Pair, x : &X){ - self.0.apply_add(&mut y.0, x); + fn apply_add>(&self, y : &mut Pair, x : I){ + self.0.apply_add(&mut y.0, x.ref_instance()); self.1.apply_add(&mut y.1, x); } } -impl Adjointable,Yʹ> for RowOp +impl Adjointable, Yʹ> for RowOp where + A : Space, + B : Space, + Yʹ : Space, S : Adjointable, T : Adjointable, Self : Linear>, - for<'a> ColOp, T::Adjoint<'a>> : Linear, + // for<'a> ColOp, T::Adjoint<'a>> : Linear< + // Yʹ, + // Codomain=Pair + // >, { - type AdjointCodomain = R; + type AdjointCodomain = Pair; type Adjoint<'a> = ColOp, T::Adjoint<'a>> where Self : 'a; fn adjoint(&self) -> Self::Adjoint<'_> { @@ -279,14 +306,21 @@ } } -impl Preadjointable,Yʹ> for RowOp +impl Preadjointable, Yʹ> for RowOp where + A : Space, + B : Space, + Yʹ : Space, S : Preadjointable, T : Preadjointable, Self : Linear>, - for<'a> ColOp, T::Preadjoint<'a>> : Adjointable, Codomain=R>, + for<'a> ColOp, T::Preadjoint<'a>> : Adjointable< + Yʹ, Pair, + Codomain=Pair, + AdjointCodomain = Self::Codomain, + >, { - type PreadjointCodomain = R; + type PreadjointCodomain = Pair; type Preadjoint<'a> = ColOp, T::Preadjoint<'a>> where Self : 'a; fn preadjoint(&self) -> Self::Preadjoint<'_> { @@ -297,10 +331,17 @@ impl Adjointable> for ColOp where - S : Adjointable, - T : Adjointable, + A : Space, + Xʹ : Space, + Yʹ : Space, + R : Space + ClosedAdd, + S : Adjointable, + T : Adjointable, Self : Linear, - for<'a> RowOp, T::Adjoint<'a>> : Linear, Codomain=R>, + // for<'a> RowOp, T::Adjoint<'a>> : Linear< + // Pair, + // Codomain=R, + // >, { type AdjointCodomain = R; type Adjoint<'a> = RowOp, T::Adjoint<'a>> where Self : 'a; @@ -312,10 +353,18 @@ impl Preadjointable> for ColOp where - S : Preadjointable, - T : Preadjointable, + A : Space, + Xʹ : Space, + Yʹ : Space, + R : Space + ClosedAdd, + S : Preadjointable, + T : Preadjointable, Self : Linear, - for<'a> RowOp, T::Preadjoint<'a>> : Adjointable, A, Codomain=R>, + for<'a> RowOp, T::Preadjoint<'a>> : Adjointable< + Pair, A, + Codomain = R, + AdjointCodomain = Self::Codomain, + >, { type PreadjointCodomain = R; type Preadjoint<'a> = RowOp, T::Preadjoint<'a>> where Self : 'a; @@ -328,67 +377,73 @@ /// Diagonal operator pub struct DiagOp(pub S, pub T); -impl Apply> for DiagOp +impl Mapping> for DiagOp where - S : Apply, - T : Apply, + A : Space, + B : Space, + S : Mapping, + T : Mapping, { - type Output = Pair; + type Codomain = Pair; - fn apply(&self, Pair(a, b) : Pair) -> Self::Output { - Pair(self.0.apply(a), self.1.apply(b)) - } -} - -impl<'a, A, B, S, T> Apply<&'a Pair> for DiagOp -where - S : Apply<&'a A>, - T : Apply<&'a B>, -{ - type Output = Pair; - - fn apply(&self, Pair(ref a, ref b) : &'a Pair) -> Self::Output { + fn apply>>(&self, x : I) -> Self::Codomain { + let Pair(a, b) = x.decompose(); Pair(self.0.apply(a), self.1.apply(b)) } } -impl Linear> for DiagOp +impl Linear> for DiagOp where - DiagOp : Apply, Output=D> + for<'a> Apply<&'a Pair, Output=D>, -{ - type Codomain = D; -} + A : Space, + B : Space, + S : Linear, + T : Linear, +{ } impl GEMV, Pair> for DiagOp where + A : Space, + B : Space, + U : Space, + V : Space, S : GEMV, T : GEMV, F : Num, - Self : Linear, Codomain=Pair> + Self : Linear, Codomain=Pair>, { - fn gemv(&self, y : &mut Pair, α : F, x : &Pair, β : F) { - self.0.gemv(&mut y.0, α, &x.0, β); - self.1.gemv(&mut y.1, α, &x.1, β); + fn gemv>>(&self, y : &mut Pair, α : F, x : I, β : F) { + let Pair(u, v) = x.decompose(); + self.0.gemv(&mut y.0, α, u, β); + self.1.gemv(&mut y.1, α, v, β); } - fn apply_mut(&self, y : &mut Pair, x : &Pair){ - self.0.apply_mut(&mut y.0, &x.0); - self.1.apply_mut(&mut y.1, &x.1); + fn apply_mut>>(&self, y : &mut Pair, x : I){ + let Pair(u, v) = x.decompose(); + self.0.apply_mut(&mut y.0, u); + self.1.apply_mut(&mut y.1, v); } /// Computes `y += Ax`, where `A` is `Self` - fn apply_add(&self, y : &mut Pair, x : &Pair){ - self.0.apply_add(&mut y.0, &x.0); - self.1.apply_add(&mut y.1, &x.1); + fn apply_add>>(&self, y : &mut Pair, x : I){ + let Pair(u, v) = x.decompose(); + self.0.apply_add(&mut y.0, u); + self.1.apply_add(&mut y.1, v); } } -impl Adjointable,Pair> for DiagOp +impl Adjointable, Pair> for DiagOp where + A : Space, + B : Space, + Xʹ: Space, + Yʹ : Space, + R : Space, S : Adjointable, T : Adjointable, Self : Linear>, - for<'a> DiagOp, T::Adjoint<'a>> : Linear, Codomain=R>, + for<'a> DiagOp, T::Adjoint<'a>> : Linear< + Pair, Codomain=R, + >, { type AdjointCodomain = R; type Adjoint<'a> = DiagOp, T::Adjoint<'a>> where Self : 'a; @@ -398,12 +453,21 @@ } } -impl Preadjointable,Pair> for DiagOp +impl Preadjointable, Pair> for DiagOp where + A : Space, + B : Space, + Xʹ: Space, + Yʹ : Space, + R : Space, S : Preadjointable, T : Preadjointable, Self : Linear>, - for<'a> DiagOp, T::Preadjoint<'a>> : Adjointable, Pair, Codomain=R>, + for<'a> DiagOp, T::Preadjoint<'a>> : Adjointable< + Pair, Pair, + Codomain=R, + AdjointCodomain = Self::Codomain, + >, { type PreadjointCodomain = R; type Preadjoint<'a> = DiagOp, T::Preadjoint<'a>> where Self : 'a; @@ -416,3 +480,65 @@ /// Block operator pub type BlockOp = ColOp, RowOp>; + +macro_rules! pairnorm { + ($expj:ty) => { + impl + BoundedLinear, PairNorm, ExpR, F> + for RowOp + where + F : Float, + A : Space, + B : Space, + S : BoundedLinear, + T : BoundedLinear, + S::Codomain : Add, + >::Output : Space, + ExpA : NormExponent, + ExpB : NormExponent, + ExpR : NormExponent, + { + fn opnorm_bound( + &self, + PairNorm(expa, expb, _) : PairNorm, + expr : ExpR + ) -> 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) + } + } + + impl + BoundedLinear, F> + for ColOp + where + F : Float, + A : Space, + S : BoundedLinear, + T : BoundedLinear, + ExpA : NormExponent, + ExpS : NormExponent, + ExpT : NormExponent, + { + fn opnorm_bound( + &self, + expa : ExpA, + PairNorm(exps, expt, _) : PairNorm + ) -> 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) + } + } + } +} + +pairnorm!(L1); +pairnorm!(L2); +pairnorm!(Linfinity); +