diff -r f75bf34adda0 -r d2994e34a5f5 src/linops.rs --- a/src/linops.rs Sun May 11 02:03:45 2025 -0500 +++ b/src/linops.rs Mon May 12 15:42:48 2025 -0500 @@ -6,7 +6,7 @@ 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}; +use crate::norms::{HasDual, Linfinity, Norm, NormExponent, PairNorm, L1, L2}; use crate::types::*; use numeric_literals::replace_float_literals; use serde::Serialize; @@ -26,16 +26,19 @@ // } /// Efficient in-place summation. -#[replace_float_literals(F::cast_from(literal))] -pub trait AXPY: Space + std::ops::MulAssign +#[replace_float_literals(Self::Field::cast_from(literal))] +pub trait AXPY: Space + std::ops::MulAssign where - F: Num, X: Space, { - type Owned: AXPY; + type Field: Num; + type Owned: AXPY; + // type OriginGen<'a>: OriginGenerator + // where + // Self: 'a; /// 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) { @@ -43,15 +46,25 @@ } /// 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) } /// Return a similar zero as `self`. fn similar_origin(&self) -> Self::Owned; + // { + // self.make_origin_generator().make_origin() + // } + + /// Return a similar zero as `x`. + fn similar_origin_inst>(x: I) -> Self::Owned { + x.eval(|xr| xr.similar_origin()) + } /// Set self to zero. fn set_zero(&mut self); + + //fn make_origin_generator(&self) -> Self::OriginGen<'_>; } /// Efficient in-place application for [`Linear`] operators. @@ -167,7 +180,7 @@ #[replace_float_literals(F::cast_from(literal))] impl GEMV for IdOp where - Y: AXPY, + Y: AXPY, X: Clone + Space, { // Computes `y = αAx + βy`, where `A` is `Self`. @@ -215,42 +228,29 @@ } } -/// The zero operator +/// The zero operator from a space to itself #[derive(Clone, Copy, Debug, Serialize, Eq, PartialEq)] -pub struct ZeroOp<'a, X, XD, Y, F> { - zero: &'a Y, // TODO: don't pass this in `new`; maybe not even store. - dual_or_predual_zero: XD, - _phantoms: PhantomData<(X, Y, F)>, -} +pub struct SimpleZeroOp; -// TODO: Need to make Zero in Instance. +impl Mapping for SimpleZeroOp +where + X: AXPY + Instance, +{ + type Codomain = X::Owned; -impl<'a, F: Num, X: Space, XD, Y: Space + Clone> ZeroOp<'a, X, XD, Y, F> { - pub fn new(zero: &'a Y, dual_or_predual_zero: XD) -> Self { - ZeroOp { - zero, - dual_or_predual_zero, - _phantoms: PhantomData, - } + fn apply>(&self, x: I) -> X::Owned { + X::similar_origin_inst(x) } } -impl<'a, F: Num, X: Space, XD, Y: AXPY + Clone> Mapping for ZeroOp<'a, X, XD, Y, F> { - type Codomain = Y; - - fn apply>(&self, _x: I) -> Y { - self.zero.clone() - } -} - -impl<'a, F: Num, X: Space, XD, Y: AXPY + Clone> Linear for ZeroOp<'a, X, XD, Y, F> {} +impl Linear for SimpleZeroOp where X: AXPY + Instance {} #[replace_float_literals(F::cast_from(literal))] -impl<'a, F, X, XD, Y> GEMV for ZeroOp<'a, X, XD, Y, F> +impl GEMV for SimpleZeroOp where F: Num, - Y: AXPY + Clone, - X: Space, + Y: AXPY, + X: AXPY + Instance, { // Computes `y = αAx + βy`, where `A` is `Self`. fn gemv>(&self, y: &mut Y, _α: F, _x: I, β: F) { @@ -262,11 +262,10 @@ } } -impl<'a, F, X, XD, Y, E1, E2> BoundedLinear for ZeroOp<'a, X, XD, Y, F> +impl BoundedLinear for SimpleZeroOp where - X: Space + Norm, - Y: AXPY + Clone + Norm, F: Num, + X: AXPY + Instance + Norm, E1: NormExponent, E2: NormExponent, { @@ -275,43 +274,184 @@ } } -impl<'a, F: Num, X, XD, Y, Yprime: Space> Adjointable for ZeroOp<'a, X, XD, Y, F> +impl Adjointable for SimpleZeroOp where - X: Space, - Y: AXPY + Clone + 'static, - XD: AXPY + Clone + 'static, + F: Num, + X: AXPY + Instance + HasDual, + X::DualSpace: AXPY, { - type AdjointCodomain = XD; + type AdjointCodomain = X::DualSpace; type Adjoint<'b> - = ZeroOp<'b, Yprime, (), XD, F> + = SimpleZeroOp where Self: 'b; // () means not (pre)adjointable. fn adjoint(&self) -> Self::Adjoint<'_> { - ZeroOp::new(&self.dual_or_predual_zero, ()) + SimpleZeroOp + } +} + +/* +/// Trait for forming origins (zeroes) in vector spaces +pub trait OriginGenerator { + fn make_origin(&self) -> X; +} + +/// Origin generator for statically sized Euclidean spaces. +pub struct StaticEuclideanOriginGenerator; + +impl OriginGenerator for StaticEuclideanOriginGenerator +where + X: StaticEuclidean + Euclidean, +{ + #[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 for SimilarOriginGenerator<'a, X> +where + X: AXPY, +{ + #[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 { + y_origin: YOrigin, + xd_origin: XDOrigin, + _phantoms: PhantomData<(X, Y, F)>, +} + +// TODO: Need to make Zero in Instance. + +impl ZeroOp +where + F: Num, + YOrigin: OriginGenerator, +{ + pub fn new(y_origin: YOrigin, xd_origin: XDOrigin) -> Self { + ZeroOp { + y_origin, + xd_origin, + _phantoms: PhantomData, + } } } -impl<'a, F, X, XD, Y, Ypre> Preadjointable for ZeroOp<'a, X, XD, Y, F> +impl Mapping for ZeroOp +where + F: Num, + YOrigin: OriginGenerator, + Y: Space, + X: Space + Instance, +{ + type Codomain = Y; + + fn apply>(&self, _: I) -> Y { + self.y_origin.make_origin() + } +} + +impl Linear for ZeroOp +where + F: Num, + YOrigin: OriginGenerator, + Y: Space, + X: Space + Instance, +{ +} + +#[replace_float_literals(F::cast_from(literal))] +impl GEMV for ZeroOp where F: Num, - X: Space, - Y: AXPY + Clone, - Ypre: Space, - XD: AXPY + Clone + 'static, + YOrigin: OriginGenerator, + Y: Space + AXPY, + X: Space + Instance, { - type PreadjointCodomain = XD; - type Preadjoint<'b> - = ZeroOp<'b, Ypre, (), XD, F> + // Computes `y = αAx + βy`, where `A` is `Self`. + fn gemv>(&self, y: &mut Y, _α: F, _x: I, β: F) { + *y *= β; + } + + fn apply_mut>(&self, y: &mut Y, _x: I) { + y.set_zero(); + } +} + +impl BoundedLinear + for ZeroOp +where + F: Num, + YOrigin: OriginGenerator, + Y: Space + AXPY + Norm, + X: Space + Instance + Norm, + E1: NormExponent, + E2: NormExponent, +{ + fn opnorm_bound(&self, _xexp: E1, _codexp: E2) -> DynResult { + Ok(F::ZERO) + } +} + +impl Adjointable + for ZeroOp +where + F: Num, + YOrigin: OriginGenerator, + Y: Space + AXPY, + X: Space + Instance + HasDual, + XDOrigin: OriginGenerator + Clone, + Yprime: Space + Instance, +{ + type AdjointCodomain = X::DualSpace; + type Adjoint<'b> + = ZeroOp where Self: 'b; // () means not (pre)adjointable. - fn preadjoint(&self) -> Self::Preadjoint<'_> { - ZeroOp::new(&self.dual_or_predual_zero, ()) + fn adjoint(&self) -> Self::Adjoint<'_> { + ZeroOp::new(self.xd_origin.clone(), NotAnOriginGenerator) } } +*/ + +/* +impl Preadjointable + for ZeroOp +where + F: Num, + YOrigin: OriginGenerator, + Y: Space + AXPY, + X: Space + Instance + HasDual, + XDOrigin: OriginGenerator + Clone, + Ypre: Space + Instance + HasDual, +{ + type PreadjointCodomain = Xpre; + type Preadjoint<'b> + = ZeroOp + where + Self: 'b; + // () means not (pre)adjointable. + + fn adjoint(&self) -> Self::Adjoint<'_> { + ZeroOp::new(self.xpre_origin.clone(), NotAnOriginGenerator) + } +} +*/ impl Linear for Composition where