--- a/src/norms.rs Tue Feb 20 12:33:16 2024 -0500 +++ b/src/norms.rs Mon Feb 03 19:22:16 2025 -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<F : Float, E : NormExponent>{ + pub(crate) exponent : E, + _phantoms : PhantomData<F> +} + /// An exponent for norms. /// -// Just a collection of desirabl attributes for a marker type -pub trait NormExponent : Copy + Send + Sync + 'static {} - +// Just a collection of desirable attributes for a marker type +pub trait NormExponent : Copy + Send + Sync + 'static { + /// Return the norm as a mappin + fn as_mapping<F : Float>(self) -> NormMapping<F, Self> { + 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<A, B, J>(pub A, pub B, pub J); + +impl<A, B, J> NormExponent for PairNorm<A, B, J> +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 @@ -82,9 +104,9 @@ } /// Trait for distances with respect to a norm. -pub trait Dist<F : Num, Exponent : NormExponent> : Norm<F, Exponent> { +pub trait Dist<F : Num, Exponent : NormExponent> : Norm<F, Exponent> + Space { /// Calculate the distance - fn dist(&self, other : &Self, _p : Exponent) -> F; + fn dist<I : Instance<Self>>(&self, other : I, _p : Exponent) -> F; } /// Trait for Euclidean projections to the `Exponent`-[`Norm`]-ball. @@ -97,7 +119,7 @@ /// /// println!("{:?}, {:?}", x.proj_ball(1.0, L2), x.proj_ball(0.5, Linfinity)); /// ``` -pub trait Projection<F : Num, Exponent : NormExponent> : Norm<F, Exponent> + Euclidean<F> +pub trait Projection<F : Num, Exponent : NormExponent> : Norm<F, Exponent> + Sized where F : Float { /// Projection of `self` to the `q`-norm-ball of radius ρ. fn proj_ball(mut self, ρ : F, q : Exponent) -> Self { @@ -106,7 +128,7 @@ } /// In-place projection of `self` to the `q`-norm-ball of radius ρ. - fn proj_ball_mut(&mut self, ρ : F, _q : Exponent); + fn proj_ball_mut(&mut self, ρ : F, q : Exponent); } /*impl<F : Float, E : Euclidean<F>> Norm<F, L2> for E { @@ -149,8 +171,149 @@ } impl<F : Float, E : Euclidean<F>> Dist<F, HuberL1<F>> for E { - fn dist(&self, other : &Self, huber : HuberL1<F>) -> F { + fn dist<I : Instance<Self>>(&self, other : I, huber : HuberL1<F>) -> F { huber.apply(self.dist2_squared(other)) } } +// impl<F : Float, E : Norm<F, L2>> Norm<F, L21> for Vec<E> { +// fn norm(&self, _l21 : L21) -> F { +// self.iter().map(|e| e.norm(L2)).sum() +// } +// } + +// impl<F : Float, E : Dist<F, L2>> Dist<F, L21> for Vec<E> { +// fn dist<I : Instance<Self>>(&self, other : I, _l21 : L21) -> F { +// self.iter().zip(other.iter()).map(|(e, g)| e.dist(g, L2)).sum() +// } +// } + +impl<E, F, Domain> Mapping<Domain> for NormMapping<F, E> +where + F : Float, + E : NormExponent, + Domain : Space + Norm<F, E>, +{ + type Codomain = F; + + #[inline] + fn apply<I : Instance<Domain>>(&self, x : I) -> F { + x.eval(|r| r.norm(self.exponent)) + } +} + +pub trait Normed<F : Num = f64> : Space + Norm<F, Self::NormExp> { + type NormExp : NormExponent; + + fn norm_exponent(&self) -> Self::NormExp; + + #[inline] + fn norm_(&self) -> F { + self.norm(self.norm_exponent()) + } + + // fn similar_origin(&self) -> Self; + + fn is_zero(&self) -> bool; +} + +pub trait HasDual<F : Num = f64> : Normed<F> { + type DualSpace : Normed<F>; +} + +/// Automatically implemented trait for reflexive spaces +pub trait Reflexive<F : Num = f64> : HasDual<F> +where + Self::DualSpace : HasDual<F, DualSpace = Self> +{ } + +impl<F : Num, X : HasDual<F>> Reflexive<F> for X +where + X::DualSpace : HasDual<F, DualSpace = X> +{ } + +pub trait HasDualExponent : NormExponent { + type DualExp : NormExponent; + + fn dual_exponent(&self) -> Self::DualExp; +} + +impl HasDualExponent for L2 { + type DualExp = L2; + + #[inline] + fn dual_exponent(&self) -> Self::DualExp { + L2 + } +} + +impl HasDualExponent for L1 { + type DualExp = Linfinity; + + #[inline] + fn dual_exponent(&self) -> Self::DualExp { + Linfinity + } +} + + +impl HasDualExponent for Linfinity { + type DualExp = L1; + + #[inline] + fn dual_exponent(&self) -> Self::DualExp { + L1 + } +} + +#[macro_export] +macro_rules! impl_weighted_norm { + ($exponent : ty) => { + impl<C, F, D> Norm<F, Weighted<$exponent, C>> for D + where + F : Float, + D : Norm<F, $exponent>, + C : Constant<Type = F>, + { + fn norm(&self, e : Weighted<$exponent, C>) -> F { + let v = e.weight.value(); + assert!(v > F::ZERO); + v * self.norm(e.base_fn) + } + } + + impl<C : Constant> NormExponent for Weighted<$exponent, C> {} + + impl<C : Constant> HasDualExponent for Weighted<$exponent, C> + where $exponent : HasDualExponent { + type DualExp = Weighted<<$exponent as HasDualExponent>::DualExp, C::Type>; + + fn dual_exponent(&self) -> Self::DualExp { + Weighted { + weight : C::Type::ONE / self.weight.value(), + base_fn : self.base_fn.dual_exponent() + } + } + } + + impl<C, F, T> Projection<F, Weighted<$exponent , C>> for T + where + T : Projection<F, $exponent >, + F : Float, + C : Constant<Type = F>, + { + fn proj_ball(self, ρ : F, q : Weighted<$exponent , C>) -> Self { + self.proj_ball(ρ / q.weight.value(), q.base_fn) + } + + fn proj_ball_mut(&mut self, ρ : F, q : Weighted<$exponent , C>) { + self.proj_ball_mut(ρ / q.weight.value(), q.base_fn) + } + } + } +} + +//impl_weighted_norm!(L1); +//impl_weighted_norm!(L2); +//impl_weighted_norm!(Linfinity); +