Extra reflexivity and hilbert-like requirements for Euclidean. Fuse Dot into Euclidean. dev

Sun, 22 Dec 2024 14:54:46 -0500

author
Tuomo Valkonen <tuomov@iki.fi>
date
Sun, 22 Dec 2024 14:54:46 -0500
branch
dev
changeset 63
f7b87d84864d
parent 62
d8305c9b6fdf
child 64
4f6ca107ccb1

Extra reflexivity and hilbert-like requirements for Euclidean. Fuse Dot into Euclidean.

src/bisection_tree/aggregator.rs file | annotate | diff | comparison | revisions
src/bisection_tree/btfn.rs file | annotate | diff | comparison | revisions
src/direct_product.rs file | annotate | diff | comparison | revisions
src/euclidean.rs file | annotate | diff | comparison | revisions
src/fe_model/p2_local_model.rs file | annotate | diff | comparison | revisions
src/loc.rs file | annotate | diff | comparison | revisions
src/nalgebra_support.rs file | annotate | diff | comparison | revisions
src/norms.rs file | annotate | diff | comparison | revisions
src/sets.rs file | annotate | diff | comparison | revisions
--- a/src/bisection_tree/aggregator.rs	Sat Dec 21 23:32:20 2024 -0500
+++ b/src/bisection_tree/aggregator.rs	Sun Dec 22 14:54:46 2024 -0500
@@ -4,6 +4,7 @@
 
 use crate::types::*;
 use crate::sets::Set;
+use crate::instance::Instance;
 
 /// Trait for aggregating information about a branch of a [bisection tree][super::BT].
 ///
@@ -136,10 +137,11 @@
 }
 
 impl<F : Float> Set<F> for Bounds<F> {
-    fn contains(&self, item : &F) -> bool {
+    fn contains<I : Instance<F>>(&self, item : I) -> bool {
+        let v = item.own();
         let &Bounds(l, u) = self;
         debug_assert!(l <= u);
-        l <= *item && *item <= u
+        l <= v && v <= u
     }
 }
 
--- a/src/bisection_tree/btfn.rs	Sat Dec 21 23:32:20 2024 -0500
+++ b/src/bisection_tree/btfn.rs	Sun Dec 22 14:54:46 2024 -0500
@@ -505,7 +505,7 @@
 ///
 /// `U` is the domain, generally [`Loc`]`<F, N>`, and `F` the type of floating point numbers.
 /// `Self` is generally a set of `U`, for example, [`Cube`]`<F, N>`.
-pub trait P2Minimise<U, F : Float> : Set<U> {
+pub trait P2Minimise<U : Space, F : Float> : Set<U> {
     /// Minimise `g` over the set presented by `Self`.
     ///
     /// The function returns `(x, v)` where `x` is the minimiser `v` an approximation of `g(x)`.
--- a/src/direct_product.rs	Sat Dec 21 23:32:20 2024 -0500
+++ b/src/direct_product.rs	Sun Dec 22 14:54:46 2024 -0500
@@ -10,7 +10,7 @@
 use serde::{Serialize, Deserialize};
 use crate::types::{Num, Float};
 use crate::{maybe_lifetime, maybe_ref};
-use crate::euclidean::{Dot, Euclidean};
+use crate::euclidean::Euclidean;
 use crate::instance::{Instance, InstanceMut, Decomposition, DecompositionMut, MyCow};
 use crate::mapping::Space;
 use crate::linops::AXPY;
@@ -237,18 +237,6 @@
 impl_pair_vectorspace_ops!((f32, f32), f32);
 impl_pair_vectorspace_ops!((f64, f64), f64);
 
-impl<A, B, U, V, F> Dot<Pair<U, V>, F> for Pair<A, B>
-where
-    A : Dot<U, F>,
-    B : Dot<V, F>,
-    F : Num
-{
-
-    fn dot(&self, Pair(ref u, ref v) : &Pair<U, V>) -> F {
-        self.0.dot(u) + self.1.dot(v)
-    }
-}
-
 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>
@@ -257,7 +245,7 @@
     B : Euclidean<F>,
     F : Float,
     PairOutput<F, A, B> : Euclidean<F>,
-    Self : Sized + Dot<Self,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>>
@@ -270,6 +258,15 @@
 {
     type Output = PairOutput<F, A, B>;
 
+    fn dot<I : Instance<Self>>(&self, other : I) -> F {
+        let Pair(u, v) = other.decompose();
+        self.0.dot(u) + self.1.dot(v)
+    }
+
+    fn norm2_squared(&self) -> F {
+        self.0.norm2_squared() + self.1.norm2_squared()
+    }
+
     fn dist2_squared(&self, Pair(ref u, ref v) : &Self) -> F {
         self.0.dist2_squared(u) + self.1.dist2_squared(v)
     }
--- a/src/euclidean.rs	Sat Dec 21 23:32:20 2024 -0500
+++ b/src/euclidean.rs	Sun Dec 22 14:54:46 2024 -0500
@@ -4,38 +4,35 @@
 
 use std::ops::{Mul, MulAssign, Div, DivAssign, Add, Sub, AddAssign, SubAssign, Neg};
 use crate::types::*;
-use crate::mapping::Space;
-
-/// Space (type) with a defined dot product.
-///
-/// `U` is the space of the multiplier, and `F` the space of scalars.
-/// Since `U` ≠ `Self`, this trait can also implement dual products.
-pub trait Dot<U, F> {
-    fn dot(&self, other : &U) -> F;
-}
+use crate::instance::Instance;
+use crate::norms::{HasDual, Reflexive};
 
 /// Space (type) with Euclidean and vector space structure
 ///
 /// The type should implement vector space operations (addition, subtraction, scalar
 /// multiplication and scalar division) along with their assignment versions, as well
-/// as the [`Dot`] product with respect to `Self`.
-pub trait Euclidean<F : Float> : Space + Dot<Self,F>
-        + Mul<F, Output=<Self as Euclidean<F>>::Output> + MulAssign<F>
-        + Div<F, Output=<Self as Euclidean<F>>::Output> + DivAssign<F>
-        + Add<Self, Output=<Self as Euclidean<F>>::Output>
-        + Sub<Self, Output=<Self as Euclidean<F>>::Output>
-        + for<'b> Add<&'b Self, Output=<Self as Euclidean<F>>::Output>
-        + for<'b> Sub<&'b Self, Output=<Self as Euclidean<F>>::Output>
-        + AddAssign<Self> + for<'b> AddAssign<&'b Self>
-        + SubAssign<Self> + for<'b> SubAssign<&'b Self>
-        + Neg<Output=<Self as Euclidean<F>>::Output> {
+/// as an inner product.
+pub trait Euclidean<F : Float> : HasDual<F, DualSpace=Self> + Reflexive<F>
+    + Mul<F, Output=<Self as Euclidean<F>>::Output> + MulAssign<F>
+    + Div<F, Output=<Self as Euclidean<F>>::Output> + DivAssign<F>
+    + Add<Self, Output=<Self as Euclidean<F>>::Output>
+    + Sub<Self, Output=<Self as Euclidean<F>>::Output>
+    + for<'b> Add<&'b Self, Output=<Self as Euclidean<F>>::Output>
+    + for<'b> Sub<&'b Self, Output=<Self as Euclidean<F>>::Output>
+    + AddAssign<Self> + for<'b> AddAssign<&'b Self>
+    + SubAssign<Self> + for<'b> SubAssign<&'b Self>
+    + Neg<Output=<Self as Euclidean<F>>::Output>
+{
     type Output : Euclidean<F>;
 
+    // Inner product
+    fn dot<I : Instance<Self>>(&self, other : I) -> F;
+
     /// Calculate the square of the 2-norm, $\frac{1}{2}\\|x\\|_2^2$, where `self` is $x$.
-    #[inline]
-    fn norm2_squared(&self) -> F {
-        self.dot(self)
-    }
+    ///
+    /// This is not automatically implemented to avoid imposing
+    /// `for <'a> &'a Self : Instance<Self>` trait bound bloat.
+    fn norm2_squared(&self) -> F;
 
     /// Calculate the square of the 2-norm divided by 2, $\frac{1}{2}\\|x\\|_2^2$,
     /// where `self` is $x$.
--- a/src/fe_model/p2_local_model.rs	Sat Dec 21 23:32:20 2024 -0500
+++ b/src/fe_model/p2_local_model.rs	Sun Dec 22 14:54:46 2024 -0500
@@ -6,7 +6,8 @@
 use crate::loc::Loc;
 use crate::sets::{Set,NPolygon,SpannedHalfspace};
 use crate::linsolve::*;
-use crate::euclidean::Dot;
+use crate::euclidean::Euclidean;
+use crate::instance::Instance;
 use super::base::{LocalModel,RealLocalModel};
 use crate::sets::Cube;
 use numeric_literals::replace_float_literals;
@@ -30,7 +31,8 @@
 
 impl<'a, F : Float> Set<Loc<F,1>> for RealInterval<F> {
     #[inline]
-    fn contains(&self, &Loc([x]) : &Loc<F,1>) -> bool {
+    fn contains<I : Instance<Loc<F, 1>>>(&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)
     }
@@ -38,11 +40,11 @@
 
 impl<'a, F : Float> Set<Loc<F,2>> for PlanarSimplex<F> {
     #[inline]
-    fn contains(&self, x : &Loc<F,2>) -> bool {
+    fn contains<I : Instance<Loc<F, 2>>>(&self, z : I) -> bool {
         let &[x0, x1, x2] = &self.0;
         NPolygon([[x0, x1].spanned_halfspace(),
                   [x1, x2].spanned_halfspace(),
-                  [x2, x0].spanned_halfspace()]).contains(x)
+                  [x2, x0].spanned_halfspace()]).contains(z)
     }
 }
 
--- a/src/loc.rs	Sat Dec 21 23:32:20 2024 -0500
+++ b/src/loc.rs	Sun Dec 22 14:54:46 2024 -0500
@@ -429,19 +429,17 @@
 domination!(Linfinity, L2);
 domination!(L2, L1);
 
-impl<F : Num,const N : usize> Dot<Loc<F, N>,F> for Loc<F, N> {
+impl<F : Float,const N : usize> Euclidean<F> for Loc<F, N> {
+    type Output = Self;
+
     /// This implementation is not stabilised as it's meant to be used for very small vectors.
     /// Use [`nalgebra`] for larger vectors.
     #[inline]
-    fn dot(&self, other : &Loc<F, N>) -> F {
+    fn dot<I : Instance<Self>>(&self, other : I) -> F {
         self.0.iter()
-              .zip(other.0.iter())
+              .zip(other.ref_instance().0.iter())
               .fold(F::ZERO, |m, (&v, &w)| m + v * w)
     }
-}
-
-impl<F : Float,const N : usize> Euclidean<F> for Loc<F, N> {
-    type Output = Self;
 
     /// This implementation is not stabilised as it's meant to be used for very small vectors.
     /// Use [`nalgebra`] for larger vectors.
@@ -696,7 +694,7 @@
     type Decomp = BasicDecomposition;
 }
 
-impl<F : Num, const N : usize> Mapping<Loc<F, N>> for Loc<F, N> {
+impl<F : Float, const N : usize> Mapping<Loc<F, N>> for Loc<F, N> {
     type Codomain = F;
 
     fn apply<I : Instance<Loc<F, N>>>(&self, x : I) -> Self::Codomain {
@@ -704,9 +702,9 @@
     }
 }
 
-impl<F : Num, const N : usize> Linear<Loc<F, N>> for Loc<F, N> { }
+impl<F : Float, const N : usize> Linear<Loc<F, N>> for Loc<F, N> { }
 
-impl<F : Num, const N : usize> AXPY<F, Loc<F, N>> for Loc<F, N> {
+impl<F : Float, const N : usize> AXPY<F, Loc<F, N>> for Loc<F, N> {
     type Owned = Self;
 
     #[inline]
--- a/src/nalgebra_support.rs	Sat Dec 21 23:32:20 2024 -0500
+++ b/src/nalgebra_support.rs	Sun Dec 22 14:54:46 2024 -0500
@@ -1,7 +1,7 @@
 /*!
 Integration with nalgebra.
 
-This module mainly implements [`Euclidean`], [`Norm`], [`Dot`], [`Linear`], etc. for [`nalgebra`]
+This module mainly implements [`Euclidean`], [`Norm`], [`Linear`], etc. for [`nalgebra`]
 matrices and vectors.
 It also provides [`ToNalgebraRealField`] as a vomit-inducingly ugly workaround to nalgebra
 force-feeding its own versions of the same basic mathematical methods on `f32` and `f64` as
@@ -153,20 +153,6 @@
     }
 }
 
-impl<E,M,S,Si> Dot<Vector<E,M,Si>,E>
-for Vector<E,M,S>
-where M : Dim,
-      E : Float + Scalar + Zero + One,
-      S : Storage<E,M>,
-      Si : Storage<E,M>,
-      DefaultAllocator : Allocator<M> {
-
-    #[inline]
-    fn dot(&self, other : &Vector<E,M,Si>) -> E {
-        Vector::<E,M,S>::dot(self, other)
-    }
-}
-
 /// This function is [`nalgebra::EuclideanNorm::metric_distance`] without the `sqrt`.
 #[inline]
 fn metric_distance_squared<T, R1, C1, S1, R2, C2, S2>(
@@ -200,7 +186,12 @@
       DefaultAllocator : Allocator<M> {
 
     type Output = OVector<E, M>;
-    
+
+    #[inline]
+    fn dot<I : Instance<Self>>(&self, other : I) -> E {
+        Vector::<E,M,S>::dot(self, other.ref_instance())
+    }
+
     #[inline]
     fn norm2_squared(&self) -> E {
         Vector::<E,M,S>::norm_squared(self)
--- a/src/norms.rs	Sat Dec 21 23:32:20 2024 -0500
+++ b/src/norms.rs	Sun Dec 22 14:54:46 2024 -0500
@@ -221,11 +221,16 @@
     type DualSpace : Normed<F>;
 }
 
+/// Automatically implemented trait for reflexive spaces
 pub trait Reflexive<F : Num = f64> : HasDual<F>
 where
     Self::DualSpace : HasDual<F, DualSpace = Self>
 { }
 
+impl<F : Num, X : HasDual<F>> Reflexive<F> for X
+where
+    X::DualSpace : HasDual<F, DualSpace = X>
+{ }
 
 pub trait HasDualExponent : NormExponent {
     type DualExp : NormExponent;
--- a/src/sets.rs	Sat Dec 21 23:32:20 2024 -0500
+++ b/src/sets.rs	Sun Dec 22 14:54:46 2024 -0500
@@ -3,19 +3,19 @@
 */
 
 use std::ops::{RangeFull,RangeFrom,Range,RangeInclusive,RangeTo,RangeToInclusive};
-use std::marker::PhantomData;
 use crate::types::*;
 use crate::loc::Loc;
-use crate::euclidean::Dot;
+use crate::euclidean::Euclidean;
+use crate::instance::{Space, Instance};
 use serde::Serialize;
 
 pub mod cube;
 pub use cube::Cube;
 
 /// Trait for arbitrary sets. The parameter `U` is the element type.
-pub trait Set<U> where U : ?Sized {
+pub trait Set<U> where U : Space {
     /// Check for element containment
-    fn contains(&self, item : &U) -> bool;
+    fn contains<I : Instance<U>>(&self, item : I) -> bool;
 }
 
 /// Additional ordering (besides [`PartialOrd`]) of a subfamily of sets:
@@ -31,22 +31,27 @@
 impl<U, const N : usize> Set<Loc<U, N>>
 for Cube<U,N>
 where U : Num + PartialOrd + Sized {
-    fn contains(&self, item : &Loc<U, N>) -> bool {
-        self.0.iter().zip(item.iter()).all(|(s, x)| s.contains(x))
+    fn contains<I : Instance<Loc<U, N>>>(&self, item : I) -> bool {
+        self.0.iter().zip(item.ref_instance().iter()).all(|(s, x)| s.contains(x))
     }
 }
 
-impl<U> Set<U> for RangeFull {
-    fn contains(&self, _item : &U) -> bool { true }
+impl<U : Space> Set<U> for RangeFull {
+    fn contains<I : Instance<U>>(&self, _item : I) -> bool { true }
 }
 
 macro_rules! impl_ranges {
     ($($range:ident),*) => { $(
-        impl<U,Idx> Set<U>
-        for $range<Idx>
-        where Idx : PartialOrd<U>, U : PartialOrd<Idx> + ?Sized, Idx : PartialOrd {
+        impl<U,Idx> Set<U> for $range<Idx>
+        where
+            Idx : PartialOrd<U>,
+            U : PartialOrd<Idx> + Space,
+            Idx : PartialOrd
+        {
             #[inline]
-            fn contains(&self, item : &U) -> bool { $range::contains(&self, item) }
+            fn contains<I : Instance<U>>(&self, item : I) -> bool {
+                item.eval(|x| $range::contains(&self, x))
+            }
         }
     )* }
 }
@@ -61,40 +66,39 @@
 /// `U` is the element type, `F` the floating point number type, and `A` the type of the
 /// orthogonal (dual) vectors. They need implement [`Dot<U, F>`].
 #[derive(Clone,Copy,Debug,Serialize,Eq,PartialEq)]
-pub struct Halfspace<A, F, U> where A : Dot<U, F>, F : Float {
+pub struct Halfspace<A, F> where A : Euclidean<F>, F : Float {
     pub orthogonal : A,
     pub offset : F,
-    _phantom : PhantomData<U>,
 }
 
-impl<A,F,U> Halfspace<A,F,U> where A : Dot<U,F>, F : Float {
+impl<A,F> Halfspace<A,F> where A : Euclidean<F>, F : Float {
     #[inline]
     pub fn new(orthogonal : A, offset : F) -> Self {
-        Halfspace{ orthogonal : orthogonal, offset : offset, _phantom : PhantomData }
+        Halfspace{ orthogonal : orthogonal, offset : offset }
     }
 }
 
 /// Trait for generating a halfspace spanned by another set `Self` of elements of type `U`.
-pub trait SpannedHalfspace<F, U> where  F : Float {
+pub trait SpannedHalfspace<F> where  F : Float {
     /// Type of the orthogonal vector describing the halfspace.
-    type A : Dot<U, F>;
+    type A : Euclidean<F>;
     /// Returns the halfspace spanned by this set.
-    fn spanned_halfspace(&self) -> Halfspace<Self::A, F, U>;
+    fn spanned_halfspace(&self) -> Halfspace<Self::A, F>;
 }
 
 // TODO: Gram-Schmidt for higher N.
-impl<F : Float> SpannedHalfspace<F,Loc<F, 1>> for [Loc<F, 1>; 2] {
+impl<F : Float> SpannedHalfspace<F> for [Loc<F, 1>; 2] {
     type A = Loc<F, 1>;
-    fn spanned_halfspace(&self) -> Halfspace<Self::A, F, Loc<F, 1>> {
+    fn spanned_halfspace(&self) -> Halfspace<Self::A, F> {
         let (x0, x1) = (self[0], self[1]);
         Halfspace::new(x1-x0, x0[0])
     }
 }
 
 // TODO: Gram-Schmidt for higher N.
-impl<F : Float> SpannedHalfspace<F,Loc<F, 2>> for [Loc<F, 2>; 2] {
+impl<F : Float> SpannedHalfspace<F> for [Loc<F, 2>; 2] {
     type A = Loc<F, 2>;
-    fn spanned_halfspace(&self) -> Halfspace<Self::A, F, Loc<F, 2>> {
+    fn spanned_halfspace(&self) -> Halfspace<Self::A, F> {
         let (x0, x1) = (&self[0], &self[1]);
         let d = x1 - x0;
         let orthog = loc![d[1], -d[0]]; // We don't normalise for efficiency
@@ -103,19 +107,29 @@
     }
 }
 
-impl<A,U,F> Set<U> for Halfspace<A,F,U> where A : Dot<U,F>, F : Float {
+impl<A,F> Set<A> for Halfspace<A,F>
+where
+    A : Euclidean<F>,
+    F : Float,
+{
     #[inline]
-    fn contains(&self, item : &U) -> bool {
+    fn contains<I : Instance<A>>(&self, item : I) -> bool {
         self.orthogonal.dot(item) >= self.offset
     }
 }
 
 /// Polygons defined by `N` `Halfspace`s.
 #[derive(Clone,Copy,Debug,Eq,PartialEq)]
-pub struct NPolygon<A, F, U, const N : usize>(pub [Halfspace<A,F,U>; N]) where A : Dot<U,F>, F : Float;
+pub struct NPolygon<A, F, const N : usize>(pub [Halfspace<A,F>; N])
+where A : Euclidean<F>, F : Float;
 
-impl<A,U,F,const N : usize> Set<U> for NPolygon<A,F,U,N> where A : Dot<U,F>, F : Float {
-    fn contains(&self, item : &U) -> bool {
-        self.0.iter().all(|halfspace| halfspace.contains(item))
+impl<A,F,const N : usize> Set<A> for NPolygon<A,F,N>
+where
+    A : Euclidean<F>,
+    F : Float,
+{
+    fn contains<I : Instance<A>>(&self, item : I) -> bool {
+        let r = item.ref_instance();
+        self.0.iter().all(|halfspace| halfspace.contains(r))
     }
 }

mercurial