Replace Instance ref_instance and decompose by eval_* for flexibility dev

Mon, 12 May 2025 20:40:14 -0500

author
Tuomo Valkonen <tuomov@iki.fi>
date
Mon, 12 May 2025 20:40:14 -0500
branch
dev
changeset 133
2b13f8a0c8ba
parent 132
89371dc4d637
child 134
d65377920b00
child 136
22fd33834ab7
child 138
593912dc3293

Replace Instance ref_instance and decompose by eval_* for flexibility

src/direct_product.rs file | annotate | diff | comparison | revisions
src/discrete_gradient.rs file | annotate | diff | comparison | revisions
src/fe_model/p2_local_model.rs file | annotate | diff | comparison | revisions
src/instance.rs file | annotate | diff | comparison | revisions
src/linops.rs file | annotate | diff | comparison | revisions
src/loc.rs file | annotate | diff | comparison | revisions
src/mapping.rs file | annotate | diff | comparison | revisions
src/nalgebra_support.rs file | annotate | diff | comparison | revisions
src/operator_arithmetic.rs file | annotate | diff | comparison | revisions
src/sets.rs file | annotate | diff | comparison | revisions
--- a/src/direct_product.rs	Mon May 12 19:30:41 2025 -0500
+++ b/src/direct_product.rs	Mon May 12 20:40:14 2025 -0500
@@ -285,8 +285,7 @@
         + Neg<Output = <Self as AXPY>::Owned>,
 {
     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 {
@@ -294,8 +293,7 @@
     }
 
     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))
     }
 }
 
@@ -316,21 +314,24 @@
     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);
+        x.eval_decompose(|Pair(u, v)| {
+            self.0.scale_from(α, u);
+            self.1.scale_from(α, v);
+        })
     }
 
     /// Return a similar zero as `self`.
@@ -384,22 +385,28 @@
     U: Instance<A, D>,
     V: Instance<B, Q>,
 {
-    #[inline]
-    fn decompose<'b>(
+    fn eval_decompose<'b, R>(
         self,
-    ) -> <PairDecomposition<D, Q> as Decomposition<Pair<A, B>>>::Decomposition<'b>
+        f: impl FnOnce(Pair<D::Decomposition<'b>, Q::Decomposition<'b>>) -> R,
+    ) -> R
     where
+        Pair<A, B>: 'b,
         Self: 'b,
-        Pair<A, B>: 'b,
     {
-        Pair(self.0.decompose(), self.1.decompose())
+        self.0
+            .eval_decompose(|a| self.1.eval_decompose(|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())
+    fn eval_ref_decompose<'b, R>(
+        &'b self,
+        f: impl FnOnce(Pair<D::Reference<'b>, Q::Reference<'b>>) -> R,
+    ) -> R
+    where
+        Pair<A, B>: 'b,
+        Self: 'b,
+    {
+        self.0
+            .eval_ref_decompose(|a| self.1.eval_ref_decompose(|b| f(Pair(a, b))))
     }
 
     #[inline]
@@ -427,25 +434,30 @@
     &'a U: Instance<A, D>,
     &'a V: Instance<B, Q>,
 {
-    #[inline]
-    fn decompose<'b>(
+    fn eval_decompose<'b, R>(
         self,
-    ) -> <PairDecomposition<D, Q> as Decomposition<Pair<A, B>>>::Decomposition<'b>
+        f: impl FnOnce(Pair<D::Decomposition<'b>, Q::Decomposition<'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_decompose(|a| {
+            self.1
+                .eval_ref_decompose(|b| f(Pair(D::lift(a), Q::lift(b))))
+        })
     }
 
-    #[inline]
-    fn ref_instance(
-        &self,
-    ) -> <PairDecomposition<D, Q> as Decomposition<Pair<A, B>>>::Reference<'_> {
-        Pair(self.0.ref_instance(), self.1.ref_instance())
+    fn eval_ref_decompose<'b, R>(
+        &'b self,
+        f: impl FnOnce(Pair<D::Reference<'b>, Q::Reference<'b>>) -> R,
+    ) -> R
+    where
+        Pair<A, B>: 'b,
+        Self: 'b,
+    {
+        self.0
+            .eval_ref_decompose(|a| self.1.eval_ref_decompose(|b| f(Pair(a, b))))
     }
 
     #[inline]
--- a/src/discrete_gradient.rs	Mon May 12 19:30:41 2025 -0500
+++ b/src/discrete_gradient.rs	Mon May 12 20:40:14 2025 -0500
@@ -316,7 +316,7 @@
         }
         let h = self.h;
         let m = self.len();
-        i.eval(|x| {
+        i.eval_decompose(|x| {
             assert_eq!(x.len(), N * m);
             for d in 0..N {
                 let v = x.generic_view((d * m, 0), (Dyn(m), U1));
--- a/src/fe_model/p2_local_model.rs	Mon May 12 19:30:41 2025 -0500
+++ b/src/fe_model/p2_local_model.rs	Mon May 12 20:40:14 2025 -0500
@@ -32,9 +32,10 @@
 impl<'a, F: Float> Set<Loc<1, F>> for RealInterval<F> {
     #[inline]
     fn contains<I: Instance<Loc<1, F>>>(&self, z: I) -> bool {
-        let &Loc([x]) = z.ref_instance();
-        let &[Loc([x0]), Loc([x1])] = &self.0;
-        (x0 < x && x < x1) || (x1 < x && x < x0)
+        z.eval_ref_decompose(|&Loc([x])| {
+            let &[Loc([x0]), Loc([x1])] = &self.0;
+            (x0 < x && x < x1) || (x1 < x && x < x0)
+        })
     }
 }
 
--- a/src/instance.rs	Mon May 12 19:30:41 2025 -0500
+++ b/src/instance.rs	Mon May 12 20:40:14 2025 -0500
@@ -25,7 +25,10 @@
 
 impl<'b, X> MyCow<'b, X> {
     #[inline]
-    pub fn into_owned(self) -> X where X : Clone {
+    pub fn into_owned(self) -> X
+    where
+        X: Clone,
+    {
         match self {
             EitherDecomp::Owned(x) => x,
             EitherDecomp::Borrowed(x) => x.clone(),
@@ -34,9 +37,9 @@
 }
 
 /// Trait for abitrary mathematical spaces.
-pub trait Space : Instance<Self, Self::Decomp> {
+pub trait Space: Instance<Self, Self::Decomp> {
     /// Default decomposition for the space
-    type Decomp : Decomposition<Self>;
+    type Decomp: Decomposition<Self>;
 }
 
 #[macro_export]
@@ -58,17 +61,21 @@
                   f32 f64);
 
 /// Marker type for decompositions to be used with [`Instance`].
-pub trait Decomposition<X : Space> : Sized {
+pub trait Decomposition<X: Space>: Sized {
     /// Possibly owned form of the decomposition
-    type Decomposition<'b> : Instance<X, Self> where X : 'b;
+    type Decomposition<'b>: Instance<X, Self>
+    where
+        X: 'b;
     /// Unlikely owned form of the decomposition.
     /// Type for a lightweight intermediate conversion that does not own the original variable.
     /// Usually this is just a reference, but may also be a lightweight structure that
     /// contains references; see the implementation for [`crate::direct_product::Pair`].
-    type Reference<'b> : Instance<X, Self> + Copy where X : 'b;
+    type Reference<'b>: Instance<X, Self> + Copy
+    where
+        X: 'b;
 
     /// Left the lightweight reference type into a full decomposition type.
-    fn lift<'b>(r : Self::Reference<'b>) -> Self::Decomposition<'b>;
+    fn lift<'b>(r: Self::Reference<'b>) -> Self::Decomposition<'b>;
 }
 
 /// Most common [`Decomposition`] (into `Either<X, &'b X>`) that allows working with owned
@@ -76,12 +83,18 @@
 #[derive(Copy, Clone, Debug)]
 pub struct BasicDecomposition;
 
-impl<X : Space + Clone> Decomposition<X> for BasicDecomposition {
-    type Decomposition<'b> = MyCow<'b, X> where X : 'b;
-    type Reference<'b> = &'b X where X : 'b;
+impl<X: Space + Clone> Decomposition<X> for BasicDecomposition {
+    type Decomposition<'b>
+        = MyCow<'b, X>
+    where
+        X: 'b;
+    type Reference<'b>
+        = &'b X
+    where
+        X: 'b;
 
     #[inline]
-    fn lift<'b>(r : Self::Reference<'b>) -> Self::Decomposition<'b> {
+    fn lift<'b>(r: Self::Reference<'b>) -> Self::Decomposition<'b> {
         MyCow::Borrowed(r)
     }
 }
@@ -91,13 +104,23 @@
 /// generalises [`std::borrow::ToOwned`], [`std::borrow::Borrow`], and [`std::borrow::Cow`].
 ///
 /// This is used, for example, by [`crate::mapping::Mapping::apply`].
-pub trait Instance<X : Space, D = <X as Space>::Decomp> : Sized where D : Decomposition<X> {
-    /// Decomposes self according to `decomposer`.
-    fn decompose<'b>(self) -> D::Decomposition<'b>
-    where Self : 'b, X : 'b;
-  
-    /// Returns a lightweight instance of `self`.
-    fn ref_instance(&self) -> D::Reference<'_>;
+pub trait Instance<X: Space, D = <X as Space>::Decomp>: Sized
+where
+    D: Decomposition<X>,
+{
+    /// Decomposes self according to `decomposer`, and evaluate `f` on the result.
+    /// Consumes self.
+    fn eval_decompose<'b, R>(self, f: impl FnOnce(D::Decomposition<'b>) -> R) -> R
+    where
+        X: 'b,
+        Self: 'b;
+
+    /// Does a light decomposition of self `decomposer`, and evaluates `f` on the result.
+    /// Does not consume self.
+    fn eval_ref_decompose<'b, R>(&'b self, f: impl FnOnce(D::Reference<'b>) -> R) -> R
+    where
+        X: 'b,
+        Self: 'b;
 
     /// Returns an owned instance of `X`, cloning or converting non-true instances when necessary.
     fn own(self) -> X;
@@ -107,16 +130,21 @@
     /// Returns an owned instance or reference to `X`, converting non-true instances when necessary.
     ///
     /// Default implementation uses [`Self::own`]. Consumes the input.
-    fn cow<'b>(self) -> MyCow<'b, X> where Self : 'b {
+    fn cow<'b>(self) -> MyCow<'b, X>
+    where
+        Self: 'b,
+    {
         MyCow::Owned(self.own())
     }
-    
+
     #[inline]
     /// Evaluates `f` on a reference to self.
     ///
     /// Default implementation uses [`Self::cow`]. Consumes the input.
-    fn eval<'b, R>(self, f : impl FnOnce(&X) -> R) -> R
-    where X : 'b, Self : 'b
+    fn eval<'b, R>(self, f: impl FnOnce(&X) -> R) -> R
+    where
+        X: 'b,
+        Self: 'b,
     {
         f(&*self.cow())
     }
@@ -125,12 +153,9 @@
     /// Evaluates `f` or `g` depending on whether a reference or owned value is available.
     ///
     /// Default implementation uses [`Self::cow`]. Consumes the input.
-    fn either<'b, R>(
-        self,
-        f : impl FnOnce(X) -> R,
-        g : impl FnOnce(&X) -> R
-    ) -> R
-    where Self : 'b
+    fn either<'b, R>(self, f: impl FnOnce(X) -> R, g: impl FnOnce(&X) -> R) -> R
+    where
+        Self: 'b,
     {
         match self.cow() {
             EitherDecomp::Owned(x) => f(x),
@@ -139,13 +164,23 @@
     }
 }
 
-
-impl<X : Space + Clone> Instance<X, BasicDecomposition> for X {
+impl<X: Space + Clone> Instance<X, BasicDecomposition> for X {
     #[inline]
-    fn decompose<'b>(self) -> <BasicDecomposition as Decomposition<X>>::Decomposition<'b>
-    where Self : 'b, X : 'b
+    fn eval_decompose<'b, R>(self, f: impl FnOnce(MyCow<'b, X>) -> R) -> R
+    where
+        X: 'b,
+        Self: 'b,
     {
-        MyCow::Owned(self)
+        f(MyCow::Owned(self))
+    }
+
+    #[inline]
+    fn eval_ref_decompose<'b, R>(&'b self, f: impl FnOnce(&'b X) -> R) -> R
+    where
+        X: 'b,
+        Self: 'b,
+    {
+        f(self)
     }
 
     #[inline]
@@ -154,22 +189,31 @@
     }
 
     #[inline]
-    fn cow<'b>(self) -> MyCow<'b, X> where Self : 'b {
+    fn cow<'b>(self) -> MyCow<'b, X>
+    where
+        Self: 'b,
+    {
         MyCow::Owned(self)
     }
+}
+
+impl<'a, X: Space + Clone> Instance<X, BasicDecomposition> for &'a X {
+    #[inline]
+    fn eval_decompose<'b, R>(self, f: impl FnOnce(MyCow<'b, X>) -> R) -> R
+    where
+        X: 'b,
+        Self: 'b,
+    {
+        f(MyCow::Borrowed(self))
+    }
 
     #[inline]
-    fn ref_instance(&self) -> <BasicDecomposition as Decomposition<X>>::Reference<'_> {
-        self
-    }
-}
-
-impl<'a, X : Space + Clone> Instance<X, BasicDecomposition> for &'a X {
-    #[inline]
-    fn decompose<'b>(self) -> <BasicDecomposition as Decomposition<X>>::Decomposition<'b>
-    where Self : 'b, X : 'b
+    fn eval_ref_decompose<'b, R>(&'b self, f: impl FnOnce(&'b X) -> R) -> R
+    where
+        X: 'b,
+        Self: 'b,
     {
-        MyCow::Borrowed(self)
+        f(*self)
     }
 
     #[inline]
@@ -178,22 +222,31 @@
     }
 
     #[inline]
-    fn cow<'b>(self) -> MyCow<'b, X> where Self : 'b {
+    fn cow<'b>(self) -> MyCow<'b, X>
+    where
+        Self: 'b,
+    {
         MyCow::Borrowed(self)
     }
+}
+
+impl<'a, X: Space + Clone> Instance<X, BasicDecomposition> for &'a mut X {
+    #[inline]
+    fn eval_decompose<'b, R>(self, f: impl FnOnce(MyCow<'b, X>) -> R) -> R
+    where
+        X: 'b,
+        Self: 'b,
+    {
+        f(EitherDecomp::Borrowed(self))
+    }
 
     #[inline]
-    fn ref_instance(&self) -> <BasicDecomposition as Decomposition<X>>::Reference<'_> {
-        *self
-    }
-}
-
-impl<'a, X : Space + Clone> Instance<X, BasicDecomposition> for &'a mut X {
-    #[inline]
-    fn  decompose<'b>(self) -> <BasicDecomposition as Decomposition<X>>::Decomposition<'b>
-    where Self : 'b, X : 'b
+    fn eval_ref_decompose<'b, R>(&'b self, f: impl FnOnce(&'b X) -> R) -> R
+    where
+        X: 'b,
+        Self: 'b,
     {
-        EitherDecomp::Borrowed(self)
+        f(*self)
     }
 
     #[inline]
@@ -202,81 +255,95 @@
     }
 
     #[inline]
-    fn cow<'b>(self) -> MyCow<'b, X> where Self : 'b, X : Clone {
+    fn cow<'b>(self) -> MyCow<'b, X>
+    where
+        Self: 'b,
+        X: Clone,
+    {
         EitherDecomp::Borrowed(self)
     }
+}
+
+impl<'a, X: Space + Clone> Instance<X, BasicDecomposition> for MyCow<'a, X> {
+    #[inline]
+    fn eval_decompose<'b, R>(self, f: impl FnOnce(MyCow<'b, X>) -> R) -> R
+    where
+        X: 'b,
+        Self: 'b,
+    {
+        f(self)
+    }
 
     #[inline]
-    fn ref_instance(&self) -> <BasicDecomposition as Decomposition<X>>::Reference<'_> {
-        *self
-    }
-}
-
-impl<'a, X : Space + Clone> Instance<X, BasicDecomposition> for MyCow<'a, X> {
-
-    #[inline]
-    fn  decompose<'b>(self) -> <BasicDecomposition as Decomposition<X>>::Decomposition<'b>
-    where Self : 'b, X : 'b
+    fn eval_ref_decompose<'b, R>(&'b self, f: impl FnOnce(&'b X) -> R) -> R
+    where
+        X: 'b,
+        Self: 'b,
     {
-        self
+        match self {
+            MyCow::Borrowed(a) => f(a),
+            MyCow::Owned(b) => f(&b),
+        }
     }
 
     #[inline]
     fn own(self) -> X {
         match self {
             MyCow::Borrowed(a) => a.own(),
-            MyCow::Owned(b) => b.own()
+            MyCow::Owned(b) => b.own(),
         }
     }
 
     #[inline]
-    fn cow<'b>(self) -> MyCow<'b, X> where Self : 'b {
+    fn cow<'b>(self) -> MyCow<'b, X>
+    where
+        Self: 'b,
+    {
         match self {
             MyCow::Borrowed(a) => a.cow(),
-            MyCow::Owned(b) => b.cow()
-        }
-    }
-
-    #[inline]
-    fn ref_instance(&self) -> <BasicDecomposition as Decomposition<X>>::Reference<'_> {
-        match self {
-            MyCow::Borrowed(a) => a,
-            MyCow::Owned(b) => &b,
+            MyCow::Owned(b) => b.cow(),
         }
     }
 }
 
 /// Marker type for mutable decompositions to be used with [`InstanceMut`].
-pub trait DecompositionMut<X : Space> : Sized {
-    type ReferenceMut<'b> : InstanceMut<X, Self> where X : 'b;
+pub trait DecompositionMut<X: Space>: Sized {
+    type ReferenceMut<'b>: InstanceMut<X, Self>
+    where
+        X: 'b;
 }
 
-
 /// Helper trait for functions to work with mutable references.
-pub trait InstanceMut<X : Space , D = <X as Space>::Decomp> : Sized where D : DecompositionMut<X> {
+pub trait InstanceMut<X: Space, D = <X as Space>::Decomp>: Sized
+where
+    D: DecompositionMut<X>,
+{
     /// Returns a mutable decomposition of self.
     fn ref_instance_mut(&mut self) -> D::ReferenceMut<'_>;
 }
 
-impl<X : Space> DecompositionMut<X> for BasicDecomposition {
-    type ReferenceMut<'b> = &'b mut X where X : 'b;
+impl<X: Space> DecompositionMut<X> for BasicDecomposition {
+    type ReferenceMut<'b>
+        = &'b mut X
+    where
+        X: 'b;
 }
 
 /// This impl may seem pointless, but allows throwaway mutable scratch variables
-impl<'a, X : Space> InstanceMut<X, BasicDecomposition> for X {
+impl<'a, X: Space> InstanceMut<X, BasicDecomposition> for X {
     #[inline]
-    fn ref_instance_mut(&mut self)
-        -> <BasicDecomposition as DecompositionMut<X>>::ReferenceMut<'_>
-    {
+    fn ref_instance_mut(
+        &mut self,
+    ) -> <BasicDecomposition as DecompositionMut<X>>::ReferenceMut<'_> {
         self
     }
 }
 
-impl<'a, X : Space> InstanceMut<X, BasicDecomposition> for &'a mut X {
+impl<'a, X: Space> InstanceMut<X, BasicDecomposition> for &'a mut X {
     #[inline]
-    fn ref_instance_mut(&mut self)
-        -> <BasicDecomposition as DecompositionMut<X>>::ReferenceMut<'_>
-    {
+    fn ref_instance_mut(
+        &mut self,
+    ) -> <BasicDecomposition as DecompositionMut<X>>::ReferenceMut<'_> {
         self
     }
 }
--- a/src/linops.rs	Mon May 12 19:30:41 2025 -0500
+++ b/src/linops.rs	Mon May 12 20:40:14 2025 -0500
@@ -530,8 +530,7 @@
     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)
+        x.eval_decompose(|Pair(a, b)| self.0.apply(a) + self.1.apply(b))
     }
 }
 
@@ -556,22 +555,25 @@
     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);
+        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);
+        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);
+        x.eval_decompose(|Pair(u, v)| {
+            self.0.apply_add(y, u);
+            self.1.apply_add(y, v);
+        })
     }
 }
 
@@ -588,7 +590,7 @@
     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))
+        Pair(a.eval_ref_decompose(|r| self.0.apply(r)), self.1.apply(a))
     }
 }
 
@@ -609,18 +611,18 @@
     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(), β);
+        x.eval_ref_decompose(|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());
+        x.eval_ref_decompose(|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());
+        x.eval_ref_decompose(|r| self.0.apply_add(&mut y.0, r));
         self.1.apply_add(&mut y.1, x);
     }
 }
@@ -732,8 +734,7 @@
     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))
+        x.eval_decompose(|Pair(a, b)| Pair(self.0.apply(a), self.1.apply(b)))
     }
 }
 
@@ -758,22 +759,25 @@
     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, β);
+        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);
+        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);
+        x.eval_decompose(|Pair(u, v)| {
+            self.0.apply_add(&mut y.0, u);
+            self.1.apply_add(&mut y.1, v);
+        })
     }
 }
 
--- a/src/loc.rs	Mon May 12 19:30:41 2025 -0500
+++ b/src/loc.rs	Mon May 12 20:40:14 2025 -0500
@@ -437,10 +437,12 @@
     /// Use [`nalgebra`] for larger vectors.
     #[inline]
     fn dot<I: Instance<Self>>(&self, other: I) -> F {
-        self.0
-            .iter()
-            .zip(other.ref_instance().0.iter())
-            .fold(F::ZERO, |m, (&v, &w)| m + v * w)
+        other.eval_ref_decompose(|r| {
+            self.0
+                .iter()
+                .zip(r.0.iter())
+                .fold(F::ZERO, |m, (&v, &w)| m + v * w)
+        })
     }
 
     /// This implementation is not stabilised as it's meant to be used for very small vectors.
@@ -451,12 +453,12 @@
     }
 
     fn dist2_squared<I: Instance<Self>>(&self, other: I) -> F {
-        self.iter()
-            .zip(other.ref_instance().iter())
-            .fold(F::ZERO, |m, (&v, &w)| {
+        other.eval_ref_decompose(|r| {
+            self.iter().zip(r.iter()).fold(F::ZERO, |m, (&v, &w)| {
                 let d = v - w;
                 m + d * d
             })
+        })
     }
 
     #[inline]
@@ -472,9 +474,10 @@
     #[inline]
     fn dist2<I: Instance<Self>>(&self, other: I) -> F {
         // Optimisation for N==1 that avoids squaring and square rooting.
-        let otherr = other.ref_instance();
         if N == 1 {
-            unsafe { *self.0.get_unchecked(0) - *otherr.0.get_unchecked(0) }.abs()
+            other.eval_ref_decompose(|r| {
+                unsafe { *self.0.get_unchecked(0) - *r.0.get_unchecked(0) }.abs()
+            })
         } else {
             self.dist2_squared(other).sqrt()
         }
@@ -631,9 +634,11 @@
 impl<F: Float, const N: usize> Dist<F, L1> for Loc<N, F> {
     #[inline]
     fn dist<I: Instance<Self>>(&self, other: I, _: L1) -> F {
-        self.iter()
-            .zip(other.ref_instance().iter())
-            .fold(F::ZERO, |m, (&v, &w)| m + (v - w).abs())
+        other.eval_ref_decompose(|r| {
+            self.iter()
+                .zip(r.iter())
+                .fold(F::ZERO, |m, (&v, &w)| m + (v - w).abs())
+        })
     }
 }
 
@@ -657,9 +662,11 @@
 impl<F: Float, const N: usize> Dist<F, Linfinity> for Loc<N, F> {
     #[inline]
     fn dist<I: Instance<Self>>(&self, other: I, _: Linfinity) -> F {
-        self.iter()
-            .zip(other.ref_instance().iter())
-            .fold(F::ZERO, |m, (&v, &w)| m.max((v - w).abs()))
+        other.eval_ref_decompose(|r| {
+            self.iter()
+                .zip(r.iter())
+                .fold(F::ZERO, |m, (&v, &w)| m.max((v - w).abs()))
+        })
     }
 }
 
@@ -702,7 +709,7 @@
     type Codomain = F;
 
     fn apply<I: Instance<Loc<N, F>>>(&self, x: I) -> Self::Codomain {
-        x.eval(|x̃| self.dot(x̃))
+        x.eval_decompose(|x̃| self.dot(x̃))
     }
 }
 
--- a/src/mapping.rs	Mon May 12 19:30:41 2025 -0500
+++ b/src/mapping.rs	Mon May 12 20:40:14 2025 -0500
@@ -308,8 +308,9 @@
         //     inner: self.inner.differential_impl(x),
         //     intermediate_norm_exponent: self.intermediate_norm_exponent,
         // }
+
         self.outer
-            .differential_impl(self.inner.apply(x.ref_instance()))
+            .differential_impl(x.eval_ref_decompose(|r| self.inner.apply(r)))
             * self.inner.differential_impl(x)
     }
 }
--- a/src/nalgebra_support.rs	Mon May 12 19:30:41 2025 -0500
+++ b/src/nalgebra_support.rs	Mon May 12 20:40:14 2025 -0500
@@ -224,7 +224,7 @@
 {
     #[inline]
     fn dot<I: Instance<Self>>(&self, other: I) -> E {
-        Vector::<E, M, S>::dot(self, other.ref_instance())
+        other.eval_ref_decompose(|r| Vector::<E, M, S>::dot(self, r))
     }
 
     #[inline]
@@ -234,7 +234,7 @@
 
     #[inline]
     fn dist2_squared<I: Instance<Self>>(&self, other: I) -> E {
-        metric_distance_squared(self, other.ref_instance())
+        other.eval_ref_decompose(|r| metric_distance_squared(self, r))
     }
 }
 
@@ -305,7 +305,7 @@
 {
     #[inline]
     fn dist<I: Instance<Self>>(&self, other: I, _: L1) -> E {
-        nalgebra::Norm::metric_distance(&LpNorm(1), self, other.ref_instance())
+        other.eval_ref_decompose(|r| nalgebra::Norm::metric_distance(&LpNorm(1), self, r))
     }
 }
 
@@ -331,7 +331,7 @@
 {
     #[inline]
     fn dist<I: Instance<Self>>(&self, other: I, _: L2) -> E {
-        nalgebra::Norm::metric_distance(&LpNorm(2), self, other.ref_instance())
+        other.eval_ref_decompose(|r| nalgebra::Norm::metric_distance(&LpNorm(2), self, r))
     }
 }
 
@@ -357,7 +357,7 @@
 {
     #[inline]
     fn dist<I: Instance<Self>>(&self, other: I, _: Linfinity) -> E {
-        nalgebra::Norm::metric_distance(&UniformNorm, self, other.ref_instance())
+        other.eval_ref_decompose(|r| nalgebra::Norm::metric_distance(&UniformNorm, self, r))
     }
 }
 
--- a/src/operator_arithmetic.rs	Mon May 12 19:30:41 2025 -0500
+++ b/src/operator_arithmetic.rs	Mon May 12 20:40:14 2025 -0500
@@ -99,8 +99,7 @@
     type Codomain = M::Codomain;
 
     fn apply<I: Instance<Domain>>(&self, x: I) -> Self::Codomain {
-        let xr = x.ref_instance();
-        self.0.iter().map(|c| c.apply(xr)).sum()
+        x.eval_ref_decompose(|xr| self.0.iter().map(|c| c.apply(xr)).sum())
     }
 }
 
@@ -113,7 +112,6 @@
     type Derivative = M::DerivativeDomain;
 
     fn differential_impl<I: Instance<Domain>>(&self, x: I) -> Self::Derivative {
-        let xr = x.ref_instance();
-        self.0.iter().map(|c| c.differential(xr)).sum()
+        x.eval_ref_decompose(|xr| self.0.iter().map(|c| c.differential(xr)).sum())
     }
 }
--- a/src/sets.rs	Mon May 12 19:30:41 2025 -0500
+++ b/src/sets.rs	Mon May 12 20:40:14 2025 -0500
@@ -36,10 +36,7 @@
     U: Num + PartialOrd + Sized,
 {
     fn contains<I: Instance<Loc<N, U>>>(&self, item: I) -> bool {
-        self.0
-            .iter()
-            .zip(item.ref_instance().iter())
-            .all(|(s, x)| s.contains(x))
+        item.eval_ref_decompose(|r| self.0.iter().zip(r.iter()).all(|(s, x)| s.contains(x)))
     }
 }
 
@@ -151,7 +148,6 @@
     F: Float,
 {
     fn contains<I: Instance<A>>(&self, item: I) -> bool {
-        let r = item.ref_instance();
-        self.0.iter().all(|halfspace| halfspace.contains(r))
+        item.eval_ref_decompose(|r| self.0.iter().all(|halfspace| halfspace.contains(r)))
     }
 }

mercurial