src/linops.rs

changeset 198
3868555d135c
parent 197
1f301affeae3
--- a/src/linops.rs	Sun Apr 27 20:29:43 2025 -0500
+++ b/src/linops.rs	Fri May 15 14:46:30 2026 -0500
@@ -2,81 +2,143 @@
 Abstract linear operators.
 */
 
-use numeric_literals::replace_float_literals;
-use std::marker::PhantomData;
-use serde::Serialize;
+use crate::direct_product::Pair;
+use crate::error::DynResult;
+use crate::euclidean::StaticEuclidean;
+use crate::instance::Instance;
+pub use crate::mapping::{ClosedSpace, Composition, DifferentiableImpl, Mapping, Space};
+use crate::norms::{HasDual, Linfinity, NormExponent, PairNorm, L1, L2};
 use crate::types::*;
-pub use crate::mapping::{Mapping, Space, Composition};
-use crate::direct_product::Pair;
-use crate::instance::Instance;
-use crate::norms::{NormExponent, PairNorm, L1, L2, Linfinity, Norm};
+use numeric_literals::replace_float_literals;
+use serde::Serialize;
+use std::marker::PhantomData;
+use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
 
 /// Trait for linear operators on `X`.
-pub trait Linear<X : Space> : Mapping<X>
-{ }
+pub trait Linear<X: Space>: Mapping<X> {}
+
+// impl<X: Space, A: Linear<X>> DifferentiableImpl<X> for A {
+//     type Derivative = <Self as Mapping<X>>::Codomain;
+
+//     /// Compute the differential of `self` at `x`, consuming the input.
+//     fn differential_impl<I: Instance<X>>(&self, x: I) -> Self::Derivative {
+//         self.apply(x)
+//     }
+// }
+
+/// Vector spaces
+#[replace_float_literals(Self::Field::cast_from(literal))]
+pub trait VectorSpace:
+    Space<Principal = Self::PrincipalV>
+    + Mul<Self::Field, Output = Self::PrincipalV>
+    + Div<Self::Field, Output = Self::PrincipalV>
+    + Add<Self, Output = Self::PrincipalV>
+    + Add<Self::PrincipalV, Output = Self::PrincipalV>
+    + Sub<Self, Output = Self::PrincipalV>
+    + Sub<Self::PrincipalV, Output = Self::PrincipalV>
+    + Neg
+    + for<'b> Add<&'b Self, Output = <Self as VectorSpace>::PrincipalV>
+    + for<'b> Sub<&'b Self, Output = <Self as VectorSpace>::PrincipalV>
+{
+    /// Underlying scalar field
+    type Field: Num;
+
+    /// Principal form of the space; always equal to [`Space::Principal`], but with
+    /// more traits guaranteed.
+    ///
+    /// `PrincipalV` is only assumed to be `AXPY` for itself, as [`AXPY`]
+    /// uses [`Instance`] to apply all other variants and avoid problems
+    /// of choosing multiple implementations of the trait.
+    type PrincipalV: ClosedSpace
+        + AXPY<
+            Self::PrincipalV,
+            Field = Self::Field,
+            PrincipalV = Self::PrincipalV,
+            OwnedVariant = Self::PrincipalV,
+            Principal = Self::PrincipalV,
+        >;
+
+    /// Return a similar zero as `self`.
+    fn similar_origin(&self) -> Self::PrincipalV;
+    // {
+    //     self.make_origin_generator().make_origin()
+    // }
+
+    /// Return a similar zero as `x`.
+    fn similar_origin_inst<I: Instance<Self>>(x: I) -> Self::PrincipalV {
+        x.eval(|xr| xr.similar_origin())
+    }
+}
 
 /// 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>:
+    VectorSpace
+    + MulAssign<Self::Field>
+    + DivAssign<Self::Field>
+    + AddAssign<Self>
+    + AddAssign<Self::PrincipalV>
+    + SubAssign<Self>
+    + SubAssign<Self::PrincipalV>
+    + for<'b> AddAssign<&'b Self>
+    + for<'b> SubAssign<&'b Self>
 where
-    F : Num,
-    X : Space,
+    X: Space,
 {
-    type Owned : AXPY<F, X>;
-
     /// 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) {
+    fn copy_from<I: Instance<X>>(&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<X>>(&mut self, α: Self::Field, x: I) {
         self.axpy(α, x, 0.0)
     }
 
-    /// Return a similar zero as `self`.
-    fn similar_origin(&self) -> Self::Owned;
-
     /// Set self to zero.
     fn set_zero(&mut self);
 }
 
+pub trait ClosedVectorSpace: Instance<Self> + VectorSpace<PrincipalV = Self> {}
+impl<X: Instance<X> + VectorSpace<PrincipalV = Self>> ClosedVectorSpace for X {}
+
 /// 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> {
+pub trait GEMV<F: Num, X: Space, Y = <Self as Mapping<X>>::Codomain>: 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 Y, α: F, x: I, β: F);
 
     #[inline]
     /// 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 Y, x: I) {
         self.gemv(y, 1.0, x, 0.0)
     }
 
     #[inline]
     /// 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 Y, 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, F = f64>: Linear<X>
 where
-    F : Num,
-    X : Space + Norm<F, XExp>,
-    XExp : NormExponent,
-    CodExp : NormExponent
+    F: Num,
+    X: Space,
+    XExp: NormExponent,
+    CodExp: NormExponent,
 {
     /// A bound on the operator norm $\|A\|$ for the linear operator $A$=`self`.
     /// 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;
+    ///
+    /// This may fail with an error if the bound is for some reason incalculable.
+    fn opnorm_bound(&self, xexp: XExp, codexp: CodExp) -> DynResult<F>;
 }
 
 // Linear operator application into mutable target. The [`AsRef`] bound
@@ -90,18 +152,45 @@
 }*/
 
 /// Trait for forming the adjoint operator of `Self`.
-pub trait Adjointable<X, Yʹ> : Linear<X>
+pub trait Adjointable<X, Yʹ>: Linear<X>
 where
-    X : Space,
-    Yʹ : Space,
+    X: Space,
+    Yʹ: Space,
 {
-    type AdjointCodomain : Space;
-    type Adjoint<'a> : Linear<Yʹ, Codomain=Self::AdjointCodomain> where Self : 'a;
+    /// Codomain of the adjoint operator.
+    type AdjointCodomain: ClosedSpace;
+    /// Type of the adjoint operator.
+    type Adjoint<'a>: Linear<Yʹ, Codomain = Self::AdjointCodomain>
+    where
+        Self: 'a;
 
     /// Form the adjoint operator of `self`.
     fn adjoint(&self) -> Self::Adjoint<'_>;
 }
 
+/// Variant of [`Adjointable`] where the adjoint does not depend on a lifetime parameter.
+/// This exists due to restrictions of Rust's type system: if `A :: Adjointable`, and we make
+/// further restrictions on the adjoint operator, through, e.g.
+/// ```
+/// for<'a> A::Adjoint<'a> : GEMV<F, Z, Y>,
+/// ```
+/// Then `'static` lifetime is forced on `X`. Having `A::SimpleAdjoint` not depend on `'a`
+/// avoids this, but makes it impossible for the adjoint to be just a light wrapper around the
+/// original operator.
+pub trait SimplyAdjointable<X, Yʹ>: Linear<X>
+where
+    X: Space,
+    Yʹ: Space,
+{
+    /// Codomain of the adjoint operator.
+    type AdjointCodomain: ClosedSpace;
+    /// Type of the adjoint operator.
+    type SimpleAdjoint: Linear<Yʹ, Codomain = Self::AdjointCodomain>;
+
+    /// Form the adjoint operator of `self`.
+    fn adjoint(&self) -> Self::SimpleAdjoint;
+}
+
 /// Trait for forming a preadjoint of an operator.
 ///
 /// For an operator $A$ this is an operator $A\_\*$
@@ -112,404 +201,667 @@
 /// We do not make additional restrictions on `Self::Preadjoint` (in particular, it
 /// does not have to be adjointable) to allow `X` to be a subspace yet the preadjoint
 /// have the full space as the codomain, etc.
-pub trait Preadjointable<X : Space, Ypre : Space> : Linear<X> {
-    type PreadjointCodomain : Space;
-    type Preadjoint<'a> : Linear<
-        Ypre, Codomain=Self::PreadjointCodomain
-    > where Self : 'a;
+pub trait Preadjointable<X: Space, Ypre: Space = <Self as Mapping<X>>::Codomain>:
+    Linear<X>
+{
+    type PreadjointCodomain: ClosedSpace;
+    type Preadjoint<'a>: Linear<Ypre, Codomain = Self::PreadjointCodomain>
+    where
+        Self: 'a;
 
     /// Form the adjoint operator of `self`.
     fn preadjoint(&self) -> Self::Preadjoint<'_>;
 }
 
-/// Adjointable operators $A: X → Y$ between reflexive spaces $X$ and $Y$.
-pub trait SimplyAdjointable<X : Space> : Adjointable<X,<Self as Mapping<X>>::Codomain> {}
-impl<'a,X : Space, T> SimplyAdjointable<X> for T
-where T : Adjointable<X,<Self as Mapping<X>>::Codomain> {}
-
 /// The identity operator
-#[derive(Clone,Copy,Debug,Serialize,Eq,PartialEq)]
-pub struct IdOp<X> (PhantomData<X>);
+#[derive(Clone, Copy, Debug, Serialize, Eq, PartialEq)]
+pub struct IdOp<X>(PhantomData<X>);
 
 impl<X> IdOp<X> {
-    pub fn new() -> IdOp<X> { IdOp(PhantomData) }
+    pub fn new() -> IdOp<X> {
+        IdOp(PhantomData)
+    }
 }
 
-impl<X : Clone + Space> Mapping<X> for IdOp<X> {
-    type Codomain = X;
+impl<X: Space> Mapping<X> for IdOp<X> {
+    type Codomain = X::Principal;
 
-    fn apply<I : Instance<X>>(&self, x : I) -> X {
+    fn apply<I: Instance<X>>(&self, x: I) -> Self::Codomain {
         x.own()
     }
 }
 
-impl<X : Clone + Space> Linear<X> for IdOp<X>
-{ }
+impl<X: Space> Linear<X> for IdOp<X> {}
 
 #[replace_float_literals(F::cast_from(literal))]
-impl<F : Num, X, Y> GEMV<F, X, Y> for IdOp<X>
+impl<F: Num, X, Y> GEMV<F, X, Y> for IdOp<X>
 where
-    Y : AXPY<F, X>,
-    X : Clone + Space
+    Y: AXPY<X, Field = F>,
+    X: Space,
 {
     // 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 Y, α: F, x: I, β: F) {
         y.axpy(α, x, β)
     }
 
-    fn apply_mut<I : Instance<X>>(&self, y : &mut Y, x : I){
+    fn apply_mut<I: Instance<X>>(&self, y: &mut Y, x: I) {
         y.copy_from(x);
     }
 }
 
 impl<F, X, E> BoundedLinear<X, E, E, F> for IdOp<X>
 where
-    X : Space + Clone + Norm<F, E>,
-    F : Num,
-    E : NormExponent
+    X: Space + Clone,
+    F: Num,
+    E: NormExponent,
 {
-    fn opnorm_bound(&self, _xexp : E, _codexp : E) -> F { F::ONE }
-}
-
-impl<X : Clone + Space> Adjointable<X,X> for IdOp<X> {
-    type AdjointCodomain=X;
-    type Adjoint<'a> = IdOp<X> where X : 'a;
-
-    fn adjoint(&self) -> Self::Adjoint<'_> { IdOp::new() }
+    fn opnorm_bound(&self, _xexp: E, _codexp: E) -> DynResult<F> {
+        Ok(F::ONE)
+    }
 }
 
-impl<X : Clone + Space> Preadjointable<X,X> for IdOp<X> {
-    type PreadjointCodomain=X;
-    type Preadjoint<'a> = IdOp<X> where X : 'a;
+impl<X: Clone + Space> Adjointable<X, X::Principal> for IdOp<X> {
+    type AdjointCodomain = X::Principal;
+    type Adjoint<'a>
+        = IdOp<X::Principal>
+    where
+        X: 'a;
 
-    fn preadjoint(&self) -> Self::Preadjoint<'_> { IdOp::new() }
+    fn adjoint(&self) -> Self::Adjoint<'_> {
+        IdOp::new()
+    }
 }
 
+impl<X: Clone + Space> SimplyAdjointable<X, X::Principal> for IdOp<X> {
+    type AdjointCodomain = X::Principal;
+    type SimpleAdjoint = IdOp<X::Principal>;
 
-/// The zero operator
-#[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)>,
-}
-
-// TODO: Need to make Zero in Instance.
-
-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 adjoint(&self) -> Self::SimpleAdjoint {
+        IdOp::new()
     }
 }
 
-impl<'a, F : Num, X : Space, XD, Y : AXPY<F> + Clone> Mapping<X> for ZeroOp<'a, X, XD, Y, F> {
-    type Codomain = Y;
+impl<X: Clone + Space> Preadjointable<X, X::Principal> for IdOp<X> {
+    type PreadjointCodomain = X::Principal;
+    type Preadjoint<'a>
+        = IdOp<X::Principal>
+    where
+        X: 'a;
 
-    fn apply<I : Instance<X>>(&self, _x : I) -> Y {
-        self.zero.clone()
+    fn preadjoint(&self) -> Self::Preadjoint<'_> {
+        IdOp::new()
     }
 }
 
-impl<'a, F : Num, X : Space, XD, Y : AXPY<F> + Clone> Linear<X> for ZeroOp<'a, X, XD, Y, F>
-{ }
+/// The zero operator from a space to itself
+#[derive(Clone, Copy, Debug, Serialize, Eq, PartialEq)]
+pub struct SimpleZeroOp;
+
+impl<X: VectorSpace> Mapping<X> for SimpleZeroOp {
+    type Codomain = X::PrincipalV;
+
+    fn apply<I: Instance<X>>(&self, x: I) -> X::PrincipalV {
+        X::similar_origin_inst(x)
+    }
+}
+
+impl<X: VectorSpace> Linear<X> for SimpleZeroOp {}
 
 #[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
+    F: Num,
+    Y: AXPY<Field = F>,
+    X: VectorSpace<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) {
+    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){
+    fn apply_mut<I: Instance<X>>(&self, y: &mut Y, _x: I) {
         y.set_zero();
     }
 }
 
-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
+    F: Num,
+    X: VectorSpace<Field = F>,
+    E1: NormExponent,
+    E2: NormExponent,
+{
+    fn opnorm_bound(&self, _xexp: E1, _codexp: E2) -> DynResult<F> {
+        Ok(F::ZERO)
+    }
+}
+
+impl<X, F> Adjointable<X, X::DualSpace> for SimpleZeroOp
 where
-    X : Space + Norm<F, E1>,
-    Y : AXPY<F> + Clone + Norm<F, E2>,
-    F : Num,
-    E1 : NormExponent,
-    E2 : NormExponent,
+    F: Num,
+    X: VectorSpace<Field = F> + HasDual<F>,
+    X::DualSpace: ClosedVectorSpace,
 {
-    fn opnorm_bound(&self, _xexp : E1, _codexp : E2) -> F { F::ZERO }
+    type AdjointCodomain = X::DualSpace;
+    type Adjoint<'b>
+        = SimpleZeroOp
+    where
+        Self: 'b;
+    // () means not (pre)adjointable.
+
+    fn adjoint(&self) -> Self::Adjoint<'_> {
+        SimpleZeroOp
+    }
+}
+
+pub trait OriginGenerator<Y: VectorSpace> {
+    type Ref<'b>: OriginGenerator<Y>
+    where
+        Self: 'b;
+
+    fn origin(&self) -> Y::PrincipalV;
+    fn as_ref(&self) -> Self::Ref<'_>;
 }
 
-impl<'a, F : Num, X, XD, Y, Yprime : Space> Adjointable<X, Yprime> for ZeroOp<'a, X, XD, Y, F>
-where
-     X : Space,
-     Y :  AXPY<F> + Clone + 'static,
-     XD : AXPY<F> + Clone + 'static,
+#[derive(Copy, Clone, Debug)]
+pub struct StaticEuclideanOriginGenerator;
+
+impl<Y: StaticEuclidean<F, Field = F>, F: Float> OriginGenerator<Y>
+    for StaticEuclideanOriginGenerator
 {
-    type AdjointCodomain = XD;
-    type Adjoint<'b> = ZeroOp<'b, Yprime, (), XD, F> where Self : 'b;
-        // () means not (pre)adjointable.
+    type Ref<'b>
+        = Self
+    where
+        Self: 'b;
+
+    #[inline]
+    fn origin(&self) -> Y::PrincipalV {
+        return Y::origin();
+    }
+
+    #[inline]
+    fn as_ref(&self) -> Self::Ref<'_> {
+        *self
+    }
+}
+
+impl<Y: VectorSpace> OriginGenerator<Y> for Y {
+    type Ref<'b>
+        = &'b Y
+    where
+        Self: 'b;
+
+    #[inline]
+    fn origin(&self) -> Y::PrincipalV {
+        return self.similar_origin();
+    }
+
+    #[inline]
+    fn as_ref(&self) -> Self::Ref<'_> {
+        self
+    }
+}
 
-    fn adjoint(&self) -> Self::Adjoint<'_> {
-        ZeroOp::new(&self.dual_or_predual_zero, ())
+impl<'b, Y: VectorSpace> OriginGenerator<Y> for &'b Y {
+    type Ref<'c>
+        = Self
+    where
+        Self: 'c;
+
+    #[inline]
+    fn origin(&self) -> Y::PrincipalV {
+        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: VectorSpace<Field = F>, OY: OriginGenerator<Y>, O, F: Float = f64> {
+    codomain_origin_generator: OY,
+    other_origin_generator: O,
+    _phantoms: PhantomData<(X, Y, F)>,
+}
+
+impl<X, Y, OY, F> ZeroOp<X, Y, OY, (), F>
+where
+    OY: OriginGenerator<Y>,
+    X: VectorSpace<Field = F>,
+    Y: VectorSpace<Field = F>,
+    F: Float,
+{
+    pub fn new(y_og: OY) -> Self {
+        ZeroOp {
+            codomain_origin_generator: y_og,
+            other_origin_generator: (),
+            _phantoms: PhantomData,
+        }
     }
 }
 
-impl<'a, F, X, XD, Y, Ypre> Preadjointable<X, Ypre> for ZeroOp<'a, X, XD, Y, 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: VectorSpace<Field = F, PrincipalV = Xprime>,
+    Xprime::PrincipalV: 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,
+    Y: VectorSpace<Field = F>,
+    F: Float,
+    OY: OriginGenerator<Y>,
+{
+    type Codomain = Y::PrincipalV;
+
+    fn apply<I: Instance<X>>(&self, _x: I) -> Y::PrincipalV {
+        self.codomain_origin_generator.origin()
+    }
+}
+
+impl<X, Y, OY, O, F> Linear<X> for ZeroOp<X, Y, OY, O, F>
+where
+    X: Space,
+    Y: VectorSpace<Field = F>,
+    F: Float,
+    OY: OriginGenerator<Y>,
+{
+}
+
+#[replace_float_literals(F::cast_from(literal))]
+impl<X, Y, OY, O, F> GEMV<F, X, Y> for ZeroOp<X, Y, OY, O, F>
+where
+    X: Space,
+    Y: AXPY<Field = F, PrincipalV = 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) {
+        *y *= β;
+    }
+
+    fn apply_mut<I: Instance<X>>(&self, y: &mut Y, _x: I) {
+        y.set_zero();
+    }
+}
+
+impl<X, Y, OY, O, F, E1, E2> BoundedLinear<X, E1, E2, F> for ZeroOp<X, Y, OY, O, F>
 where
-    F : Num,
-    X : Space,
-    Y : AXPY<F> + Clone,
-    Ypre : Space,
-    XD : AXPY<F> + Clone + 'static,
+    X: Space + Instance<X>,
+    Y: VectorSpace<Field = F>,
+    Y::PrincipalV: Clone,
+    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, 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>,
+    F: Float,
+    Xprime: ClosedVectorSpace<Field = F>,
+    //Xprime::Owned: AXPY<Field = F>,
+    Yprime: ClosedSpace,
+    OY: OriginGenerator<Y>,
+    OXprime: OriginGenerator<X::DualSpace>,
 {
-    type PreadjointCodomain = XD;
-    type Preadjoint<'b> = ZeroOp<'b, Ypre, (), XD, F> where Self : 'b;
-        // () means not (pre)adjointable.
+    type AdjointCodomain = Xprime;
+    type Adjoint<'c>
+        = ZeroOp<Yprime, Xprime, OXprime::Ref<'c>, (), F>
+    where
+        Self: 'c;
+    // () means not (pre)adjointable.
+
+    fn adjoint(&self) -> Self::Adjoint<'_> {
+        ZeroOp {
+            codomain_origin_generator: self.other_origin_generator.as_ref(),
+            other_origin_generator: (),
+            _phantoms: PhantomData,
+        }
+    }
+}
 
-    fn preadjoint(&self) -> Self::Preadjoint<'_> {
-        ZeroOp::new(&self.dual_or_predual_zero, ())
+impl<'b, X, Y, OY, OXprime, Xprime, Yprime, F> SimplyAdjointable<X, Yprime>
+    for ZeroOp<X, Y, OY, OXprime, F>
+where
+    X: HasDual<F, DualSpace = Xprime>,
+    Y: HasDual<F, DualSpace = Yprime>,
+    F: Float,
+    Xprime: ClosedVectorSpace<Field = F>,
+    //Xprime::Owned: AXPY<Field = F>,
+    Yprime: ClosedSpace,
+    OY: OriginGenerator<Y>,
+    OXprime: OriginGenerator<X::DualSpace> + Clone,
+{
+    type AdjointCodomain = Xprime;
+    type SimpleAdjoint = ZeroOp<Yprime, Xprime, OXprime, (), F>;
+    // () means not (pre)adjointable.
+
+    fn adjoint(&self) -> Self::SimpleAdjoint {
+        ZeroOp {
+            codomain_origin_generator: self.other_origin_generator.clone(),
+            other_origin_generator: (),
+            _phantoms: PhantomData,
+        }
     }
 }
 
 impl<S, T, E, X> Linear<X> for Composition<S, T, E>
 where
-    X : Space,
-    T : Linear<X>,
-    S : Linear<T::Codomain>
-{ }
+    X: Space,
+    T: Linear<X>,
+    S: Linear<T::Codomain>,
+{
+}
 
 impl<F, S, T, E, X, Y> GEMV<F, X, Y> for Composition<S, T, E>
 where
-    F : Num,
-    X : Space,
-    T : Linear<X>,
-    S : GEMV<F, T::Codomain, Y>,
+    F: Num,
+    X: Space,
+    T: Linear<X>,
+    S: GEMV<F, T::Codomain, Y>,
 {
-    fn gemv<I : Instance<X>>(&self, y : &mut Y, α : F, x : I, β : F) {
+    fn gemv<I: Instance<X>>(&self, y: &mut Y, α: F, x: I, β: F) {
         self.outer.gemv(y, α, self.inner.apply(x), β)
     }
 
     /// 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 Y, x: I) {
         self.outer.apply_mut(y, self.inner.apply(x))
     }
 
     /// 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 Y, x: I) {
         self.outer.apply_add(y, self.inner.apply(x))
     }
 }
 
 impl<F, S, T, X, Z, Xexp, Yexp, Zexp> BoundedLinear<X, Xexp, Yexp, F> for Composition<S, T, Zexp>
 where
-    F : Num,
-    X : Space + Norm<F, Xexp>,
-    Z : Space + Norm<F, Zexp>,
-    Xexp : NormExponent,
-    Yexp : NormExponent,
-    Zexp : NormExponent,
-    T : BoundedLinear<X, Xexp, Zexp, F, Codomain=Z>,
-    S : BoundedLinear<Z, Zexp, Yexp, F>,
+    F: Num,
+    X: Space,
+    Z: Space,
+    Xexp: NormExponent,
+    Yexp: NormExponent,
+    Zexp: NormExponent,
+    T: BoundedLinear<X, Xexp, Zexp, F, Codomain = Z>,
+    S: BoundedLinear<Z, Zexp, Yexp, F>,
 {
-    fn opnorm_bound(&self, xexp : Xexp, yexp : Yexp) -> F {
+    fn opnorm_bound(&self, xexp: Xexp, yexp: Yexp) -> DynResult<F> {
         let zexp = self.intermediate_norm_exponent;
-        self.outer.opnorm_bound(zexp, yexp) * self.inner.opnorm_bound(xexp, zexp)
+        Ok(self.outer.opnorm_bound(zexp, yexp)? * self.inner.opnorm_bound(xexp, zexp)?)
     }
 }
 
 /// “Row operator” $(S, T)$; $(S, T)(x, y)=Sx + Ty$.
+#[derive(Clone, Copy, Debug, Serialize, Eq, PartialEq)]
 pub struct RowOp<S, T>(pub S, pub T);
 
-use std::ops::Add;
-
 impl<A, B, S, T> Mapping<Pair<A, B>> for RowOp<S, T>
 where
-    A : Space,
-    B : Space,
-    S : Mapping<A>,
-    T : Mapping<B>,
-    S::Codomain : Add<T::Codomain>,
-    <S::Codomain as Add<T::Codomain>>::Output : Space,
-
+    A: Space,
+    B: Space,
+    S: Mapping<A>,
+    T: Mapping<B>,
+    S::Codomain: Add<T::Codomain>,
+    <S::Codomain as Add<T::Codomain>>::Output: ClosedSpace,
 {
     type Codomain = <S::Codomain as Add<T::Codomain>>::Output;
 
-    fn apply<I : Instance<Pair<A, B>>>(&self, x : I) -> Self::Codomain {
-        let Pair(a, b) = x.decompose();
-        self.0.apply(a) + self.1.apply(b)
+    fn apply<I: Instance<Pair<A, B>>>(&self, x: I) -> Self::Codomain {
+        x.eval_decompose(|Pair(a, b)| self.0.apply(a) + self.1.apply(b))
     }
 }
 
 impl<A, B, S, T> Linear<Pair<A, B>> for RowOp<S, T>
 where
-    A : Space,
-    B : Space,
-    S : Linear<A>,
-    T : Linear<B>,
-    S::Codomain : Add<T::Codomain>,
-    <S::Codomain as Add<T::Codomain>>::Output : Space,
-{ }
-
+    A: Space,
+    B: Space,
+    S: Linear<A>,
+    T: Linear<B>,
+    S::Codomain: Add<T::Codomain>,
+    <S::Codomain as Add<T::Codomain>>::Output: ClosedSpace,
+{
+}
 
 impl<'b, F, S, T, Y, U, V> GEMV<F, Pair<U, V>, Y> 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: Space,
+    V: Space,
+    S: GEMV<F, U, Y>,
+    T: GEMV<F, V, Y>,
+    F: Num,
+    Self: Linear<Pair<U, V>, Codomain = Y>,
 {
-    fn gemv<I : Instance<Pair<U, V>>>(&self, y : &mut Y, α : F, x : I, β : F) {
-        let Pair(u, v) = x.decompose();
-        self.0.gemv(y, α, u, β);
-        self.1.gemv(y, α, v, F::ONE);
+    fn gemv<I: Instance<Pair<U, V>>>(&self, y: &mut Y, α: F, x: I, β: F) {
+        x.eval_decompose(|Pair(u, v)| {
+            self.0.gemv(y, α, u, β);
+            self.1.gemv(y, α, v, F::ONE);
+        })
     }
 
-    fn apply_mut<I : Instance<Pair<U, V>>>(&self, y : &mut Y, x : I) {
-        let Pair(u, v) = x.decompose();
-        self.0.apply_mut(y, u);
-        self.1.apply_add(y, v);
+    fn apply_mut<I: Instance<Pair<U, V>>>(&self, y: &mut Y, x: I) {
+        x.eval_decompose(|Pair(u, v)| {
+            self.0.apply_mut(y, u);
+            self.1.apply_add(y, v);
+        })
     }
 
     /// Computes `y += Ax`, where `A` is `Self`
-    fn apply_add<I : Instance<Pair<U, V>>>(&self, y : &mut Y, x : I) {
-        let Pair(u, v) = x.decompose();
-        self.0.apply_add(y, u);
-        self.1.apply_add(y, v);
+    fn apply_add<I: Instance<Pair<U, V>>>(&self, y: &mut Y, x: I) {
+        x.eval_decompose(|Pair(u, v)| {
+            self.0.apply_add(y, u);
+            self.1.apply_add(y, v);
+        })
     }
 }
 
 /// “Column operator” $(S; T)$; $(S; T)x=(Sx, Tx)$.
+#[derive(Clone, Copy, Debug, Serialize, Eq, PartialEq)]
 pub struct ColOp<S, T>(pub S, pub T);
 
 impl<A, S, T> Mapping<A> for ColOp<S, T>
 where
-    A : Space,
-    S : Mapping<A>,
-    T : Mapping<A>,
+    A: Space,
+    S: Mapping<A>,
+    T: Mapping<A>,
 {
     type Codomain = Pair<S::Codomain, T::Codomain>;
 
-    fn apply<I : Instance<A>>(&self, a : I) -> Self::Codomain {
-        Pair(self.0.apply(a.ref_instance()), self.1.apply(a))
+    fn apply<I: Instance<A>>(&self, a: I) -> Self::Codomain {
+        Pair(a.eval_ref(|r| self.0.apply(r)), self.1.apply(a))
     }
 }
 
 impl<A, S, T> Linear<A> for ColOp<S, T>
 where
-    A : Space,
-    S : Mapping<A>,
-    T : Mapping<A>,
-{ }
+    A: Space,
+    S: Mapping<A>,
+    T: Mapping<A>,
+{
+}
 
 impl<F, S, T, A, B, X> GEMV<F, X, Pair<A, B>> 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>>
+    X: Space,
+    S: GEMV<F, X, A>,
+    T: GEMV<F, X, B>,
+    F: Num,
+    Self: Linear<X, Codomain = Pair<A, B>>,
 {
-    fn gemv<I : Instance<X>>(&self, y : &mut Pair<A, B>, α : F, x : I, β : F) {
-        self.0.gemv(&mut y.0, α, x.ref_instance(), β);
+    fn gemv<I: Instance<X>>(&self, y: &mut Pair<A, B>, α: F, x: I, β: F) {
+        x.eval_ref(|r| self.0.gemv(&mut y.0, α, r, β));
         self.1.gemv(&mut y.1, α, x, β);
     }
 
-    fn apply_mut<I : Instance<X>>(&self, y : &mut Pair<A, B>, x : I){
-        self.0.apply_mut(&mut y.0, x.ref_instance());
+    fn apply_mut<I: Instance<X>>(&self, y: &mut Pair<A, B>, x: I) {
+        x.eval_ref(|r| self.0.apply_mut(&mut y.0, r));
         self.1.apply_mut(&mut y.1, x);
     }
 
     /// Computes `y += Ax`, where `A` is `Self`
-    fn apply_add<I : Instance<X>>(&self, y : &mut Pair<A, B>, x : I){
-        self.0.apply_add(&mut y.0, x.ref_instance());
+    fn apply_add<I: Instance<X>>(&self, y: &mut Pair<A, B>, x: I) {
+        x.eval_ref(|r| self.0.apply_add(&mut y.0, r));
         self.1.apply_add(&mut y.1, x);
     }
 }
 
-
-impl<A, B, Yʹ, S, T> Adjointable<Pair<A,B>, Yʹ> for RowOp<S, T>
+impl<A, B, Yʹ, S, T> Adjointable<Pair<A, B>, Yʹ> for RowOp<S, T>
 where
-    A : Space,
-    B : Space,
-    Yʹ : Space,
-    S : Adjointable<A, Yʹ>,
-    T : Adjointable<B, Yʹ>,
-    Self : Linear<Pair<A, B>>,
+    A: Space,
+    B: Space,
+    Yʹ: Space,
+    S: Adjointable<A, Yʹ>,
+    T: Adjointable<B, Yʹ>,
+    Self: Linear<Pair<A, B>>,
     // for<'a> ColOp<S::Adjoint<'a>, T::Adjoint<'a>> : Linear<
     //     Yʹ,
     //     Codomain=Pair<S::AdjointCodomain, T::AdjointCodomain>
     // >,
 {
     type AdjointCodomain = Pair<S::AdjointCodomain, T::AdjointCodomain>;
-    type Adjoint<'a> = ColOp<S::Adjoint<'a>, T::Adjoint<'a>> where Self : 'a;
+    type Adjoint<'a>
+        = ColOp<S::Adjoint<'a>, T::Adjoint<'a>>
+    where
+        Self: 'a;
 
     fn adjoint(&self) -> Self::Adjoint<'_> {
         ColOp(self.0.adjoint(), self.1.adjoint())
     }
 }
 
-impl<A, B, Yʹ, S, T> Preadjointable<Pair<A,B>, Yʹ> for RowOp<S, T>
+impl<A, B, Yʹ, S, T> SimplyAdjointable<Pair<A, B>, Yʹ> for RowOp<S, T>
 where
-    A : Space,
-    B : Space,
-    Yʹ : Space,
-    S : Preadjointable<A, Yʹ>,
-    T : Preadjointable<B, Yʹ>,
-    Self : Linear<Pair<A, B>>,
-    for<'a> ColOp<S::Preadjoint<'a>, T::Preadjoint<'a>> : Linear<
-        Yʹ, Codomain=Pair<S::PreadjointCodomain, T::PreadjointCodomain>,
-    >,
+    A: Space,
+    B: Space,
+    Yʹ: Space,
+    S: SimplyAdjointable<A, Yʹ>,
+    T: SimplyAdjointable<B, Yʹ>,
+    Self: Linear<Pair<A, B>>,
+    // for<'a> ColOp<S::Adjoint<'a>, T::Adjoint<'a>> : Linear<
+    //     Yʹ,
+    //     Codomain=Pair<S::AdjointCodomain, T::AdjointCodomain>
+    // >,
+{
+    type AdjointCodomain = Pair<S::AdjointCodomain, T::AdjointCodomain>;
+    type SimpleAdjoint = ColOp<S::SimpleAdjoint, T::SimpleAdjoint>;
+
+    fn adjoint(&self) -> Self::SimpleAdjoint {
+        ColOp(self.0.adjoint(), self.1.adjoint())
+    }
+}
+
+impl<A, B, Yʹ, S, T> Preadjointable<Pair<A, B>, Yʹ> for RowOp<S, T>
+where
+    A: Space,
+    B: Space,
+    Yʹ: Space,
+    S: Preadjointable<A, Yʹ>,
+    T: Preadjointable<B, Yʹ>,
+    Self: Linear<Pair<A, B>>,
+    for<'a> ColOp<S::Preadjoint<'a>, T::Preadjoint<'a>>:
+        Linear<Yʹ, Codomain = Pair<S::PreadjointCodomain, T::PreadjointCodomain>>,
 {
     type PreadjointCodomain = Pair<S::PreadjointCodomain, T::PreadjointCodomain>;
-    type Preadjoint<'a> = ColOp<S::Preadjoint<'a>, T::Preadjoint<'a>> where Self : 'a;
+    type Preadjoint<'a>
+        = ColOp<S::Preadjoint<'a>, T::Preadjoint<'a>>
+    where
+        Self: 'a;
 
     fn preadjoint(&self) -> Self::Preadjoint<'_> {
         ColOp(self.0.preadjoint(), self.1.preadjoint())
     }
 }
 
-
-impl<A, Xʹ, Yʹ, R, S, T> Adjointable<A,Pair<Xʹ,Yʹ>> for ColOp<S, T>
+impl<A, Xʹ, Yʹ, R, S, T> Adjointable<A, Pair<Xʹ, Yʹ>> for ColOp<S, T>
 where
-    A : Space,
-    Xʹ : Space,
-    Yʹ : Space,
-    R : Space + ClosedAdd,
-    S : Adjointable<A, Xʹ, AdjointCodomain = R>,
-    T : Adjointable<A, Yʹ, AdjointCodomain = R>,
-    Self : Linear<A>,
+    A: Space,
+    Xʹ: Space,
+    Yʹ: Space,
+    R: ClosedSpace + ClosedAdd,
+    S: Adjointable<A, Xʹ, AdjointCodomain = R>,
+    T: Adjointable<A, Yʹ, AdjointCodomain = R>,
+    Self: Linear<A>,
     // for<'a> RowOp<S::Adjoint<'a>, T::Adjoint<'a>> : Linear<
     //     Pair<Xʹ,Yʹ>,
     //     Codomain=R,
     // >,
 {
     type AdjointCodomain = R;
-    type Adjoint<'a> = RowOp<S::Adjoint<'a>, T::Adjoint<'a>> where Self : 'a;
+    type Adjoint<'a>
+        = RowOp<S::Adjoint<'a>, T::Adjoint<'a>>
+    where
+        Self: 'a;
 
     fn adjoint(&self) -> Self::Adjoint<'_> {
         RowOp(self.0.adjoint(), self.1.adjoint())
     }
 }
 
-impl<A, Xʹ, Yʹ, R, S, T> Preadjointable<A,Pair<Xʹ,Yʹ>> for ColOp<S, T>
+impl<A, Xʹ, Yʹ, R, S, T> SimplyAdjointable<A, Pair<Xʹ, Yʹ>> for ColOp<S, T>
 where
-    A : Space,
-    Xʹ : Space,
-    Yʹ : Space,
-    R : Space + ClosedAdd,
-    S : Preadjointable<A, Xʹ, PreadjointCodomain = R>,
-    T : Preadjointable<A, Yʹ, PreadjointCodomain = R>,
-    Self : Linear<A>,
-    for<'a> RowOp<S::Preadjoint<'a>, T::Preadjoint<'a>> : Linear<
-        Pair<Xʹ,Yʹ>, Codomain = R,
-    >,
+    A: Space,
+    Xʹ: Space,
+    Yʹ: Space,
+    R: ClosedSpace + ClosedAdd,
+    S: SimplyAdjointable<A, Xʹ, AdjointCodomain = R>,
+    T: SimplyAdjointable<A, Yʹ, AdjointCodomain = R>,
+    Self: Linear<A>,
+    // for<'a> RowOp<S::Adjoint<'a>, T::Adjoint<'a>> : Linear<
+    //     Pair<Xʹ,Yʹ>,
+    //     Codomain=R,
+    // >,
+{
+    type AdjointCodomain = R;
+    type SimpleAdjoint = RowOp<S::SimpleAdjoint, T::SimpleAdjoint>;
+
+    fn adjoint(&self) -> Self::SimpleAdjoint {
+        RowOp(self.0.adjoint(), self.1.adjoint())
+    }
+}
+
+impl<A, Xʹ, Yʹ, R, S, T> Preadjointable<A, Pair<Xʹ, Yʹ>> for ColOp<S, T>
+where
+    A: Space,
+    Xʹ: Space,
+    Yʹ: Space,
+    R: ClosedSpace + ClosedAdd,
+    S: Preadjointable<A, Xʹ, PreadjointCodomain = R>,
+    T: Preadjointable<A, Yʹ, PreadjointCodomain = R>,
+    Self: Linear<A>,
+    for<'a> RowOp<S::Preadjoint<'a>, T::Preadjoint<'a>>: Linear<Pair<Xʹ, Yʹ>, Codomain = R>,
 {
     type PreadjointCodomain = R;
-    type Preadjoint<'a> = RowOp<S::Preadjoint<'a>, T::Preadjoint<'a>> where Self : 'a;
+    type Preadjoint<'a>
+        = RowOp<S::Preadjoint<'a>, T::Preadjoint<'a>>
+    where
+        Self: 'a;
 
     fn preadjoint(&self) -> Self::Preadjoint<'_> {
         RowOp(self.0.preadjoint(), self.1.preadjoint())
@@ -517,100 +869,126 @@
 }
 
 /// Diagonal operator
+#[derive(Clone, Copy, Debug, Serialize, Eq, PartialEq)]
 pub struct DiagOp<S, T>(pub S, pub T);
 
 impl<A, B, S, T> Mapping<Pair<A, B>> for DiagOp<S, T>
 where
-    A : Space,
-    B : Space,
-    S : Mapping<A>,
-    T : Mapping<B>,
+    A: Space,
+    B: Space,
+    S: Mapping<A>,
+    T: Mapping<B>,
 {
     type Codomain = Pair<S::Codomain, T::Codomain>;
 
-    fn apply<I : Instance<Pair<A, B>>>(&self, x : I) -> Self::Codomain {
-        let Pair(a, b) = x.decompose();
-        Pair(self.0.apply(a), self.1.apply(b))
+    fn apply<I: Instance<Pair<A, B>>>(&self, x: I) -> Self::Codomain {
+        x.eval_decompose(|Pair(a, b)| Pair(self.0.apply(a), self.1.apply(b)))
     }
 }
 
 impl<A, B, S, T> Linear<Pair<A, B>> for DiagOp<S, T>
 where
-    A : Space,
-    B : Space,
-    S : Linear<A>,
-    T : Linear<B>,
-{ }
+    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>
 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>>,
+    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>>,
 {
-    fn gemv<I : Instance<Pair<U, V>>>(&self, y : &mut Pair<A, B>, α : F, x : I, β : F) {
-        let Pair(u, v) = x.decompose();
-        self.0.gemv(&mut y.0, α, u, β);
-        self.1.gemv(&mut y.1, α, v, β);
+    fn gemv<I: Instance<Pair<U, V>>>(&self, y: &mut Pair<A, B>, α: F, x: I, β: F) {
+        x.eval_decompose(|Pair(u, v)| {
+            self.0.gemv(&mut y.0, α, u, β);
+            self.1.gemv(&mut y.1, α, v, β);
+        })
     }
 
-    fn apply_mut<I : Instance<Pair<U, V>>>(&self, y : &mut Pair<A, B>, x : I){
-        let Pair(u, v) = x.decompose();
-        self.0.apply_mut(&mut y.0, u);
-        self.1.apply_mut(&mut y.1, v);
+    fn apply_mut<I: Instance<Pair<U, V>>>(&self, y: &mut Pair<A, B>, x: I) {
+        x.eval_decompose(|Pair(u, v)| {
+            self.0.apply_mut(&mut y.0, u);
+            self.1.apply_mut(&mut y.1, v);
+        })
     }
 
     /// Computes `y += Ax`, where `A` is `Self`
-    fn apply_add<I : Instance<Pair<U, V>>>(&self, y : &mut Pair<A, B>, x : I){
-        let Pair(u, v) = x.decompose();
-        self.0.apply_add(&mut y.0, u);
-        self.1.apply_add(&mut y.1, v);
+    fn apply_add<I: Instance<Pair<U, V>>>(&self, y: &mut Pair<A, B>, x: I) {
+        x.eval_decompose(|Pair(u, v)| {
+            self.0.apply_add(&mut y.0, u);
+            self.1.apply_add(&mut y.1, v);
+        })
     }
 }
 
-impl<A, B, Xʹ, Yʹ, R, S, T> Adjointable<Pair<A,B>, Pair<Xʹ,Yʹ>> for DiagOp<S, T>
+impl<A, B, Xʹ, Yʹ, R, S, T> Adjointable<Pair<A, B>, Pair<Xʹ, Yʹ>> for DiagOp<S, T>
 where
-    A : Space,
-    B : Space,
+    A: Space,
+    B: Space,
     Xʹ: Space,
-    Yʹ : Space,
-    R : Space,
-    S : Adjointable<A, Xʹ>,
-    T : Adjointable<B, Yʹ>,
-    Self : Linear<Pair<A, B>>,
-    for<'a> DiagOp<S::Adjoint<'a>, T::Adjoint<'a>> : Linear<
-        Pair<Xʹ,Yʹ>, Codomain=R,
-    >,
+    Yʹ: Space,
+    R: ClosedSpace,
+    S: Adjointable<A, Xʹ>,
+    T: Adjointable<B, Yʹ>,
+    Self: Linear<Pair<A, B>>,
+    for<'a> DiagOp<S::Adjoint<'a>, T::Adjoint<'a>>: Linear<Pair<Xʹ, Yʹ>, Codomain = R>,
 {
     type AdjointCodomain = R;
-    type Adjoint<'a> = DiagOp<S::Adjoint<'a>, T::Adjoint<'a>> where Self : 'a;
+    type Adjoint<'a>
+        = DiagOp<S::Adjoint<'a>, T::Adjoint<'a>>
+    where
+        Self: 'a;
 
     fn adjoint(&self) -> Self::Adjoint<'_> {
         DiagOp(self.0.adjoint(), self.1.adjoint())
     }
 }
 
-impl<A, B, Xʹ, Yʹ, R, S, T> Preadjointable<Pair<A,B>, Pair<Xʹ,Yʹ>> for DiagOp<S, T>
+impl<A, B, Xʹ, Yʹ, R, S, T> SimplyAdjointable<Pair<A, B>, Pair<Xʹ, Yʹ>> for DiagOp<S, T>
 where
-    A : Space,
-    B : Space,
+    A: Space,
+    B: Space,
     Xʹ: Space,
-    Yʹ : Space,
-    R : Space,
-    S : Preadjointable<A, Xʹ>,
-    T : Preadjointable<B, Yʹ>,
-    Self : Linear<Pair<A, B>>,
-    for<'a> DiagOp<S::Preadjoint<'a>, T::Preadjoint<'a>> : Linear<
-        Pair<Xʹ,Yʹ>, Codomain=R,
-    >,
+    Yʹ: Space,
+    R: ClosedSpace,
+    S: SimplyAdjointable<A, Xʹ>,
+    T: SimplyAdjointable<B, Yʹ>,
+    Self: Linear<Pair<A, B>>,
+    for<'a> DiagOp<S::SimpleAdjoint, T::SimpleAdjoint>: Linear<Pair<Xʹ, Yʹ>, Codomain = R>,
+{
+    type AdjointCodomain = R;
+    type SimpleAdjoint = DiagOp<S::SimpleAdjoint, T::SimpleAdjoint>;
+
+    fn adjoint(&self) -> Self::SimpleAdjoint {
+        DiagOp(self.0.adjoint(), self.1.adjoint())
+    }
+}
+
+impl<A, B, Xʹ, Yʹ, R, S, T> Preadjointable<Pair<A, B>, Pair<Xʹ, Yʹ>> for DiagOp<S, T>
+where
+    A: Space,
+    B: Space,
+    Xʹ: Space,
+    Yʹ: Space,
+    R: ClosedSpace,
+    S: Preadjointable<A, Xʹ>,
+    T: Preadjointable<B, Yʹ>,
+    Self: Linear<Pair<A, B>>,
+    for<'a> DiagOp<S::Preadjoint<'a>, T::Preadjoint<'a>>: Linear<Pair<Xʹ, Yʹ>, Codomain = R>,
 {
     type PreadjointCodomain = R;
-    type Preadjoint<'a> = DiagOp<S::Preadjoint<'a>, T::Preadjoint<'a>> where Self : 'a;
+    type Preadjoint<'a>
+        = DiagOp<S::Preadjoint<'a>, T::Preadjoint<'a>>
+    where
+        Self: 'a;
 
     fn preadjoint(&self) -> Self::Preadjoint<'_> {
         DiagOp(self.0.preadjoint(), self.1.preadjoint())
@@ -620,65 +998,90 @@
 /// Block operator
 pub type BlockOp<S11, S12, S21, S22> = ColOp<RowOp<S11, S12>, RowOp<S21, S22>>;
 
-
 macro_rules! pairnorm {
     ($expj:ty) => {
         impl<F, A, B, S, T, ExpA, ExpB, ExpR>
-        BoundedLinear<Pair<A, B>, PairNorm<ExpA, ExpB, $expj>, ExpR, F>
-        for RowOp<S, T>
+            BoundedLinear<Pair<A, B>, PairNorm<ExpA, ExpB, $expj>, ExpR, F> for RowOp<S, T>
         where
-            F : Float,
-            A : Space + Norm<F, ExpA>,
-            B : Space + 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,
-            ExpA : NormExponent,
-            ExpB : NormExponent,
-            ExpR : NormExponent,
+            F: Float,
+            A: Space,
+            B: Space,
+            S: BoundedLinear<A, ExpA, ExpR, F>,
+            T: BoundedLinear<B, ExpB, ExpR, F>,
+            S::Codomain: Add<T::Codomain>,
+            <S::Codomain as Add<T::Codomain>>::Output: ClosedSpace,
+            ExpA: NormExponent,
+            ExpB: NormExponent,
+            ExpR: NormExponent,
         {
             fn opnorm_bound(
                 &self,
-                PairNorm(expa, expb, _) : PairNorm<ExpA, ExpB, $expj>,
-                expr : ExpR
-            ) -> F {
+                PairNorm(expa, expb, _): PairNorm<ExpA, ExpB, $expj>,
+                expr: ExpR,
+            ) -> DynResult<F> {
                 // 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);
-                let nb = self.1.opnorm_bound(expb, expr);
-                na.max(nb)
+                let na = self.0.opnorm_bound(expa, expr)?;
+                let nb = self.1.opnorm_bound(expb, expr)?;
+                Ok(na.max(nb))
             }
         }
-        
-        impl<F, A, S, T, ExpA, ExpS, ExpT>
-        BoundedLinear<A, ExpA, PairNorm<ExpS, ExpT, $expj>, F>
-        for ColOp<S, T>
+
+        impl<F, A, S, T, ExpA, ExpS, ExpT> BoundedLinear<A, ExpA, PairNorm<ExpS, ExpT, $expj>, F>
+            for ColOp<S, T>
         where
-            F : Float,
-            A : Space + Norm<F, ExpA>,
-            S : BoundedLinear<A, ExpA, ExpS, F>,
-            T : BoundedLinear<A, ExpA, ExpT, F>,
-            ExpA : NormExponent,
-            ExpS : NormExponent,
-            ExpT : NormExponent,
+            F: Float,
+            A: Space,
+            S: BoundedLinear<A, ExpA, ExpS, F>,
+            T: BoundedLinear<A, ExpA, ExpT, F>,
+            ExpA: NormExponent,
+            ExpS: NormExponent,
+            ExpT: NormExponent,
         {
             fn opnorm_bound(
                 &self,
-                expa : ExpA,
-                PairNorm(exps, expt, _) : PairNorm<ExpS, ExpT, $expj>
-            ) -> F {
+                expa: ExpA,
+                PairNorm(exps, expt, _): PairNorm<ExpS, ExpT, $expj>,
+            ) -> DynResult<F> {
                 // 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);
-                let nt = self.1.opnorm_bound(expa, expt);
-                ns.max(nt)
+                let ns = self.0.opnorm_bound(expa, exps)?;
+                let nt = self.1.opnorm_bound(expa, expt)?;
+                Ok(ns.max(nt))
             }
         }
-    }
+    };
 }
 
 pairnorm!(L1);
 pairnorm!(L2);
 pairnorm!(Linfinity);
 
+/// The simplest linear mapping, scaling by a scalar.
+///
+/// TODO: redefined/replace `Weighted` by composition with [`Scaled`].
+pub struct Scaled<F: Float>(pub F);
+
+impl<Domain, F> Mapping<Domain> for Scaled<F>
+where
+    F: Float,
+    Domain: Space,
+    Domain::Principal: Mul<F>,
+    <Domain::Principal as Mul<F>>::Output: ClosedSpace,
+{
+    type Codomain = <Domain::Principal as Mul<F>>::Output;
+
+    /// Compute the value of `self` at `x`.
+    fn apply<I: Instance<Domain>>(&self, x: I) -> Self::Codomain {
+        x.own() * self.0
+    }
+}
+
+impl<Domain, F> Linear<Domain> for Scaled<F>
+where
+    F: Float,
+    Domain: Space,
+    Domain::Principal: Mul<F>,
+    <Domain::Principal as Mul<F>>::Output: ClosedSpace,
+{
+}

mercurial