Mon, 01 Sep 2025 13:51:03 -0500
fubar
--- a/src/bisection_tree/btfn.rs Mon Sep 01 00:04:22 2025 -0500 +++ b/src/bisection_tree/btfn.rs Mon Sep 01 13:51:03 2025 -0500 @@ -1,6 +1,5 @@ -use crate::mapping::{ - BasicDecomposition, DifferentiableImpl, DifferentiableMapping, Instance, Mapping, Space, -}; +use crate::instance::{ClosedSpace, Instance, Ownable, Space}; +use crate::mapping::{BasicDecomposition, DifferentiableImpl, DifferentiableMapping, Mapping}; use crate::types::Float; use numeric_literals::replace_float_literals; use std::iter::Sum; @@ -37,12 +36,31 @@ _phantoms: PhantomData<F>, } +impl<F: Float, G, BT, const N: usize> Ownable for BTFN<F, G, BT, N> +where + G: SupportGenerator<N, F, Id = BT::Data>, + G::SupportType: LocalAnalysis<F, BT::Agg, N>, + BT: BTImpl<N, F>, +{ + type OwnedVariant = Self; + + fn into_owned(self) -> Self::OwnedVariant { + self + } + + /// Returns an owned instance of a reference. + fn clone_owned(&self) -> Self::OwnedVariant { + self.clone() + } +} + impl<F: Float, G, BT, const N: usize> Space for BTFN<F, G, BT, N> where G: SupportGenerator<N, F, Id = BT::Data>, G::SupportType: LocalAnalysis<F, BT::Agg, N>, BT: BTImpl<N, F>, { + type OwnedSpace = Self; type Decomp = BasicDecomposition; } @@ -64,11 +82,7 @@ } fn new_arc(bt: BT, generator: Arc<G>) -> Self { - BTFN { - bt: bt, - generator: generator, - _phantoms: std::marker::PhantomData, - } + BTFN { bt, generator, _phantoms: std::marker::PhantomData } } /// Create a new BTFN support generator and a pre-initialised bisection tree, @@ -161,11 +175,7 @@ { /// Create a new [`PreBTFN`] with no bisection tree. pub fn new_pre(generator: G) -> Self { - BTFN { - bt: (), - generator: Arc::new(generator), - _phantoms: std::marker::PhantomData, - } + BTFN { bt: (), generator: Arc::new(generator), _phantoms: std::marker::PhantomData } } } @@ -188,11 +198,7 @@ bt.insert(d, &support); } - BTFN { - bt: bt, - generator: Arc::new(both), - _phantoms: std::marker::PhantomData, - } + BTFN { bt: bt, generator: Arc::new(both), _phantoms: std::marker::PhantomData } } } @@ -411,7 +417,7 @@ BT: BTImpl<N, F>, G: SupportGenerator<N, F, Id = BT::Data>, G::SupportType: LocalAnalysis<F, BT::Agg, N> + Mapping<Loc<N, F>, Codomain = V>, - V: Sum + Space, + V: Sum + ClosedSpace, { type Codomain = V; @@ -430,7 +436,7 @@ G: SupportGenerator<N, F, Id = BT::Data>, G::SupportType: LocalAnalysis<F, BT::Agg, N> + DifferentiableMapping<Loc<N, F>, DerivativeDomain = V>, - V: Sum + Space, + V: Sum + ClosedSpace, { type Derivative = V; @@ -846,12 +852,7 @@ Cube<N, F>: P2Minimise<Loc<N, F>, F>, { fn maximise(&mut self, tolerance: F, max_steps: usize) -> (Loc<N, F>, F) { - let refiner = P2Refiner { - tolerance, - max_steps, - how: RefineMax, - bound: None, - }; + let refiner = P2Refiner { tolerance, max_steps, how: RefineMax, bound: None }; self.bt .search_and_refine(refiner, &self.generator) .expect("Refiner failure.") @@ -864,24 +865,14 @@ tolerance: F, max_steps: usize, ) -> Option<(Loc<N, F>, F)> { - let refiner = P2Refiner { - tolerance, - max_steps, - how: RefineMax, - bound: Some(bound), - }; + let refiner = P2Refiner { tolerance, max_steps, how: RefineMax, bound: Some(bound) }; self.bt .search_and_refine(refiner, &self.generator) .expect("Refiner failure.") } fn minimise(&mut self, tolerance: F, max_steps: usize) -> (Loc<N, F>, F) { - let refiner = P2Refiner { - tolerance, - max_steps, - how: RefineMin, - bound: None, - }; + let refiner = P2Refiner { tolerance, max_steps, how: RefineMin, bound: None }; self.bt .search_and_refine(refiner, &self.generator) .expect("Refiner failure.") @@ -894,36 +885,21 @@ tolerance: F, max_steps: usize, ) -> Option<(Loc<N, F>, F)> { - let refiner = P2Refiner { - tolerance, - max_steps, - how: RefineMin, - bound: Some(bound), - }; + let refiner = P2Refiner { tolerance, max_steps, how: RefineMin, bound: Some(bound) }; self.bt .search_and_refine(refiner, &self.generator) .expect("Refiner failure.") } fn has_upper_bound(&mut self, bound: F, tolerance: F, max_steps: usize) -> bool { - let refiner = BoundRefiner { - bound, - tolerance, - max_steps, - how: RefineMax, - }; + let refiner = BoundRefiner { bound, tolerance, max_steps, how: RefineMax }; self.bt .search_and_refine(refiner, &self.generator) .expect("Refiner failure.") } fn has_lower_bound(&mut self, bound: F, tolerance: F, max_steps: usize) -> bool { - let refiner = BoundRefiner { - bound, - tolerance, - max_steps, - how: RefineMin, - }; + let refiner = BoundRefiner { bound, tolerance, max_steps, how: RefineMin }; self.bt .search_and_refine(refiner, &self.generator) .expect("Refiner failure.")
--- a/src/bisection_tree/either.rs Mon Sep 01 00:04:22 2025 -0500 +++ b/src/bisection_tree/either.rs Mon Sep 01 13:51:03 2025 -0500 @@ -3,7 +3,9 @@ use crate::iter::{MapF, MapZ, Mappable}; use crate::loc::Loc; -use crate::mapping::{DifferentiableImpl, DifferentiableMapping, Instance, Mapping, Space}; +use crate::mapping::{ + ClosedSpace, DifferentiableImpl, DifferentiableMapping, Instance, Mapping, Space, +}; use crate::sets::Cube; use crate::types::*; @@ -192,7 +194,7 @@ impl<F, S1, S2, X> Mapping<X> for EitherSupport<S2, S1> where - F: Space, + F: ClosedSpace, X: Space, S1: Mapping<X, Codomain = F>, S2: Mapping<X, Codomain = F>, @@ -210,8 +212,8 @@ impl<X, S1, S2, O> DifferentiableImpl<X> for EitherSupport<S2, S1> where - O: Space, - X: Space, + O: ClosedSpace, + X: ClosedSpace, S1: DifferentiableMapping<X, DerivativeDomain = O>, S2: DifferentiableMapping<X, DerivativeDomain = O>, {
--- a/src/bisection_tree/support.rs Mon Sep 01 00:04:22 2025 -0500 +++ b/src/bisection_tree/support.rs Mon Sep 01 13:51:03 2025 -0500 @@ -4,7 +4,9 @@ use super::aggregator::Bounds; pub use crate::bounds::{GlobalAnalysis, LocalAnalysis}; use crate::loc::Loc; -use crate::mapping::{DifferentiableImpl, DifferentiableMapping, Instance, Mapping, Space}; +use crate::mapping::{ + ClosedSpace, DifferentiableImpl, DifferentiableMapping, Instance, Mapping, Space, +}; use crate::maputil::map2; use crate::norms::{Linfinity, Norm, L1, L2}; pub use crate::operator_arithmetic::{Constant, Weighted}; @@ -46,10 +48,7 @@ /// Translate `self` by `x`. #[inline] fn shift(self, x: Loc<N, F>) -> Shift<Self, N, F> { - Shift { - shift: x, - base_fn: self, - } + Shift { shift: x, base_fn: self } } } @@ -60,7 +59,7 @@ base_fn: T, } -impl<'a, T, V: Space, F: Float, const N: usize> Mapping<Loc<N, F>> for Shift<T, N, F> +impl<'a, T, V: ClosedSpace, F: Float, const N: usize> Mapping<Loc<N, F>> for Shift<T, N, F> where T: Mapping<Loc<N, F>, Codomain = V>, { @@ -72,7 +71,8 @@ } } -impl<'a, T, V: Space, F: Float, const N: usize> DifferentiableImpl<Loc<N, F>> for Shift<T, N, F> +impl<'a, T, V: ClosedSpace, F: Float, const N: usize> DifferentiableImpl<Loc<N, F>> + for Shift<T, N, F> where T: DifferentiableMapping<Loc<N, F>, DerivativeDomain = V>, { @@ -226,10 +226,7 @@ type Output = Weighted<T, F>; #[inline] fn $fn(self, t: F) -> Self::Output { - Weighted { - weight: self.weight.$fn(t), - base_fn: self.base_fn.clone(), - } + Weighted { weight: self.weight.$fn(t), base_fn: self.base_fn.clone() } } } };
--- a/src/convex.rs Mon Sep 01 00:04:22 2025 -0500 +++ b/src/convex.rs Mon Sep 01 13:51:03 2025 -0500 @@ -4,7 +4,7 @@ use crate::error::DynResult; use crate::euclidean::Euclidean; -use crate::instance::{DecompositionMut, Instance, InstanceMut}; +use crate::instance::{ClosedSpace, DecompositionMut, Instance}; use crate::linops::{IdOp, Scaled, SimpleZeroOp, AXPY}; use crate::mapping::{DifferentiableImpl, LipschitzDifferentiableImpl, Mapping, Space}; use crate::norms::*; @@ -57,7 +57,7 @@ /// The conjugate type has to implement [`ConvexMapping`], but a `Conjugable` mapping need /// not be convex. pub trait Prox<Domain: Space>: Mapping<Domain> { - type Prox<'a>: Mapping<Domain, Codomain = Domain> + type Prox<'a>: Mapping<Domain, Codomain = Domain::OwnedSpace> where Self: 'a; @@ -65,16 +65,15 @@ fn prox_mapping(&self, τ: Self::Codomain) -> Self::Prox<'_>; /// Calculate the proximal mapping with weight τ - fn prox<I: Instance<Domain>>(&self, τ: Self::Codomain, z: I) -> Domain { + fn prox<I: Instance<Domain>>(&self, τ: Self::Codomain, z: I) -> Domain::OwnedSpace { self.prox_mapping(τ).apply(z) } /// Calculate the proximal mapping with weight τ in-place - fn prox_mut<'b>(&self, τ: Self::Codomain, y: &'b mut Domain) + fn prox_mut<'b>(&self, τ: Self::Codomain, y: &'b mut Domain::OwnedSpace) where - &'b mut Domain: InstanceMut<Domain>, Domain::Decomp: DecompositionMut<Domain>, - for<'a> &'a Domain: Instance<Domain>, + for<'a> &'a Domain::OwnedSpace: Instance<Domain>, { *y = self.prox(τ, &*y); } @@ -165,7 +164,7 @@ Domain: Space + Norm<E, F>, E: NormExponent, F: Float, - NormProjection<F, E>: Mapping<Domain, Codomain = Domain>, + NormProjection<F, E>: Mapping<Domain, Codomain = Domain::OwnedSpace>, { type Prox<'a> = NormProjection<F, E> @@ -203,12 +202,13 @@ impl<F, E, Domain> Mapping<Domain> for NormProjection<F, E> where Domain: Normed<F> + Projection<F, E>, + Domain::OwnedSpace: ClosedSpace, F: Float, E: NormExponent, { - type Codomain = Domain; + type Codomain = Domain::OwnedSpace; - fn apply<I: Instance<Domain>>(&self, d: I) -> Domain { + fn apply<I: Instance<Domain>>(&self, d: I) -> Self::Codomain { d.own().proj_ball(self.radius, self.exponent) } } @@ -262,7 +262,7 @@ } } -impl<Domain: Space + Clone, F: Num> Prox<Domain> for Zero<Domain, F> { +impl<Domain: Space, F: Num> Prox<Domain> for Zero<Domain, F> { type Prox<'a> = IdOp<Domain> where @@ -395,7 +395,7 @@ impl<X, F> Prox<X> for Norm222<F> where F: Float, - X: Euclidean<F, Owned = X>, + X: Euclidean<F, Owned = X> + ClosedMul<F>, { type Prox<'a> = Scaled<F> @@ -410,11 +410,11 @@ impl<X, F> DifferentiableImpl<X> for Norm222<F> where F: Float, - X: Euclidean<F, Owned = X>, + X: Euclidean<F>, { - type Derivative = X; + type Derivative = X::Owned; - fn differential_impl<I: Instance<X>>(&self, x: I) -> X { + fn differential_impl<I: Instance<X>>(&self, x: I) -> Self::Derivative { x.own() } } @@ -422,7 +422,7 @@ impl<X, F> LipschitzDifferentiableImpl<X, L2> for Norm222<F> where F: Float, - X: Euclidean<F, Owned = X>, + X: Euclidean<F>, { type FloatType = F;
--- a/src/direct_product.rs Mon Sep 01 00:04:22 2025 -0500 +++ b/src/direct_product.rs Mon Sep 01 13:51:03 2025 -0500 @@ -6,8 +6,10 @@ */ use crate::euclidean::Euclidean; -use crate::instance::{Decomposition, DecompositionMut, Instance, InstanceMut, MyCow}; -use crate::linops::AXPY; +use crate::instance::{ + ClosedSpace, 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}; @@ -262,6 +264,25 @@ 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()) + } + + /// Returns an owned instance of a reference. + fn clone_owned(&self) -> Self::OwnedVariant { + Pair(self.0.clone_owned(), self.1.clone_owned()) + } +} + /// 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> @@ -270,19 +291,19 @@ B: Euclidean<F>, //Pair<A, B>: Euclidean<F>, Self: Sized - + Mul<F, Output = <Self as AXPY>::Owned> + + Mul<F, Output = <Self as VectorSpace>::Owned> + MulAssign<F> - + Div<F, Output = <Self as AXPY>::Owned> + + Div<F, Output = <Self as VectorSpace>::Owned> + DivAssign<F> - + Add<Self, Output = <Self as AXPY>::Owned> - + Sub<Self, Output = <Self as AXPY>::Owned> - + for<'b> Add<&'b Self, Output = <Self as AXPY>::Owned> - + for<'b> Sub<&'b Self, Output = <Self as AXPY>::Owned> + + Add<Self, Output = <Self as VectorSpace>::Owned> + + Sub<Self, Output = <Self as VectorSpace>::Owned> + + for<'b> Add<&'b Self, Output = <Self as VectorSpace>::Owned> + + for<'b> Sub<&'b Self, Output = <Self as VectorSpace>::Owned> + AddAssign<Self> + for<'b> AddAssign<&'b Self> + SubAssign<Self> + for<'b> SubAssign<&'b Self> - + Neg<Output = <Self as AXPY>::Owned>, + + Neg<Output = <Self as VectorSpace>::Owned>, { fn dot<I: Instance<Self>>(&self, other: I) -> F { other.eval_decompose(|Pair(u, v)| self.0.dot(u) + self.1.dot(v)) @@ -297,6 +318,28 @@ } } +impl<F, A, B, O, P> VectorSpace for Pair<A, B> +where + A: VectorSpace<Field = F, Owned = O, OwnedVariant = O>, + B: VectorSpace<Field = F, Owned = P, OwnedVariant = P>, + O: ClosedSpace + AXPY<A, Field = F, Owned = O, OwnedVariant = O>, + P: ClosedSpace + AXPY<B, Field = F, Owned = P, OwnedVariant = P>, + F: Num, +{ + type Field = F; + type Owned = Pair<O, P>; + + /// Return a similar zero as `self`. + fn similar_origin(&self) -> Self::Owned { + 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, @@ -306,13 +349,7 @@ F: Num, Self: MulAssign<F> + DivAssign<F>, Pair<A, B>: MulAssign<F> + DivAssign<F>, - //A::Owned: MulAssign<F>, - //B::Owned: MulAssign<F>, - //Pair<A::Owned, B::Owned>: AXPY<Pair<U, V>, Field = F>, { - type Field = F; - type Owned = Pair<A::Owned, B::Owned>; - fn axpy<I: Instance<Pair<U, V>>>(&mut self, α: F, x: I, β: F) { x.eval_decompose(|Pair(u, v)| { self.0.axpy(α, u, β); @@ -334,11 +371,6 @@ }) } - /// Return a similar zero as `self`. - fn similar_origin(&self) -> Self::Owned { - Pair(self.0.similar_origin(), self.1.similar_origin()) - } - /// Set self to zero. fn set_zero(&mut self) { self.0.set_zero(); @@ -562,7 +594,7 @@ { type DualSpace = Pair<A::DualSpace, B::DualSpace>; - fn dual_origin(&self) -> <Self::DualSpace as AXPY>::Owned { + fn dual_origin(&self) -> <Self::DualSpace as VectorSpace>::Owned { Pair(self.0.dual_origin(), self.1.dual_origin()) } }
--- a/src/euclidean.rs Mon Sep 01 00:04:22 2025 -0500 +++ b/src/euclidean.rs Mon Sep 01 13:51:03 2025 -0500 @@ -3,13 +3,15 @@ */ use crate::instance::Instance; -use crate::linops::AXPY; +use crate::linops::{VectorSpace, AXPY}; use crate::norms::{HasDual, Reflexive}; use crate::types::*; use std::ops::{Add, AddAssign, Sub, SubAssign}; pub mod wrap; +// TODO: Euclidean & EuclideanMut +// /// Space (type) with Euclidean and vector space structure /// /// The type should implement vector space operations (addition, subtraction, scalar @@ -18,11 +20,11 @@ // TODO: remove F parameter, use AXPY::Field pub trait Euclidean<F: Float = f64>: HasDual<F, DualSpace = Self> - + AXPY<Field = F> + + VectorSpace<Field = F> + Reflexive<F> // TODO: move the following to AXPY - + for<'b> Add<&'b Self, Output = <Self as AXPY>::Owned> - + for<'b> Sub<&'b Self, Output = <Self as AXPY>::Owned> + + for<'b> Add<&'b Self, Output = <Self as VectorSpace>::Owned> + + for<'b> Sub<&'b Self, Output = <Self as VectorSpace>::Owned> + for<'b> AddAssign<&'b Self> + for<'b> SubAssign<&'b Self> { @@ -59,11 +61,20 @@ /// Projection to the 2-ball. #[inline] - fn proj_ball2(mut self, ρ: F) -> Self { - self.proj_ball2_mut(ρ); - self + fn proj_ball2(self, ρ: F) -> Self::Owned { + let r = self.norm2(); + if r > ρ { + self * (ρ / r) + } else { + self.into_owned() + } } +} +// TODO: remove F parameter, use AXPY::Field +pub trait EuclideanMut<F: Float = f64>: + Euclidean<F> + AXPY<Field = F> + for<'b> AddAssign<&'b Self> + for<'b> SubAssign<&'b Self> +{ /// In-place projection to the 2-ball. #[inline] fn proj_ball2_mut(&mut self, ρ: F) { @@ -74,8 +85,13 @@ } } +impl<X, F: Float> EuclideanMut<F> for X where + X: Euclidean<F> + AXPY<Field = F> + for<'b> AddAssign<&'b Self> + for<'b> SubAssign<&'b Self> +{ +} + /// Trait for [`Euclidean`] spaces with dimensions known at compile time. pub trait StaticEuclidean<F: Float = f64>: Euclidean<F> { /// Returns the origin - fn origin() -> <Self as AXPY>::Owned; + fn origin() -> <Self as VectorSpace>::Owned; }
--- a/src/instance.rs Mon Sep 01 00:04:22 2025 -0500 +++ b/src/instance.rs Mon Sep 01 13:51:03 2025 -0500 @@ -23,35 +23,117 @@ } } -impl<'b, X> MyCow<'b, X> { +/// Trait for ownable-by-consumption objects +pub trait Ownable { + type OwnedVariant: Clone; + + /// Returns an owned instance, possibly consuming the original, + /// avoiding cloning when possible. + fn into_owned(self) -> Self::OwnedVariant; + + /// Returns an owned instance of a reference. + fn clone_owned(&self) -> Self::OwnedVariant; +} + +impl<'a, X: Ownable> Ownable for &'a X { + type OwnedVariant = X::OwnedVariant; + + #[inline] + /// Returns an owned instance. + fn into_owned(self) -> Self::OwnedVariant { + self.clone_owned() + } + #[inline] - pub fn into_owned(self) -> X - where - X: Clone, - { + /// Returns an owned instance. + fn clone_owned(&self) -> Self::OwnedVariant { + (*self).into_owned() + } +} + +impl<'a, X: Ownable> Ownable for &'a mut X { + type OwnedVariant = X::OwnedVariant; + + #[inline] + /// Returns an owned instance. + fn into_owned(self) -> Self::OwnedVariant { + self.clone_owned() + } + + #[inline] + /// Returns an owned instance. + fn clone_owned(&self) -> Self::OwnedVariant { + (&**self).into_owned() + } +} + +impl<'a, X: Ownable> Ownable for MyCow<'a, X> { + type OwnedVariant = X::OwnedVariant; + + #[inline] + /// Returns an owned instance. + fn into_owned(self) -> Self::OwnedVariant { match self { - EitherDecomp::Owned(x) => x, - EitherDecomp::Borrowed(x) => x.clone(), + EitherDecomp::Owned(x) => x.into_owned(), + EitherDecomp::Borrowed(x) => x.into_owned(), + } + } + + #[inline] + /// Returns an owned instance. + fn clone_owned(&self) -> Self::OwnedVariant { + match self { + EitherDecomp::Owned(x) => x.into_owned(), + EitherDecomp::Borrowed(x) => x.into_owned(), } } } /// Trait for abitrary mathematical spaces. -pub trait Space: Instance<Self, Self::Decomp> { +pub trait Space: Ownable<OwnedVariant = Self::OwnedSpace> + Sized { + type OwnedSpace: ClosedSpace; + /// Default decomposition for the space type Decomp: Decomposition<Self>; } +mod private { + pub trait Sealed {} +} + +/// Helper trait for working with own types. +pub trait Owned: Ownable<OwnedVariant = Self> + private::Sealed {} +impl<X: Ownable<OwnedVariant = X>> private::Sealed for X {} +impl<X: Ownable<OwnedVariant = X>> Owned for X {} + +/// Helper trait for working with closed spaces, operations in which should +/// return members of the same space +pub trait ClosedSpace: Space<OwnedSpace = Self> + Owned + Instance<Self> {} +impl<X: Space<OwnedSpace = Self> + Owned + Instance<Self>> ClosedSpace for X {} + #[macro_export] macro_rules! impl_basic_space { - ($($type:ty)*) => { $( - impl $crate::instance::Space for $type { + ($($type:ty)*) => { + $( $crate::impl_basic_space!($type where ); )* + }; + ($type:ty where $($where:tt)*) => { + impl<$($where)*> $crate::instance::Space for $type { + type OwnedSpace = Self; type Decomp = $crate::instance::BasicDecomposition; } - )* }; - ($type:ty where $($where:tt)*) => { - impl<$($where)*> $crate::instance::Space for $type { - type Decomp = $crate::instance::BasicDecomposition; + + impl<$($where)*> $crate::instance::Ownable for $type { + type OwnedVariant = Self; + + #[inline] + fn into_owned(self) -> Self::OwnedVariant { + self + } + + #[inline] + fn clone_owned(&self) -> Self::OwnedVariant { + *self + } } }; } @@ -83,7 +165,7 @@ #[derive(Copy, Clone, Debug)] pub struct BasicDecomposition; -impl<X: Space + Clone> Decomposition<X> for BasicDecomposition { +impl<X: Space> Decomposition<X> for BasicDecomposition { type Decomposition<'b> = MyCow<'b, X> where @@ -104,8 +186,9 @@ /// 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 +pub trait Instance<X, D = <X as Space>::Decomp>: Sized + Ownable where + X: Space, D: Decomposition<X>, { /// Decomposes self according to `decomposer`, and evaluate `f` on the result. @@ -164,7 +247,7 @@ } } -impl<X: Space + Clone> Instance<X, BasicDecomposition> for X { +impl<X: Space> Instance<X, BasicDecomposition> for X { #[inline] fn eval_decompose<'b, R>(self, f: impl FnOnce(MyCow<'b, X>) -> R) -> R where @@ -197,7 +280,7 @@ } } -impl<'a, X: Space + Clone> Instance<X, BasicDecomposition> for &'a X { +impl<'a, X: Space> Instance<X, BasicDecomposition> for &'a X { #[inline] fn eval_decompose<'b, R>(self, f: impl FnOnce(MyCow<'b, X>) -> R) -> R where @@ -218,7 +301,7 @@ #[inline] fn own(self) -> X { - self.clone() + self.into_owned() } #[inline] @@ -230,7 +313,7 @@ } } -impl<'a, X: Space + Clone> Instance<X, BasicDecomposition> for &'a mut X { +impl<'a, X: Space> Instance<X, BasicDecomposition> for &'a mut X { #[inline] fn eval_decompose<'b, R>(self, f: impl FnOnce(MyCow<'b, X>) -> R) -> R where @@ -251,20 +334,19 @@ #[inline] fn own(self) -> X { - self.clone() + self.into_owned() } #[inline] 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> { +impl<'a, X: Space> Instance<X, BasicDecomposition> for MyCow<'a, X> { #[inline] fn eval_decompose<'b, R>(self, f: impl FnOnce(MyCow<'b, X>) -> R) -> R where
--- a/src/linops.rs Mon Sep 01 00:04:22 2025 -0500 +++ b/src/linops.rs Mon Sep 01 13:51:03 2025 -0500 @@ -5,7 +5,7 @@ use crate::direct_product::Pair; use crate::error::DynResult; use crate::instance::Instance; -pub use crate::mapping::{Composition, DifferentiableImpl, Mapping, Space}; +pub use crate::mapping::{ClosedSpace, Composition, DifferentiableImpl, Mapping, Space}; use crate::norms::{HasDual, Linfinity, Norm, NormExponent, PairNorm, L1, L2}; use crate::types::*; use numeric_literals::replace_float_literals; @@ -25,16 +25,10 @@ // } // } -/// Efficient in-place summation. +/// Vector spaces #[replace_float_literals(Self::Field::cast_from(literal))] -pub trait AXPY<X = Self>: - Space - + MulAssign<Self::Field> - + DivAssign<Self::Field> - + AddAssign<Self> - + AddAssign<Self::Owned> - + SubAssign<Self> - + SubAssign<Self::Owned> +pub trait VectorSpace: + Space<OwnedSpace = Self::Owned> + Mul<Self::Field, Output = Self::Owned> + Div<Self::Field, Output = Self::Owned> + Add<Self, Output = Self::Owned> @@ -42,12 +36,42 @@ + Sub<Self, Output = Self::Owned> + Sub<Self::Owned, Output = Self::Owned> + Neg +{ + type Field: Num; + type Owned: ClosedSpace + + AXPY< + Self, + Field = Self::Field, + Owned = Self::Owned, + OwnedVariant = Self::Owned, + OwnedSpace = Self::Owned, + >; + + /// Return a similar zero as `self`. + fn similar_origin(&self) -> Self::Owned; + // { + // self.make_origin_generator().make_origin() + // } + + /// Return a similar zero as `x`. + fn similar_origin_inst<I: Instance<Self>>(x: I) -> Self::Owned { + x.eval(|xr| xr.similar_origin()) + } +} + +/// Efficient in-place summation. +#[replace_float_literals(Self::Field::cast_from(literal))] +pub trait AXPY<X = Self>: + VectorSpace + + MulAssign<Self::Field> + + DivAssign<Self::Field> + + AddAssign<Self> + + AddAssign<Self::Owned> + + SubAssign<Self> + + SubAssign<Self::Owned> where X: Space, { - type Field: Num; - type Owned: AXPY<X, Field = Self::Field>; - /// Computes `y = βy + αx`, where `y` is `Self`. fn axpy<I: Instance<X>>(&mut self, α: Self::Field, x: I, β: Self::Field); @@ -61,21 +85,8 @@ self.axpy(α, x, 0.0) } - /// Return a similar zero as `self`. - fn similar_origin(&self) -> Self::Owned; - // { - // self.make_origin_generator().make_origin() - // } - - /// Return a similar zero as `x`. - fn similar_origin_inst<I: Instance<Self>>(x: I) -> Self::Owned { - x.eval(|xr| xr.similar_origin()) - } - /// Set self to zero. fn set_zero(&mut self); - - //fn make_origin_generator(&self) -> Self::OriginGen<'_>; } /// Efficient in-place application for [`Linear`] operators. @@ -178,21 +189,21 @@ } } -impl<X: Clone + Space> Mapping<X> for IdOp<X> { - type Codomain = X; +impl<X: Space> Mapping<X> for IdOp<X> { + type Codomain = X::OwnedVariant; fn apply<I: Instance<X>>(&self, x: I) -> X { 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> where Y: AXPY<X, Field = F>, - X: Clone + Space, + X: Space, { // Computes `y = αAx + βy`, where `A` is `Self`. fn gemv<I: Instance<X>>(&self, y: &mut Y, α: F, x: I, β: F) { @@ -243,10 +254,7 @@ #[derive(Clone, Copy, Debug, Serialize, Eq, PartialEq)] pub struct SimpleZeroOp; -impl<X> Mapping<X> for SimpleZeroOp -where - X: AXPY + Instance<X>, -{ +impl<X: VectorSpace> Mapping<X> for SimpleZeroOp { type Codomain = X::Owned; fn apply<I: Instance<X>>(&self, x: I) -> X::Owned { @@ -254,14 +262,14 @@ } } -impl<X> Linear<X> for SimpleZeroOp where X: AXPY + Instance<X> {} +impl<X: VectorSpace> Linear<X> for SimpleZeroOp {} #[replace_float_literals(F::cast_from(literal))] impl<X, Y, F> GEMV<F, X, Y> for SimpleZeroOp where F: Num, Y: AXPY<Field = F>, - X: AXPY<Field = F> + Instance<X>, + 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) { @@ -276,7 +284,7 @@ impl<X, F, E1, E2> BoundedLinear<X, E1, E2, F> for SimpleZeroOp where F: Num, - X: AXPY<Field = F> + Instance<X> + Norm<E1, F>, + X: VectorSpace<Field = F> + Norm<E1, F>, E1: NormExponent, E2: NormExponent, { @@ -288,8 +296,8 @@ impl<X, F> Adjointable<X, X::DualSpace> for SimpleZeroOp where F: Num, - X: AXPY<Field = F> + Instance<X> + HasDual<F>, - X::DualSpace: AXPY<Owned = X::DualSpace>, + X: VectorSpace<Field = F> + HasDual<F>, + X::DualSpace: VectorSpace<Owned = X::DualSpace>, { type AdjointCodomain = X::DualSpace; type Adjoint<'b> @@ -303,7 +311,7 @@ } } -pub trait OriginGenerator<Y: AXPY> { +pub trait OriginGenerator<Y: VectorSpace> { type Ref<'b>: OriginGenerator<Y> where Self: 'b; @@ -312,7 +320,7 @@ fn as_ref(&self) -> Self::Ref<'_>; } -impl<Y: AXPY> OriginGenerator<Y> for Y { +impl<Y: VectorSpace> OriginGenerator<Y> for Y { type Ref<'b> = &'b Y where @@ -329,7 +337,7 @@ } } -impl<'b, Y: AXPY> OriginGenerator<Y> for &'b Y { +impl<'b, Y: VectorSpace> OriginGenerator<Y> for &'b Y { type Ref<'c> = Self where @@ -348,7 +356,7 @@ /// A zero operator that can be eitherh dualised or predualised (once). /// This is achieved by storing an oppropriate zero. -pub struct ZeroOp<X, Y: AXPY<Field = F>, OY: OriginGenerator<Y>, O, F: Float = f64> { +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)>, @@ -357,8 +365,8 @@ impl<X, Y, OY, F> ZeroOp<X, Y, OY, (), F> where OY: OriginGenerator<Y>, - X: AXPY<Field = F>, - Y: AXPY<Field = F>, + X: VectorSpace<Field = F>, + Y: VectorSpace<Field = F>, F: Float, { pub fn new(y_og: OY) -> Self { @@ -377,7 +385,7 @@ X: HasDual<F, DualSpace = Xprime>, Y: HasDual<F, DualSpace = Yprime>, F: Float, - Xprime: AXPY<Field = F, Owned = Xprime>, + Xprime: VectorSpace<Field = F, Owned = Xprime>, Xprime::Owned: AXPY<Field = F>, Yprime: Space + Instance<Yprime>, { @@ -392,8 +400,8 @@ impl<X, Y, O, OY, F> Mapping<X> for ZeroOp<X, Y, OY, O, F> where - X: Space + Instance<X>, - Y: AXPY<Field = F>, + X: Space, + Y: VectorSpace<Field = F>, F: Float, OY: OriginGenerator<Y>, { @@ -406,8 +414,8 @@ impl<X, Y, OY, O, F> Linear<X> for ZeroOp<X, Y, OY, O, F> where - X: Space + Instance<X>, - Y: AXPY<Field = F>, + X: Space, + Y: VectorSpace<Field = F>, F: Float, OY: OriginGenerator<Y>, { @@ -416,7 +424,7 @@ #[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 + Instance<X>, + X: Space, Y: AXPY<Field = F, Owned = Y>, F: Float, OY: OriginGenerator<Y>, @@ -434,7 +442,7 @@ impl<X, Y, OY, O, F, E1, E2> BoundedLinear<X, E1, E2, F> for ZeroOp<X, Y, OY, O, F> where X: Space + Instance<X> + Norm<E1, F>, - Y: AXPY<Field = F>, + Y: VectorSpace<Field = F>, Y::Owned: Clone, F: Float, E1: NormExponent, @@ -452,7 +460,7 @@ X: HasDual<F, DualSpace = Xprime>, Y: HasDual<F, DualSpace = Yprime>, F: Float, - Xprime: AXPY<Field = F, Owned = Xprime>, + Xprime: VectorSpace<Field = F, Owned = Xprime>, Xprime::Owned: AXPY<Field = F>, Yprime: Space + Instance<Yprime>, OY: OriginGenerator<Y>,
--- a/src/loc.rs Mon Sep 01 00:04:22 2025 -0500 +++ b/src/loc.rs Mon Sep 01 13:51:03 2025 -0500 @@ -4,8 +4,8 @@ */ use crate::euclidean::*; -use crate::instance::{BasicDecomposition, Instance}; -use crate::linops::{Linear, Mapping, AXPY}; +use crate::instance::{BasicDecomposition, Instance, Ownable}; +use crate::linops::{Linear, Mapping, VectorSpace, AXPY}; use crate::mapping::Space; use crate::maputil::{map1, map1_mut, map2, map2_mut, FixedLength, FixedLengthMut}; use crate::norms::*; @@ -27,6 +27,21 @@ pub [F; N], ); +/// Trait for ownable-by-consumption objects +impl<const N: usize, F: Copy> Ownable for Loc<N, F> { + type OwnedVariant = Self; + + #[inline] + fn into_owned(self) -> Self::OwnedVariant { + self + } + + /// Returns an owned instance of a reference. + fn clone_owned(&self) -> Self::OwnedVariant { + self.clone() + } +} + impl<F: Display, const N: usize> Display for Loc<N, F> { // Required method fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { @@ -648,6 +663,14 @@ impl<F: Float, const N: usize> Projection<F, Linfinity> for Loc<N, F> { #[inline] + fn proj_ball(mut self, ρ: F, exp: Linfinity) -> Self { + self.proj_ball_mut(ρ, exp); + self + } +} + +impl<F: Float, const N: usize> ProjectionMut<F, Linfinity> for Loc<N, F> { + #[inline] fn proj_ball_mut(&mut self, ρ: F, _: Linfinity) { self.iter_mut() .for_each(|v| *v = num_traits::clamp(*v, -ρ, ρ)) @@ -706,6 +729,7 @@ } impl<F: Num, const N: usize> Space for Loc<N, F> { + type OwnedSpace = Self; type Decomp = BasicDecomposition; } @@ -719,10 +743,32 @@ impl<F: Float, const N: usize> Linear<Loc<N, F>> for Loc<N, F> {} -impl<F: Float, const N: usize> AXPY<Loc<N, F>> for Loc<N, F> { +impl<F: Float, const N: usize> VectorSpace for Loc<N, F> { type Field = F; type Owned = Self; + // #[inline] + // fn make_origin_generator(&self) -> StaticEuclideanOriginGenerator { + // StaticEuclideanOriginGenerator + // } + + #[inline] + fn similar_origin(&self) -> Self::Owned { + Self::ORIGIN + } + + #[inline] + fn similar_origin_inst<I: Instance<Self>>(_: I) -> Self::Owned { + Self::ORIGIN + } + + // #[inline] + // fn into_owned(self) -> Self::Owned { + // self + // } +} + +impl<F: Float, const N: usize> AXPY<Loc<N, F>> for Loc<N, F> { #[inline] fn axpy<I: Instance<Loc<N, F>>>(&mut self, α: F, x: I, β: F) { x.eval(|x̃| { @@ -739,21 +785,6 @@ x.eval(|x̃| map2_mut(self, x̃, |yi, xi| *yi = *xi)) } - // #[inline] - // fn make_origin_generator(&self) -> StaticEuclideanOriginGenerator { - // StaticEuclideanOriginGenerator - // } - - #[inline] - fn similar_origin(&self) -> Self::Owned { - Self::ORIGIN - } - - #[inline] - fn similar_origin_inst<I: Instance<Self>>(_: I) -> Self::Owned { - Self::ORIGIN - } - #[inline] fn set_zero(&mut self) { *self = Self::ORIGIN;
--- a/src/mapping.rs Mon Sep 01 00:04:22 2025 -0500 +++ b/src/mapping.rs Mon Sep 01 13:51:03 2025 -0500 @@ -4,7 +4,7 @@ use crate::error::DynResult; use crate::instance::MyCow; -pub use crate::instance::{BasicDecomposition, Decomposition, Instance, Space}; +pub use crate::instance::{BasicDecomposition, ClosedSpace, Decomposition, Instance, Space}; use crate::loc::Loc; use crate::norms::{Norm, NormExponent}; use crate::operator_arithmetic::{Constant, Weighted}; @@ -14,7 +14,7 @@ /// A mapping from `Domain` to `Self::Codomain`. pub trait Mapping<Domain: Space> { - type Codomain: Space; + type Codomain: ClosedSpace; /// Compute the value of `self` at `x`. fn apply<I: Instance<Domain>>(&self, x: I) -> Self::Codomain; @@ -25,11 +25,7 @@ where Self: Sized, { - Composition { - outer: self, - inner: other, - intermediate_norm_exponent: (), - } + Composition { outer: self, inner: other, intermediate_norm_exponent: () } } #[inline] @@ -43,11 +39,7 @@ Domain: Norm<E, F>, F: Num, { - Composition { - outer: self, - inner: other, - intermediate_norm_exponent: norm, - } + Composition { outer: self, inner: other, intermediate_norm_exponent: norm } } /// Multiply `self` by the scalar `a`. @@ -58,10 +50,7 @@ C: Constant, Self::Codomain: ClosedMul<C::Type>, { - Weighted { - weight: a, - base_fn: self, - } + Weighted { weight: a, base_fn: self } } } @@ -86,7 +75,7 @@ /// /// This is automatically implemented when [`DifferentiableImpl`] is. pub trait DifferentiableMapping<Domain: Space>: Mapping<Domain> { - type DerivativeDomain: Space; + type DerivativeDomain: ClosedSpace; type Differential<'b>: Mapping<Domain, Codomain = Self::DerivativeDomain> where Self: 'b; @@ -115,7 +104,7 @@ /// Helper trait for implementing [`DifferentiableMapping`] pub trait DifferentiableImpl<X: Space>: Sized { - type Derivative: Space; + type Derivative: ClosedSpace; /// Compute the differential of `self` at `x`, consuming the input. fn differential_impl<I: Instance<X>>(&self, x: I) -> Self::Derivative; @@ -138,17 +127,11 @@ } fn diff(self) -> Differential<'static, Domain, Self> { - Differential { - g: MyCow::Owned(self), - _space: PhantomData, - } + Differential { g: MyCow::Owned(self), _space: PhantomData } } fn diff_ref(&self) -> Differential<'_, Domain, Self> { - Differential { - g: MyCow::Borrowed(self), - _space: PhantomData, - } + Differential { g: MyCow::Borrowed(self), _space: PhantomData } } } @@ -201,10 +184,7 @@ pub trait FlattenCodomain<X: Space, F>: Mapping<X, Codomain = Loc<1, F>> + Sized { /// Flatten the codomain from [`Loc`]`<F, 1>` to `F`. fn flatten_codomain(self) -> FlattenedCodomain<X, F, Self> { - FlattenedCodomain { - g: self, - _phantoms: PhantomData, - } + FlattenedCodomain { g: self, _phantoms: PhantomData } } } @@ -241,21 +221,13 @@ /// Flatten the codomain from [`Loc`]`<F, 1>` to `F`. fn slice_codomain(self, slice: usize) -> SlicedCodomain<'static, X, F, Self, N> { assert!(slice < N); - SlicedCodomain { - g: MyCow::Owned(self), - slice, - _phantoms: PhantomData, - } + SlicedCodomain { g: MyCow::Owned(self), slice, _phantoms: PhantomData } } /// Flatten the codomain from [`Loc`]`<F, 1>` to `F`. fn slice_codomain_ref(&self, slice: usize) -> SlicedCodomain<'_, X, F, Self, N> { assert!(slice < N); - SlicedCodomain { - g: MyCow::Borrowed(self), - slice, - _phantoms: PhantomData, - } + SlicedCodomain { g: MyCow::Borrowed(self), slice, _phantoms: PhantomData } } } @@ -294,7 +266,7 @@ E: Copy, //Composition<S::Derivative, T::Derivative, E>: Space, S::Derivative: Mul<T::Derivative, Output = Y>, - Y: Space, + Y: ClosedSpace, { //type Derivative = Composition<S::Derivative, T::Derivative, E>; type Derivative = Y;
--- a/src/mapping/dataterm.rs Mon Sep 01 00:04:22 2025 -0500 +++ b/src/mapping/dataterm.rs Mon Sep 01 13:51:03 2025 -0500 @@ -8,7 +8,7 @@ use super::{DifferentiableImpl, DifferentiableMapping, LipschitzDifferentiableImpl, Mapping}; use crate::convex::ConvexMapping; use crate::error::DynResult; -use crate::instance::{Instance, Space}; +use crate::instance::{ClosedSpace, Instance, Space}; use crate::linops::{BoundedLinear, Linear, Preadjointable}; use crate::norms::{Normed, L2}; use crate::types::Float; @@ -41,11 +41,7 @@ G: Mapping<A::Codomain, Codomain = F> + Clone, { fn clone(&self) -> Self { - DataTerm { - opA: self.opA.clone(), - b: self.b.clone(), - g: self.g.clone(), - } + DataTerm { opA: self.opA.clone(), b: self.b.clone(), g: self.g.clone() } } } @@ -78,7 +74,7 @@ X: Space, A: Mapping<X>, G: Mapping<A::Codomain, Codomain = F>, - A::Codomain: for<'a> Sub<&'a A::Codomain, Output = A::Codomain>, + A::Codomain: ClosedSpace + for<'a> Sub<&'a A::Codomain, Output = A::Codomain>, { type Codomain = F; @@ -97,7 +93,7 @@ X: Normed<F>, A: Linear<X>, G: ConvexMapping<A::Codomain, F>, - A::Codomain: Normed<F> + for<'a> Sub<&'a A::Codomain, Output = A::Codomain>, + A::Codomain: ClosedSpace + Normed<F> + for<'a> Sub<&'a A::Codomain, Output = A::Codomain>, { } @@ -105,9 +101,11 @@ where F: Float, X: Space, - Y: Space + for<'a> Sub<&'a Y, Output = Y>, + Y: Space + Instance<Y> + for<'a> Sub<&'a Y, Output = Y>, //<A as Mapping<X>>::Codomain: Euclidean<F>, A: Linear<X, Codomain = Y> + Preadjointable<X, G::DerivativeDomain>, + G::DerivativeDomain: Instance<G::DerivativeDomain>, + A::PreadjointCodomain: ClosedSpace, //<<A as Mapping<X>>::Codomain as Euclidean<F>>::Output: Instance<<A as Mapping<X>>::Codomain>, G: DifferentiableMapping<Y, Codomain = F>, {
--- a/src/nalgebra_support.rs Mon Sep 01 00:04:22 2025 -0500 +++ b/src/nalgebra_support.rs Mon Sep 01 13:51:03 2025 -0500 @@ -9,7 +9,7 @@ */ use crate::euclidean::*; -use crate::instance::Instance; +use crate::instance::{Instance, Ownable}; use crate::linops::*; use crate::mapping::{BasicDecomposition, Space}; use crate::norms::*; @@ -24,14 +24,36 @@ use num_traits::identities::{One, Zero}; use std::ops::Mul; +impl<S, M, N, E> Ownable for Matrix<E, M, N, S> +where + S: Storage<E, M, N>, + M: Dim, + N: Dim, + E: Scalar + Zero + One, + DefaultAllocator: Allocator<M, N>, +{ + type OwnedVariant = OMatrix<E, M, N>; + + #[inline] + fn into_owned(self) -> Self::OwnedVariant { + Matrix::into_owned(self) + } + + /// Returns an owned instance of a reference. + fn clone_owned(&self) -> Self::OwnedVariant { + Matrix::clone_owned(self) + } +} + impl<SM, N, M, E> Space for Matrix<E, N, M, SM> where - SM: Storage<E, N, M> + Clone, + SM: Storage<E, N, M>, N: Dim, M: Dim, - E: Scalar + Zero + One + ClosedAddAssign + ClosedMulAssign, + E: Scalar + Zero + One, DefaultAllocator: Allocator<N, M>, { + type OwnedSpace = OMatrix<E, N, M>; type Decomp = BasicDecomposition; } @@ -103,10 +125,9 @@ } } -impl<SM, SV1, M, N, E> AXPY<Matrix<E, M, N, SV1>> for Matrix<E, M, N, SM> +impl<S, M, N, E> VectorSpace for Matrix<E, M, N, S> where - SM: StorageMut<E, M, N> + Clone, - SV1: Storage<E, M, N> + Clone, + S: Storage<E, M, N>, M: Dim, N: Dim, E: Scalar + Zero + One + Float, @@ -116,6 +137,22 @@ type Owned = OMatrix<E, M, N>; #[inline] + fn similar_origin(&self) -> Self::Owned { + let (n, m) = self.shape_generic(); + OMatrix::zeros_generic(n, m) + } +} + +impl<SM, SV1, M, N, E> AXPY<Matrix<E, M, N, SV1>> for Matrix<E, M, N, SM> +where + SM: StorageMut<E, M, N>, + SV1: Storage<E, M, N>, + M: Dim, + N: Dim, + E: Scalar + Zero + One + Float, + DefaultAllocator: Allocator<M, N>, +{ + #[inline] fn axpy<I: Instance<Matrix<E, M, N, SV1>>>(&mut self, α: E, x: I, β: E) { x.eval(|x̃| { assert_eq!(self.ncols(), x̃.ncols()); @@ -136,12 +173,6 @@ fn set_zero(&mut self) { self.iter_mut().for_each(|e| *e = E::ZERO); } - - #[inline] - fn similar_origin(&self) -> Self::Owned { - let (n, m) = self.shape_generic(); - OMatrix::zeros_generic(n, m) - } } /* Implemented automatically as Euclidean. @@ -160,6 +191,21 @@ impl<SM, M, E> Projection<E, Linfinity> for Vector<E, M, SM> where + SM: Storage<E, M> + Clone, + M: Dim, + E: Scalar + Zero + One + Float + RealField, + DefaultAllocator: Allocator<M>, +{ + #[inline] + fn proj_ball(self, ρ: E, exp: Linfinity) -> <Self as Space>::OwnedSpace { + let mut owned = self.into_owned(); + owned.proj_ball_mut(ρ, exp); + owned + } +} + +impl<SM, M, E> ProjectionMut<E, Linfinity> for Vector<E, M, SM> +where SM: StorageMut<E, M> + Clone, M: Dim, E: Scalar + Zero + One + Float + RealField,
--- a/src/norms.rs Mon Sep 01 00:04:22 2025 -0500 +++ b/src/norms.rs Mon Sep 01 13:51:03 2025 -0500 @@ -3,7 +3,8 @@ */ use crate::euclidean::*; -use crate::linops::AXPY; +use crate::instance::Ownable; +use crate::linops::VectorSpace; use crate::mapping::{Instance, Mapping, Space}; use crate::types::*; use serde::{Deserialize, Serialize}; @@ -123,16 +124,12 @@ /// /// println!("{:?}, {:?}", x.proj_ball(1.0, L2), x.proj_ball(0.5, Linfinity)); /// ``` -pub trait Projection<F: Num, Exponent: NormExponent>: Norm<Exponent, F> + Sized -where - F: Float, -{ +pub trait Projection<F: Num, Exponent: NormExponent>: Ownable + Norm<Exponent, F> { /// Projection of `self` to the `q`-norm-ball of radius ρ. - fn proj_ball(mut self, ρ: F, q: Exponent) -> Self { - self.proj_ball_mut(ρ, q); - self - } + fn proj_ball(self, ρ: F, q: Exponent) -> Self::OwnedVariant; +} +pub trait ProjectionMut<F: Num, Exponent: NormExponent>: Projection<F, Exponent> { /// In-place projection of `self` to the `q`-norm-ball of radius ρ. fn proj_ball_mut(&mut self, ρ: F, q: Exponent); } @@ -146,10 +143,12 @@ impl<F: Float, E: Euclidean<F> + Norm<L2, F>> Projection<F, L2> for E { #[inline] - fn proj_ball(self, ρ: F, _p: L2) -> Self { + fn proj_ball(self, ρ: F, _p: L2) -> Self::OwnedVariant { self.proj_ball2(ρ) } +} +impl<F: Float, E: EuclideanMut<F> + Norm<L2, F>> ProjectionMut<F, L2> for E { #[inline] fn proj_ball_mut(&mut self, ρ: F, _p: L2) { self.proj_ball2_mut(ρ) @@ -229,10 +228,10 @@ } } -pub trait HasDual<F: Num = f64>: Normed<F> + AXPY<Field = F> { - type DualSpace: Normed<F> + AXPY<Field = F>; +pub trait HasDual<F: Num = f64>: Normed<F> + VectorSpace<Field = F> { + type DualSpace: Normed<F> + VectorSpace<Field = F>; - fn dual_origin(&self) -> <Self::DualSpace as AXPY>::Owned; + fn dual_origin(&self) -> <Self::DualSpace as VectorSpace>::Owned; } /// Automatically implemented trait for reflexive spaces
--- a/src/operator_arithmetic.rs Mon Sep 01 00:04:22 2025 -0500 +++ b/src/operator_arithmetic.rs Mon Sep 01 13:51:03 2025 -0500 @@ -2,7 +2,7 @@ Arithmetic of [`Mapping`]s. */ -use crate::instance::{Instance, Space}; +use crate::instance::{ClosedSpace, Instance, Space}; use crate::mapping::{DifferentiableImpl, DifferentiableMapping, Mapping}; use crate::types::*; use serde::Serialize; @@ -63,7 +63,7 @@ F: Float, D: Space, T: DifferentiableMapping<D, DerivativeDomain = V>, - V: Space + std::ops::Mul<F, Output = V>, + V: ClosedSpace + std::ops::Mul<F, Output = V>, C: Constant<Type = F>, { type Derivative = V;
--- a/src/types.rs Mon Sep 01 00:04:22 2025 -0500 +++ b/src/types.rs Mon Sep 01 13:51:03 2025 -0500 @@ -10,15 +10,12 @@ */ //use trait_set::trait_set; +pub use num_traits::cast::AsPrimitive; pub use num_traits::Float as NumTraitsFloat; // needed to re-export functions. -pub use num_traits::cast::AsPrimitive; pub use simba::scalar::{ - ClosedAdd, ClosedAddAssign, + ClosedAdd, ClosedAddAssign, ClosedDiv, ClosedDivAssign, ClosedMul, ClosedMulAssign, ClosedNeg, ClosedSub, ClosedSubAssign, - ClosedMul, ClosedMulAssign, - ClosedDiv, ClosedDivAssign, - ClosedNeg }; /// Typical integer type @@ -34,8 +31,8 @@ pub type float = f64; /// Casts of abstract numerical types to others via the standard `as` keyword. -pub trait CastFrom<T : 'static + Copy> : num_traits::cast::AsPrimitive<T> { - fn cast_from(other : T) -> Self; +pub trait CastFrom<T: 'static + Copy>: num_traits::cast::AsPrimitive<T> { + fn cast_from(other: T) -> Self; } macro_rules! impl_casts { @@ -58,53 +55,71 @@ f32 f64); /// Trait for general numeric types -pub trait Num : 'static + Copy + Sync + Send + num::Num + num_traits::NumAssign - + std::iter::Sum + std::iter::Product - + std::fmt::Debug + std::fmt::Display + serde::Serialize - + CastFrom<u8> + CastFrom<u16> + CastFrom<u32> + CastFrom<u64> - + CastFrom<u128> + CastFrom<usize> - + CastFrom<i8> + CastFrom<i16> + CastFrom<i32> + CastFrom<i64> - + CastFrom<i128> + CastFrom<isize> - + CastFrom<f32> + CastFrom<f64> - + crate::instance::Space { - - const ZERO : Self; - const ONE : Self; - const TWO : Self; +pub trait Num: + 'static + + Copy + + Sync + + Send + + num::Num + + num_traits::NumAssign + + std::iter::Sum + + std::iter::Product + + std::fmt::Debug + + std::fmt::Display + + serde::Serialize + + CastFrom<u8> + + CastFrom<u16> + + CastFrom<u32> + + CastFrom<u64> + + CastFrom<u128> + + CastFrom<usize> + + CastFrom<i8> + + CastFrom<i16> + + CastFrom<i32> + + CastFrom<i64> + + CastFrom<i128> + + CastFrom<isize> + + CastFrom<f32> + + CastFrom<f64> + + crate::instance::ClosedSpace +{ + const ZERO: Self; + const ONE: Self; + const TWO: Self; /// Generic version of `Self::MAX` - const RANGE_MAX : Self; + const RANGE_MAX: Self; /// Generic version of `Self::MIN` - const RANGE_MIN : Self; + const RANGE_MIN: Self; } /// Trait for signed numeric types -pub trait SignedNum : Num + num::Signed + std::ops::Neg<Output=Self> {} -impl<U : Num + num::Signed + std::ops::Neg<Output=Self>> SignedNum for U { } +pub trait SignedNum: Num + num::Signed + std::ops::Neg<Output = Self> {} +impl<U: Num + num::Signed + std::ops::Neg<Output = Self>> SignedNum for U {} /// Trait for floating point numbers -pub trait Float : SignedNum + num::Float /*+ From<Self::CompatibleSize>*/ { +pub trait Float: SignedNum + num::Float /*+ From<Self::CompatibleSize>*/ { // An unsigned integer that can be used for indexing operations and // converted to F without loss. //type CompatibleSize : CompatibleUnsigned<Self>; - const PI : Self; - const E : Self; - const EPSILON : Self; - const SQRT_2 : Self; - const INFINITY : Self; - const NEG_INFINITY : Self; - const NAN : Self; - const FRAC_2_SQRT_PI : Self; + const PI: Self; + const E: Self; + const EPSILON: Self; + const SQRT_2: Self; + const INFINITY: Self; + const NEG_INFINITY: Self; + const NAN: Self; + const FRAC_2_SQRT_PI: Self; } /// Trait for integers -pub trait Integer : Num + num::Integer {} +pub trait Integer: Num + num::Integer {} /// Trait for unsigned integers -pub trait Unsigned : Num + Integer + num::Unsigned {} +pub trait Unsigned: Num + Integer + num::Unsigned {} /// Trait for signed integers -pub trait Signed : SignedNum + Integer {} +pub trait Signed: SignedNum + Integer {} macro_rules! impl_num_consts { ($($type:ty)*) => { $( @@ -137,14 +152,14 @@ #[cfg(any(target_pointer_width = "32", target_pointer_width = "16"))] type CompatibleSize = usize;*/ - const PI : Self = std::f64::consts::PI; - const E : Self = std::f64::consts::E; - const EPSILON : Self = std::f64::EPSILON; - const SQRT_2 : Self = std::f64::consts::SQRT_2; - const INFINITY : Self = std::f64::INFINITY; - const NEG_INFINITY : Self = std::f64::NEG_INFINITY; - const NAN : Self = std::f64::NAN; - const FRAC_2_SQRT_PI : Self = std::f64::consts::FRAC_2_SQRT_PI; + const PI: Self = std::f64::consts::PI; + const E: Self = std::f64::consts::E; + const EPSILON: Self = std::f64::EPSILON; + const SQRT_2: Self = std::f64::consts::SQRT_2; + const INFINITY: Self = std::f64::INFINITY; + const NEG_INFINITY: Self = std::f64::NEG_INFINITY; + const NAN: Self = std::f64::NAN; + const FRAC_2_SQRT_PI: Self = std::f64::consts::FRAC_2_SQRT_PI; } impl Float for f32 { @@ -155,14 +170,14 @@ type CompatibleSize = usize; */ - const PI : Self = std::f32::consts::PI; - const E : Self = std::f32::consts::E; - const EPSILON : Self = std::f32::EPSILON; - const SQRT_2 : Self = std::f32::consts::SQRT_2; - const INFINITY : Self = std::f32::INFINITY; - const NEG_INFINITY : Self = std::f32::NEG_INFINITY; - const NAN : Self = std::f32::NAN; - const FRAC_2_SQRT_PI : Self = std::f32::consts::FRAC_2_SQRT_PI; + const PI: Self = std::f32::consts::PI; + const E: Self = std::f32::consts::E; + const EPSILON: Self = std::f32::EPSILON; + const SQRT_2: Self = std::f32::consts::SQRT_2; + const INFINITY: Self = std::f32::INFINITY; + const NEG_INFINITY: Self = std::f32::NEG_INFINITY; + const NAN: Self = std::f32::NAN; + const FRAC_2_SQRT_PI: Self = std::f32::consts::FRAC_2_SQRT_PI; } /* @@ -171,4 +186,3 @@ pub trait CompatibleSigned<F : Float> = Signed + Into<F>; } */ -