diff -r cebedc4a8331 -r d63e40672dd6 src/linops.rs --- a/src/linops.rs Sat Dec 21 14:27:14 2024 -0500 +++ b/src/linops.rs Fri Dec 20 16:14:17 2024 -0500 @@ -12,53 +12,66 @@ use crate::norms::{NormExponent, PairNorm, L1, L2, Linfinity, Norm, HasDual}; /// Trait for linear operators on `X`. -pub trait Linear : Mapping -{ } +pub trait Linear : Mapping { + type LinCodomain : AXPY; // = Self::Codomain, but with AXPY enforced without trait bound bloat. +} /// Efficient in-place summation. -#[replace_float_literals(F::cast_from(literal))] -pub trait AXPY : Space -where - F : Num, - X : Space, -{ +#[replace_float_literals(Self::Field::cast_from(literal))] +pub trait AXPY : Space { + type Field : Num; + /// Computes `y = βy + αx`, where `y` is `Self`. - fn axpy>(&mut self, α : F, x : I, β : F); + fn axpy>(&mut self, α : Self::Field, x : I, β : Self::Field); /// Copies `x` to `self`. - fn copy_from>(&mut self, x : I) { + 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 : I) { + fn scale_from>(&mut self, α : Self::Field, x : I) { self.axpy(α, x, 0.0) } } +macro_rules! impl_axpy { + ($($type:ty)*) => { $( + impl AXPY for $type { + type Field = $type; + fn axpy>(&mut self, α : $type, x : I, β : $type) { + *self = *self * α + x.own() * β; + } + } + )* }; +} + +impl_axpy!(i8 i16 i32 i64 i128 isize f32 f64); + + /// Efficient in-place application for [`Linear`] operators. -#[replace_float_literals(F::cast_from(literal))] -pub trait GEMV>::Codomain> : Linear { +#[replace_float_literals(X::Field::cast_from(literal))] +pub trait GEMV : Linear { /// Computes `y = αAx + βy`, where `A` is `Self`. - fn gemv>(&self, y : &mut Y, α : F, x : I, β : F); + fn gemv>(&self, y : &mut Self::LinCodomain, α : ::Field, x : I, β : ::Field); /// Computes `y = Ax`, where `A` is `Self` - fn apply_mut>(&self, y : &mut Y, x : I){ + fn apply_mut>(&self, y : &mut Self::LinCodomain, 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 : I){ + fn apply_add>(&self, y : &mut Self::LinCodomain, x : I){ self.gemv(y, 1.0, x, 1.0) } } /// Bounded linear operators -pub trait BoundedLinear : Linear +pub trait BoundedLinear : Linear where F : Num, - X : Space + Norm, + X : AXP + Norm, XExp : NormExponent, CodExp : NormExponent { @@ -66,7 +79,7 @@ /// 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; + fn opnorm_bound(&self, xexp : XExp, codexp : CodExp) -> X::Field; // TODO: Should pick real subfield } // Linear operator application into mutable target. The [`AsRef`] bound @@ -105,27 +118,26 @@ /// /// We do not set further restrictions on the spacds, to allow preadjointing when `X` /// is on the dual space of `Ypre`, but a subset of it. -pub trait Preadjointable : Linear +pub trait Preadjointable : Linear //where // Ypre : HasDual, { - type PreadjointCodomain : Space; + type PreadjointCodomain : AXPY; type Preadjoint<'a> : Linear where Self : 'a; /// Form the adjoint operator of `self`. fn preadjoint(&self) -> Self::Preadjoint<'_>; } - /// The identity operator #[derive(Clone,Copy,Debug,Serialize,Eq,PartialEq)] -pub struct IdOp (PhantomData); +pub struct IdOp (PhantomData); -impl IdOp { +impl IdOp { pub fn new() -> IdOp { IdOp(PhantomData) } } -impl Mapping for IdOp { +impl Mapping for IdOp { type Codomain = X; fn apply>(&self, x : I) -> X { @@ -133,32 +145,29 @@ } } -impl Linear for IdOp -{ } +impl Linear for IdOp { + type LinCodomain = X; +} #[replace_float_literals(F::cast_from(literal))] -impl GEMV for IdOp -where - Y : AXPY, - X : Clone + Space -{ +impl GEMV for IdOp { // Computes `y = αAx + βy`, where `A` is `Self`. - fn gemv>(&self, y : &mut Y, α : F, x : I, β : F) { + fn gemv>(&self, y : &mut Self::LinCodomain, α : X::Field, x : I, β : X::Field) { y.axpy(α, x, β) } - fn apply_mut>(&self, y : &mut Y, x : I){ + fn apply_mut>(&self, y : &mut X, x : I){ y.copy_from(x); } } -impl BoundedLinear for IdOp +impl BoundedLinear for IdOp where - X : Space + Clone + Norm, - F : Num, + X : AXPY + Clone + Norm, + F : Num + AXPY, E : NormExponent { - fn opnorm_bound(&self, _xexp : E, _codexp : E) -> F { F::ONE } + fn opnorm_bound(&self, _xexp : E, _codexp : E) -> X::Field { F::ONE } } impl, F : Float> Adjointable for IdOp { @@ -168,7 +177,7 @@ fn adjoint(&self) -> Self::Adjoint<'_> { IdOp::new() } } -impl Preadjointable for IdOp { +impl Preadjointable for IdOp { type PreadjointCodomain = X; type Preadjoint<'a> = IdOp where X : 'a; @@ -178,10 +187,12 @@ impl Linear for Composition where - X : Space, + X : AXPY, T : Linear, - S : Linear -{ } + S : Linear +{ + type LinCodomain = S::LinCodomain; +} impl GEMV for Composition where @@ -227,17 +238,16 @@ use std::ops::Add; -impl Mapping> for RowOp +impl Mapping> for RowOp where A : Space, B : Space, + Y : Space, S : Mapping, T : Mapping, - S::Codomain : Add, - >::Output : Space, - + S::Codomain : Add, { - type Codomain = >::Output; + type Codomain = Y; fn apply>>(&self, x : I) -> Self::Codomain { let Pair(a, b) = x.decompose(); @@ -245,25 +255,30 @@ } } -impl Linear> for RowOp +impl Linear> for RowOp where - A : Space, - B : Space, + F : Num + AXPY, + A : AXPY, + B : AXPY, + Y : AXPY, S : Linear, T : Linear, - S::Codomain : Add, - >::Output : Space, -{ } + S::LinCodomain : Add, +{ + type LinCodomain = Y; +} -impl<'b, F, S, T, Y, U, V> GEMV, Y> for RowOp +impl<'b, F, S, T, Y, G, U, V> GEMV> for RowOp where - U : Space, - V : Space, - S : GEMV, - T : GEMV, - F : Num, - Self : Linear, Codomain=Y> + U : AXPY, + V : AXPY, + Y : AXPY, + S : GEMV, + T : GEMV, + F : Num + AXPY, + G : Num + AXPY, + S::LinCodomain : Add, { fn gemv>>(&self, y : &mut Y, α : F, x : I, β : F) { let Pair(u, v) = x.decompose(); @@ -288,33 +303,39 @@ /// “Column operator” $(S; T)$; $(S; T)x=(Sx, Tx)$. pub struct ColOp(pub S, pub T); -impl Mapping for ColOp +impl Mapping for ColOp where - A : Space, - S : Mapping, - T : Mapping, + X : AXPY, + S : Mapping, + T : Mapping, { type Codomain = Pair; - fn apply>(&self, a : I) -> Self::Codomain { + 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 - A : Space, - S : Mapping, - T : Mapping, -{ } + F : Num + AXPY, + X : AXPY, + S : Linear, + T : Linear, + A : AXPY, + B : AXPY, +{ + type LinCodomain = Pair; +} -impl GEMV> for ColOp +impl GEMV for ColOp where - X : Space, - S : GEMV, - T : GEMV, - F : Num, - Self : Linear> + F : Num + AXPY, + X : AXPY, + S : GEMV, + T : GEMV, + A : AXPY, + B : AXPY, { fn gemv>(&self, y : &mut Pair, α : F, x : I, β : F) { self.0.gemv(&mut y.0, α, x.ref_instance(), β); @@ -341,7 +362,7 @@ B : HasDual, S : Adjointable, T : Adjointable, - Yʹ : Space, + Yʹ : AXPY, S :: Codomain : HasDual, T :: Codomain : HasDual, S::Codomain : Add, @@ -361,15 +382,15 @@ } } -impl Preadjointable, Yʹ> for RowOp -where - A : Space, - B : Space, - Yʹ : Space, +impl Preadjointable, Yʹ> for RowOp +wherea + A : AXPY, + B : AXPY, + Yʹ : AXPY, S : Preadjointable, T : Preadjointable, S::Codomain : Add, - >::Output : Space, + >::Output : AXPY, //Self : Linear, Codomain=Y>, //for<'a> ColOp, T::Preadjoint<'a>> : Adjointable, { @@ -407,10 +428,10 @@ impl Preadjointable> for ColOp where - A : Space, - Aʹ : Space, - Xʹ : Space, - Yʹ : Space, + A : AXPY, + Aʹ : AXPY, + Xʹ : AXPY, + Yʹ : AXPY, S : Preadjointable, T : Preadjointable, Aʹ : ClosedAdd, @@ -429,8 +450,8 @@ impl Mapping> for DiagOp where - A : Space, - B : Space, + A : AXPY, + B : AXPY, S : Mapping, T : Mapping, { @@ -442,26 +463,32 @@ } } -impl Linear> for DiagOp -where - A : Space, - B : Space, - S : Linear, - T : Linear, -{ } - -impl GEMV, Pair> for DiagOp +impl Linear> for DiagOp where - A : Space, - B : Space, - U : Space, - V : Space, - S : GEMV, - T : GEMV, - F : Num, - Self : Linear, Codomain=Pair>, + F : Num + AXPY, + G : Num + AXPY, + A : AXPY, + B : AXPY, + U : AXPY, + V : AXPY, + S : Linear, + T : Linear, { - fn gemv>>(&self, y : &mut Pair, α : F, x : I, β : F) { + type LinCodomain = Pair; +} + +impl GEMV> for DiagOp +where + F : Num + AXPY, + G : Num + AXPY, + A : AXPY, + B : AXPY, + U : AXPY, + V : AXPY, + S : GEMV, + T : GEMV, +{ + fn gemv>>(&self, y : &mut Pair, α : G, x : I, β : G) { let Pair(u, v) = x.decompose(); self.0.gemv(&mut y.0, α, u, β); self.1.gemv(&mut y.1, α, v, β); @@ -501,10 +528,10 @@ impl Preadjointable, Pair> for DiagOp where - A : Space, - B : Space, - Xʹ : Space, - Yʹ : Space, + A : AXPY, + B : AXPY, + Xʹ : AXPY, + Yʹ : AXPY, S : Preadjointable, T : Preadjointable, { @@ -523,16 +550,16 @@ macro_rules! pairnorm { ($expj:ty) => { impl - BoundedLinear, PairNorm, ExpR, F> + BoundedLinear, PairNorm, ExpR> for RowOp where F : Float, - A : Space + Norm, - B : Space + Norm, + A : AXPY + Norm, + B : AXPY + Norm, S : BoundedLinear, T : BoundedLinear, S::Codomain : Add, - >::Output : Space, + >::Output : AXPY, ExpA : NormExponent, ExpB : NormExponent, ExpR : NormExponent, @@ -541,7 +568,7 @@ &self, PairNorm(expa, expb, _) : PairNorm, expr : ExpR - ) -> F { + ) -> as AXPY>::Field { // 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); @@ -550,12 +577,12 @@ } } - impl - BoundedLinear, F> + impl + BoundedLinear> for ColOp where F : Float, - A : Space + Norm, + A : AXPY + Norm, S : BoundedLinear, T : BoundedLinear, ExpA : NormExponent, @@ -566,7 +593,7 @@ &self, expa : ExpA, PairNorm(exps, expt, _) : PairNorm - ) -> F { + ) -> A::Field { // 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);