Fri, 20 Dec 2024 16:14:17 -0500
AXPY as LinSpace attempts, difficulties with Pairs and nalgebra
nalgebra should allow various storages, so InstanceMut as &self, but that won't work.
src/direct_product.rs | file | annotate | diff | comparison | revisions | |
src/linops.rs | file | annotate | diff | comparison | revisions | |
src/loc.rs | file | annotate | diff | comparison | revisions | |
src/nalgebra_support.rs | file | annotate | diff | comparison | revisions | |
src/norms.rs | file | annotate | diff | comparison | revisions |
--- a/src/direct_product.rs Sat Dec 21 14:27:14 2024 -0500 +++ b/src/direct_product.rs Fri Dec 20 16:14:17 2024 -0500 @@ -279,28 +279,27 @@ } } -impl<F, A, B, U, V> AXPY<F, Pair<U, V>> for Pair<A, B> +impl<F, A, B> AXPY for Pair<A, B> where - U : Space, - V : Space, - A : AXPY<F, U>, - B : AXPY<F, V>, - F : Num + A : AXPY<Field=F>, + B : AXPY<Field=F>, + F : Num + AXPY { + type Field = F; - fn axpy<I : Instance<Pair<U,V>>>(&mut self, α : F, x : I, β : F) { + fn axpy<I : Instance<Pair<A,B>>>(&mut self, α : F, x : I, β : F) { let Pair(u, v) = x.decompose(); self.0.axpy(α, u, β); self.1.axpy(α, v, β); } - fn copy_from<I : Instance<Pair<U,V>>>(&mut self, x : I) { + fn copy_from<I : Instance<Pair<A,B>>>(&mut self, x : I) { let Pair(u, v) = x.decompose(); self.0.copy_from(u); self.1.copy_from(v); } - fn scale_from<I : Instance<Pair<U,V>>>(&mut self, α : F, x : I) { + fn scale_from<I : Instance<Pair<A,B>>>(&mut self, α : F, x : I) { let Pair(u, v) = x.decompose(); self.0.scale_from(α, u); self.1.scale_from(α, v);
--- 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<X : Space> : Mapping<X> -{ } +pub trait Linear<X : AXPY> : Mapping<X, Codomain = Self::LinCodomain> { + 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<F, X = Self> : 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<I : Instance<X>>(&mut self, α : F, x : I, β : F); + fn axpy<I : Instance<Self>>(&mut self, α : Self::Field, x : I, β : Self::Field); /// Copies `x` to `self`. - fn copy_from<I : Instance<X>>(&mut self, x : I) { + fn copy_from<I : Instance<Self>>(&mut self, x : I) { self.axpy(1.0, x, 0.0) } /// Computes `y = αx`, where `y` is `Self`. - fn scale_from<I : Instance<X>>(&mut self, α : F, x : I) { + fn scale_from<I : Instance<Self>>(&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<I : Instance<Self>>(&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<F : Num, X : Space, Y = <Self as Mapping<X>>::Codomain> : Linear<X> { +#[replace_float_literals(X::Field::cast_from(literal))] +pub trait GEMV<X : AXPY> : Linear<X> { /// Computes `y = αAx + βy`, where `A` is `Self`. - fn gemv<I : Instance<X>>(&self, y : &mut Y, α : F, x : I, β : F); + fn gemv<I : Instance<X>>(&self, y : &mut Self::LinCodomain, α : <Self::LinCodomain as AXPY>::Field, x : I, β : <Self::LinCodomain as AXPY>::Field); /// Computes `y = Ax`, where `A` is `Self` - fn apply_mut<I : Instance<X>>(&self, y : &mut Y, x : I){ + fn apply_mut<I : Instance<X>>(&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<I : Instance<X>>(&self, y : &mut Y, x : I){ + fn apply_add<I : Instance<X>>(&self, y : &mut Self::LinCodomain, x : I){ self.gemv(y, 1.0, x, 1.0) } } /// Bounded linear operators -pub trait BoundedLinear<X, XExp, CodExp, F = f64> : Linear<X> +pub trait BoundedLinear<X, XExp, CodExp> : Linear<X> where F : Num, - X : Space + Norm<F, XExp>, + X : AXP + Norm<F, XExp>, 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<X : Space, Ypre : Space> : Linear<X> +pub trait Preadjointable<X : AXPY, Ypre : AXPY> : Linear<X> //where // Ypre : HasDual<F, DualSpace=Self::Codomain>, { - type PreadjointCodomain : Space; + type PreadjointCodomain : AXPY; type Preadjoint<'a> : Linear<Ypre, Codomain=Self::PreadjointCodomain> 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<X> (PhantomData<X>); +pub struct IdOp<X : AXPY> (PhantomData<X>); -impl<X> IdOp<X> { +impl<X : AXPY> IdOp<X> { pub fn new() -> IdOp<X> { IdOp(PhantomData) } } -impl<X : Clone + Space> Mapping<X> for IdOp<X> { +impl<X : Clone + AXPY> Mapping<X> for IdOp<X> { type Codomain = X; fn apply<I : Instance<X>>(&self, x : I) -> X { @@ -133,32 +145,29 @@ } } -impl<X : Clone + Space> Linear<X> for IdOp<X> -{ } +impl<X : Clone + AXPY> Linear<X> for IdOp<X> { + type LinCodomain = X; +} #[replace_float_literals(F::cast_from(literal))] -impl<F : Num, X, Y> GEMV<F, X, Y> for IdOp<X> -where - Y : AXPY<F, X>, - X : Clone + Space -{ +impl<X : Clone + AXPY> GEMV<X> for IdOp<X> { // Computes `y = αAx + βy`, where `A` is `Self`. - fn gemv<I : Instance<X>>(&self, y : &mut Y, α : F, x : I, β : F) { + fn gemv<I : Instance<X>>(&self, y : &mut Self::LinCodomain, α : X::Field, x : I, β : X::Field) { y.axpy(α, x, β) } - fn apply_mut<I : Instance<X>>(&self, y : &mut Y, x : I){ + fn apply_mut<I : Instance<X>>(&self, y : &mut X, x : I){ y.copy_from(x); } } -impl<F, X, E> BoundedLinear<X, E, E, F> for IdOp<X> +impl<X, E> BoundedLinear<X, E, E> for IdOp<X> where - X : Space + Clone + Norm<F, E>, - F : Num, + X : AXPY + Clone + Norm<F, E>, + 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<X : Clone + HasDual<F, DualSpace=X>, F : Float> Adjointable<X, F> for IdOp<X> { @@ -168,7 +177,7 @@ fn adjoint(&self) -> Self::Adjoint<'_> { IdOp::new() } } -impl<X : Clone + Space> Preadjointable<X, X> for IdOp<X> { +impl<X : Clone + AXPY> Preadjointable<X, X> for IdOp<X> { type PreadjointCodomain = X; type Preadjoint<'a> = IdOp<X> where X : 'a; @@ -178,10 +187,12 @@ impl<S, T, E, X> Linear<X> for Composition<S, T, E> where - X : Space, + X : AXPY, T : Linear<X>, - S : Linear<T::Codomain> -{ } + S : Linear<T::LinCodomain> +{ + type LinCodomain = S::LinCodomain; +} impl<F, S, T, E, X, Y> GEMV<F, X, Y> for Composition<S, T, E> where @@ -227,17 +238,16 @@ use std::ops::Add; -impl<A, B, S, T> Mapping<Pair<A, B>> for RowOp<S, T> +impl<A, B, S, T, Y> Mapping<Pair<A, B>> for RowOp<S, T> where A : Space, B : Space, + Y : Space, S : Mapping<A>, T : Mapping<B>, - S::Codomain : Add<T::Codomain>, - <S::Codomain as Add<T::Codomain>>::Output : Space, - + S::Codomain : Add<T::Codomain, Output = Y>, { - type Codomain = <S::Codomain as Add<T::Codomain>>::Output; + type Codomain = Y; fn apply<I : Instance<Pair<A, B>>>(&self, x : I) -> Self::Codomain { let Pair(a, b) = x.decompose(); @@ -245,25 +255,30 @@ } } -impl<A, B, S, T> Linear<Pair<A, B>> for RowOp<S, T> +impl<F, A, B, S, T, Y> Linear<Pair<A, B>> for RowOp<S, T> where - A : Space, - B : Space, + F : Num + AXPY, + A : AXPY<Field=F>, + B : AXPY<Field=F>, + Y : AXPY, S : Linear<A>, T : Linear<B>, - S::Codomain : Add<T::Codomain>, - <S::Codomain as Add<T::Codomain>>::Output : Space, -{ } + S::LinCodomain : Add<T::LinCodomain, Output=Y>, +{ + type LinCodomain = Y; +} -impl<'b, F, S, T, Y, U, V> GEMV<F, Pair<U, V>, Y> for RowOp<S, T> +impl<'b, F, S, T, Y, G, U, V> GEMV<Pair<U, V>> for RowOp<S, T> where - U : Space, - V : Space, - S : GEMV<F, U, Y>, - T : GEMV<F, V, Y>, - F : Num, - Self : Linear<Pair<U, V>, Codomain=Y> + U : AXPY<Field=G>, + V : AXPY<Field=G>, + Y : AXPY<Field=F>, + S : GEMV<U>, + T : GEMV<V>, + F : Num + AXPY, + G : Num + AXPY, + S::LinCodomain : Add<T::LinCodomain, Output=Y>, { fn gemv<I : Instance<Pair<U, V>>>(&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<S, T>(pub S, pub T); -impl<A, S, T> Mapping<A> for ColOp<S, T> +impl<X, S, T> Mapping<X> for ColOp<S, T> where - A : Space, - S : Mapping<A>, - T : Mapping<A>, + X : AXPY, + S : Mapping<X>, + T : Mapping<X>, { type Codomain = Pair<S::Codomain, T::Codomain>; - fn apply<I : Instance<A>>(&self, a : I) -> Self::Codomain { + fn apply<I : Instance<X>>(&self, a : I) -> Self::Codomain { Pair(self.0.apply(a.ref_instance()), self.1.apply(a)) } } -impl<A, S, T> Linear<A> for ColOp<S, T> +impl<X, S, T, A, B, F> Linear<X> for ColOp<S, T> where - A : Space, - S : Mapping<A>, - T : Mapping<A>, -{ } + F : Num + AXPY, + X : AXPY, + S : Linear<X, LinCodomain=A>, + T : Linear<X, LinCodomain=B>, + A : AXPY<Field=F>, + B : AXPY<Field=F>, +{ + type LinCodomain = Pair<S::LinCodomain, T::LinCodomain>; +} -impl<F, S, T, A, B, X> GEMV<F, X, Pair<A, B>> for ColOp<S, T> +impl<F, S, T, A, B, X> GEMV<X> for ColOp<S, T> where - X : Space, - S : GEMV<F, X, A>, - T : GEMV<F, X, B>, - F : Num, - Self : Linear<X, Codomain=Pair<A, B>> + F : Num + AXPY, + X : AXPY, + S : GEMV<X, LinCodomain=A>, + T : GEMV<X, LinCodomain=B>, + A : AXPY<Field=F>, + B : AXPY<Field=F>, { fn gemv<I : Instance<X>>(&self, y : &mut Pair<A, B>, α : F, x : I, β : F) { self.0.gemv(&mut y.0, α, x.ref_instance(), β); @@ -341,7 +362,7 @@ B : HasDual<F>, S : Adjointable<A, F>, T : Adjointable<B, F>, - Yʹ : Space, + Yʹ : AXPY, S :: Codomain : HasDual<F, DualSpace=Yʹ>, T :: Codomain : HasDual<F, DualSpace=Yʹ>, S::Codomain : Add<T::Codomain>, @@ -361,15 +382,15 @@ } } -impl<A, B, Yʹ, S, T> Preadjointable<Pair<A,B>, Yʹ> for RowOp<S, T> -where - A : Space, - B : Space, - Yʹ : Space, +impl<F, A, B, Y, Yʹ, S, T> Preadjointable<Pair<A,B>, Yʹ> for RowOp<S, T> +wherea + A : AXPY, + B : AXPY, + Yʹ : AXPY, S : Preadjointable<A, Yʹ>, T : Preadjointable<B, Yʹ>, S::Codomain : Add<T::Codomain>, - <S::Codomain as Add<T::Codomain>>::Output : Space, + <S::Codomain as Add<T::Codomain>>::Output : AXPY, //Self : Linear<Pair<A, B>, Codomain=Y>, //for<'a> ColOp<S::Preadjoint<'a>, T::Preadjoint<'a>> : Adjointable<Yʹ, F>, { @@ -407,10 +428,10 @@ impl<A, Aʹ, Xʹ, Yʹ, S, T> Preadjointable<A,Pair<Xʹ,Yʹ>> for ColOp<S, T> where - A : Space, - Aʹ : Space, - Xʹ : Space, - Yʹ : Space, + A : AXPY, + Aʹ : AXPY, + Xʹ : AXPY, + Yʹ : AXPY, S : Preadjointable<A, Xʹ, PreadjointCodomain=Aʹ>, T : Preadjointable<A, Yʹ, PreadjointCodomain=Aʹ>, Aʹ : ClosedAdd, @@ -429,8 +450,8 @@ impl<A, B, S, T> Mapping<Pair<A, B>> for DiagOp<S, T> where - A : Space, - B : Space, + A : AXPY, + B : AXPY, S : Mapping<A>, T : Mapping<B>, { @@ -442,26 +463,32 @@ } } -impl<A, B, S, T> Linear<Pair<A, B>> for DiagOp<S, T> -where - A : Space, - B : Space, - S : Linear<A>, - T : Linear<B>, -{ } - -impl<F, S, T, A, B, U, V> GEMV<F, Pair<U, V>, Pair<A, B>> for DiagOp<S, T> +impl<F, A, B, S, T, G, U, V> Linear<Pair<A, B>> for DiagOp<S, T> where - A : Space, - B : Space, - U : Space, - V : Space, - S : GEMV<F, U, A>, - T : GEMV<F, V, B>, - F : Num, - Self : Linear<Pair<U, V>, Codomain=Pair<A, B>>, + F : Num + AXPY, + G : Num + AXPY, + A : AXPY<Field=F>, + B : AXPY<Field=F>, + U : AXPY<Field=G>, + V : AXPY<Field=G>, + S : Linear<A, LinCodomain=U>, + T : Linear<B, LinCodomain=V>, { - fn gemv<I : Instance<Pair<U, V>>>(&self, y : &mut Pair<A, B>, α : F, x : I, β : F) { + type LinCodomain = Pair<U, V>; +} + +impl<F, A, B, S, T, G, U, V> GEMV<Pair<U, V>> for DiagOp<S, T> +where + F : Num + AXPY, + G : Num + AXPY, + A : AXPY<Field=F>, + B : AXPY<Field=F>, + U : AXPY<Field=G>, + V : AXPY<Field=G>, + S : GEMV<A, LinCodomain=U>, + T : GEMV<B, LinCodomain=V>, +{ + fn gemv<I : Instance<Pair<U, V>>>(&self, y : &mut Pair<A, B>, α : 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<A, B, Xʹ, Yʹ, S, T> Preadjointable<Pair<A,B>, Pair<Xʹ,Yʹ>> for DiagOp<S, T> where - A : Space, - B : Space, - Xʹ : Space, - Yʹ : Space, + A : AXPY, + B : AXPY, + Xʹ : AXPY, + Yʹ : AXPY, S : Preadjointable<A, Xʹ>, T : Preadjointable<B, Yʹ>, { @@ -523,16 +550,16 @@ macro_rules! pairnorm { ($expj:ty) => { impl<F, A, B, S, T, ExpA, ExpB, ExpR> - BoundedLinear<Pair<A, B>, PairNorm<ExpA, ExpB, $expj>, ExpR, F> + BoundedLinear<Pair<A, B>, PairNorm<ExpA, ExpB, $expj>, ExpR> for RowOp<S, T> where F : Float, - A : Space + Norm<F, ExpA>, - B : Space + Norm<F, ExpB>, + A : AXPY + Norm<F, ExpA>, + B : AXPY + Norm<F, ExpB>, S : BoundedLinear<A, ExpA, ExpR, F>, T : BoundedLinear<B, ExpB, ExpR, F>, S::Codomain : Add<T::Codomain>, - <S::Codomain as Add<T::Codomain>>::Output : Space, + <S::Codomain as Add<T::Codomain>>::Output : AXPY, ExpA : NormExponent, ExpB : NormExponent, ExpR : NormExponent, @@ -541,7 +568,7 @@ &self, PairNorm(expa, expb, _) : PairNorm<ExpA, ExpB, $expj>, expr : ExpR - ) -> F { + ) -> <Pair<A, B> 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<F, A, S, T, ExpA, ExpS, ExpT> - BoundedLinear<A, ExpA, PairNorm<ExpS, ExpT, $expj>, F> + impl<A, S, T, ExpA, ExpS, ExpT> + BoundedLinear<A, ExpA, PairNorm<ExpS, ExpT, $expj>> for ColOp<S, T> where F : Float, - A : Space + Norm<F, ExpA>, + A : AXPY + Norm<F, ExpA>, S : BoundedLinear<A, ExpA, ExpS, F>, T : BoundedLinear<A, ExpA, ExpT, F>, ExpA : NormExponent, @@ -566,7 +593,7 @@ &self, expa : ExpA, PairNorm(exps, expt, _) : PairNorm<ExpS, ExpT, $expj> - ) -> 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);
--- a/src/loc.rs Sat Dec 21 14:27:14 2024 -0500 +++ b/src/loc.rs Fri Dec 20 16:14:17 2024 -0500 @@ -709,9 +709,12 @@ } } -impl<F : Num, const N : usize> Linear<Loc<F, N>> for Loc<F, N> { } +impl<F : Num + AXPY, const N : usize> Linear<Loc<F, N>> for Loc<F, N> { + type LinCodomain = F; +} -impl<F : Num, const N : usize> AXPY<F, Loc<F, N>> for Loc<F, N> { +impl<F : Num + AXPY, const N : usize> AXPY for Loc<F, N> { + type Field = F; #[inline] fn axpy<I : Instance<Loc<F, N>>>(&mut self, α : F, x : I, β : F) { x.eval(|x̃| {
--- a/src/nalgebra_support.rs Sat Dec 21 14:27:14 2024 -0500 +++ b/src/nalgebra_support.rs Fri Dec 20 16:14:17 2024 -0500 @@ -61,11 +61,13 @@ DefaultAllocator : Allocator<N,K>, DefaultAllocator : Allocator<M,K>, DefaultAllocator : Allocator<N,M>, - DefaultAllocator : Allocator<M,N> { + DefaultAllocator : Allocator<M,N> +{ + type LinCodomain = OMatrix<E,N,K>; } -impl<SM,SV1,SV2,N,M,K,E> GEMV<E, Matrix<E,M,K,SV1>, Matrix<E,N,K,SV2>> for Matrix<E,N,M,SM> -where SM: Storage<E,N,M>, SV1: Storage<E,M,K> + Clone, SV2: StorageMut<E,N,K>, +impl<SM,SV1,N,M,K,E> GEMV<Matrix<E,M,K,SV1>> for Matrix<E,N,M,SM> +where SM: Storage<E,N,M>, SV1: Storage<E,M,K> + Clone, N : Dim, M : Dim, K : Dim, E : Scalar + Zero + One + Float, DefaultAllocator : Allocator<N,K>, DefaultAllocator : Allocator<M,K>, @@ -85,18 +87,19 @@ } } -impl<SM,SV1,M,E> AXPY<E, Vector<E,M,SV1>> for Vector<E,M,SM> -where SM: StorageMut<E,M> + Clone, SV1: Storage<E,M> + Clone, +impl<SM,M,E> AXPY for Vector<E,M,SM> +where SM: StorageMut<E,M> + Clone, M : Dim, E : Scalar + Zero + One + Float, DefaultAllocator : Allocator<M> { + type Field = E; #[inline] - fn axpy<I : Instance<Vector<E,M,SV1>>>(&mut self, α : E, x : I, β : E) { + fn axpy<I : Instance<Vector<E,M,SM>>>(&mut self, α : E, x : I, β : E) { x.eval(|x̃| Matrix::axpy(self, α, x̃, β)) } #[inline] - fn copy_from<I : Instance<Vector<E,M,SV1>>>(&mut self, y : I) { + fn copy_from<I : Instance<Vector<E,M,SM>>>(&mut self, y : I) { y.eval(|ỹ| Matrix::copy_from(self, ỹ)) } }
--- a/src/norms.rs Sat Dec 21 14:27:14 2024 -0500 +++ b/src/norms.rs Fri Dec 20 16:14:17 2024 -0500 @@ -7,6 +7,7 @@ use crate::types::*; use crate::euclidean::*; use crate::mapping::{Mapping, Space, Instance}; +use crate::linops::AXPY; // // Abstract norms @@ -202,7 +203,7 @@ } } -pub trait Normed<F : Num = f64> : Space + Norm<F, Self::NormExp> { +pub trait Normed<F : Num = f64> : AXPY + Norm<F, Self::NormExp> { type NormExp : NormExponent; fn norm_exponent(&self) -> Self::NormExp;