src/direct_product.rs

changeset 198
3868555d135c
parent 189
14193146d8f2
--- a/src/direct_product.rs	Sun Apr 27 20:29:43 2025 -0500
+++ b/src/direct_product.rs	Fri May 15 14:46:30 2026 -0500
@@ -6,11 +6,11 @@
 */
 
 use crate::euclidean::Euclidean;
-use crate::instance::{Decomposition, DecompositionMut, Instance, InstanceMut, MyCow};
-use crate::linops::AXPY;
+use crate::instance::{Decomposition, DecompositionMut, Instance, InstanceMut, MyCow, Ownable};
+use crate::linops::{VectorSpace, AXPY};
 use crate::loc::Loc;
 use crate::mapping::Space;
-use crate::norms::{HasDual, Norm, NormExponent, Normed, PairNorm, L2};
+use crate::norms::{Dist, HasDual, Norm, NormExponent, Normed, PairNorm, L2};
 use crate::types::{Float, Num};
 use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
 use serde::{Deserialize, Serialize};
@@ -39,239 +39,290 @@
     }
 }
 
-macro_rules! impl_binop {
-    (($a : ty, $b : ty), $trait : ident, $fn : ident, $refl:ident, $refr:ident) => {
-        impl_binop!(@doit: $a, $b, $trait, $fn;
-                           maybe_lifetime!($refl, &'l Pair<$a,$b>),
-                           (maybe_lifetime!($refl, &'l $a),
-                            maybe_lifetime!($refl, &'l $b));
-                           maybe_lifetime!($refr, &'r Pair<Ai,Bi>),
-                           (maybe_lifetime!($refr, &'r Ai),
-                            maybe_lifetime!($refr, &'r Bi));
-                           $refl, $refr);
-    };
-
-    (@doit: $a:ty, $b:ty,
-            $trait:ident, $fn:ident;
-            $self:ty, ($aself:ty, $bself:ty);
-            $in:ty, ($ain:ty, $bin:ty);
-            $refl:ident, $refr:ident) => {
-        impl<'l, 'r, Ai, Bi> $trait<$in>
-        for $self
-        where $aself: $trait<$ain>,
-              $bself: $trait<$bin> {
-            type Output = Pair<<$aself as $trait<$ain>>::Output,
-                               <$bself as $trait<$bin>>::Output>;
-
-            #[inline]
-            fn $fn(self, y : $in) -> Self::Output {
-                Pair(maybe_ref!($refl, self.0).$fn(maybe_ref!($refr, y.0)),
-                     maybe_ref!($refl, self.1).$fn(maybe_ref!($refr, y.1)))
+macro_rules! impl_unary {
+    ($trait:ident, $fn:ident) => {
+        impl<A, B> $trait for Pair<A, B>
+        where
+            A: $trait,
+            B: $trait,
+        {
+            type Output = Pair<A::Output, B::Output>;
+            fn $fn(self) -> Self::Output {
+                let Pair(a, b) = self;
+                Pair(a.$fn(), b.$fn())
             }
         }
+
+        // Compiler overflow
+        // impl<'a, A, B> $trait for &'a Pair<A, B>
+        // where
+        //     &'a A: $trait,
+        //     &'a B: $trait,
+        // {
+        //     type Output = Pair<<&'a A as $trait>::Output, <&'a B as $trait>::Output>;
+        //     fn $fn(self) -> Self::Output {
+        //         let Pair(ref a, ref b) = self;
+        //         Pair(a.$fn(), b.$fn())
+        //     }
+        // }
     };
 }
 
-macro_rules! impl_assignop {
-    (($a : ty, $b : ty), $trait : ident, $fn : ident, $refr:ident) => {
-        impl_assignop!(@doit: $a, $b,
-                              $trait, $fn;
-                              maybe_lifetime!($refr, &'r Pair<Ai,Bi>),
-                              (maybe_lifetime!($refr, &'r Ai),
-                               maybe_lifetime!($refr, &'r Bi));
-                              $refr);
-    };
-    (@doit: $a : ty, $b : ty,
-            $trait:ident, $fn:ident;
-            $in:ty, ($ain:ty, $bin:ty);
-            $refr:ident) => {
-        impl<'r, Ai, Bi> $trait<$in>
-        for Pair<$a,$b>
-        where $a: $trait<$ain>,
-              $b: $trait<$bin> {
-            #[inline]
-            fn $fn(&mut self, y : $in) -> () {
-                self.0.$fn(maybe_ref!($refr, y.0));
-                self.1.$fn(maybe_ref!($refr, y.1));
+impl_unary!(Neg, neg);
+
+macro_rules! impl_binary {
+    ($trait:ident, $fn:ident) => {
+        impl<A, B, C, D> $trait<Pair<C, D>> for Pair<A, B>
+        where
+            A: $trait<C>,
+            B: $trait<D>,
+        {
+            type Output = Pair<A::Output, B::Output>;
+            fn $fn(self, Pair(c, d): Pair<C, D>) -> Self::Output {
+                let Pair(a, b) = self;
+                Pair(a.$fn(c), b.$fn(d))
             }
         }
-    }
-}
 
-macro_rules! impl_scalarop {
-    (($a : ty, $b : ty), $field : ty, $trait : ident, $fn : ident, $refl:ident) => {
-        impl_scalarop!(@doit: $field,
-                              $trait, $fn;
-                              maybe_lifetime!($refl, &'l Pair<$a,$b>),
-                              (maybe_lifetime!($refl, &'l $a),
-                               maybe_lifetime!($refl, &'l $b));
-                              $refl);
-    };
-    (@doit: $field : ty,
-            $trait:ident, $fn:ident;
-            $self:ty, ($aself:ty, $bself:ty);
-            $refl:ident) => {
-        // Scalar as Rhs
-        impl<'l> $trait<$field>
-        for $self
-        where $aself: $trait<$field>,
-              $bself: $trait<$field> {
-            type Output = Pair<<$aself as $trait<$field>>::Output,
-                               <$bself as $trait<$field>>::Output>;
-            #[inline]
-            fn $fn(self, a : $field) -> Self::Output {
-                Pair(maybe_ref!($refl, self.0).$fn(a),
-                     maybe_ref!($refl, self.1).$fn(a))
+        impl<'a, A, B, C, D> $trait<Pair<C, D>> for &'a Pair<A, B>
+        where
+            &'a A: $trait<C>,
+            &'a B: $trait<D>,
+        {
+            type Output = Pair<<&'a A as $trait<C>>::Output, <&'a B as $trait<D>>::Output>;
+            fn $fn(self, Pair(c, d): Pair<C, D>) -> Self::Output {
+                let Pair(ref a, ref b) = self;
+                Pair(a.$fn(c), b.$fn(d))
             }
         }
-    }
-}
 
-// Not used due to compiler overflow
-#[allow(unused_macros)]
-macro_rules! impl_scalarlhs_op {
-    (($a : ty, $b : ty), $field : ty, $trait:ident, $fn:ident, $refr:ident) => {
-        impl_scalarlhs_op!(@doit: $trait, $fn,
-                                  maybe_lifetime!($refr, &'r Pair<$a,$b>),
-                                  (maybe_lifetime!($refr, &'r $a),
-                                   maybe_lifetime!($refr, &'r $b));
-                                  $refr, $field);
-    };
-    (@doit: $trait:ident, $fn:ident,
-            $in:ty, ($ain:ty, $bin:ty);
-            $refr:ident, $field:ty) => {
-        impl<'r> $trait<$in>
-        for $field
-        where $field : $trait<$ain>
-                     + $trait<$bin> {
-            type Output = Pair<<$field as $trait<$ain>>::Output,
-                               <$field as $trait<$bin>>::Output>;
-            #[inline]
-            fn $fn(self, x : $in) -> Self::Output {
-                Pair(self.$fn(maybe_ref!($refr, x.0)),
-                     self.$fn(maybe_ref!($refr, x.1)))
+        impl<'a, 'b, A, B, C, D> $trait<&'b Pair<C, D>> for &'a Pair<A, B>
+        where
+            &'a A: $trait<&'b C>,
+            &'a B: $trait<&'b D>,
+        {
+            type Output = Pair<<&'a A as $trait<&'b C>>::Output, <&'a B as $trait<&'b D>>::Output>;
+            fn $fn(self, Pair(ref c, ref d): &'b Pair<C, D>) -> Self::Output {
+                let Pair(ref a, ref b) = self;
+                Pair(a.$fn(c), b.$fn(d))
+            }
+        }
+
+        impl<'b, A, B, C, D> $trait<&'b Pair<C, D>> for Pair<A, B>
+        where
+            A: $trait<&'b C>,
+            B: $trait<&'b D>,
+        {
+            type Output = Pair<<A as $trait<&'b C>>::Output, <B as $trait<&'b D>>::Output>;
+            fn $fn(self, Pair(ref c, ref d): &'b Pair<C, D>) -> Self::Output {
+                let Pair(a, b) = self;
+                Pair(a.$fn(c), b.$fn(d))
             }
         }
     };
 }
 
-macro_rules! impl_scalar_assignop {
-    (($a : ty, $b : ty), $field : ty, $trait : ident, $fn : ident) => {
-        impl<'r> $trait<$field> for Pair<$a, $b>
+impl_binary!(Add, add);
+impl_binary!(Sub, sub);
+
+macro_rules! impl_scalar {
+    ($trait:ident, $fn:ident) => {
+        impl<A, B, F: Num> $trait<F> for Pair<A, B>
+        where
+            A: $trait<F>,
+            B: $trait<F>,
+        {
+            type Output = Pair<A::Output, B::Output>;
+            fn $fn(self, t: F) -> Self::Output {
+                let Pair(a, b) = self;
+                Pair(a.$fn(t), b.$fn(t))
+            }
+        }
+
+        impl<'a, A, B, F: Num> $trait<F> for &'a Pair<A, B>
         where
-            $a: $trait<$field>,
-            $b: $trait<$field>,
+            &'a A: $trait<F>,
+            &'a B: $trait<F>,
+        {
+            type Output = Pair<<&'a A as $trait<F>>::Output, <&'a B as $trait<F>>::Output>;
+            fn $fn(self, t: F) -> Self::Output {
+                let Pair(ref a, ref b) = self;
+                Pair(a.$fn(t), b.$fn(t))
+            }
+        }
+
+        // impl<'a, 'b, A, B> $trait<&'b $F> for &'a Pair<A, B>
+        // where
+        //     &'a A: $trait<&'b $F>,
+        //     &'a B: $trait<&'b $F>,
+        // {
+        //     type Output =
+        //         Pair<<&'a A as $trait<&'b $F>>::Output, <&'a B as $trait<&'b $F>>::Output>;
+        //     fn $fn(self, t: &'b $F) -> Self::Output {
+        //         let Pair(ref a, ref b) = self;
+        //         Pair(a.$fn(t), b.$fn(t))
+        //     }
+        // }
+
+        // impl<'b, A, B> $trait<&'b $F> for Pair<A, B>
+        // where
+        //     A: $trait<&'b $F>,
+        //     B: $trait<&'b $F>,
+        // {
+        //     type Output = Pair<<A as $trait<&'b $F>>::Output, <B as $trait<&'b $F>>::Output>;
+        //     fn $fn(self, t: &'b $F) -> Self::Output {
+        //         let Pair(a, b) = self;
+        //         Pair(a.$fn(t), b.$fn(t))
+        //     }
+        // }
+    };
+}
+
+impl_scalar!(Mul, mul);
+impl_scalar!(Div, div);
+
+macro_rules! impl_scalar_lhs {
+    ($trait:ident, $fn:ident, $F:ty) => {
+        impl<A, B> $trait<Pair<A, B>> for $F
+        where
+            $F: $trait<A> + $trait<B>,
         {
-            #[inline]
-            fn $fn(&mut self, a: $field) -> () {
-                self.0.$fn(a);
-                self.1.$fn(a);
+            type Output = Pair<<$F as $trait<A>>::Output, <$F as $trait<B>>::Output>;
+            fn $fn(self, Pair(a, b): Pair<A, B>) -> Self::Output {
+                Pair(self.$fn(a), self.$fn(b))
+            }
+        }
+
+        // Compiler overflow:
+        //
+        // impl<'a, A, B> $trait<&'a Pair<A, B>> for $F
+        // where
+        //     $F: $trait<&'a A> + $trait<&'a B>,
+        // {
+        //     type Output = Pair<<$F as $trait<&'a A>>::Output, <$F as $trait<&'a B>>::Output>;
+        //     fn $fn(self, Pair(a, b): &'a Pair<A, B>) -> Self::Output {
+        //         Pair(self.$fn(a), self.$fn(b))
+        //     }
+        // }
+    };
+}
+
+impl_scalar_lhs!(Mul, mul, f32);
+impl_scalar_lhs!(Mul, mul, f64);
+impl_scalar_lhs!(Div, div, f32);
+impl_scalar_lhs!(Div, div, f64);
+
+macro_rules! impl_binary_mut {
+    ($trait:ident, $fn:ident) => {
+        impl<'a, A, B, C, D> $trait<Pair<C, D>> for Pair<A, B>
+        where
+            A: $trait<C>,
+            B: $trait<D>,
+        {
+            fn $fn(&mut self, Pair(c, d): Pair<C, D>) {
+                let Pair(ref mut a, ref mut b) = self;
+                a.$fn(c);
+                b.$fn(d);
+            }
+        }
+
+        impl<'a, 'b, A, B, C, D> $trait<&'b Pair<C, D>> for Pair<A, B>
+        where
+            A: $trait<&'b C>,
+            B: $trait<&'b D>,
+        {
+            fn $fn(&mut self, Pair(ref c, ref d): &'b Pair<C, D>) {
+                let Pair(ref mut a, ref mut b) = self;
+                a.$fn(c);
+                b.$fn(d);
             }
         }
     };
 }
 
-macro_rules! impl_unaryop {
-    (($a : ty, $b : ty), $trait:ident, $fn:ident, $refl:ident) => {
-        impl_unaryop!(@doit: $trait, $fn;
-                             maybe_lifetime!($refl, &'l Pair<$a,$b>),
-                             (maybe_lifetime!($refl, &'l $a),
-                              maybe_lifetime!($refl, &'l $b));
-                             $refl);
-    };
-    (@doit: $trait:ident, $fn:ident;
-            $self:ty, ($aself:ty, $bself:ty);
-            $refl : ident) => {
-        impl<'l> $trait
-        for $self
-        where $aself: $trait,
-              $bself: $trait {
-            type Output = Pair<<$aself as $trait>::Output,
-                               <$bself as $trait>::Output>;
-            #[inline]
-            fn $fn(self) -> Self::Output {
-                Pair(maybe_ref!($refl, self.0).$fn(),
-                     maybe_ref!($refl, self.1).$fn())
+impl_binary_mut!(AddAssign, add_assign);
+impl_binary_mut!(SubAssign, sub_assign);
+
+macro_rules! impl_scalar_mut {
+    ($trait:ident, $fn:ident) => {
+        impl<'a, A, B, F: Num> $trait<F> for Pair<A, B>
+        where
+            A: $trait<F>,
+            B: $trait<F>,
+        {
+            fn $fn(&mut self, t: F) {
+                let Pair(ref mut a, ref mut b) = self;
+                a.$fn(t);
+                b.$fn(t);
             }
         }
+    };
+}
+
+impl_scalar_mut!(MulAssign, mul_assign);
+impl_scalar_mut!(DivAssign, div_assign);
+
+/// Trait for ownable-by-consumption objects
+impl<A, B> Ownable for Pair<A, B>
+where
+    A: Ownable,
+    B: Ownable,
+{
+    type OwnedVariant = Pair<A::OwnedVariant, B::OwnedVariant>;
+
+    #[inline]
+    fn into_owned(self) -> Self::OwnedVariant {
+        Pair(self.0.into_owned(), self.1.into_owned())
+    }
+
+    #[inline]
+    fn clone_owned(&self) -> Self::OwnedVariant {
+        Pair(self.0.clone_owned(), self.1.clone_owned())
+    }
+
+    #[inline]
+    fn cow_owned<'b>(self) -> MyCow<'b, Self::OwnedVariant>
+    where
+        Self: 'b,
+    {
+        MyCow::Owned(self.into_owned())
+    }
+
+    #[inline]
+    fn ref_cow_owned<'b>(&'b self) -> MyCow<'b, Self::OwnedVariant>
+    where
+        Self: 'b,
+    {
+        MyCow::Owned(self.clone_owned())
     }
 }
 
-#[macro_export]
-macro_rules! impl_pair_vectorspace_ops {
-    (($a:ty, $b:ty), $field:ty) => {
-        impl_pair_vectorspace_ops!(@binary, ($a, $b), Add, add);
-        impl_pair_vectorspace_ops!(@binary, ($a, $b), Sub, sub);
-        impl_pair_vectorspace_ops!(@assign, ($a, $b), AddAssign, add_assign);
-        impl_pair_vectorspace_ops!(@assign, ($a, $b), SubAssign, sub_assign);
-        impl_pair_vectorspace_ops!(@scalar, ($a, $b), $field, Mul, mul);
-        impl_pair_vectorspace_ops!(@scalar, ($a, $b), $field, Div, div);
-        // Compiler overflow
-        // $(
-        //     impl_pair_vectorspace_ops!(@scalar_lhs, ($a, $b), $field, $impl_scalarlhs_op, Mul, mul);
-        // )*
-        impl_pair_vectorspace_ops!(@scalar_assign, ($a, $b), $field, MulAssign, mul_assign);
-        impl_pair_vectorspace_ops!(@scalar_assign, ($a, $b), $field, DivAssign, div_assign);
-        impl_pair_vectorspace_ops!(@unary, ($a, $b), Neg, neg);
-    };
-    (@binary, ($a : ty, $b : ty), $trait : ident, $fn : ident) => {
-        impl_binop!(($a, $b), $trait, $fn, ref, ref);
-        impl_binop!(($a, $b), $trait, $fn, ref, noref);
-        impl_binop!(($a, $b), $trait, $fn, noref, ref);
-        impl_binop!(($a, $b), $trait, $fn, noref, noref);
-    };
-    (@assign, ($a : ty, $b : ty), $trait : ident, $fn :ident) => {
-        impl_assignop!(($a, $b), $trait, $fn, ref);
-        impl_assignop!(($a, $b), $trait, $fn, noref);
-    };
-    (@scalar, ($a : ty, $b : ty), $field : ty, $trait : ident, $fn :ident) => {
-        impl_scalarop!(($a, $b), $field, $trait, $fn, ref);
-        impl_scalarop!(($a, $b), $field, $trait, $fn, noref);
-    };
-    (@scalar_lhs, ($a : ty, $b : ty), $field : ty, $trait : ident, $fn : ident) => {
-        impl_scalarlhs_op!(($a, $b), $field, $trait, $fn, ref);
-        impl_scalarlhs_op!(($a, $b), $field, $trait, $fn, noref);
-    };
-    (@scalar_assign, ($a : ty, $b : ty), $field : ty, $trait : ident, $fn : ident) => {
-        impl_scalar_assignop!(($a, $b), $field, $trait, $fn);
-    };
-    (@unary, ($a : ty, $b : ty), $trait : ident, $fn :  ident) => {
-        impl_unaryop!(($a, $b), $trait, $fn, ref);
-        impl_unaryop!(($a, $b), $trait, $fn, noref);
-    };
-}
-
-impl_pair_vectorspace_ops!((f32, f32), f32);
-impl_pair_vectorspace_ops!((f64, f64), f64);
-
-type PairOutput<F, A, B> = Pair<<A as Euclidean<F>>::Output, <B as Euclidean<F>>::Output>;
-
-impl<A, B, F> Euclidean<F> for Pair<A, B>
+/// We only support 'closed' `Euclidean` `Pair`s, as more general ones cause
+/// compiler overflows.
+impl<A, B, F: Float> Euclidean<F> for Pair<A, B>
 where
     A: Euclidean<F>,
     B: Euclidean<F>,
-    F: Float,
-    PairOutput<F, A, B>: Euclidean<F>,
-    Self: Sized
-        + Mul<F, Output = PairOutput<F, A, B>>
-        + MulAssign<F>
-        + Div<F, Output = PairOutput<F, A, B>>
-        + DivAssign<F>
-        + Add<Self, Output = PairOutput<F, A, B>>
-        + Sub<Self, Output = PairOutput<F, A, B>>
-        + for<'b> Add<&'b Self, Output = PairOutput<F, A, B>>
-        + for<'b> Sub<&'b Self, Output = PairOutput<F, A, B>>
-        + AddAssign<Self>
-        + for<'b> AddAssign<&'b Self>
-        + SubAssign<Self>
-        + for<'b> SubAssign<&'b Self>
-        + Neg<Output = PairOutput<F, A, B>>,
+    // //Pair<A, B>: Euclidean<F>,
+    // Self: Sized
+    //     + Mul<F, Output = Self::OwnedEuclidean>
+    //     + MulAssign<F>
+    //     + Div<F, Output = Self::OwnedEuclidean>
+    //     + DivAssign<F>
+    //     + Add<Self, Output = Self::OwnedEuclidean>
+    //     + Sub<Self, Output = Self::OwnedEuclidean>
+    //     + for<'b> Add<&'b Self, Output = Self::OwnedEuclidean>
+    //     + for<'b> Sub<&'b Self, Output = Self::OwnedEuclidean>
+    //     + AddAssign<Self>
+    //     + for<'b> AddAssign<&'b Self>
+    //     + SubAssign<Self>
+    //     + for<'b> SubAssign<&'b Self>
+    //     + Neg<Output = Self::OwnedEuclidean>,
 {
-    type Output = PairOutput<F, A, B>;
+    type PrincipalE = Pair<A::PrincipalE, B::PrincipalE>;
 
     fn dot<I: Instance<Self>>(&self, other: I) -> F {
-        let Pair(u, v) = other.decompose();
-        self.0.dot(u) + self.1.dot(v)
+        other.eval_decompose(|Pair(u, v)| self.0.dot(u) + self.1.dot(v))
     }
 
     fn norm2_squared(&self) -> F {
@@ -279,45 +330,59 @@
     }
 
     fn dist2_squared<I: Instance<Self>>(&self, other: I) -> F {
-        let Pair(u, v) = other.decompose();
-        self.0.dist2_squared(u) + self.1.dist2_squared(v)
+        other.eval_decompose(|Pair(u, v)| self.0.dist2_squared(u) + self.1.dist2_squared(v))
     }
 }
 
-impl<F, A, B, U, V> AXPY<F, Pair<U, V>> for Pair<A, B>
+impl<F, A, B> VectorSpace for Pair<A, B>
+where
+    A: VectorSpace<Field = F>,
+    B: VectorSpace<Field = F>,
+    F: Num,
+{
+    type Field = F;
+    type PrincipalV = Pair<A::PrincipalV, B::PrincipalV>;
+
+    /// Return a similar zero as `self`.
+    fn similar_origin(&self) -> Self::PrincipalV {
+        Pair(self.0.similar_origin(), self.1.similar_origin())
+    }
+
+    // #[inline]
+    // fn into_owned(self) -> Self::Owned {
+    //     Pair(self.0.into_owned(), self.1.into_owned())
+    // }
+}
+
+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>>,
+    // Self: MulAssign<F> + DivAssign<F>,
+    // Pair<A, B>: MulAssign<F> + DivAssign<F>,
 {
-    type Owned = Pair<A::Owned, B::Owned>;
-
     fn axpy<I: Instance<Pair<U, V>>>(&mut self, α: F, x: I, β: F) {
-        let Pair(u, v) = x.decompose();
-        self.0.axpy(α, u, β);
-        self.1.axpy(α, v, β);
+        x.eval_decompose(|Pair(u, v)| {
+            self.0.axpy(α, u, β);
+            self.1.axpy(α, v, β);
+        })
     }
 
     fn copy_from<I: Instance<Pair<U, V>>>(&mut self, x: I) {
-        let Pair(u, v) = x.decompose();
-        self.0.copy_from(u);
-        self.1.copy_from(v);
+        x.eval_decompose(|Pair(u, v)| {
+            self.0.copy_from(u);
+            self.1.copy_from(v);
+        })
     }
 
     fn scale_from<I: Instance<Pair<U, V>>>(&mut self, α: F, x: I) {
-        let Pair(u, v) = x.decompose();
-        self.0.scale_from(α, u);
-        self.1.scale_from(α, v);
-    }
-
-    /// Return a similar zero as `self`.
-    fn similar_origin(&self) -> Self::Owned {
-        Pair(self.0.similar_origin(), self.1.similar_origin())
+        x.eval_decompose(|Pair(u, v)| {
+            self.0.scale_from(α, u);
+            self.1.scale_from(α, v);
+        })
     }
 
     /// Set self to zero.
@@ -332,6 +397,7 @@
 pub struct PairDecomposition<D, Q>(D, Q);
 
 impl<A: Space, B: Space> Space for Pair<A, B> {
+    type Principal = Pair<A::Principal, B::Principal>;
     type Decomp = PairDecomposition<A::Decomp, B::Decomp>;
 }
 
@@ -367,25 +433,16 @@
     V: Instance<B, Q>,
 {
     #[inline]
-    fn decompose<'b>(
-        self,
-    ) -> <PairDecomposition<D, Q> as Decomposition<Pair<A, B>>>::Decomposition<'b>
+    fn eval_ref<'b, R>(&'b self, f: impl FnOnce(Pair<D::Reference<'b>, Q::Reference<'b>>) -> R) -> R
     where
+        Pair<A, B>: 'b,
         Self: 'b,
-        Pair<A, B>: 'b,
     {
-        Pair(self.0.decompose(), self.1.decompose())
+        self.0.eval_ref(|a| self.1.eval_ref(|b| f(Pair(a, b))))
     }
 
     #[inline]
-    fn ref_instance(
-        &self,
-    ) -> <PairDecomposition<D, Q> as Decomposition<Pair<A, B>>>::Reference<'_> {
-        Pair(self.0.ref_instance(), self.1.ref_instance())
-    }
-
-    #[inline]
-    fn cow<'b>(self) -> MyCow<'b, Pair<A, B>>
+    fn cow<'b>(self) -> MyCow<'b, Pair<A::Principal, B::Principal>>
     where
         Self: 'b,
     {
@@ -393,9 +450,17 @@
     }
 
     #[inline]
-    fn own(self) -> Pair<A, B> {
+    fn own(self) -> Pair<A::Principal, B::Principal> {
         Pair(self.0.own(), self.1.own())
     }
+
+    #[inline]
+    fn decompose<'b>(self) -> Pair<D::Decomposition<'b>, Q::Decomposition<'b>>
+    where
+        Self: 'b,
+    {
+        Pair(self.0.decompose(), self.1.decompose())
+    }
 }
 
 impl<'a, A, B, U, V, D, Q> Instance<Pair<A, B>, PairDecomposition<D, Q>> for &'a Pair<U, V>
@@ -409,29 +474,16 @@
     &'a U: Instance<A, D>,
     &'a V: Instance<B, Q>,
 {
-    #[inline]
-    fn decompose<'b>(
-        self,
-    ) -> <PairDecomposition<D, Q> as Decomposition<Pair<A, B>>>::Decomposition<'b>
+    fn eval_ref<'b, R>(&'b self, f: impl FnOnce(Pair<D::Reference<'b>, Q::Reference<'b>>) -> R) -> R
     where
+        Pair<A, B>: 'b,
         Self: 'b,
-        Pair<A, B>: 'b,
     {
-        Pair(
-            D::lift(self.0.ref_instance()),
-            Q::lift(self.1.ref_instance()),
-        )
+        self.0.eval_ref(|a| self.1.eval_ref(|b| f(Pair(a, b))))
     }
 
     #[inline]
-    fn ref_instance(
-        &self,
-    ) -> <PairDecomposition<D, Q> as Decomposition<Pair<A, B>>>::Reference<'_> {
-        Pair(self.0.ref_instance(), self.1.ref_instance())
-    }
-
-    #[inline]
-    fn cow<'b>(self) -> MyCow<'b, Pair<A, B>>
+    fn cow<'b>(self) -> MyCow<'b, Pair<A::Principal, B::Principal>>
     where
         Self: 'b,
     {
@@ -439,10 +491,19 @@
     }
 
     #[inline]
-    fn own(self) -> Pair<A, B> {
+    fn own(self) -> Pair<A::Principal, B::Principal> {
         let Pair(ref u, ref v) = self;
         Pair(u.own(), v.own())
     }
+
+    #[inline]
+    fn decompose<'b>(self) -> Pair<D::Decomposition<'b>, Q::Decomposition<'b>>
+    where
+        Self: 'b,
+    {
+        let Pair(u, v) = self;
+        Pair(u.decompose(), v.decompose())
+    }
 }
 
 impl<A, B, D, Q> DecompositionMut<Pair<A, B>> for PairDecomposition<D, Q>
@@ -492,18 +553,63 @@
     }
 }
 
-impl<F, A, B, ExpA, ExpB, ExpJ> Norm<F, PairNorm<ExpA, ExpB, ExpJ>> for Pair<A, B>
+impl<F, A, B, ExpA, ExpB, ExpJ> Norm<PairNorm<ExpA, ExpB, ExpJ>, F> for Pair<A, B>
+where
+    F: Num,
+    ExpA: NormExponent,
+    ExpB: NormExponent,
+    ExpJ: NormExponent,
+    A: Norm<ExpA, F>,
+    B: Norm<ExpB, F>,
+    Loc<2, F>: Norm<ExpJ, F>,
+{
+    fn norm(&self, PairNorm(expa, expb, expj): PairNorm<ExpA, ExpB, ExpJ>) -> F {
+        Loc([self.0.norm(expa), self.1.norm(expb)]).norm(expj)
+    }
+}
+
+impl<F, A, B, ExpA, ExpB, ExpJ> Dist<PairNorm<ExpA, ExpB, ExpJ>, F> for Pair<A, B>
 where
     F: Num,
     ExpA: NormExponent,
     ExpB: NormExponent,
     ExpJ: NormExponent,
-    A: Norm<F, ExpA>,
-    B: Norm<F, ExpB>,
-    Loc<F, 2>: Norm<F, ExpJ>,
+    A: Dist<ExpA, F>,
+    B: Dist<ExpB, F>,
+    Loc<2, F>: Norm<ExpJ, F>,
 {
-    fn norm(&self, PairNorm(expa, expb, expj): PairNorm<ExpA, ExpB, ExpJ>) -> F {
-        Loc([self.0.norm(expa), self.1.norm(expb)]).norm(expj)
+    fn dist<I: Instance<Self>>(
+        &self,
+        x: I,
+        PairNorm(expa, expb, expj): PairNorm<ExpA, ExpB, ExpJ>,
+    ) -> F {
+        x.eval_decompose(|Pair(x1, x2)| {
+            Loc([self.0.dist(x1, expa), self.1.dist(x2, expb)]).norm(expj)
+        })
+    }
+}
+
+impl<F, A, B> Norm<L2, F> for Pair<A, B>
+where
+    F: Num,
+    A: Norm<L2, F>,
+    B: Norm<L2, F>,
+    Loc<2, F>: Norm<L2, F>,
+{
+    fn norm(&self, _: L2) -> F {
+        Loc([self.0.norm(L2), self.1.norm(L2)]).norm(L2)
+    }
+}
+
+impl<F, A, B> Dist<L2, F> for Pair<A, B>
+where
+    F: Num,
+    A: Dist<L2, F>,
+    B: Dist<L2, F>,
+    Loc<2, F>: Norm<L2, F>,
+{
+    fn dist<I: Instance<Self>>(&self, x: I, _: L2) -> F {
+        x.eval_decompose(|Pair(x1, x2)| Loc([self.0.dist(x1, L2), self.1.dist(x2, L2)]).norm(L2))
     }
 }
 
@@ -531,4 +637,72 @@
     B: HasDual<F>,
 {
     type DualSpace = Pair<A::DualSpace, B::DualSpace>;
+
+    fn dual_origin(&self) -> <Self::DualSpace as VectorSpace>::PrincipalV {
+        Pair(self.0.dual_origin(), self.1.dual_origin())
+    }
 }
+
+#[cfg(feature = "pyo3")]
+mod python {
+    use super::Pair;
+    use pyo3::conversion::FromPyObject;
+    use pyo3::types::{PyAny, PyTuple};
+    use pyo3::{Borrowed, Bound, IntoPyObject, PyErr, Python};
+
+    impl<'py, A, B> IntoPyObject<'py> for Pair<A, B>
+    where
+        A: IntoPyObject<'py>,
+        B: IntoPyObject<'py>,
+    {
+        type Target = PyTuple;
+        type Error = PyErr;
+        type Output = Bound<'py, Self::Target>;
+
+        fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
+            (self.0, self.1).into_pyobject(py)
+        }
+    }
+
+    /*
+    impl<'a, 'py, A, B> IntoPyObject<'py> for &'a mut Pair<A, B>
+    where
+        &'a mut A: IntoPyObject<'py>,
+        &'a mut B: IntoPyObject<'py>,
+    {
+        type Target = PyTuple;
+        type Error = PyErr;
+        type Output = Bound<'py, Self::Target>;
+
+        fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
+            (&mut self.0, &mut self.1).into_pyobject(py)
+        }
+    }
+
+    impl<'a, 'py, A, B> IntoPyObject<'py> for &'a Pair<A, B>
+    where
+        &'a A: IntoPyObject<'py>,
+        &'a B: IntoPyObject<'py>,
+    {
+        type Target = PyTuple;
+        type Error = PyErr;
+        type Output = Bound<'py, Self::Target>;
+
+        fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
+            (&self.0, &self.1).into_pyobject(py)
+        }
+    }
+    */
+
+    impl<'a, 'py, A, B> FromPyObject<'a, 'py> for Pair<A, B>
+    where
+        A: Clone + FromPyObject<'a, 'py>,
+        B: Clone + FromPyObject<'a, 'py>,
+    {
+        type Error = PyErr;
+
+        fn extract(ob: Borrowed<'a, 'py, PyAny>) -> Result<Self, PyErr> {
+            FromPyObject::extract(ob).map(|(a, b)| Pair(a, b))
+        }
+    }
+}

mercurial