diff -r d14c877e14b7 -r b3c35d16affe src/loc.rs --- a/src/loc.rs Tue Feb 20 12:33:16 2024 -0500 +++ b/src/loc.rs Mon Feb 03 19:22:16 2025 -0500 @@ -5,13 +5,17 @@ use std::ops::{Add,Sub,AddAssign,SubAssign,Mul,Div,MulAssign,DivAssign,Neg,Index,IndexMut}; use std::slice::{Iter,IterMut}; +use std::fmt::{Display, Formatter}; use crate::types::{Float,Num,SignedNum}; 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 @@ -22,6 +26,19 @@ pub [F; N] ); +impl Display for Loc{ + // Required method + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "[")?; + let mut comma = ""; + for e in self.iter() { + write!(f, "{comma}{e}")?; + comma = ", "; + } + write!(f, "]") + } +} + // Need to manually implement as [F; N] serialisation is provided only for some N. impl Serialize for Loc where @@ -135,6 +152,14 @@ } } +impl Loc { + #[inline] + pub fn flatten1d(self) -> F { + let Loc([v]) = self; + v + } +} + impl From> for [F; N] { #[inline] fn from(other : Loc) -> [F; N] { @@ -237,6 +262,20 @@ make_binop!(Add, add, AddAssign, add_assign); make_binop!(Sub, sub, SubAssign, sub_assign); +impl std::iter::Sum for Loc { + fn sum>>(mut iter: I) -> Self { + match iter.next() { + None => Self::ORIGIN, + Some(mut v) => { + for w in iter { + v += w + } + v + } + } + } +} + macro_rules! make_scalarop_rhs { ($trait:ident, $fn:ident, $trait_assign:ident, $fn_assign:ident) => { impl $trait for Loc { @@ -390,24 +429,17 @@ domination!(Linfinity, L2); domination!(L2, L1); -impl Dot,F> for Loc { +impl Euclidean for Loc { + type Output = Self; + /// This implementation is not stabilised as it's meant to be used for very small vectors. /// Use [`nalgebra`] for larger vectors. #[inline] - fn dot(&self, other : &Loc) -> F { + fn dot>(&self, other : I) -> F { self.0.iter() - .zip(other.0.iter()) + .zip(other.ref_instance().0.iter()) .fold(F::ZERO, |m, (&v, &w)| m + v * w) } -} - -impl Euclidean for Loc { - type Output = Self; - - #[inline] - fn similar_origin(&self) -> Self { - Self::ORIGIN - } /// This implementation is not stabilised as it's meant to be used for very small vectors. /// Use [`nalgebra`] for larger vectors. @@ -416,9 +448,9 @@ self.iter().fold(F::ZERO, |m, &v| m + v * v) } - fn dist2_squared(&self, other : &Self) -> F { + fn dist2_squared>(&self, other : I) -> F { self.iter() - .zip(other.iter()) + .zip(other.ref_instance().iter()) .fold(F::ZERO, |m, (&v, &w)| { let d = v - w; m + d * d }) } @@ -433,10 +465,11 @@ } #[inline] - fn dist2(&self, other : &Self) -> F { + fn dist2>(&self, other : I) -> F { // Optimisation for N==1 that avoids squaring and square rooting. + let otherr = other.ref_instance(); if N==1 { - unsafe { *self.0.get_unchecked(0) - *other.0.get_unchecked(0) }.abs() + unsafe { *self.0.get_unchecked(0) - *otherr.0.get_unchecked(0) }.abs() } else { self.dist2_squared(other).sqrt() } @@ -444,9 +477,94 @@ } impl Loc { + /// Origin point pub const ORIGIN : Self = Loc([F::ZERO; N]); } +impl, const N : usize> Loc { + /// Reflects along the given coordinate + pub fn reflect(mut self, i : usize) -> Self { + self[i] = -self[i]; + self + } + + /// Reflects along the given coordinate sequences + pub fn reflect_many>(mut self, idxs : I) -> Self { + for i in idxs { + self[i]=-self[i] + } + self + } +} + +impl> Loc { + /// Reflect x coordinate + pub fn reflect_x(self) -> Self { + let Loc([x, y]) = self; + [-x, y].into() + } + + /// Reflect y coordinate + pub fn reflect_y(self) -> Self { + let Loc([x, y]) = self; + [x, -y].into() + } +} + +impl Loc { + /// Rotate by angle φ + pub fn rotate(self, φ : F) -> Self { + let Loc([x, y]) = self; + let sin_φ = φ.sin(); + let cos_φ = φ.cos(); + [cos_φ * x - sin_φ * y, + sin_φ * x + cos_φ * y].into() + } +} + +impl> Loc { + /// Reflect x coordinate + pub fn reflect_x(self) -> Self { + let Loc([x, y, z]) = self; + [-x, y, z].into() + } + + /// Reflect y coordinate + pub fn reflect_y(self) -> Self { + let Loc([x, y, z]) = self; + [x, -y, z].into() + } + + /// Reflect y coordinate + pub fn reflect_z(self) -> Self { + let Loc([x, y, z]) = self; + [x, y, -z].into() + } +} + +impl Loc { + /// Rotate by angles (yaw, pitch, roll) + pub fn rotate(self, Loc([φ, ψ, θ]) : Self) -> Self { + let Loc([mut x, mut y, mut z]) = self; + let sin_φ = φ.sin(); + let cos_φ = φ.cos(); + [x, y, z] = [cos_φ * x - sin_φ *y, + sin_φ * x + cos_φ * y, + z]; + let sin_ψ = ψ.sin(); + let cos_ψ = ψ.cos(); + [x, y, z] = [cos_ψ * x + sin_ψ * z, + y, + -sin_ψ * x + cos_ψ * z]; + let sin_θ = θ.sin(); + let cos_θ = θ.cos(); + [x, y, z] = [x, + cos_θ * y - sin_θ * z, + sin_θ * y + cos_θ * z]; + [x, y, z].into() + } +} + impl StaticEuclidean for Loc { #[inline] fn origin() -> Self { @@ -454,6 +572,31 @@ } } + +/// The default norm for `Loc` is [`L2`]. +impl Normed for Loc { + type NormExp = L2; + + #[inline] + fn norm_exponent(&self) -> Self::NormExp { + L2 + } + + // #[inline] + // fn similar_origin(&self) -> Self { + // [F::ZERO; N].into() + // } + + #[inline] + fn is_zero(&self) -> bool { + self.norm2_squared() == F::ZERO + } +} + +impl HasDual for Loc { + type DualSpace = Self; +} + impl Norm for Loc { #[inline] fn norm(&self, _ : L2) -> F { self.norm2() } @@ -461,9 +604,20 @@ impl Dist for Loc { #[inline] - fn dist(&self, other : &Self, _ : L2) -> F { self.dist2(other) } + fn dist>(&self, other : I, _ : L2) -> F { self.dist2(other) } } +/* Implemented automatically as Euclidean. +impl Projection for Loc { + #[inline] + fn proj_ball_mut(&mut self, ρ : F, nrm : L2) { + let n = self.norm(nrm); + if n > ρ { + *self *= ρ/n; + } + } +}*/ + impl Norm for Loc { /// This implementation is not stabilised as it's meant to be used for very small vectors. /// Use [`nalgebra`] for larger vectors. @@ -475,9 +629,9 @@ impl Dist for Loc { #[inline] - fn dist(&self, other : &Self, _ : L1) -> F { + fn dist>(&self, other : I, _ : L1) -> F { self.iter() - .zip(other.iter()) + .zip(other.ref_instance().iter()) .fold(F::ZERO, |m, (&v, &w)| m + (v-w).abs() ) } } @@ -500,9 +654,9 @@ impl Dist for Loc { #[inline] - fn dist(&self, other : &Self, _ : Linfinity) -> F { + fn dist>(&self, other : I, _ : Linfinity) -> F { self.iter() - .zip(other.iter()) + .zip(other.ref_instance().iter()) .fold(F::ZERO, |m, (&v, &w)| m.max((v-w).abs())) } } @@ -536,19 +690,48 @@ } } -impl AXPY> for Loc { + +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 { + type Owned = Self; #[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 )) + } + + #[inline] + fn similar_origin(&self) -> Self::Owned { + Self::ORIGIN + } + + #[inline] + fn set_zero(&mut self) { + *self = Self::ORIGIN; } } +