--- a/src/linops.rs Tue May 13 00:24:51 2025 -0500 +++ b/src/linops.rs Tue May 13 08:44:41 2025 -0500 @@ -47,9 +47,6 @@ { type Field: Num; type Owned: AXPY<X, Field = Self::Field>; - // type OriginGen<'a>: OriginGenerator<Self::Owned, F> - // where - // Self: 'a; /// Computes `y = βy + αx`, where `y` is `Self`. fn axpy<I: Instance<X>>(&mut self, α: Self::Field, x: I, β: Self::Field); @@ -306,57 +303,123 @@ } } +pub trait OriginGenerator<Y: AXPY> { + type Ref<'b>: OriginGenerator<Y> + where + Self: 'b; + + fn origin(&self) -> Y::Owned; + fn as_ref(&self) -> Self::Ref<'_>; +} + +impl<Y: AXPY> OriginGenerator<Y> for Y { + type Ref<'b> + = &'b Y + where + Self: 'b; + + #[inline] + fn origin(&self) -> Y::Owned { + return self.similar_origin(); + } + + #[inline] + fn as_ref(&self) -> Self::Ref<'_> { + self + } +} + +impl<'b, Y: AXPY> OriginGenerator<Y> for &'b Y { + type Ref<'c> + = Self + where + Self: 'c; + + #[inline] + fn origin(&self) -> Y::Owned { + return self.similar_origin(); + } + + #[inline] + fn as_ref(&self) -> Self::Ref<'_> { + self + } +} + /// A zero operator that can be eitherh dualised or predualised (once). /// This is achieved by storing an oppropriate zero. -pub struct ZeroOp<X, Y: AXPY<Field = F>, C, O, F: Float = f64> { - codomain_sample: C, - other_sample: O, +pub struct ZeroOp<X, Y: AXPY<Field = F>, OY: OriginGenerator<Y>, O, F: Float = f64> { + codomain_origin_generator: OY, + other_origin_generator: O, _phantoms: PhantomData<(X, Y, F)>, } -impl<'b, X, Y, F> ZeroOp<X, Y, &'b Y, &'b X::DualSpace, F> +impl<X, Y, OY, F> ZeroOp<X, Y, OY, (), F> where - X: HasDual<F>, - Y: HasDual<F>, - Y::Owned: Clone, + OY: OriginGenerator<Y>, + X: AXPY<Field = F>, + Y: AXPY<Field = F>, F: Float, { - pub fn new_dualisable(x_dual_sample: &'b X::DualSpace, y_sample: &'b Y) -> Self { + pub fn new(y_og: OY) -> Self { ZeroOp { - codomain_sample: y_sample, - other_sample: x_dual_sample, + codomain_origin_generator: y_og, + other_origin_generator: (), _phantoms: PhantomData, } } } -impl<'b, X, Y, O, F> Mapping<X> for ZeroOp<X, Y, &'b Y, O, F> +impl<X, Y, OY, OXprime, Xprime, Yprime, F> ZeroOp<X, Y, OY, OXprime, F> +where + OY: OriginGenerator<Y>, + OXprime: OriginGenerator<Xprime>, + X: HasDual<F, DualSpace = Xprime>, + Y: HasDual<F, DualSpace = Yprime>, + F: Float, + Xprime: AXPY<Field = F, Owned = Xprime>, + Xprime::Owned: AXPY<Field = F>, + Yprime: Space + Instance<Yprime>, +{ + pub fn new_dualisable(y_og: OY, xprime_og: OXprime) -> Self { + ZeroOp { + codomain_origin_generator: y_og, + other_origin_generator: xprime_og, + _phantoms: PhantomData, + } + } +} + +impl<X, Y, O, OY, F> Mapping<X> for ZeroOp<X, Y, OY, O, F> where X: Space + Instance<X>, Y: AXPY<Field = F>, F: Float, + OY: OriginGenerator<Y>, { type Codomain = Y::Owned; fn apply<I: Instance<X>>(&self, _x: I) -> Y::Owned { - self.codomain_sample.similar_origin() + self.codomain_origin_generator.origin() } } -impl<'b, X, Y, O, F> Linear<X> for ZeroOp<X, Y, &'b Y, O, F> +impl<X, Y, OY, O, F> Linear<X> for ZeroOp<X, Y, OY, O, F> where X: Space + Instance<X>, Y: AXPY<Field = F>, F: Float, + OY: OriginGenerator<Y>, { } #[replace_float_literals(F::cast_from(literal))] -impl<'b, X, Y, O, F> GEMV<F, X, Y> for ZeroOp<X, Y, &'b Y, O, F> +impl<X, Y, OY, O, F> GEMV<F, X, Y> for ZeroOp<X, Y, OY, O, F> where X: Space + Instance<X>, - Y: AXPY<Field = F>, + Y: AXPY<Field = F, Owned = Y>, F: Float, + OY: OriginGenerator<Y>, { // Computes `y = αAx + βy`, where `A` is `Self`. fn gemv<I: Instance<X>>(&self, y: &mut Y, _α: F, _x: I, β: F) { @@ -368,7 +431,7 @@ } } -impl<'b, X, Y, O, F, E1, E2> BoundedLinear<X, E1, E2, F> for ZeroOp<X, Y, &'b Y, O, F> +impl<X, Y, OY, O, F, E1, E2> BoundedLinear<X, E1, E2, F> for ZeroOp<X, Y, OY, O, F> where X: Space + Instance<X> + Norm<E1, F>, Y: AXPY<Field = F>, @@ -376,13 +439,15 @@ F: Float, E1: NormExponent, E2: NormExponent, + OY: OriginGenerator<Y>, { fn opnorm_bound(&self, _xexp: E1, _codexp: E2) -> DynResult<F> { Ok(F::ZERO) } } -impl<'b, X, Y, Xprime, Yprime, F> Adjointable<X, Yprime> for ZeroOp<X, Y, &'b Y, &'b Xprime, F> +impl<'b, X, Y, OY, OXprime, Xprime, Yprime, F> Adjointable<X, Yprime> + for ZeroOp<X, Y, OY, OXprime, F> where X: HasDual<F, DualSpace = Xprime>, Y: HasDual<F, DualSpace = Yprime>, @@ -390,184 +455,25 @@ Xprime: AXPY<Field = F, Owned = Xprime>, Xprime::Owned: AXPY<Field = F>, Yprime: Space + Instance<Yprime>, + OY: OriginGenerator<Y>, + OXprime: OriginGenerator<X::DualSpace>, { type AdjointCodomain = Xprime; type Adjoint<'c> - = ZeroOp<Yprime, Xprime, &'b Xprime, (), F> + = ZeroOp<Yprime, Xprime, OXprime::Ref<'c>, (), F> where Self: 'c; // () means not (pre)adjointable. fn adjoint(&self) -> Self::Adjoint<'_> { ZeroOp { - codomain_sample: self.other_sample, - other_sample: (), - _phantoms: PhantomData, - } - } -} - -/* -/// Trait for forming origins (zeroes) in vector spaces -pub trait OriginGenerator<X, F: Num = f64> { - fn make_origin(&self) -> X; -} - -/// Origin generator for statically sized Euclidean spaces. -pub struct StaticEuclideanOriginGenerator; - -impl<X, F: Float> OriginGenerator<X, F> for StaticEuclideanOriginGenerator -where - X: StaticEuclidean<F> + Euclidean<F, Output = X>, -{ - #[inline] - fn make_origin(&self) -> X { - X::origin() - } -} - -/// Origin generator arbitrary spaces that implement [`AXPY`], based on a sample vector. -pub struct SimilarOriginGenerator<'a, X>(&'a X); - -impl<'a, X, F: Float> OriginGenerator<X, F> for SimilarOriginGenerator<'a, X> -where - X: AXPY<F, Owned = X>, -{ - #[inline] - fn make_origin(&self) -> X { - self.0.similar_origin() - } -} - -pub struct NotAnOriginGenerator; - -/// The zero operator -#[derive(Clone, Copy, Debug, Serialize, Eq, PartialEq)] -pub struct ZeroOp<X, Y, YOrigin, XDOrigin = NotAnOriginGenerator, F: Num = f64> { - y_origin: YOrigin, - xd_origin: XDOrigin, - _phantoms: PhantomData<(X, Y, F)>, -} - -// TODO: Need to make Zero in Instance. - -impl<X, Y, F, YOrigin, XDOrigin> ZeroOp<X, Y, YOrigin, XDOrigin, F> -where - F: Num, - YOrigin: OriginGenerator<Y, F>, -{ - pub fn new(y_origin: YOrigin, xd_origin: XDOrigin) -> Self { - ZeroOp { - y_origin, - xd_origin, + codomain_origin_generator: self.other_origin_generator.as_ref(), + other_origin_generator: (), _phantoms: PhantomData, } } } -impl<X, Y, F, YOrigin, XDOrigin> Mapping<X> for ZeroOp<X, Y, YOrigin, XDOrigin, F> -where - F: Num, - YOrigin: OriginGenerator<Y, F>, - Y: Space, - X: Space + Instance<X>, -{ - type Codomain = Y; - - fn apply<I: Instance<X>>(&self, _: I) -> Y { - self.y_origin.make_origin() - } -} - -impl<X, Y, F, YOrigin, XDOrigin> Linear<X> for ZeroOp<X, Y, YOrigin, XDOrigin, F> -where - F: Num, - YOrigin: OriginGenerator<Y, F>, - Y: Space, - X: Space + Instance<X>, -{ -} - -#[replace_float_literals(F::cast_from(literal))] -impl<X, Y, F, YOrigin, XDOrigin> GEMV<F, X, Y> for ZeroOp<X, Y, YOrigin, XDOrigin, F> -where - F: Num, - YOrigin: OriginGenerator<Y, F>, - Y: Space + AXPY<F>, - X: Space + Instance<X>, -{ - // Computes `y = αAx + βy`, where `A` is `Self`. - fn gemv<I: Instance<X>>(&self, y: &mut Y, _α: F, _x: I, β: F) { - *y *= β; - } - - fn apply_mut<I: Instance<X>>(&self, y: &mut Y, _x: I) { - y.set_zero(); - } -} - -impl<X, Y, F, YOrigin, XDOrigin, E1, E2> BoundedLinear<X, E1, E2, F> - for ZeroOp<X, Y, YOrigin, XDOrigin, F> -where - F: Num, - YOrigin: OriginGenerator<Y, F>, - Y: Space + AXPY<F> + Norm<E2, F>, - X: Space + Instance<X> + Norm<E1, F>, - E1: NormExponent, - E2: NormExponent, -{ - fn opnorm_bound(&self, _xexp: E1, _codexp: E2) -> DynResult<F> { - Ok(F::ZERO) - } -} - -impl<X, Y, Yprime, F, YOrigin, XDOrigin> Adjointable<X, Yprime> - for ZeroOp<X, Y, YOrigin, XDOrigin, F> -where - F: Num, - YOrigin: OriginGenerator<Y, F>, - Y: Space + AXPY<F>, - X: Space + Instance<X> + HasDual<F>, - XDOrigin: OriginGenerator<X::DualSpace, F> + Clone, - Yprime: Space + Instance<Yprime>, -{ - type AdjointCodomain = X::DualSpace; - type Adjoint<'b> - = ZeroOp<Yprime, X::DualSpace, XDOrigin, NotAnOriginGenerator, F> - where - Self: 'b; - // () means not (pre)adjointable. - - fn adjoint(&self) -> Self::Adjoint<'_> { - ZeroOp::new(self.xd_origin.clone(), NotAnOriginGenerator) - } -} -*/ - -/* -impl<X, Y, Ypre, Xpre, F, YOrigin, XDOrigin> Preadjointable<X, Ypre> - for ZeroOp<X, Y, YOrigin, XDOrigin, F> -where - F: Num, - YOrigin: OriginGenerator<Y, F>, - Y: Space + AXPY<F>, - X: Space + Instance<X> + HasDual<F>, - XDOrigin: OriginGenerator<Xpre, F> + Clone, - Ypre: Space + Instance<Ypre> + HasDual<DualSpace = X>, -{ - type PreadjointCodomain = Xpre; - type Preadjoint<'b> - = ZeroOp<Ypre, Xpre, XDOrigin, NotAnOriginGenerator, F> - where - Self: 'b; - // () means not (pre)adjointable. - - fn adjoint(&self) -> Self::Adjoint<'_> { - ZeroOp::new(self.xpre_origin.clone(), NotAnOriginGenerator) - } -} -*/ - impl<S, T, E, X> Linear<X> for Composition<S, T, E> where X: Space,