# HG changeset patch # User Tuomo Valkonen # Date 1735651802 18000 # Node ID 9226980e45a704d75ae1b3c8c808e318647792af # Parent 1a38447a89fa5c573aef980d6cc92ffa34ab6dfd Significantly simplify Mapping / Apply through Instance diff -r 1a38447a89fa -r 9226980e45a7 Cargo.lock --- a/Cargo.lock Sat Dec 14 09:31:27 2024 -0500 +++ b/Cargo.lock Tue Dec 31 08:30:02 2024 -0500 @@ -17,6 +17,7 @@ "rayon", "serde", "serde_json", + "simba", ] [[package]] diff -r 1a38447a89fa -r 9226980e45a7 Cargo.toml --- a/Cargo.toml Sat Dec 14 09:31:27 2024 -0500 +++ b/Cargo.toml Tue Dec 31 08:30:02 2024 -0500 @@ -23,6 +23,7 @@ cpu-time = "~1.0.0" serde_json = "~1.0.85" rayon = "1.5.3" +simba = "0.9.0" [package.metadata.docs.rs] rustdoc-args = [ "--html-in-header", "katex-header.html" ] @@ -32,7 +33,7 @@ debug = true [features] -default = [] +default = ["nightly"] use_custom_thread_pool = [] nightly = [] # enable for higher-performance implementations of some things diff -r 1a38447a89fa -r 9226980e45a7 src/bisection_tree/btfn.rs --- a/src/bisection_tree/btfn.rs Sat Dec 14 09:31:27 2024 -0500 +++ b/src/bisection_tree/btfn.rs Tue Dec 31 08:30:02 2024 -0500 @@ -4,7 +4,10 @@ use std::marker::PhantomData; use std::sync::Arc; use crate::types::Float; -use crate::mapping::{Apply, Mapping, Differentiable}; +use crate::mapping::{ + Instance, Mapping, DifferentiableImpl, DifferentiableMapping, Space, + BasicDecomposition, +}; //use crate::linops::{Apply, Linear}; use crate::sets::Set; use crate::sets::Cube; @@ -40,10 +43,22 @@ } impl +Space for BTFN +where + G : SupportGenerator, + G::SupportType : LocalAnalysis, + BT : BTImpl +{ + type Decomp = BasicDecomposition; +} + +impl BTFN -where G : SupportGenerator, - G::SupportType : LocalAnalysis, - BT : BTImpl { +where + G : SupportGenerator, + G::SupportType : LocalAnalysis, + BT : BTImpl +{ /// Create a new BTFN from a support generator and a pre-initialised bisection tree. /// @@ -390,64 +405,40 @@ // Apply, Mapping, Differentiate // -impl<'a, F : Float, G, BT, V, const N : usize> Apply<&'a Loc> +impl Mapping> for BTFN -where BT : BTImpl, - G : SupportGenerator, - G::SupportType : LocalAnalysis + Apply<&'a Loc, Output = V>, - V : Sum { +where + BT : BTImpl, + G : SupportGenerator, + G::SupportType : LocalAnalysis + Mapping, Codomain = V>, + V : Sum + Space, +{ - type Output = V; + type Codomain = V; - fn apply(&self, x : &'a Loc) -> Self::Output { - self.bt.iter_at(x) - .map(|&d| self.generator.support_for(d).apply(x)).sum() + fn apply>>(&self, x : I) -> Self::Codomain { + let xc = x.cow(); + self.bt.iter_at(&*xc) + .map(|&d| self.generator.support_for(d).apply(&*xc)).sum() } } -impl Apply> +impl DifferentiableImpl> for BTFN -where BT : BTImpl, - G : SupportGenerator, - G::SupportType : LocalAnalysis + Apply, Output = V>, - V : Sum { - - type Output = V; - - fn apply(&self, x : Loc) -> Self::Output { - self.bt.iter_at(&x) - .map(|&d| self.generator.support_for(d).apply(x)).sum() - } -} - -impl<'a, F : Float, G, BT, V, const N : usize> Differentiable<&'a Loc> -for BTFN -where BT : BTImpl, - G : SupportGenerator, - G::SupportType : LocalAnalysis + Differentiable<&'a Loc, Derivative = V>, - V : Sum { +where + BT : BTImpl, + G : SupportGenerator, + G::SupportType : LocalAnalysis + + DifferentiableMapping, DerivativeDomain = V>, + V : Sum + Space, +{ type Derivative = V; - fn differential(&self, x : &'a Loc) -> Self::Derivative { - self.bt.iter_at(x) - .map(|&d| self.generator.support_for(d).differential(x)) - .sum() - } -} - -impl Differentiable> -for BTFN -where BT : BTImpl, - G : SupportGenerator, - G::SupportType : LocalAnalysis + Differentiable, Derivative = V>, - V : Sum { - - type Derivative = V; - - fn differential(&self, x : Loc) -> Self::Derivative { - self.bt.iter_at(&x) - .map(|&d| self.generator.support_for(d).differential(x)) + fn differential_impl>>(&self, x :I) -> Self::Derivative { + let xc = x.cow(); + self.bt.iter_at(&*xc) + .map(|&d| self.generator.support_for(d).differential(&*xc)) .sum() } } @@ -840,7 +831,7 @@ impl BTFN where BT : BTSearch>, G : SupportGenerator, - G::SupportType : Mapping,Codomain=F> + G::SupportType : Mapping, Codomain=F> + LocalAnalysis, N>, Cube : P2Minimise, F> { diff -r 1a38447a89fa -r 9226980e45a7 src/bisection_tree/either.rs --- a/src/bisection_tree/either.rs Sat Dec 14 09:31:27 2024 -0500 +++ b/src/bisection_tree/either.rs Tue Dec 31 08:30:02 2024 -0500 @@ -3,7 +3,13 @@ use std::sync::Arc; use crate::types::*; -use crate::mapping::{Apply, Differentiable}; +use crate::mapping::{ + Instance, + Mapping, + DifferentiableImpl, + DifferentiableMapping, + Space, +}; use crate::iter::{Mappable, MapF, MapZ}; use crate::sets::Cube; use crate::loc::Loc; @@ -177,12 +183,17 @@ } } -impl Apply for EitherSupport -where S1 : Apply, - S2 : Apply { - type Output = F; +impl Mapping for EitherSupport +where + F : Space, + X : Space, + S1 : Mapping, + S2 : Mapping, +{ + type Codomain = F; + #[inline] - fn apply(&self, x : X) -> F { + fn apply>(&self, x : I) -> F { match self { EitherSupport::Left(ref a) => a.apply(x), EitherSupport::Right(ref b) => b.apply(x), @@ -190,12 +201,17 @@ } } -impl Differentiable for EitherSupport -where S1 : Differentiable, - S2 : Differentiable { - type Derivative = F; +impl DifferentiableImpl for EitherSupport +where + O : Space, + X : Space, + S1 : DifferentiableMapping, + S2 : DifferentiableMapping, +{ + type Derivative = O; + #[inline] - fn differential(&self, x : X) -> F { + fn differential_impl>(&self, x : I) -> O { match self { EitherSupport::Left(ref a) => a.differential(x), EitherSupport::Right(ref b) => b.differential(x), diff -r 1a38447a89fa -r 9226980e45a7 src/bisection_tree/support.rs --- a/src/bisection_tree/support.rs Sat Dec 14 09:31:27 2024 -0500 +++ b/src/bisection_tree/support.rs Tue Dec 31 08:30:02 2024 -0500 @@ -6,7 +6,9 @@ use std::ops::{MulAssign,DivAssign,Neg}; use crate::types::{Float, Num}; use crate::maputil::map2; -use crate::mapping::{Apply, Differentiable}; +use crate::mapping::{ + Instance, Mapping, DifferentiableImpl, DifferentiableMapping, Space +}; use crate::sets::Cube; use crate::loc::Loc; use super::aggregator::Bounds; @@ -127,39 +129,23 @@ base_fn : T, } -impl<'a, T, V, F : Float, const N : usize> Apply<&'a Loc> for Shift -where T : Apply, Output=V> { - type Output = V; +impl<'a, T, V : Space, F : Float, const N : usize> Mapping> for Shift +where T : Mapping, Codomain=V> { + type Codomain = V; + #[inline] - fn apply(&self, x : &'a Loc) -> Self::Output { - self.base_fn.apply(x - &self.shift) + fn apply>>(&self, x : I) -> Self::Codomain { + self.base_fn.apply(x.own() - &self.shift) } } -impl<'a, T, V, F : Float, const N : usize> Apply> for Shift -where T : Apply, Output=V> { - type Output = V; - #[inline] - fn apply(&self, x : Loc) -> Self::Output { - self.base_fn.apply(x - &self.shift) - } -} - -impl<'a, T, V, F : Float, const N : usize> Differentiable<&'a Loc> for Shift -where T : Differentiable, Derivative=V> { +impl<'a, T, V : Space, F : Float, const N : usize> DifferentiableImpl> for Shift +where T : DifferentiableMapping, DerivativeDomain=V> { type Derivative = V; + #[inline] - fn differential(&self, x : &'a Loc) -> Self::Derivative { - self.base_fn.differential(x - &self.shift) - } -} - -impl<'a, T, V, F : Float, const N : usize> Differentiable> for Shift -where T : Differentiable, Derivative=V> { - type Derivative = V; - #[inline] - fn differential(&self, x : Loc) -> Self::Derivative { - self.base_fn.differential(x - &self.shift) + fn differential_impl>>(&self, x : I) -> Self::Derivative { + self.base_fn.differential(x.own() - &self.shift) } } @@ -228,46 +214,26 @@ pub base_fn : T, } -impl<'a, T, V, F : Float, C, const N : usize> Apply<&'a Loc> for Weighted -where T : for<'b> Apply<&'b Loc, Output=V>, - V : std::ops::Mul, +impl<'a, T, V, F : Float, C, const N : usize> Mapping> for Weighted +where T : Mapping, Codomain=V>, + V : Space + std::ops::Mul, C : Constant { - type Output = V; + type Codomain = V; + #[inline] - fn apply(&self, x : &'a Loc) -> Self::Output { + fn apply>>(&self, x : I) -> Self::Codomain { self.base_fn.apply(x) * self.weight.value() } } -impl<'a, T, V, F : Float, C, const N : usize> Apply> for Weighted -where T : Apply, Output=V>, - V : std::ops::Mul, - C : Constant { - type Output = V; - #[inline] - fn apply(&self, x : Loc) -> Self::Output { - self.base_fn.apply(x) * self.weight.value() - } -} - -impl<'a, T, V, F : Float, C, const N : usize> Differentiable<&'a Loc> for Weighted -where T : for<'b> Differentiable<&'b Loc, Derivative=V>, - V : std::ops::Mul, +impl<'a, T, V, F : Float, C, const N : usize> DifferentiableImpl> for Weighted +where T : DifferentiableMapping, DerivativeDomain=V>, + V : Space + std::ops::Mul, C : Constant { type Derivative = V; + #[inline] - fn differential(&self, x : &'a Loc) -> Self::Derivative { - self.base_fn.differential(x) * self.weight.value() - } -} - -impl<'a, T, V, F : Float, C, const N : usize> Differentiable> for Weighted -where T : Differentiable, Derivative=V>, - V : std::ops::Mul, - C : Constant { - type Derivative = V; - #[inline] - fn differential(&self, x : Loc) -> Self::Derivative { + fn differential_impl>>(&self, x : I) -> Self::Derivative { self.base_fn.differential(x) * self.weight.value() } } @@ -380,21 +346,12 @@ pub T ); -impl<'a, T, F : Float, const N : usize> Apply<&'a Loc> for Normalised -where T : Norm + for<'b> Apply<&'b Loc, Output=F> { - type Output = F; +impl<'a, T, F : Float, const N : usize> Mapping> for Normalised +where T : Norm + Mapping, Codomain=F> { + type Codomain = F; + #[inline] - fn apply(&self, x : &'a Loc) -> Self::Output { - let w = self.0.norm(L1); - if w == F::ZERO { F::ZERO } else { self.0.apply(x) / w } - } -} - -impl<'a, T, F : Float, const N : usize> Apply> for Normalised -where T : Norm + Apply, Output=F> { - type Output = F; - #[inline] - fn apply(&self, x : Loc) -> Self::Output { + fn apply>>(&self, x : I) -> Self::Codomain { let w = self.0.norm(L1); if w == F::ZERO { F::ZERO } else { self.0.apply(x) / w } } diff -r 1a38447a89fa -r 9226980e45a7 src/collection.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/collection.rs Tue Dec 31 08:30:02 2024 -0500 @@ -0,0 +1,54 @@ +/*! +Abstract collections of objects. +*/ + +use crate::loc::Loc; + +/// An abstract collection of elements. +pub trait Collection : IntoIterator { + /// Type of elements of the collection + type Element; + /// Iterator over references to elements of the collection + type RefsIter<'a> : Iterator where Self : 'a; + + /// Returns an iterator over references to elements of the collection. + fn iter_refs(&self) -> Self::RefsIter<'_>; +} + +/// An abstract collection of mutable elements. +pub trait CollectionMut : Collection { + /// Iterator over references to elements of the collection + type RefsIterMut<'a> : Iterator where Self : 'a; + + /// Returns an iterator over references to elements of the collection. + fn iter_refs_mut(&mut self) -> Self::RefsIterMut<'_>; +} + +/// Helps implement Collection and CollectionMut for slice-like collections. +#[macro_export] +macro_rules! slice_like_collection { + ($type : ty where $($where:tt)*) => { + impl<$($where)*> Collection for $type { + type Element = E; + type RefsIter<'_a> = std::slice::Iter<'_a, E> where Self : '_a; + + #[inline] + fn iter_refs(&self) -> Self::RefsIter<'_> { + self.iter() + } + } + + impl<$($where)*> CollectionMut for $type { + type RefsIterMut<'_a> = std::slice::IterMut<'_a, E> where Self : '_a; + + #[inline] + fn iter_refs_mut(&mut self) -> Self::RefsIterMut<'_> { + self.iter_mut() + } + } + } +} + +slice_like_collection!(Vec where E); +slice_like_collection!([E; N] where E, const N : usize); +slice_like_collection!(Loc where E, const N : usize); diff -r 1a38447a89fa -r 9226980e45a7 src/convex.rs --- a/src/convex.rs Sat Dec 14 09:31:27 2024 -0500 +++ b/src/convex.rs Tue Dec 31 08:30:02 2024 -0500 @@ -2,20 +2,24 @@ Some convex analysis basics */ -use crate::mapping::{Apply, Mapping}; +use crate::types::*; +use crate::mapping::{Mapping, Space}; +use crate::instance::{Instance, InstanceMut, DecompositionMut}; +use crate::norms::*; /// Trait for convex mappings. Has no features, just serves as a constraint /// /// TODO: should constrain `Mapping::Codomain` to implement a partial order, /// but this makes everything complicated with little benefit. -pub trait ConvexMapping : Mapping {} +pub trait ConvexMapping : Mapping +{} /// Trait for mappings with a Fenchel conjugate /// /// The conjugate type has to implement [`ConvexMapping`], but a `Conjugable` mapping need /// not be convex. -pub trait Conjugable : Mapping { - type DualDomain; +pub trait Conjugable : Mapping { + type DualDomain : Space; type Conjugate<'a> : ConvexMapping where Self : 'a; fn conjugate(&self) -> Self::Conjugate<'_>; @@ -25,8 +29,8 @@ /// /// In contrast to [`Conjugable`], the preconjugate need not implement [`ConvexMapping`], /// but a `Preconjugable` mapping has be convex. -pub trait Preconjugable : ConvexMapping { - type PredualDomain; +pub trait Preconjugable : ConvexMapping { + type PredualDomain : Space; type Preconjugate<'a> : Mapping where Self : 'a; fn preconjugate(&self) -> Self::Preconjugate<'_>; @@ -36,15 +40,78 @@ /// /// The conjugate type has to implement [`ConvexMapping`], but a `Conjugable` mapping need /// not be convex. -pub trait HasProx : Mapping { +pub trait Prox : Mapping { type Prox<'a> : Mapping where Self : 'a; /// Returns a proximal mapping with weight τ fn prox_mapping(&self, τ : Self::Codomain) -> Self::Prox<'_>; /// Calculate the proximal mapping with weight τ - fn prox(&self, z : Domain, τ : Self::Codomain) -> Domain { + fn prox>(&self, τ : Self::Codomain, z : I) -> Domain { self.prox_mapping(τ).apply(z) } + + /// Calculate the proximal mapping with weight τ in-place + fn prox_mut<'b>(&self, τ : Self::Codomain, y : &'b mut Domain) + where + &'b mut Domain : InstanceMut, + Domain:: Decomp : DecompositionMut, + for<'a> &'a Domain : Instance, + { + *y = self.prox(τ, &*y); + } } + +pub struct NormConjugate(NormMapping); + +impl ConvexMapping for NormMapping +where + Domain : Space, + E : NormExponent, + F : Float, + Self : Mapping {} + + +impl ConvexMapping for NormConjugate +where + Domain : Space, + E : NormExponent, + F : Float, + Self : Mapping {} + + +impl Mapping for NormConjugate +where + Domain : Space + Norm, + F : Float, + E : NormExponent, +{ + type Codomain = F; + + fn apply>(&self, d : I) -> F { + if d.eval(|x| x.norm(self.0.exponent)) <= F::ONE { + F::ZERO + } else { + F::INFINITY + } + } +} + + + +impl Conjugable for NormMapping +where + E : NormExponent + Clone, + F : Float, + Domain : Norm + Space, +{ + + type DualDomain = Domain; + type Conjugate<'a> = NormConjugate where Self : 'a; + + fn conjugate(&self) -> Self::Conjugate<'_> { + NormConjugate(self.clone()) + } +} + diff -r 1a38447a89fa -r 9226980e45a7 src/direct_product.rs --- a/src/direct_product.rs Sat Dec 14 09:31:27 2024 -0500 +++ b/src/direct_product.rs Tue Dec 31 08:30:02 2024 -0500 @@ -9,65 +9,81 @@ use std::clone::Clone; use serde::{Serialize, Deserialize}; use crate::types::{Num, Float}; -use crate::{maybe_lifetime, maybe_ref, impl_vectorspace_ops}; +use crate::{maybe_lifetime, maybe_ref}; use crate::euclidean::{Dot, Euclidean}; +use crate::instance::{Instance, InstanceMut, Decomposition, DecompositionMut, MyCow}; +use crate::mapping::Space; +use crate::linops::AXPY; +use crate::loc::Loc; +use crate::norms::{Norm, PairNorm, NormExponent}; -#[derive(Debug,Clone,PartialEq,Eq,Serialize,Deserialize)] +#[derive(Debug,Clone,Copy,PartialEq,Eq,Serialize,Deserialize)] pub struct Pair (pub A, pub B); impl Pair { - pub fn new(a : A, b : B) -> Pair { Pair{ 0 : a, 1 : b } } + pub fn new(a : A, b : B) -> Pair { Pair(a, b) } } impl From<(A,B)> for Pair { #[inline] - fn from((a, b) : (A, B)) -> Pair { Pair{ 0 : a, 1 : b } } + fn from((a, b) : (A, B)) -> Pair { Pair(a, b) } +} + +impl From> for (A,B) { + #[inline] + fn from(Pair(a, b) : Pair) -> (A,B) { (a, b) } } macro_rules! impl_binop { - ($trait : ident, $fn : ident, $refl:ident, $refr:ident) => { - impl_binop!(@doit: $trait, $fn; - maybe_lifetime!($refl, &'l Pair), - (maybe_lifetime!($refl, &'l A), maybe_lifetime!($refl, &'l B)); + (($a : ty, $b : ty), $trait : ident, $fn : ident, $refl:ident, $refr:ident) => { + impl_binop!(@doit: $a, $b, $trait, $fn; + maybe_lifetime!($refl, &'l Pair<$a,$b>), + (maybe_lifetime!($refl, &'l $a), + maybe_lifetime!($refl, &'l $b)); maybe_lifetime!($refr, &'r Pair), - (maybe_lifetime!($refr, &'r Ai), maybe_lifetime!($refr, &'r Bi)); + (maybe_lifetime!($refr, &'r Ai), + maybe_lifetime!($refr, &'r Bi)); $refl, $refr); }; - (@doit: $trait:ident, $fn:ident; + (@doit: $a:ty, $b:ty, + $trait:ident, $fn:ident; $self:ty, ($aself:ty, $bself:ty); $in:ty, ($ain:ty, $bin:ty); $refl:ident, $refr:ident) => { - impl<'l, 'r, A, B, Ai, Bi> $trait<$in> + impl<'l, 'r, Ai, Bi> $trait<$in> for $self where $aself: $trait<$ain>, $bself: $trait<$bin> { type Output = Pair<<$aself as $trait<$ain>>::Output, - <$bself as $trait<$bin>>::Output>; + <$bself as $trait<$bin>>::Output>; #[inline] fn $fn(self, y : $in) -> Self::Output { - Pair { 0 : maybe_ref!($refl, self.0).$fn(maybe_ref!($refr, y.0)), - 1 : maybe_ref!($refl, self.1).$fn(maybe_ref!($refr, y.1)) } + Pair(maybe_ref!($refl, self.0).$fn(maybe_ref!($refr, y.0)), + maybe_ref!($refl, self.1).$fn(maybe_ref!($refr, y.1))) } } }; } macro_rules! impl_assignop { - ($trait : ident, $fn : ident, $refr:ident) => { - impl_assignop!(@doit: $trait, $fn; + (($a : ty, $b : ty), $trait : ident, $fn : ident, $refr:ident) => { + impl_assignop!(@doit: $a, $b, + $trait, $fn; maybe_lifetime!($refr, &'r Pair), - (maybe_lifetime!($refr, &'r Ai), maybe_lifetime!($refr, &'r Bi)); + (maybe_lifetime!($refr, &'r Ai), + maybe_lifetime!($refr, &'r Bi)); $refr); }; - (@doit: $trait:ident, $fn:ident; + (@doit: $a : ty, $b : ty, + $trait:ident, $fn:ident; $in:ty, ($ain:ty, $bin:ty); $refr:ident) => { - impl<'r, A, B, Ai, Bi> $trait<$in> - for Pair - where A: $trait<$ain>, - B: $trait<$bin> { + impl<'r, Ai, Bi> $trait<$in> + for Pair<$a,$b> + where $a: $trait<$ain>, + $b: $trait<$bin> { #[inline] fn $fn(&mut self, y : $in) -> () { self.0.$fn(maybe_ref!($refr, y.0)); @@ -78,26 +94,29 @@ } macro_rules! impl_scalarop { - ($trait : ident, $fn : ident, $refl:ident) => { - impl_scalarop!(@doit: $trait, $fn; - maybe_lifetime!($refl, &'l Pair), - (maybe_lifetime!($refl, &'l A), maybe_lifetime!($refl, &'l B)); + (($a : ty, $b : ty), $field : ty, $trait : ident, $fn : ident, $refl:ident) => { + impl_scalarop!(@doit: $field, + $trait, $fn; + maybe_lifetime!($refl, &'l Pair<$a,$b>), + (maybe_lifetime!($refl, &'l $a), + maybe_lifetime!($refl, &'l $b)); $refl); }; - (@doit: $trait:ident, $fn:ident; + (@doit: $field : ty, + $trait:ident, $fn:ident; $self:ty, ($aself:ty, $bself:ty); $refl:ident) => { // Scalar as Rhs - impl<'l, F : Num, A, B> $trait + impl<'l> $trait<$field> for $self - where $aself: $trait, - $bself: $trait { - type Output = Pair<<$aself as $trait>::Output, - <$bself as $trait>::Output>; + where $aself: $trait<$field>, + $bself: $trait<$field> { + type Output = Pair<<$aself as $trait<$field>>::Output, + <$bself as $trait<$field>>::Output>; #[inline] - fn $fn(self, a : F) -> Self::Output { - Pair{ 0 : maybe_ref!($refl, self.0).$fn(a), - 1 : maybe_ref!($refl, self.1).$fn(a)} + fn $fn(self, a : $field) -> Self::Output { + Pair(maybe_ref!($refl, self.0).$fn(a), + maybe_ref!($refl, self.1).$fn(a)) } } } @@ -106,37 +125,38 @@ // Not used due to compiler overflow #[allow(unused_macros)] macro_rules! impl_scalarlhs_op { - ($trait:ident, $fn:ident, $refr:ident, $field:ty) => { - impl_scalarlhs_op!(@doit: $trait, $fn, - maybe_lifetime!($refr, &'r Pair), - (maybe_lifetime!($refr, &'r Ai), maybe_lifetime!($refr, &'r Bi)); + (($a : ty, $b : ty), $field : ty, $trait:ident, $fn:ident, $refr:ident) => { + impl_scalarlhs_op!(@doit: $trait, $fn, + maybe_lifetime!($refr, &'r Pair<$a,$b>), + (maybe_lifetime!($refr, &'r $a), + maybe_lifetime!($refr, &'r $b)); $refr, $field); }; - (@doit: $trait:ident, $fn:ident, + (@doit: $trait:ident, $fn:ident, $in:ty, ($ain:ty, $bin:ty); $refr:ident, $field:ty) => { - impl<'r, Ai, Bi> $trait<$in> + impl<'r> $trait<$in> for $field where $field : $trait<$ain> + $trait<$bin> { type Output = Pair<<$field as $trait<$ain>>::Output, - <$field as $trait<$bin>>::Output>; + <$field as $trait<$bin>>::Output>; #[inline] fn $fn(self, x : $in) -> Self::Output { - Pair{ 0 : self.$fn(maybe_ref!($refr, x.0)), - 1 : self.$fn(maybe_ref!($refr, x.1))} + Pair(self.$fn(maybe_ref!($refr, x.0)), + self.$fn(maybe_ref!($refr, x.1))) } } }; } macro_rules! impl_scalar_assignop { - ($trait : ident, $fn : ident) => { - impl<'r, F : Num, A, B> $trait - for Pair - where A: $trait, B: $trait { + (($a : ty, $b : ty), $field : ty, $trait : ident, $fn : ident) => { + impl<'r> $trait<$field> + for Pair<$a, $b> + where $a: $trait<$field>, $b: $trait<$field> { #[inline] - fn $fn(&mut self, a : F) -> () { + fn $fn(&mut self, a : $field) -> () { self.0.$fn(a); self.1.$fn(a); } @@ -145,34 +165,77 @@ } macro_rules! impl_unaryop { - ($trait:ident, $fn:ident, $refl:ident) => { + (($a : ty, $b : ty), $trait:ident, $fn:ident, $refl:ident) => { impl_unaryop!(@doit: $trait, $fn; - maybe_lifetime!($refl, &'l Pair), - (maybe_lifetime!($refl, &'l A), maybe_lifetime!($refl, &'l B)); + maybe_lifetime!($refl, &'l Pair<$a,$b>), + (maybe_lifetime!($refl, &'l $a), + maybe_lifetime!($refl, &'l $b)); $refl); }; (@doit: $trait:ident, $fn:ident; $self:ty, ($aself:ty, $bself:ty); $refl : ident) => { - impl<'l, A, B> $trait + impl<'l> $trait for $self where $aself: $trait, $bself: $trait { type Output = Pair<<$aself as $trait>::Output, - <$bself as $trait>::Output>; + <$bself as $trait>::Output>; #[inline] fn $fn(self) -> Self::Output { - Pair{ 0 : maybe_ref!($refl, self.0).$fn(), - 1 : maybe_ref!($refl, self.1).$fn()} + Pair(maybe_ref!($refl, self.0).$fn(), + maybe_ref!($refl, self.1).$fn()) } } } } +#[macro_export] +macro_rules! impl_pair_vectorspace_ops { + (($a:ty, $b:ty), $field:ty) => { + impl_pair_vectorspace_ops!(@binary, ($a, $b), Add, add); + impl_pair_vectorspace_ops!(@binary, ($a, $b), Sub, sub); + impl_pair_vectorspace_ops!(@assign, ($a, $b), AddAssign, add_assign); + impl_pair_vectorspace_ops!(@assign, ($a, $b), SubAssign, sub_assign); + impl_pair_vectorspace_ops!(@scalar, ($a, $b), $field, Mul, mul); + impl_pair_vectorspace_ops!(@scalar, ($a, $b), $field, Div, div); + // Compiler overflow + // $( + // impl_pair_vectorspace_ops!(@scalar_lhs, ($a, $b), $field, $impl_scalarlhs_op, Mul, mul); + // )* + impl_pair_vectorspace_ops!(@scalar_assign, ($a, $b), $field, MulAssign, mul_assign); + impl_pair_vectorspace_ops!(@scalar_assign, ($a, $b), $field, DivAssign, div_assign); + impl_pair_vectorspace_ops!(@unary, ($a, $b), Neg, neg); + }; + (@binary, ($a : ty, $b : ty), $trait : ident, $fn : ident) => { + impl_binop!(($a, $b), $trait, $fn, ref, ref); + impl_binop!(($a, $b), $trait, $fn, ref, noref); + impl_binop!(($a, $b), $trait, $fn, noref, ref); + impl_binop!(($a, $b), $trait, $fn, noref, noref); + }; + (@assign, ($a : ty, $b : ty), $trait : ident, $fn :ident) => { + impl_assignop!(($a, $b), $trait, $fn, ref); + impl_assignop!(($a, $b), $trait, $fn, noref); + }; + (@scalar, ($a : ty, $b : ty), $field : ty, $trait : ident, $fn :ident) => { + impl_scalarop!(($a, $b), $field, $trait, $fn, ref); + impl_scalarop!(($a, $b), $field, $trait, $fn, noref); + }; + (@scalar_lhs, ($a : ty, $b : ty), $field : ty, $trait : ident, $fn : ident) => { + impl_scalarlhs_op!(($a, $b), $field, $trait, $fn, ref); + impl_scalarlhs_op!(($a, $b), $field, $trait, $fn, noref); + }; + (@scalar_assign, ($a : ty, $b : ty), $field : ty, $trait : ident, $fn : ident) => { + impl_scalar_assignop!(($a, $b), $field, $trait, $fn); + }; + (@unary, ($a : ty, $b : ty), $trait : ident, $fn : ident) => { + impl_unaryop!(($a, $b), $trait, $fn, ref); + impl_unaryop!(($a, $b), $trait, $fn, noref); + }; +} -impl_vectorspace_ops!(impl_binop, impl_assignop, impl_scalarop, impl_scalarlhs_op, - impl_scalar_assignop, impl_unaryop); - +impl_pair_vectorspace_ops!((f32, f32), f32); +impl_pair_vectorspace_ops!((f64, f64), f64); impl Dot, F> for Pair where @@ -186,15 +249,28 @@ } } -impl Euclidean for Pair +type PairOutput = Pair<>::Output, >::Output>; + +impl Euclidean for Pair where A : Euclidean, B : Euclidean, - F : Float + F : Float, + PairOutput : Euclidean, + Self : Sized + Dot + + Mul> + MulAssign + + Div> + DivAssign + + Add> + + Sub> + + for<'b> Add<&'b Self, Output=PairOutput> + + for<'b> Sub<&'b Self, Output=PairOutput> + + AddAssign + for<'b> AddAssign<&'b Self> + + SubAssign + for<'b> SubAssign<&'b Self> + + Neg> { - type Output = Pair<>::Output, >::Output>; + type Output = PairOutput; - fn similar_origin(&self) -> >::Output { + fn similar_origin(&self) -> PairOutput { Pair(self.0.similar_origin(), self.1.similar_origin()) } @@ -203,26 +279,192 @@ } } -use crate::linops::AXPY; - impl AXPY> for Pair where + U : Space, + V : Space, A : AXPY, B : AXPY, F : Num { - fn axpy(&mut self, α : F, x : &Pair, β : F) { - self.0.axpy(α, &x.0, β); - self.1.axpy(α, &x.1, β); + + fn axpy>>(&mut self, α : F, x : I, β : F) { + let Pair(u, v) = x.decompose(); + self.0.axpy(α, u, β); + self.1.axpy(α, v, β); + } + + fn copy_from>>(&mut self, x : I) { + let Pair(u, v) = x.decompose(); + self.0.copy_from(u); + self.1.copy_from(v); + } + + fn scale_from>>(&mut self, α : F, x : I) { + let Pair(u, v) = x.decompose(); + self.0.scale_from(α, u); + self.1.scale_from(α, v); + } +} + +/// [`Decomposition`] for working with [`Pair`]s. +#[derive(Copy, Clone, Debug)] +pub struct PairDecomposition(D, Q); + +impl Space for Pair { + type Decomp = PairDecomposition; +} + +impl Decomposition> for PairDecomposition +where + A : Space, + B : Space, + D : Decomposition, + Q : Decomposition, +{ + type Decomposition<'b> = Pair, Q::Decomposition<'b>> where Pair : 'b; + type Reference<'b> = Pair, Q::Reference<'b>> where Pair : 'b; + + #[inline] + fn lift<'b>(Pair(u, v) : Self::Reference<'b>) -> Self::Decomposition<'b> { + Pair(D::lift(u), Q::lift(v)) + } +} + +impl Instance, PairDecomposition> for Pair +where + A : Space, + B : Space, + D : Decomposition, + Q : Decomposition, + U : Instance, + V : Instance, +{ + #[inline] + fn decompose<'b>(self) + -> as Decomposition>>::Decomposition<'b> + where Self : 'b, Pair : 'b + { + Pair(self.0.decompose(), self.1.decompose()) + } + + #[inline] + fn ref_instance(&self) + -> as Decomposition>>::Reference<'_> + { + Pair(self.0.ref_instance(), self.1.ref_instance()) + } + + #[inline] + fn cow<'b>(self) -> MyCow<'b, Pair> where Self : 'b{ + MyCow::Owned(Pair(self.0.own(), self.1.own())) } - fn copy_from(&mut self, x : &Pair,) { - self.0.copy_from(&x.0); - self.1.copy_from(&x.1); + #[inline] + fn own(self) -> Pair { + Pair(self.0.own(), self.1.own()) + } +} + + +impl<'a, A, B, U, V, D, Q> Instance, PairDecomposition> for &'a Pair +where + A : Space, + B : Space, + D : Decomposition, + Q : Decomposition, + U : Instance, + V : Instance, + &'a U : Instance, + &'a V : Instance, +{ + #[inline] + fn decompose<'b>(self) + -> as Decomposition>>::Decomposition<'b> + where Self : 'b, Pair : 'b + { + Pair(D::lift(self.0.ref_instance()), Q::lift(self.1.ref_instance())) + } + + #[inline] + fn ref_instance(&self) + -> as Decomposition>>::Reference<'_> + { + Pair(self.0.ref_instance(), self.1.ref_instance()) + } + + #[inline] + fn cow<'b>(self) -> MyCow<'b, Pair> where Self : 'b { + MyCow::Owned(self.own()) + } + + #[inline] + fn own(self) -> Pair { + let Pair(ref u, ref v) = self; + Pair(u.own(), v.own()) } - fn scale_from(&mut self, α : F, x : &Pair) { - self.0.scale_from(α, &x.0); - self.1.scale_from(α, &x.1); +} + +impl DecompositionMut> for PairDecomposition +where + A : Space, + B : Space, + D : DecompositionMut, + Q : DecompositionMut, +{ + type ReferenceMut<'b> = Pair, Q::ReferenceMut<'b>> where Pair : 'b; +} + +impl InstanceMut, PairDecomposition> for Pair +where + A : Space, + B : Space, + D : DecompositionMut, + Q : DecompositionMut, + U : InstanceMut, + V : InstanceMut, +{ + #[inline] + fn ref_instance_mut(&mut self) + -> as DecompositionMut>>::ReferenceMut<'_> + { + Pair(self.0.ref_instance_mut(), self.1.ref_instance_mut()) } -} \ No newline at end of file +} + +impl<'a, A, B, U, V, D, Q> InstanceMut, PairDecomposition> for &'a mut Pair +where + A : Space, + B : Space, + D : DecompositionMut, + Q : DecompositionMut, + U : InstanceMut, + V : InstanceMut, +{ + #[inline] + fn ref_instance_mut(&mut self) + -> as DecompositionMut>>::ReferenceMut<'_> + { + Pair(self.0.ref_instance_mut(), self.1.ref_instance_mut()) + } +} + + +impl Norm> +for Pair +where + F : Num, + ExpA : NormExponent, + ExpB : NormExponent, + ExpJ : NormExponent, + A : Norm, + B : Norm, + Loc : Norm, +{ + fn norm(&self, PairNorm(expa, expb, expj) : PairNorm) -> F { + Loc([self.0.norm(expa), self.1.norm(expb)]).norm(expj) + } +} + + diff -r 1a38447a89fa -r 9226980e45a7 src/euclidean.rs --- a/src/euclidean.rs Sat Dec 14 09:31:27 2024 -0500 +++ b/src/euclidean.rs Tue Dec 31 08:30:02 2024 -0500 @@ -2,8 +2,9 @@ Euclidean spaces. */ +use std::ops::{Mul, MulAssign, Div, DivAssign, Add, Sub, AddAssign, SubAssign, Neg}; use crate::types::*; -use std::ops::{Mul, MulAssign, Div, DivAssign, Add, Sub, AddAssign, SubAssign, Neg}; +use crate::mapping::Space; /// Space (type) with a defined dot product. /// @@ -18,7 +19,7 @@ /// 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 : Sized + Dot +pub trait Euclidean : Space + Dot + Mul>::Output> + MulAssign + Div>::Output> + DivAssign + Add>::Output> diff -r 1a38447a89fa -r 9226980e45a7 src/instance.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/instance.rs Tue Dec 31 08:30:02 2024 -0500 @@ -0,0 +1,282 @@ +/*! +Helper traits to work with references or owned values of types and their subsets. +*/ + +#[derive(Clone, Copy)] +pub enum EitherDecomp { + Owned(A), + Borrowed(B), +} + +/// A very basic implementation of [`Cow`] without a [`Clone`] trait dependency. +pub type MyCow<'b, X> = EitherDecomp; + +impl<'b, X> std::ops::Deref for MyCow<'b, X> { + type Target = X; + + #[inline] + fn deref(&self) -> &Self::Target { + match self { + EitherDecomp::Owned(x) => &x, + EitherDecomp::Borrowed(x) => x, + } + } +} + +impl<'b, X> MyCow<'b, X> { + #[inline] + pub fn into_owned(self) -> X where X : Clone { + match self { + EitherDecomp::Owned(x) => x, + EitherDecomp::Borrowed(x) => x.clone(), + } + } +} + +/// Trait for abitrary mathematical spaces. +pub trait Space : Instance { + /// Default decomposition for the space + type Decomp : Decomposition; +} + +#[macro_export] +macro_rules! impl_basic_space { + ($($type:ty)*) => { $( + impl $crate::instance::Space for $type { + type Decomp = $crate::instance::BasicDecomposition; + } + )* }; + ($type:ty where $($where:tt)*) => { + impl<$($where)*> $crate::instance::Space for $type { + type Decomp = $crate::instance::BasicDecomposition; + } + }; +} + +impl_basic_space!(u8 u16 u32 u64 u128 usize + i8 i16 i32 i64 i128 isize + f32 f64); + +/// Marker type for decompositions to be used with [`Instance`]. +pub trait Decomposition : Sized { + /// Possibly owned form of the decomposition + type Decomposition<'b> : Instance 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 + Copy where X : 'b; + + /// Left the lightweight reference type into a full decomposition type. + fn lift<'b>(r : Self::Reference<'b>) -> Self::Decomposition<'b>; +} + +/// Most common [`Decomposition`] (into `Either`) that allows working with owned +/// values and all sorts of references. +#[derive(Copy, Clone, Debug)] +pub struct BasicDecomposition; + +impl Decomposition 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> { + MyCow::Borrowed(r) + } +} + +/// Helper trait for functions to work with either owned values or references to either the +/// “principal type” `X` or types some present a subset of `X`. In the latter sense, this +/// generalises [`std::borrow::ToOwned`], [`std::borrow::Borrow`], and [`std::borrow::Cow`]. +/// This type also includes iteratation facilities when `X` is a [`Collection`], to avoid +/// the possibly costly conversion of such subset types into `X`. +pub trait Instance::Decomp> : Sized where D : Decomposition { + /// 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<'_>; + + /// Returns an owned instance of `X`, cloning or converting non-true instances when necessary. + fn own(self) -> X; + + // ************** automatically implemented methods below from here ************** + + /// 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 { + 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 + { + f(&*self.cow()) + } + + #[inline] + /// 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 + { + match self.cow() { + EitherDecomp::Owned(x) => f(x), + EitherDecomp::Borrowed(x) => g(x), + } + } +} + + +impl Instance for X { + #[inline] + fn decompose<'b>(self) -> >::Decomposition<'b> + where Self : 'b, X : 'b + { + MyCow::Owned(self) + } + + #[inline] + fn own(self) -> X { + self + } + + #[inline] + fn cow<'b>(self) -> MyCow<'b, X> where Self : 'b { + MyCow::Owned(self) + } + + #[inline] + fn ref_instance(&self) -> >::Reference<'_> { + self + } +} + +impl<'a, X : Space + Clone> Instance for &'a X { + #[inline] + fn decompose<'b>(self) -> >::Decomposition<'b> + where Self : 'b, X : 'b + { + MyCow::Borrowed(self) + } + + #[inline] + fn own(self) -> X { + self.clone() + } + + #[inline] + fn cow<'b>(self) -> MyCow<'b, X> where Self : 'b { + MyCow::Borrowed(self) + } + + #[inline] + fn ref_instance(&self) -> >::Reference<'_> { + *self + } +} + +impl<'a, X : Space + Clone> Instance for &'a mut X { + #[inline] + fn decompose<'b>(self) -> >::Decomposition<'b> + where Self : 'b, X : 'b + { + EitherDecomp::Borrowed(self) + } + + #[inline] + fn own(self) -> X { + self.clone() + } + + #[inline] + fn cow<'b>(self) -> MyCow<'b, X> where Self : 'b, X : Clone { + EitherDecomp::Borrowed(self) + } + + #[inline] + fn ref_instance(&self) -> >::Reference<'_> { + *self + } +} + +impl<'a, X : Space + Clone> Instance for MyCow<'a, X> { + + #[inline] + fn decompose<'b>(self) -> >::Decomposition<'b> + where Self : 'b, X : 'b + { + self + } + + #[inline] + fn own(self) -> X { + match self { + MyCow::Borrowed(a) => a.own(), + MyCow::Owned(b) => b.own() + } + } + + #[inline] + 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) -> >::Reference<'_> { + match self { + MyCow::Borrowed(a) => a, + MyCow::Owned(b) => &b, + } + } +} + +/// Marker type for mutable decompositions to be used with [`InstanceMut`]. +pub trait DecompositionMut : Sized { + type ReferenceMut<'b> : InstanceMut where X : 'b; +} + + +/// Helper trait for functions to work with mutable references. +pub trait InstanceMut::Decomp> : Sized where D : DecompositionMut { + /// Returns a mutable decomposition of self. + fn ref_instance_mut(&mut self) -> D::ReferenceMut<'_>; +} + +impl DecompositionMut 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 for X { + #[inline] + fn ref_instance_mut(&mut self) + -> >::ReferenceMut<'_> + { + self + } +} + +impl<'a, X : Space> InstanceMut for &'a mut X { + #[inline] + fn ref_instance_mut(&mut self) + -> >::ReferenceMut<'_> + { + self + } +} diff -r 1a38447a89fa -r 9226980e45a7 src/lib.rs --- a/src/lib.rs Sat Dec 14 09:31:27 2024 -0500 +++ b/src/lib.rs Tue Dec 31 08:30:02 2024 -0500 @@ -11,13 +11,12 @@ feature(maybe_uninit_uninit_array,maybe_uninit_array_assume_init,maybe_uninit_slice), feature(float_minimum_maximum), feature(get_mut_unchecked), + feature(cow_is_borrowed), )] -// They don't work: -//#![feature(negative_impls)] -//#![feature(specialization)] - pub mod types; +pub mod instance; +pub mod collection; pub mod nanleast; pub mod error; pub mod parallelism; diff -r 1a38447a89fa -r 9226980e45a7 src/linops.rs --- a/src/linops.rs Sat Dec 14 09:31:27 2024 -0500 +++ b/src/linops.rs Tue Dec 31 08:30:02 2024 -0500 @@ -4,59 +4,69 @@ use numeric_literals::replace_float_literals; use std::marker::PhantomData; +use serde::Serialize; use crate::types::*; -use serde::Serialize; -pub use crate::mapping::Apply; +pub use crate::mapping::{Mapping, Space}; use crate::direct_product::Pair; +use crate::instance::Instance; +use crate::norms::{NormExponent, PairNorm, L1, L2, Linfinity}; /// Trait for linear operators on `X`. -pub trait Linear : Apply - + for<'a> Apply<&'a X, Output=Self::Codomain> { - type Codomain; -} +pub trait Linear : Mapping +{ } /// Efficient in-place summation. #[replace_float_literals(F::cast_from(literal))] -pub trait AXPY { +pub trait AXPY : Space +where + F : Num, + X : Space, +{ /// Computes `y = βy + αx`, where `y` is `Self`. - fn axpy(&mut self, α : F, x : &X, β : F); + fn axpy>(&mut self, α : F, x : I, β : F); /// Copies `x` to `self`. - fn copy_from(&mut self, x : &X) { + fn copy_from>(&mut self, x : I) { self.axpy(1.0, x, 0.0) } /// Computes `y = αx`, where `y` is `Self`. - fn scale_from(&mut self, α : F, x : &X) { + fn scale_from>(&mut self, α : F, x : I) { self.axpy(α, x, 0.0) } } /// Efficient in-place application for [`Linear`] operators. #[replace_float_literals(F::cast_from(literal))] -pub trait GEMV>::Codomain> : Linear { +pub trait GEMV>::Codomain> : Linear { /// Computes `y = αAx + βy`, where `A` is `Self`. - fn gemv(&self, y : &mut Y, α : F, x : &X, β : F); + fn gemv>(&self, y : &mut Y, α : F, x : I, β : F); /// Computes `y = Ax`, where `A` is `Self` - fn apply_mut(&self, y : &mut Y, x : &X){ + fn apply_mut>(&self, y : &mut Y, x : I){ self.gemv(y, 1.0, x, 0.0) } /// Computes `y += Ax`, where `A` is `Self` - fn apply_add(&self, y : &mut Y, x : &X){ + fn apply_add>(&self, y : &mut Y, x : I){ self.gemv(y, 1.0, x, 1.0) } } /// Bounded linear operators -pub trait BoundedLinear : Linear { - type FloatType : Float; +pub trait BoundedLinear : Linear +where + F : Num, + X : Space, + XExp : NormExponent, + CodExp : NormExponent +{ /// A bound on the operator norm $\|A\|$ for the linear operator $A$=`self`. /// This is not expected to be the norm, just any bound on it that can be - /// reasonably implemented. - fn opnorm_bound(&self) -> Self::FloatType; + /// reasonably implemented. The [`NormExponent`] `xexp` indicates the norm + /// in `X`, and `codexp` in the codomain. + fn opnorm_bound(&self, xexp : XExp, codexp : CodExp) -> F; } // Linear operator application into mutable target. The [`AsRef`] bound @@ -70,16 +80,16 @@ }*/ /// Trait for forming the adjoint operator of `Self`. -pub trait Adjointable : Linear { - type AdjointCodomain; +pub trait Adjointable : Linear +where + X : Space, + Yʹ : Space, +{ + type AdjointCodomain : Space; type Adjoint<'a> : Linear where Self : 'a; /// Form the adjoint operator of `self`. fn adjoint(&self) -> Self::Adjoint<'_>; - - /*fn adjoint_apply(&self, y : &Yʹ) -> Self::AdjointCodomain { - self.adjoint().apply(y) - }*/ } /// Trait for forming a preadjoint of an operator. @@ -89,189 +99,206 @@ /// operator. The space `Ypre` is the predual of its codomain, and should be the /// domain of the adjointed operator. `Self::Preadjoint` should be /// [`Adjointable`]`<'a,Ypre,X>`. -pub trait Preadjointable : Linear { - type PreadjointCodomain; - type Preadjoint<'a> : Adjointable where Self : 'a; - /// Form the preadjoint operator of `self`. +pub trait Preadjointable : Linear { + type PreadjointCodomain : Space; + type Preadjoint<'a> : Adjointable< + Ypre, X, AdjointCodomain=Self::Codomain, Codomain=Self::PreadjointCodomain + > where Self : 'a; + + /// Form the adjoint operator of `self`. fn preadjoint(&self) -> Self::Preadjoint<'_>; } /// Adjointable operators $A: X → Y$ between reflexive spaces $X$ and $Y$. -pub trait SimplyAdjointable : Adjointable>::Codomain> {} -impl<'a,X,T> SimplyAdjointable for T where T : Adjointable>::Codomain> {} +pub trait SimplyAdjointable : Adjointable>::Codomain> {} +impl<'a,X : Space, T> SimplyAdjointable for T +where T : Adjointable>::Codomain> {} /// The identity operator #[derive(Clone,Copy,Debug,Serialize,Eq,PartialEq)] pub struct IdOp (PhantomData); impl IdOp { - fn new() -> IdOp { IdOp(PhantomData) } + pub fn new() -> IdOp { IdOp(PhantomData) } } -impl Apply for IdOp { - type Output = X; +impl Mapping for IdOp { + type Codomain = X; - fn apply(&self, x : X) -> X { - x + fn apply>(&self, x : I) -> X { + x.own() } } -impl<'a, X> Apply<&'a X> for IdOp where X : Clone { - type Output = X; - - fn apply(&self, x : &'a X) -> X { - x.clone() - } -} - -impl Linear for IdOp where X : Clone { - type Codomain = X; -} - +impl Linear for IdOp +{ } #[replace_float_literals(F::cast_from(literal))] -impl GEMV for IdOp where Y : AXPY, X : Clone { +impl GEMV for IdOp +where + Y : AXPY, + X : Clone + Space +{ // Computes `y = αAx + βy`, where `A` is `Self`. - fn gemv(&self, y : &mut Y, α : F, x : &X, β : F) { + fn gemv>(&self, y : &mut Y, α : F, x : I, β : F) { y.axpy(α, x, β) } - fn apply_mut(&self, y : &mut Y, x : &X){ + fn apply_mut>(&self, y : &mut Y, x : I){ y.copy_from(x); } } -impl BoundedLinear for IdOp where X : Clone { - type FloatType = float; - fn opnorm_bound(&self) -> float { 1.0 } +impl BoundedLinear for IdOp +where + X : Space + Clone, + F : Num, + E : NormExponent +{ + fn opnorm_bound(&self, _xexp : E, _codexp : E) -> F { F::ONE } } -impl Adjointable for IdOp where X : Clone { +impl Adjointable for IdOp { type AdjointCodomain=X; type Adjoint<'a> = IdOp where X : 'a; + fn adjoint(&self) -> Self::Adjoint<'_> { IdOp::new() } } +impl Preadjointable for IdOp { + type PreadjointCodomain=X; + type Preadjoint<'a> = IdOp where X : 'a; + + fn preadjoint(&self) -> Self::Preadjoint<'_> { IdOp::new() } +} + /// “Row operator” $(S, T)$; $(S, T)(x, y)=Sx + Ty$. pub struct RowOp(pub S, pub T); use std::ops::Add; -impl Apply> for RowOp +impl Mapping> for RowOp where - S : Apply, - T : Apply, - S::Output : Add + A : Space, + B : Space, + S : Mapping, + T : Mapping, + S::Codomain : Add, + >::Output : Space, + { - type Output = >::Output; + type Codomain = >::Output; - fn apply(&self, Pair(a, b) : Pair) -> Self::Output { + fn apply>>(&self, x : I) -> Self::Codomain { + let Pair(a, b) = x.decompose(); self.0.apply(a) + self.1.apply(b) } } -impl<'a, A, B, S, T> Apply<&'a Pair> for RowOp +impl Linear> for RowOp where - S : Apply<&'a A>, - T : Apply<&'a B>, - S::Output : Add -{ - type Output = >::Output; + A : Space, + B : Space, + S : Linear, + T : Linear, + S::Codomain : Add, + >::Output : Space, +{ } - fn apply(&self, Pair(ref a, ref b) : &'a Pair) -> Self::Output { - self.0.apply(a) + self.1.apply(b) - } -} -impl Linear> for RowOp +impl<'b, F, S, T, Y, U, V> GEMV, Y> for RowOp where - RowOp : Apply, Output=D> + for<'a> Apply<&'a Pair, Output=D>, -{ - type Codomain = D; -} - -impl GEMV, Y> for RowOp -where + U : Space, + V : Space, S : GEMV, T : GEMV, F : Num, Self : Linear, Codomain=Y> { - fn gemv(&self, y : &mut Y, α : F, x : &Pair, β : F) { - self.0.gemv(y, α, &x.0, β); - self.1.gemv(y, α, &x.1, β); + fn gemv>>(&self, y : &mut Y, α : F, x : I, β : F) { + let Pair(u, v) = x.decompose(); + self.0.gemv(y, α, u, β); + self.1.gemv(y, α, v, F::ONE); } - fn apply_mut(&self, y : &mut Y, x : &Pair){ - self.0.apply_mut(y, &x.0); - self.1.apply_mut(y, &x.1); + fn apply_mut>>(&self, y : &mut Y, x : I) { + let Pair(u, v) = x.decompose(); + self.0.apply_mut(y, u); + self.1.apply_mut(y, v); } /// Computes `y += Ax`, where `A` is `Self` - fn apply_add(&self, y : &mut Y, x : &Pair){ - self.0.apply_add(y, &x.0); - self.1.apply_add(y, &x.1); + fn apply_add>>(&self, y : &mut Y, x : I) { + let Pair(u, v) = x.decompose(); + self.0.apply_add(y, u); + self.1.apply_add(y, v); } } - /// “Column operator” $(S; T)$; $(S; T)x=(Sx, Tx)$. pub struct ColOp(pub S, pub T); -impl Apply for ColOp +impl Mapping for ColOp where - S : for<'a> Apply<&'a A, Output=O>, - T : Apply, - A : std::borrow::Borrow, + A : Space, + S : Mapping, + T : Mapping, { - type Output = Pair; + type Codomain = Pair; - fn apply(&self, a : A) -> Self::Output { - Pair(self.0.apply(a.borrow()), self.1.apply(a)) + fn apply>(&self, a : I) -> Self::Codomain { + Pair(self.0.apply(a.ref_instance()), self.1.apply(a)) } } -impl Linear for ColOp +impl Linear for ColOp where - ColOp : Apply + for<'a> Apply<&'a A, Output=D>, -{ - type Codomain = D; -} + A : Space, + S : Mapping, + T : Mapping, +{ } impl GEMV> for ColOp where + X : Space, S : GEMV, T : GEMV, F : Num, Self : Linear> { - fn gemv(&self, y : &mut Pair, α : F, x : &X, β : F) { - self.0.gemv(&mut y.0, α, x, β); + fn gemv>(&self, y : &mut Pair, α : F, x : I, β : F) { + self.0.gemv(&mut y.0, α, x.ref_instance(), β); self.1.gemv(&mut y.1, α, x, β); } - fn apply_mut(&self, y : &mut Pair, x : &X){ - self.0.apply_mut(&mut y.0, x); + fn apply_mut>(&self, y : &mut Pair, x : I){ + self.0.apply_mut(&mut y.0, x.ref_instance()); self.1.apply_mut(&mut y.1, x); } /// Computes `y += Ax`, where `A` is `Self` - fn apply_add(&self, y : &mut Pair, x : &X){ - self.0.apply_add(&mut y.0, x); + fn apply_add>(&self, y : &mut Pair, x : I){ + self.0.apply_add(&mut y.0, x.ref_instance()); self.1.apply_add(&mut y.1, x); } } -impl Adjointable,Yʹ> for RowOp +impl Adjointable, Yʹ> for RowOp where + A : Space, + B : Space, + Yʹ : Space, S : Adjointable, T : Adjointable, Self : Linear>, - for<'a> ColOp, T::Adjoint<'a>> : Linear, + // for<'a> ColOp, T::Adjoint<'a>> : Linear< + // Yʹ, + // Codomain=Pair + // >, { - type AdjointCodomain = R; + type AdjointCodomain = Pair; type Adjoint<'a> = ColOp, T::Adjoint<'a>> where Self : 'a; fn adjoint(&self) -> Self::Adjoint<'_> { @@ -279,14 +306,21 @@ } } -impl Preadjointable,Yʹ> for RowOp +impl Preadjointable, Yʹ> for RowOp where + A : Space, + B : Space, + Yʹ : Space, S : Preadjointable, T : Preadjointable, Self : Linear>, - for<'a> ColOp, T::Preadjoint<'a>> : Adjointable, Codomain=R>, + for<'a> ColOp, T::Preadjoint<'a>> : Adjointable< + Yʹ, Pair, + Codomain=Pair, + AdjointCodomain = Self::Codomain, + >, { - type PreadjointCodomain = R; + type PreadjointCodomain = Pair; type Preadjoint<'a> = ColOp, T::Preadjoint<'a>> where Self : 'a; fn preadjoint(&self) -> Self::Preadjoint<'_> { @@ -297,10 +331,17 @@ impl Adjointable> for ColOp where - S : Adjointable, - T : Adjointable, + A : Space, + Xʹ : Space, + Yʹ : Space, + R : Space + ClosedAdd, + S : Adjointable, + T : Adjointable, Self : Linear, - for<'a> RowOp, T::Adjoint<'a>> : Linear, Codomain=R>, + // for<'a> RowOp, T::Adjoint<'a>> : Linear< + // Pair, + // Codomain=R, + // >, { type AdjointCodomain = R; type Adjoint<'a> = RowOp, T::Adjoint<'a>> where Self : 'a; @@ -312,10 +353,18 @@ impl Preadjointable> for ColOp where - S : Preadjointable, - T : Preadjointable, + A : Space, + Xʹ : Space, + Yʹ : Space, + R : Space + ClosedAdd, + S : Preadjointable, + T : Preadjointable, Self : Linear, - for<'a> RowOp, T::Preadjoint<'a>> : Adjointable, A, Codomain=R>, + for<'a> RowOp, T::Preadjoint<'a>> : Adjointable< + Pair, A, + Codomain = R, + AdjointCodomain = Self::Codomain, + >, { type PreadjointCodomain = R; type Preadjoint<'a> = RowOp, T::Preadjoint<'a>> where Self : 'a; @@ -328,67 +377,73 @@ /// Diagonal operator pub struct DiagOp(pub S, pub T); -impl Apply> for DiagOp +impl Mapping> for DiagOp where - S : Apply, - T : Apply, + A : Space, + B : Space, + S : Mapping, + T : Mapping, { - type Output = Pair; + type Codomain = Pair; - fn apply(&self, Pair(a, b) : Pair) -> Self::Output { - Pair(self.0.apply(a), self.1.apply(b)) - } -} - -impl<'a, A, B, S, T> Apply<&'a Pair> for DiagOp -where - S : Apply<&'a A>, - T : Apply<&'a B>, -{ - type Output = Pair; - - fn apply(&self, Pair(ref a, ref b) : &'a Pair) -> Self::Output { + fn apply>>(&self, x : I) -> Self::Codomain { + let Pair(a, b) = x.decompose(); Pair(self.0.apply(a), self.1.apply(b)) } } -impl Linear> for DiagOp +impl Linear> for DiagOp where - DiagOp : Apply, Output=D> + for<'a> Apply<&'a Pair, Output=D>, -{ - type Codomain = D; -} + A : Space, + B : Space, + S : Linear, + T : Linear, +{ } impl GEMV, Pair> for DiagOp where + A : Space, + B : Space, + U : Space, + V : Space, S : GEMV, T : GEMV, F : Num, - Self : Linear, Codomain=Pair> + Self : Linear, Codomain=Pair>, { - fn gemv(&self, y : &mut Pair, α : F, x : &Pair, β : F) { - self.0.gemv(&mut y.0, α, &x.0, β); - self.1.gemv(&mut y.1, α, &x.1, β); + fn gemv>>(&self, y : &mut Pair, α : F, x : I, β : F) { + let Pair(u, v) = x.decompose(); + self.0.gemv(&mut y.0, α, u, β); + self.1.gemv(&mut y.1, α, v, β); } - fn apply_mut(&self, y : &mut Pair, x : &Pair){ - self.0.apply_mut(&mut y.0, &x.0); - self.1.apply_mut(&mut y.1, &x.1); + fn apply_mut>>(&self, y : &mut Pair, x : I){ + let Pair(u, v) = x.decompose(); + 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(&self, y : &mut Pair, x : &Pair){ - self.0.apply_add(&mut y.0, &x.0); - self.1.apply_add(&mut y.1, &x.1); + fn apply_add>>(&self, y : &mut Pair, x : I){ + let Pair(u, v) = x.decompose(); + self.0.apply_add(&mut y.0, u); + self.1.apply_add(&mut y.1, v); } } -impl Adjointable,Pair> for DiagOp +impl Adjointable, Pair> for DiagOp where + A : Space, + B : Space, + Xʹ: Space, + Yʹ : Space, + R : Space, S : Adjointable, T : Adjointable, Self : Linear>, - for<'a> DiagOp, T::Adjoint<'a>> : Linear, Codomain=R>, + for<'a> DiagOp, T::Adjoint<'a>> : Linear< + Pair, Codomain=R, + >, { type AdjointCodomain = R; type Adjoint<'a> = DiagOp, T::Adjoint<'a>> where Self : 'a; @@ -398,12 +453,21 @@ } } -impl Preadjointable,Pair> for DiagOp +impl Preadjointable, Pair> for DiagOp where + A : Space, + B : Space, + Xʹ: Space, + Yʹ : Space, + R : Space, S : Preadjointable, T : Preadjointable, Self : Linear>, - for<'a> DiagOp, T::Preadjoint<'a>> : Adjointable, Pair, Codomain=R>, + for<'a> DiagOp, T::Preadjoint<'a>> : Adjointable< + Pair, Pair, + Codomain=R, + AdjointCodomain = Self::Codomain, + >, { type PreadjointCodomain = R; type Preadjoint<'a> = DiagOp, T::Preadjoint<'a>> where Self : 'a; @@ -416,3 +480,65 @@ /// Block operator pub type BlockOp = ColOp, RowOp>; + +macro_rules! pairnorm { + ($expj:ty) => { + impl + BoundedLinear, PairNorm, ExpR, F> + for RowOp + where + F : Float, + A : Space, + B : Space, + S : BoundedLinear, + T : BoundedLinear, + S::Codomain : Add, + >::Output : Space, + ExpA : NormExponent, + ExpB : NormExponent, + ExpR : NormExponent, + { + fn opnorm_bound( + &self, + PairNorm(expa, expb, _) : PairNorm, + expr : ExpR + ) -> F { + // An application of the triangle inequality bounds the norm by the maximum + // of the individual norms. A simple observation shows this to be exact. + let na = self.0.opnorm_bound(expa, expr); + let nb = self.1.opnorm_bound(expb, expr); + na.max(nb) + } + } + + impl + BoundedLinear, F> + for ColOp + where + F : Float, + A : Space, + S : BoundedLinear, + T : BoundedLinear, + ExpA : NormExponent, + ExpS : NormExponent, + ExpT : NormExponent, + { + fn opnorm_bound( + &self, + expa : ExpA, + PairNorm(exps, expt, _) : PairNorm + ) -> F { + // This is based on the rule for RowOp and ‖A^*‖ = ‖A‖, hence, + // for A=[S; T], ‖A‖=‖[S^*, T^*]‖ ≤ max{‖S^*‖, ‖T^*‖} = max{‖S‖, ‖T‖} + let ns = self.0.opnorm_bound(expa, exps); + let nt = self.1.opnorm_bound(expa, expt); + ns.max(nt) + } + } + } +} + +pairnorm!(L1); +pairnorm!(L2); +pairnorm!(Linfinity); + diff -r 1a38447a89fa -r 9226980e45a7 src/loc.rs --- a/src/loc.rs Sat Dec 14 09:31:27 2024 -0500 +++ b/src/loc.rs Tue Dec 31 08:30:02 2024 -0500 @@ -10,9 +10,12 @@ use crate::maputil::{FixedLength,FixedLengthMut,map1,map2,map1_mut,map2_mut}; use crate::euclidean::*; use crate::norms::*; -use crate::linops::AXPY; +use crate::linops::{AXPY, Mapping, Linear}; +use crate::instance::{Instance, BasicDecomposition}; +use crate::mapping::Space; use serde::ser::{Serialize, Serializer, SerializeSeq}; + /// A container type for (short) `N`-dimensional vectors of element type `F`. /// /// Supports basic operations of an [`Euclidean`] space, several [`Norm`]s, and @@ -657,19 +660,35 @@ } } + +impl Space for Loc { + type Decomp = BasicDecomposition; +} + +impl Mapping> for Loc { + type Codomain = F; + + fn apply>>(&self, x : I) -> Self::Codomain { + x.eval(|x̃| self.dot(x̃)) + } +} + +impl Linear> for Loc { } + impl AXPY> for Loc { - #[inline] - fn axpy(&mut self, α : F, x : &Loc, β : F) { - if β == F::ZERO { - map2_mut(self, x, |yi, xi| { *yi = α * (*xi) }) - } else { - map2_mut(self, x, |yi, xi| { *yi = β * (*yi) + α * (*xi) }) - } + fn axpy>>(&mut self, α : F, x : I, β : F) { + x.eval(|x̃| { + if β == F::ZERO { + map2_mut(self, x̃, |yi, xi| { *yi = α * (*xi) }) + } else { + map2_mut(self, x̃, |yi, xi| { *yi = β * (*yi) + α * (*xi) }) + } + }) } #[inline] - fn copy_from(&mut self, x : &Loc) { - map2_mut(self, x, |yi, xi| *yi = *xi ) + fn copy_from>>(&mut self, x : I) { + x.eval(|x̃| map2_mut(self, x̃, |yi, xi| *yi = *xi )) } } diff -r 1a38447a89fa -r 9226980e45a7 src/mapping.rs --- a/src/mapping.rs Sat Dec 14 09:31:27 2024 -0500 +++ b/src/mapping.rs Tue Dec 31 08:30:02 2024 -0500 @@ -3,43 +3,20 @@ */ use std::marker::PhantomData; +use std::borrow::Cow; use crate::types::Float; use serde::Serialize; use crate::loc::Loc; - -/// Trait for application of `Self` as a mathematical function or operator on `X`. -pub trait Apply { - type Output; - - /// Compute the value of `self` at `x`. - fn apply(&self, x : X) -> Self::Output; -} - -/// This blanket implementation is a workaround helper to Rust trait system limitations. -/// -/// It is introduced because the trait system does not allow blanket implementations of both -/// [`Apply`] and [`Apply<&'a X>`]. With this, the latter is implemented automatically for -/// the reference, which can be sufficient to apply the operation in another blanket implementation. -impl<'a, T, X> Apply for &'a T where T : Apply { - type Output = >::Output; - - #[inline] - fn apply(&self, x : X) -> Self::Output { - (*self).apply(x) - } -} +pub use crate::instance::{Instance, Decomposition, BasicDecomposition, Space}; /// A mapping from `Domain` to `Codomain`. /// /// This is automatically implemented when the relevant [`Apply`] are implemented. -pub trait Mapping : Apply - + for<'a> Apply<&'a Domain, Output=Self::Codomain> { - type Codomain; -} +pub trait Mapping { + type Codomain : Space; -impl Mapping for T -where T : Apply + for<'a> Apply<&'a Domain, Output=Codomain> { - type Codomain = Codomain; + /// Compute the value of `self` at `x`. + fn apply>(&self, x : I) -> Self::Codomain; } /// Automatically implemented shorthand for referring to [`Mapping`]s from [`Loc`] to `F`. @@ -49,15 +26,6 @@ impl RealMapping for T where T : Mapping, Codomain = F> {} -/// Automatically implemented shorthand for referring to differentiable [`Mapping`]s from -/// [`Loc`] to `F`. -pub trait DifferentiableRealMapping -: DifferentiableMapping, Codomain = F, DerivativeDomain=Loc> {} - -impl DifferentiableRealMapping for T -where T : DifferentiableMapping, Codomain = F, DerivativeDomain=Loc> {} - - /// A helper trait alias for referring to [`Mapping`]s from [`Loc`] to [`Loc`]. pub trait RealVectorField : Mapping, Codomain = Loc> {} @@ -65,69 +33,70 @@ impl RealVectorField for T where T : Mapping, Codomain = Loc> {} - -/// Trait for calculation the differential of `Self` as a mathematical function on `X`. -pub trait Differentiable : Sized { - type Derivative; - - /// Compute the differential of `self` at `x`. - fn differential(&self, x : X) -> Self::Derivative; -} - -impl<'g, X, G : Differentiable> Differentiable for &'g G { - type Derivative = G::Derivative; - #[inline] - fn differential(&self, x : X) -> Self::Derivative { - (*self).differential(x) - } -} - /// A differentiable mapping from `Domain` to [`Mapping::Codomain`], with differentials /// `Differential`. /// -/// This is automatically implemented when the relevant [`Differentiable`] are implemented. -pub trait DifferentiableMapping -: Mapping - + Differentiable - + for<'a> Differentiable<&'a Domain, Derivative=Self::DerivativeDomain> { - type DerivativeDomain; - type Differential : Mapping; - type DifferentialRef<'b> : Mapping where Self : 'b; +/// This is automatically implemented when [`DifferentiableImpl`] is. +pub trait DifferentiableMapping : Mapping { + type DerivativeDomain : Space; + type Differential<'b> : Mapping where Self : 'b; + + /// Calculate differential at `x` + fn differential>(&self, x : I) -> Self::DerivativeDomain; /// Form the differential mapping of `self`. - fn diff(self) -> Self::Differential; + fn diff(self) -> Self::Differential<'static>; + /// Form the differential mapping of `self`. - fn diff_ref(&self) -> Self::DifferentialRef<'_>; + fn diff_ref(&self) -> Self::Differential<'_>; } +/// Automatically implemented shorthand for referring to differentiable [`Mapping`]s from +/// [`Loc`] to `F`. +pub trait DifferentiableRealMapping +: DifferentiableMapping, Codomain = F, DerivativeDomain = Loc> {} -impl DifferentiableMapping for T -where T : Mapping - + Differentiable - + for<'a> Differentiable<&'a Domain,Derivative=Derivative> { - type DerivativeDomain = Derivative; - type Differential = Differential; - type DifferentialRef<'b> = Differential where Self : 'b; +impl DifferentiableRealMapping for T +where T : DifferentiableMapping, Codomain = F, DerivativeDomain = Loc> {} + +/// Helper trait for implementing [`DifferentiableMapping`] +pub trait DifferentiableImpl : Sized { + type Derivative : Space; + + /// Compute the differential of `self` at `x`, consuming the input. + fn differential_impl>(&self, x : I) -> Self::Derivative; +} + +impl DifferentiableMapping for T +where + Domain : Space, + T : Clone + Mapping + DifferentiableImpl +{ + type DerivativeDomain = T::Derivative; + type Differential<'b> = Differential<'b, Domain, Self> where Self : 'b; - /// Form the differential mapping of `self`. - fn diff(self) -> Self::Differential { - Differential{ g : self, _space : PhantomData } + #[inline] + fn differential>(&self, x : I) -> Self::DerivativeDomain { + self.differential_impl(x) } - /// Form the differential mapping of `self`. - fn diff_ref(&self) -> Self::DifferentialRef<'_> { - Differential{ g : self, _space : PhantomData } + fn diff(self) -> Differential<'static, Domain, Self> { + Differential{ g : Cow::Owned(self), _space : PhantomData } + } + + fn diff_ref(&self) -> Differential<'_, Domain, Self> { + Differential{ g : Cow::Borrowed(self), _space : PhantomData } } } /// A sum of [`Mapping`]s. #[derive(Serialize, Debug, Clone)] -pub struct Sum> { +pub struct Sum { components : Vec, _domain : PhantomData, } -impl> Sum { +impl Sum { /// Construct from an iterator. pub fn new>(iter : I) -> Self { Sum { components : iter.collect(), _domain : PhantomData } @@ -140,123 +109,107 @@ } -impl Apply for Sum -where M : Mapping, - M::Codomain : std::iter::Sum { - type Output = M::Codomain; +impl Mapping for Sum +where + Domain : Space + Clone, + M : Mapping, + M::Codomain : std::iter::Sum + Clone +{ + type Codomain = M::Codomain; - fn apply(&self, x : Domain) -> Self::Output { - self.components.iter().map(|c| c.apply(&x)).sum() + fn apply>(&self, x : I) -> Self::Codomain { + let xr = x.ref_instance(); + self.components.iter().map(|c| c.apply(xr)).sum() } } -impl<'a, Domain, M> Apply<&'a Domain> for Sum -where M : Mapping, - M::Codomain : std::iter::Sum { - type Output = M::Codomain; - - fn apply(&self, x : &'a Domain) -> Self::Output { - self.components.iter().map(|c| c.apply(x)).sum() - } -} - -impl Differentiable for Sum -where M : DifferentiableMapping, - M :: Codomain : std::iter::Sum, - M :: DerivativeDomain : std::iter::Sum, - Domain : Copy { - +impl DifferentiableImpl for Sum +where + Domain : Space + Clone, + M : DifferentiableMapping, + M :: DerivativeDomain : std::iter::Sum +{ type Derivative = M::DerivativeDomain; - fn differential(&self, x : Domain) -> Self::Derivative { - self.components.iter().map(|c| c.differential(x)).sum() + fn differential_impl>(&self, x : I) -> Self::Derivative { + let xr = x.ref_instance(); + self.components.iter().map(|c| c.differential(xr)).sum() } } /// Container for the differential [`Mapping`] of a [`Differentiable`] mapping. -pub struct Differential> { - g : G, +pub struct Differential<'a, X, G : Clone> { + g : Cow<'a, G>, _space : PhantomData } -impl> Differential { +impl<'a, X, G : Clone> Differential<'a, X, G> { pub fn base_fn(&self) -> &G { &self.g } } -impl> Apply for Differential { - type Output = G::DerivativeDomain; +impl<'a, X, G> Mapping for Differential<'a, X, G> +where + X : Space, + G : Clone + DifferentiableMapping +{ + type Codomain = G::DerivativeDomain; #[inline] - fn apply(&self, x : X) -> Self::Output { - self.g.differential(x) + fn apply>(&self, x : I) -> Self::Codomain { + (*self.g).differential(x) } } -impl<'a, X, G : DifferentiableMapping> Apply<&'a X> for Differential { - type Output = G::DerivativeDomain; - - #[inline] - fn apply(&self, x : &'a X) -> Self::Output { - self.g.differential(x) - } -} - - /// Container for flattening [`Loc`]`` codomain of a [`Mapping`] to `F`. -pub struct FlattenedCodomain>> { +pub struct FlattenedCodomain { g : G, _phantoms : PhantomData<(X, F)> } -impl>> Apply for FlattenedCodomain { - type Output = F; +impl Mapping for FlattenedCodomain +where + X : Space, + G: Mapping> +{ + type Codomain = F; #[inline] - fn apply(&self, x : X) -> Self::Output { + fn apply>(&self, x : I) -> Self::Codomain { self.g.apply(x).flatten1d() } } /// An auto-trait for constructing a [`FlattenCodomain`] structure for /// flattening the codomain of a [`Mapping`] from [`Loc`]`` to `F`. -pub trait FlattenCodomain : Mapping> + Sized { +pub trait FlattenCodomain : Mapping> + Sized { /// Flatten the codomain from [`Loc`]`` to `F`. fn flatten_codomain(self) -> FlattenedCodomain { FlattenedCodomain{ g : self, _phantoms : PhantomData } } } -impl>> FlattenCodomain for G {} - +impl>> FlattenCodomain for G {} /// Container for dimensional slicing [`Loc`]`` codomain of a [`Mapping`] to `F`. -pub struct SlicedCodomain>, const N : usize> { - g : G, +pub struct SlicedCodomain<'a, X, F, G : Clone, const N : usize> { + g : Cow<'a, G>, slice : usize, _phantoms : PhantomData<(X, F)> } -impl>, const N : usize> Apply -for SlicedCodomain { - type Output = F; +impl<'a, X, F, G, const N : usize> Mapping for SlicedCodomain<'a, X, F, G, N> +where + X : Space, + F : Copy + Space, + G : Mapping> + Clone, +{ + type Codomain = F; #[inline] - fn apply(&self, x : X) -> Self::Output { - let tmp : [F; N] = self.g.apply(x).into(); - // Safety: `slice_codomain` below checks the range. - unsafe { *tmp.get_unchecked(self.slice) } - } -} - -impl<'a, X, F : Copy, G : Mapping>, const N : usize> Apply<&'a X> -for SlicedCodomain { - type Output = F; - - #[inline] - fn apply(&self, x : &'a X) -> Self::Output { - let tmp : [F; N] = self.g.apply(x).into(); + fn apply>(&self, x : I) -> Self::Codomain { + let tmp : [F; N] = (*self.g).apply(x).into(); // Safety: `slice_codomain` below checks the range. unsafe { *tmp.get_unchecked(self.slice) } } @@ -264,21 +217,22 @@ /// An auto-trait for constructing a [`FlattenCodomain`] structure for /// flattening the codomain of a [`Mapping`] from [`Loc`]`` to `F`. -pub trait SliceCodomain : Mapping> + Sized { +pub trait SliceCodomain + : Mapping> + Clone + Sized +{ /// Flatten the codomain from [`Loc`]`` to `F`. - fn slice_codomain(self, slice : usize) -> SlicedCodomain { + fn slice_codomain(self, slice : usize) -> SlicedCodomain<'static, X, F, Self, N> { assert!(slice < N); - SlicedCodomain{ g : self, slice, _phantoms : PhantomData } + SlicedCodomain{ g : Cow::Owned(self), slice, _phantoms : PhantomData } } /// Flatten the codomain from [`Loc`]`` to `F`. - fn slice_codomain_ref(&self, slice : usize) -> SlicedCodomain { + fn slice_codomain_ref(&self, slice : usize) -> SlicedCodomain<'_, X, F, Self, N> { assert!(slice < N); - SlicedCodomain{ g : self, slice, _phantoms : PhantomData } + SlicedCodomain{ g : Cow::Borrowed(self), slice, _phantoms : PhantomData } } } -impl>, const N : usize> +impl> + Clone, const N : usize> SliceCodomain for G {} - diff -r 1a38447a89fa -r 9226980e45a7 src/metaprogramming.rs --- a/src/metaprogramming.rs Sat Dec 14 09:31:27 2024 -0500 +++ b/src/metaprogramming.rs Tue Dec 31 08:30:02 2024 -0500 @@ -41,63 +41,3 @@ (noref, &$x:ty) => { $x }; } -/// Use as -/// ```ignore -/// impl_vectorspace_ops!(impl_binop, impl_assignop, impl_scalarop, impl_scalar_assignop, impl_unaryop); -/// ``` -/// with `impl_binop`, `impl_assignop`, `impl_scalarop`, and `impl_unaryop` macros provided. -/// For example usage see the [power][crate::vectorspace::powerspace] and -/// [product spaces][crate::vectorspace::productspace] implementations. -#[macro_export] -macro_rules! impl_vectorspace_ops { - ($impl_binop:ident, $impl_assignop:ident, $impl_scalarop:ident, $impl_scalarlhs_op:ident, - $impl_scalar_assignop:ident, $impl_unaryop:ident) => { - impl_vectorspace_ops!($impl_binop, $impl_assignop, $impl_scalarop, $impl_scalarlhs_op, - $impl_scalar_assignop, $impl_unaryop, - (f32, f64, - num::complex::Complex, - num::complex::Complex)); - }; - ($impl_binop:ident, $impl_assignop:ident, $impl_scalarop:ident, $impl_scalarlhs_op:ident, - $impl_scalar_assignop:ident, $impl_unaryop:ident, ($($field:ty),+)) => { - impl_vectorspace_ops!(@binary, $impl_binop, Add, add); - impl_vectorspace_ops!(@binary, $impl_binop, Sub, sub); - impl_vectorspace_ops!(@assign, $impl_assignop, AddAssign, add_assign); - impl_vectorspace_ops!(@assign, $impl_assignop, SubAssign, sub_assign); - impl_vectorspace_ops!(@scalar, $impl_scalarop, Mul, mul); - impl_vectorspace_ops!(@scalar, $impl_scalarop, Div, div); - // Compiler overflow - // $( - // impl_vectorspace_ops!(@scalar_lhs, $impl_scalarlhs_op, Mul, mul, $field); - // )* - impl_vectorspace_ops!(@scalar_assign, $impl_scalar_assignop, MulAssign, mul_assign); - impl_vectorspace_ops!(@scalar_assign, $impl_scalar_assignop, DivAssign, div_assign); - impl_vectorspace_ops!(@unary, $impl_unaryop, Neg, neg); - }; - (@binary, $impl:ident, $trait : ident, $fn : ident) => { - $impl!($trait, $fn, ref, ref); - $impl!($trait, $fn, ref, noref); - $impl!($trait, $fn, noref, ref); - $impl!($trait, $fn, noref, noref); - }; - (@assign, $impl:ident, $trait : ident, $fn :ident) => { - $impl!($trait, $fn, ref); - $impl!($trait, $fn, noref); - }; - (@scalar, $impl:ident, $trait : ident, $fn :ident) => { - $impl!($trait, $fn, ref); - $impl!($trait, $fn, noref); - }; - (@scalar_lhs, $impl:ident, $trait : ident, $fn : ident, $field: ty) => { - // These operators need workarounds - $impl!($trait, $fn, ref, $field); - $impl!($trait, $fn, noref, $field); - }; - (@scalar_assign, $impl:ident, $trait : ident, $fn :ident) => { - $impl!($trait, $fn); - }; - (@unary, $impl:ident, $trait : ident, $fn :ident) => { - $impl!($trait, $fn, ref); - $impl!($trait, $fn, noref); - }; -} diff -r 1a38447a89fa -r 9226980e45a7 src/nalgebra_support.rs --- a/src/nalgebra_support.rs Sat Dec 14 09:31:27 2024 -0500 +++ b/src/nalgebra_support.rs Tue Dec 31 08:30:02 2024 -0500 @@ -23,51 +23,49 @@ use num_traits::identities::{Zero, One}; use crate::linops::*; use crate::euclidean::*; +use crate::mapping::{Space, BasicDecomposition}; use crate::types::Float; use crate::norms::*; +use crate::instance::Instance; -impl Apply> for Matrix -where SM: Storage, SV: Storage, - N : Dim, M : Dim, K : Dim, E : Scalar + Zero + One + ClosedAddAssign + ClosedMulAssign, - DefaultAllocator : Allocator, - DefaultAllocator : Allocator, - DefaultAllocator : Allocator, - DefaultAllocator : Allocator { - type Output = OMatrix; - - #[inline] - fn apply(&self, x : Matrix) -> Self::Output { - self.mul(x) - } +impl Space for Matrix +where + SM: Storage + Clone, + N : Dim, M : Dim, E : Scalar + Zero + One + ClosedAddAssign + ClosedMulAssign, + DefaultAllocator : Allocator, +{ + type Decomp = BasicDecomposition; } -impl<'a, SM,SV,N,M,K,E> Apply<&'a Matrix> for Matrix -where SM: Storage, SV: Storage, - N : Dim, M : Dim, K : Dim, E : Scalar + Zero + One + ClosedAddAssign + ClosedMulAssign, - DefaultAllocator : Allocator, - DefaultAllocator : Allocator, - DefaultAllocator : Allocator, - DefaultAllocator : Allocator { - type Output = OMatrix; - - #[inline] - fn apply(&self, x : &'a Matrix) -> Self::Output { - self.mul(x) - } -} - -impl<'a, SM,SV,N,M,K,E> Linear> for Matrix -where SM: Storage, SV: Storage, +impl Mapping> for Matrix +where SM: Storage, SV: Storage + Clone, N : Dim, M : Dim, K : Dim, E : Scalar + Zero + One + ClosedAddAssign + ClosedMulAssign, DefaultAllocator : Allocator, DefaultAllocator : Allocator, DefaultAllocator : Allocator, DefaultAllocator : Allocator { type Codomain = OMatrix; + + #[inline] + fn apply>>( + &self, x : I + ) -> Self::Codomain { + x.either(|owned| self.mul(owned), |refr| self.mul(refr)) + } +} + + +impl<'a, SM,SV,N,M,K,E> Linear> for Matrix +where SM: Storage, SV: Storage + Clone, + N : Dim, M : Dim, K : Dim, E : Scalar + Zero + One + ClosedAddAssign + ClosedMulAssign, + DefaultAllocator : Allocator, + DefaultAllocator : Allocator, + DefaultAllocator : Allocator, + DefaultAllocator : Allocator { } impl GEMV, Matrix> for Matrix -where SM: Storage, SV1: Storage, SV2: StorageMut, +where SM: Storage, SV1: Storage + Clone, SV2: StorageMut, N : Dim, M : Dim, K : Dim, E : Scalar + Zero + One + Float, DefaultAllocator : Allocator, DefaultAllocator : Allocator, @@ -75,34 +73,36 @@ DefaultAllocator : Allocator { #[inline] - fn gemv(&self, y : &mut Matrix, α : E, x : &Matrix, β : E) { - Matrix::gemm(y, α, self, x, β) + fn gemv>>( + &self, y : &mut Matrix, α : E, x : I, β : E + ) { + x.eval(|x̃| Matrix::gemm(y, α, self, x̃, β)) } #[inline] - fn apply_mut<'a>(&self, y : &mut Matrix, x : &Matrix) { - self.mul_to(x, y) + fn apply_mut<'a, I : Instance>>(&self, y : &mut Matrix, x : I) { + x.eval(|x̃| self.mul_to(x̃, y)) } } impl AXPY> for Vector -where SM: StorageMut, SV1: Storage, +where SM: StorageMut + Clone, SV1: Storage + Clone, M : Dim, E : Scalar + Zero + One + Float, DefaultAllocator : Allocator { #[inline] - fn axpy(&mut self, α : E, x : &Vector, β : E) { - Matrix::axpy(self, α, x, β) + fn axpy>>(&mut self, α : E, x : I, β : E) { + x.eval(|x̃| Matrix::axpy(self, α, x̃, β)) } #[inline] - fn copy_from(&mut self, y : &Vector) { - Matrix::copy_from(self, y) + fn copy_from>>(&mut self, y : I) { + y.eval(|ỹ| Matrix::copy_from(self, ỹ)) } } impl Projection for Vector -where SM: StorageMut, +where SM: StorageMut + Clone, M : Dim, E : Scalar + Zero + One + Float + RealField, DefaultAllocator : Allocator { #[inline] @@ -111,9 +111,9 @@ } } -impl<'own,SV1,SV2,SM,N,M,K,E> Adjointable,Matrix> +impl<'own,SV1,SV2,SM,N,M,K,E> Adjointable, Matrix> for Matrix -where SM: Storage, SV1: Storage, SV2: Storage, +where SM: Storage, SV1: Storage + Clone, SV2: Storage + Clone, N : Dim, M : Dim, K : Dim, E : Scalar + Zero + One + SimdComplexField, DefaultAllocator : Allocator, DefaultAllocator : Allocator, @@ -170,7 +170,7 @@ impl Euclidean for Vector where M : Dim, - S : StorageMut, + S : StorageMut + Clone, E : Float + Scalar + Zero + One + RealField, DefaultAllocator : Allocator { @@ -195,7 +195,7 @@ impl StaticEuclidean for Vector where M : DimName, - S : StorageMut, + S : StorageMut + Clone, E : Float + Scalar + Zero + One + RealField, DefaultAllocator : Allocator { diff -r 1a38447a89fa -r 9226980e45a7 src/norms.rs --- a/src/norms.rs Sat Dec 14 09:31:27 2024 -0500 +++ b/src/norms.rs Tue Dec 31 08:30:02 2024 -0500 @@ -3,18 +3,31 @@ */ use serde::Serialize; +use std::marker::PhantomData; use crate::types::*; use crate::euclidean::*; +use crate::mapping::{Mapping, Space, Instance}; // // Abstract norms // +#[derive(Copy,Clone,Debug)] +/// Helper structure to convert a [`NormExponent`] into a [`Mapping`] +pub struct NormMapping{ + pub(crate) exponent : E, + _phantoms : PhantomData +} + /// An exponent for norms. /// // Just a collection of desirable attributes for a marker type -pub trait NormExponent : Copy + Send + Sync + 'static {} - +pub trait NormExponent : Copy + Send + Sync + 'static { + /// Return the norm as a mappin + fn as_mapping(self) -> NormMapping { + NormMapping{ exponent : self, _phantoms : PhantomData } + } +} /// Exponent type for the 1-[`Norm`]. #[derive(Copy,Debug,Clone,Serialize,Eq,PartialEq)] @@ -37,6 +50,15 @@ pub struct L21; impl NormExponent for L21 {} +/// Norms for pairs (a, b). ‖(a,b)‖ = ‖(‖a‖_A, ‖b‖_B)‖_J +/// For use with [`crate::direct_product::Pair`] +#[derive(Copy,Debug,Clone,Serialize,Eq,PartialEq)] +pub struct PairNorm(pub A, pub B, pub J); + +impl NormExponent for PairNorm +where A : NormExponent, B : NormExponent, J : NormExponent {} + + /// A Huber/Moreau–Yosida smoothed [`L1`] norm. (Not a norm itself.) /// /// The parameter γ of this type is the smoothing factor. Zero means no smoothing, and higher @@ -154,3 +176,29 @@ } } +impl> Norm for Vec { + fn norm(&self, _l21 : L21) -> F { + self.iter().map(|e| e.norm(L2)).sum() + } +} + +impl> Dist for Vec { + fn dist(&self, other : &Self, _l21 : L21) -> F { + self.iter().zip(other.iter()).map(|(e, g)| e.dist(g, L2)).sum() + } +} + +impl Mapping for NormMapping +where + F : Float, + E : NormExponent, + Domain : Space + Norm, +{ + type Codomain = F; + + #[inline] + fn apply>(&self, x : I) -> F { + x.eval(|r| r.norm(self.exponent)) + } +} + diff -r 1a38447a89fa -r 9226980e45a7 src/types.rs --- a/src/types.rs Sat Dec 14 09:31:27 2024 -0500 +++ b/src/types.rs Tue Dec 31 08:30:02 2024 -0500 @@ -12,6 +12,13 @@ //use trait_set::trait_set; pub use num_traits::Float as NumTraitsFloat; // needed to re-export functions. pub use num_traits::cast::AsPrimitive; +pub use simba::scalar::{ + ClosedAdd, ClosedAddAssign, + ClosedSub, ClosedSubAssign, + ClosedMul, ClosedMulAssign, + ClosedDiv, ClosedDivAssign, + ClosedNeg +}; /// Typical integer type #[allow(non_camel_case_types)] @@ -57,7 +64,8 @@ + CastFrom + CastFrom + CastFrom + CastFrom + CastFrom + CastFrom + CastFrom + CastFrom - + CastFrom + CastFrom { + + CastFrom + CastFrom + + crate::instance::Space { const ZERO : Self; const ONE : Self;