Sat, 14 Dec 2024 09:31:27 -0500
Convex analysis basics
/*! Direct products of the form $A \times B$. TODO: This could be easily much more generic if `derive_more` could derive arithmetic operations on references. */ use core::ops::{Mul,MulAssign,Div,DivAssign,Add,AddAssign,Sub,SubAssign,Neg}; use std::clone::Clone; use serde::{Serialize, Deserialize}; use crate::types::{Num, Float}; use crate::{maybe_lifetime, maybe_ref, impl_vectorspace_ops}; use crate::euclidean::{Dot, Euclidean}; #[derive(Debug,Clone,PartialEq,Eq,Serialize,Deserialize)] pub struct Pair<A, B> (pub A, pub B); impl<A, B> Pair<A,B> { pub fn new(a : A, b : B) -> Pair<A,B> { Pair{ 0 : a, 1 : b } } } impl<A, B> From<(A,B)> for Pair<A,B> { #[inline] fn from((a, b) : (A, B)) -> Pair<A,B> { Pair{ 0 : a, 1 : b } } } macro_rules! impl_binop { ($trait : ident, $fn : ident, $refl:ident, $refr:ident) => { impl_binop!(@doit: $trait, $fn; maybe_lifetime!($refl, &'l Pair<A,B>), (maybe_lifetime!($refl, &'l A), maybe_lifetime!($refl, &'l B)); maybe_lifetime!($refr, &'r Pair<Ai,Bi>), (maybe_lifetime!($refr, &'r Ai), maybe_lifetime!($refr, &'r Bi)); $refl, $refr); }; (@doit: $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> for $self where $aself: $trait<$ain>, $bself: $trait<$bin> { type Output = Pair<<$aself as $trait<$ain>>::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)) } } } }; } macro_rules! impl_assignop { ($trait : ident, $fn : ident, $refr:ident) => { impl_assignop!(@doit: $trait, $fn; maybe_lifetime!($refr, &'r Pair<Ai,Bi>), (maybe_lifetime!($refr, &'r Ai), maybe_lifetime!($refr, &'r Bi)); $refr); }; (@doit: $trait:ident, $fn:ident; $in:ty, ($ain:ty, $bin:ty); $refr:ident) => { impl<'r, A, B, 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)); self.1.$fn(maybe_ref!($refr, y.1)); } } } } macro_rules! impl_scalarop { ($trait : ident, $fn : ident, $refl:ident) => { impl_scalarop!(@doit: $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; $self:ty, ($aself:ty, $bself:ty); $refl:ident) => { // Scalar as Rhs impl<'l, F : Num, A, B> $trait<F> for $self where $aself: $trait<F>, $bself: $trait<F> { type Output = Pair<<$aself as $trait<F>>::Output, <$bself as $trait<F>>::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)} } } } } // 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<Ai,Bi>), (maybe_lifetime!($refr, &'r Ai), maybe_lifetime!($refr, &'r Bi)); $refr, $field); }; (@doit: $trait:ident, $fn:ident, $in:ty, ($ain:ty, $bin:ty); $refr:ident, $field:ty) => { impl<'r, Ai, Bi> $trait<$in> for $field where $field : $trait<$ain> + $trait<$bin> { type Output = Pair<<$field as $trait<$ain>>::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))} } } }; } macro_rules! impl_scalar_assignop { ($trait : ident, $fn : ident) => { impl<'r, F : Num, A, B> $trait<F> for Pair<A,B> where A: $trait<F>, B: $trait<F> { #[inline] fn $fn(&mut self, a : F) -> () { self.0.$fn(a); self.1.$fn(a); } } } } macro_rules! impl_unaryop { ($trait:ident, $fn:ident, $refl:ident) => { impl_unaryop!(@doit: $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; $self:ty, ($aself:ty, $bself:ty); $refl : ident) => { impl<'l, A, B> $trait for $self where $aself: $trait, $bself: $trait { type Output = Pair<<$aself 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()} } } } } impl_vectorspace_ops!(impl_binop, impl_assignop, impl_scalarop, impl_scalarlhs_op, impl_scalar_assignop, impl_unaryop); impl<A, B, U, V, F> Dot<Pair<U, V>, F> for Pair<A, B> where A : Dot<U, F>, B : Dot<V, F>, F : Num { fn dot(&self, Pair(ref u, ref v) : &Pair<U, V>) -> F { self.0.dot(u) + self.1.dot(v) } } impl<A, B, F> Euclidean<F> for Pair<A, B> where A : Euclidean<F>, B : Euclidean<F>, F : Float { type Output = Pair<<A as Euclidean<F>>::Output, <B as Euclidean<F>>::Output>; fn similar_origin(&self) -> <Self as Euclidean<F>>::Output { Pair(self.0.similar_origin(), self.1.similar_origin()) } fn dist2_squared(&self, Pair(ref u, ref v) : &Self) -> F { self.0.dist2_squared(u) + self.1.dist2_squared(v) } } use crate::linops::AXPY; impl<F, A, B, U, V> AXPY<F, Pair<U, V>> for Pair<A, B> where A : AXPY<F, U>, B : AXPY<F, V>, F : Num { fn axpy(&mut self, α : F, x : &Pair<U, V>, β : F) { self.0.axpy(α, &x.0, β); self.1.axpy(α, &x.1, β); } fn copy_from(&mut self, x : &Pair<U, V>,) { self.0.copy_from(&x.0); self.1.copy_from(&x.1); } fn scale_from(&mut self, α : F, x : &Pair<U, V>) { self.0.scale_from(α, &x.0); self.1.scale_from(α, &x.1); } }