src/linops.rs

branch
dev
changeset 129
d2994e34a5f5
parent 124
6aa955ad8122
child 130
0a689881b0f1
--- 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<F, X = Self>: Space + std::ops::MulAssign<F>
+#[replace_float_literals(Self::Field::cast_from(literal))]
+pub trait AXPY<X = Self>: Space + std::ops::MulAssign<Self::Field>
 where
-    F: Num,
     X: Space,
 {
-    type Owned: AXPY<F, X>;
+    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, α: F, x: I, β: F);
+    fn axpy<I: Instance<X>>(&mut self, α: Self::Field, x: I, β: Self::Field);
 
     /// Copies `x` to `self`.
     fn copy_from<I: Instance<X>>(&mut self, x: I) {
@@ -43,15 +46,25 @@
     }
 
     /// Computes  `y = αx`, where `y` is `Self`.
-    fn scale_from<I: Instance<X>>(&mut self, α: F, x: I) {
+    fn scale_from<I: Instance<X>>(&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<I: Instance<Self>>(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<F: Num, X, Y> GEMV<F, X, Y> for IdOp<X>
 where
-    Y: AXPY<F, X>,
+    Y: AXPY<X, Field = F>,
     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<X> Mapping<X> for SimpleZeroOp
+where
+    X: AXPY + Instance<X>,
+{
+    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<I: Instance<X>>(&self, x: I) -> X::Owned {
+        X::similar_origin_inst(x)
     }
 }
 
-impl<'a, F: Num, X: Space, XD, Y: AXPY<F> + Clone> Mapping<X> for ZeroOp<'a, X, XD, Y, F> {
-    type Codomain = Y;
-
-    fn apply<I: Instance<X>>(&self, _x: I) -> Y {
-        self.zero.clone()
-    }
-}
-
-impl<'a, F: Num, X: Space, XD, Y: AXPY<F> + Clone> Linear<X> for ZeroOp<'a, X, XD, Y, F> {}
+impl<X> Linear<X> for SimpleZeroOp where X: AXPY + Instance<X> {}
 
 #[replace_float_literals(F::cast_from(literal))]
-impl<'a, F, X, XD, Y> GEMV<F, X, Y> for ZeroOp<'a, X, XD, Y, F>
+impl<X, Y, F> GEMV<F, X, Y> for SimpleZeroOp
 where
     F: Num,
-    Y: AXPY<F, Y> + Clone,
-    X: Space,
+    Y: AXPY<Field = F>,
+    X: AXPY<Field = F> + Instance<X>,
 {
     // Computes  `y = αAx + βy`, where `A` is `Self`.
     fn gemv<I: Instance<X>>(&self, y: &mut Y, _α: F, _x: I, β: F) {
@@ -262,11 +262,10 @@
     }
 }
 
-impl<'a, F, X, XD, Y, E1, E2> BoundedLinear<X, E1, E2, F> for ZeroOp<'a, X, XD, Y, F>
+impl<X, F, E1, E2> BoundedLinear<X, E1, E2, F> for SimpleZeroOp
 where
-    X: Space + Norm<E1, F>,
-    Y: AXPY<F> + Clone + Norm<E2, F>,
     F: Num,
+    X: AXPY<Field = F> + Instance<X> + Norm<E1, F>,
     E1: NormExponent,
     E2: NormExponent,
 {
@@ -275,43 +274,184 @@
     }
 }
 
-impl<'a, F: Num, X, XD, Y, Yprime: Space> Adjointable<X, Yprime> for ZeroOp<'a, X, XD, Y, F>
+impl<X, F> Adjointable<X, X::DualSpace> for SimpleZeroOp
 where
-    X: Space,
-    Y: AXPY<F> + Clone + 'static,
-    XD: AXPY<F> + Clone + 'static,
+    F: Num,
+    X: AXPY<Field = F> + Instance<X> + HasDual<F>,
+    X::DualSpace: AXPY<Owned = X::DualSpace>,
 {
-    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<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,
+            _phantoms: PhantomData,
+        }
     }
 }
 
-impl<'a, F, X, XD, Y, Ypre> Preadjointable<X, Ypre> for ZeroOp<'a, X, XD, Y, F>
+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,
-    X: Space,
-    Y: AXPY<F> + Clone,
-    Ypre: Space,
-    XD: AXPY<F> + Clone + 'static,
+    YOrigin: OriginGenerator<Y, F>,
+    Y: Space + AXPY<F>,
+    X: Space + Instance<X>,
 {
-    type PreadjointCodomain = XD;
-    type Preadjoint<'b>
-        = ZeroOp<'b, Ypre, (), XD, F>
+    // 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 preadjoint(&self) -> Self::Preadjoint<'_> {
-        ZeroOp::new(&self.dual_or_predual_zero, ())
+    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

mercurial