Simplify ZeroOp to SimpleZeroOp, only from X to X. Add Prox for ZeroIndicator. Move F parameter to AXPY::Field. dev

Mon, 12 May 2025 15:42:48 -0500

author
Tuomo Valkonen <tuomov@iki.fi>
date
Mon, 12 May 2025 15:42:48 -0500
branch
dev
changeset 129
d2994e34a5f5
parent 128
f75bf34adda0
child 130
0a689881b0f1

Simplify ZeroOp to SimpleZeroOp, only from X to X. Add Prox for ZeroIndicator. Move F parameter to AXPY::Field.

src/convex.rs file | annotate | diff | comparison | revisions
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
--- a/src/convex.rs	Sun May 11 02:03:45 2025 -0500
+++ b/src/convex.rs	Mon May 12 15:42:48 2025 -0500
@@ -5,7 +5,7 @@
 use crate::error::DynResult;
 use crate::euclidean::Euclidean;
 use crate::instance::{DecompositionMut, Instance, InstanceMut};
-use crate::linops::{IdOp, Scaled};
+use crate::linops::{IdOp, Scaled, SimpleZeroOp, AXPY};
 use crate::mapping::{DifferentiableImpl, LipschitzDifferentiableImpl, Mapping, Space};
 use crate::norms::*;
 use crate::operator_arithmetic::{Constant, Weighted};
@@ -333,6 +333,22 @@
     }
 }
 
+impl<Domain, F> Prox<Domain> for ZeroIndicator<Domain, F>
+where
+    Domain: AXPY<Field = F, Owned = Domain> + Normed<F>,
+    F: Float,
+{
+    type Prox<'a>
+        = SimpleZeroOp
+    where
+        Self: 'a;
+
+    /// Returns a proximal mapping with weight τ
+    fn prox_mapping(&self, _τ: F) -> Self::Prox<'_> {
+        return SimpleZeroOp;
+    }
+}
+
 /// The squared Euclidean norm divided by two
 #[derive(Copy, Clone, Serialize, Deserialize)]
 pub struct Norm222<F: Float>(PhantomData<F>);
--- a/src/direct_product.rs	Sun May 11 02:03:45 2025 -0500
+++ b/src/direct_product.rs	Mon May 12 15:42:48 2025 -0500
@@ -244,13 +244,13 @@
 impl_binary_mut!(SubAssign, sub_assign);
 
 macro_rules! impl_scalar_mut {
-    ($trait:ident, $fn:ident, $F:ty) => {
-        impl<'a, A, B> $trait<$F> for Pair<A, B>
+    ($trait:ident, $fn:ident) => {
+        impl<'a, A, B, F: Num> $trait<F> for Pair<A, B>
         where
-            A: $trait<$F>,
-            B: $trait<$F>,
+            A: $trait<F>,
+            B: $trait<F>,
         {
-            fn $fn(&mut self, t: $F) {
+            fn $fn(&mut self, t: F) {
                 let Pair(ref mut a, ref mut b) = self;
                 a.$fn(t);
                 b.$fn(t);
@@ -259,10 +259,8 @@
     };
 }
 
-impl_scalar_mut!(MulAssign, mul_assign, f32);
-impl_scalar_mut!(MulAssign, mul_assign, f64);
-impl_scalar_mut!(DivAssign, div_assign, f32);
-impl_scalar_mut!(DivAssign, div_assign, f64);
+impl_scalar_mut!(MulAssign, mul_assign);
+impl_scalar_mut!(DivAssign, div_assign);
 
 /// We only support 'closed' `Euclidean` `Pair`s, as more general ones cause
 /// compiler overflows.
@@ -303,17 +301,20 @@
     }
 }
 
-impl<F, A, B, U, V> AXPY<F, Pair<U, V>> for Pair<A, B>
+impl<F, A, B, U, V> AXPY<Pair<U, V>> for Pair<A, B>
 where
     U: Space,
     V: Space,
-    A: AXPY<F, U>,
-    B: AXPY<F, V>,
+    A: AXPY<U, Field = F>,
+    B: AXPY<V, Field = F>,
     F: Num,
     Self: MulAssign<F>,
     Pair<A, B>: MulAssign<F>,
-    Pair<A::Owned, B::Owned>: AXPY<F, Pair<U, V>>,
+    //A::Owned: MulAssign<F>,
+    //B::Owned: MulAssign<F>,
+    //Pair<A::Owned, B::Owned>: AXPY<Pair<U, V>, Field = F>,
 {
+    type Field = F;
     type Owned = Pair<A::Owned, B::Owned>;
 
     fn axpy<I: Instance<Pair<U, V>>>(&mut self, α: F, x: I, β: F) {
--- 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
--- a/src/loc.rs	Sun May 11 02:03:45 2025 -0500
+++ b/src/loc.rs	Mon May 12 15:42:48 2025 -0500
@@ -710,7 +710,8 @@
 
 impl<F: Float, const N: usize> Linear<Loc<N, F>> for Loc<N, F> {}
 
-impl<F: Float, const N: usize> AXPY<F, Loc<N, F>> for Loc<N, F> {
+impl<F: Float, const N: usize> AXPY<Loc<N, F>> for Loc<N, F> {
+    type Field = F;
     type Owned = Self;
 
     #[inline]
@@ -729,12 +730,22 @@
         x.eval(|x̃| map2_mut(self, x̃, |yi, xi| *yi = *xi))
     }
 
+    // #[inline]
+    // fn make_origin_generator(&self) -> StaticEuclideanOriginGenerator {
+    //     StaticEuclideanOriginGenerator
+    // }
+
     #[inline]
     fn similar_origin(&self) -> Self::Owned {
         Self::ORIGIN
     }
 
     #[inline]
+    fn similar_origin_inst<I: Instance<Self>>(_: I) -> Self::Owned {
+        Self::ORIGIN
+    }
+
+    #[inline]
     fn set_zero(&mut self) {
         *self = Self::ORIGIN;
     }
--- a/src/nalgebra_support.rs	Sun May 11 02:03:45 2025 -0500
+++ b/src/nalgebra_support.rs	Mon May 12 15:42:48 2025 -0500
@@ -103,7 +103,7 @@
     }
 }
 
-impl<SM, SV1, M, E> AXPY<E, Vector<E, M, SV1>> for Vector<E, M, SM>
+impl<SM, SV1, M, E> AXPY<Vector<E, M, SV1>> for Vector<E, M, SM>
 where
     SM: StorageMut<E, M> + Clone,
     SV1: Storage<E, M> + Clone,
@@ -111,6 +111,7 @@
     E: Scalar + Zero + One + Float,
     DefaultAllocator: Allocator<M>,
 {
+    type Field = E;
     type Owned = OVector<E, M>;
 
     #[inline]

mercurial