3 For working with small vectors in $ℝ^2$ or $ℝ^3$. |
3 For working with small vectors in $ℝ^2$ or $ℝ^3$. |
4 */ |
4 */ |
5 |
5 |
6 use std::ops::{Add,Sub,AddAssign,SubAssign,Mul,Div,MulAssign,DivAssign,Neg,Index,IndexMut}; |
6 use std::ops::{Add,Sub,AddAssign,SubAssign,Mul,Div,MulAssign,DivAssign,Neg,Index,IndexMut}; |
7 use std::slice::{Iter,IterMut}; |
7 use std::slice::{Iter,IterMut}; |
|
8 use std::fmt::{Display, Formatter}; |
8 use crate::types::{Float,Num,SignedNum}; |
9 use crate::types::{Float,Num,SignedNum}; |
9 use crate::maputil::{FixedLength,FixedLengthMut,map1,map2,map1_mut,map2_mut}; |
10 use crate::maputil::{FixedLength,FixedLengthMut,map1,map2,map1_mut,map2_mut}; |
10 use crate::euclidean::*; |
11 use crate::euclidean::*; |
11 use crate::norms::*; |
12 use crate::norms::*; |
12 use crate::linops::AXPY; |
13 use crate::linops::{AXPY, Mapping, Linear}; |
|
14 use crate::instance::{Instance, BasicDecomposition}; |
|
15 use crate::mapping::Space; |
13 use serde::ser::{Serialize, Serializer, SerializeSeq}; |
16 use serde::ser::{Serialize, Serializer, SerializeSeq}; |
|
17 |
14 |
18 |
15 /// A container type for (short) `N`-dimensional vectors of element type `F`. |
19 /// A container type for (short) `N`-dimensional vectors of element type `F`. |
16 /// |
20 /// |
17 /// Supports basic operations of an [`Euclidean`] space, several [`Norm`]s, and |
21 /// Supports basic operations of an [`Euclidean`] space, several [`Norm`]s, and |
18 /// fused [`AXPY`] operations, among others. |
22 /// fused [`AXPY`] operations, among others. |
19 #[derive(Copy,Clone,Debug,PartialEq,Eq)] |
23 #[derive(Copy,Clone,Debug,PartialEq,Eq)] |
20 pub struct Loc<F, const N : usize>( |
24 pub struct Loc<F, const N : usize>( |
21 /// An array of the elements of the vector |
25 /// An array of the elements of the vector |
22 pub [F; N] |
26 pub [F; N] |
23 ); |
27 ); |
|
28 |
|
29 impl<F : Display, const N : usize> Display for Loc<F, N>{ |
|
30 // Required method |
|
31 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
|
32 write!(f, "[")?; |
|
33 let mut comma = ""; |
|
34 for e in self.iter() { |
|
35 write!(f, "{comma}{e}")?; |
|
36 comma = ", "; |
|
37 } |
|
38 write!(f, "]") |
|
39 } |
|
40 } |
24 |
41 |
25 // Need to manually implement as [F; N] serialisation is provided only for some N. |
42 // Need to manually implement as [F; N] serialisation is provided only for some N. |
26 impl<F, const N : usize> Serialize for Loc<F, N> |
43 impl<F, const N : usize> Serialize for Loc<F, N> |
27 where |
44 where |
28 F: Serialize, |
45 F: Serialize, |
234 } |
259 } |
235 } |
260 } |
236 |
261 |
237 make_binop!(Add, add, AddAssign, add_assign); |
262 make_binop!(Add, add, AddAssign, add_assign); |
238 make_binop!(Sub, sub, SubAssign, sub_assign); |
263 make_binop!(Sub, sub, SubAssign, sub_assign); |
|
264 |
|
265 impl<F : Float, const N : usize> std::iter::Sum for Loc<F, N> { |
|
266 fn sum<I: Iterator<Item = Loc<F, N>>>(mut iter: I) -> Self { |
|
267 match iter.next() { |
|
268 None => Self::ORIGIN, |
|
269 Some(mut v) => { |
|
270 for w in iter { |
|
271 v += w |
|
272 } |
|
273 v |
|
274 } |
|
275 } |
|
276 } |
|
277 } |
239 |
278 |
240 macro_rules! make_scalarop_rhs { |
279 macro_rules! make_scalarop_rhs { |
241 ($trait:ident, $fn:ident, $trait_assign:ident, $fn_assign:ident) => { |
280 ($trait:ident, $fn:ident, $trait_assign:ident, $fn_assign:ident) => { |
242 impl<F : Num, const N : usize> $trait<F> for Loc<F, N> { |
281 impl<F : Num, const N : usize> $trait<F> for Loc<F, N> { |
243 type Output = Loc<F, N>; |
282 type Output = Loc<F, N>; |
388 |
427 |
389 domination!(Linfinity, L1); |
428 domination!(Linfinity, L1); |
390 domination!(Linfinity, L2); |
429 domination!(Linfinity, L2); |
391 domination!(L2, L1); |
430 domination!(L2, L1); |
392 |
431 |
393 impl<F : Num,const N : usize> Dot<Loc<F, N>,F> for Loc<F, N> { |
432 impl<F : Float,const N : usize> Euclidean<F> for Loc<F, N> { |
|
433 type Output = Self; |
|
434 |
394 /// This implementation is not stabilised as it's meant to be used for very small vectors. |
435 /// This implementation is not stabilised as it's meant to be used for very small vectors. |
395 /// Use [`nalgebra`] for larger vectors. |
436 /// Use [`nalgebra`] for larger vectors. |
396 #[inline] |
437 #[inline] |
397 fn dot(&self, other : &Loc<F, N>) -> F { |
438 fn dot<I : Instance<Self>>(&self, other : I) -> F { |
398 self.0.iter() |
439 self.0.iter() |
399 .zip(other.0.iter()) |
440 .zip(other.ref_instance().0.iter()) |
400 .fold(F::ZERO, |m, (&v, &w)| m + v * w) |
441 .fold(F::ZERO, |m, (&v, &w)| m + v * w) |
401 } |
|
402 } |
|
403 |
|
404 impl<F : Float,const N : usize> Euclidean<F> for Loc<F, N> { |
|
405 type Output = Self; |
|
406 |
|
407 #[inline] |
|
408 fn similar_origin(&self) -> Self { |
|
409 Self::ORIGIN |
|
410 } |
442 } |
411 |
443 |
412 /// This implementation is not stabilised as it's meant to be used for very small vectors. |
444 /// This implementation is not stabilised as it's meant to be used for very small vectors. |
413 /// Use [`nalgebra`] for larger vectors. |
445 /// Use [`nalgebra`] for larger vectors. |
414 #[inline] |
446 #[inline] |
415 fn norm2_squared(&self) -> F { |
447 fn norm2_squared(&self) -> F { |
416 self.iter().fold(F::ZERO, |m, &v| m + v * v) |
448 self.iter().fold(F::ZERO, |m, &v| m + v * v) |
417 } |
449 } |
418 |
450 |
419 fn dist2_squared(&self, other : &Self) -> F { |
451 fn dist2_squared<I : Instance<Self>>(&self, other : I) -> F { |
420 self.iter() |
452 self.iter() |
421 .zip(other.iter()) |
453 .zip(other.ref_instance().iter()) |
422 .fold(F::ZERO, |m, (&v, &w)| { let d = v - w; m + d * d }) |
454 .fold(F::ZERO, |m, (&v, &w)| { let d = v - w; m + d * d }) |
423 } |
455 } |
424 |
456 |
425 #[inline] |
457 #[inline] |
426 fn norm2(&self) -> F { |
458 fn norm2(&self) -> F { |
431 self.norm2_squared().sqrt() |
463 self.norm2_squared().sqrt() |
432 } |
464 } |
433 } |
465 } |
434 |
466 |
435 #[inline] |
467 #[inline] |
436 fn dist2(&self, other : &Self) -> F { |
468 fn dist2<I : Instance<Self>>(&self, other : I) -> F { |
437 // Optimisation for N==1 that avoids squaring and square rooting. |
469 // Optimisation for N==1 that avoids squaring and square rooting. |
|
470 let otherr = other.ref_instance(); |
438 if N==1 { |
471 if N==1 { |
439 unsafe { *self.0.get_unchecked(0) - *other.0.get_unchecked(0) }.abs() |
472 unsafe { *self.0.get_unchecked(0) - *otherr.0.get_unchecked(0) }.abs() |
440 } else { |
473 } else { |
441 self.dist2_squared(other).sqrt() |
474 self.dist2_squared(other).sqrt() |
442 } |
475 } |
443 } |
476 } |
444 } |
477 } |
445 |
478 |
446 impl<F : Num, const N : usize> Loc<F, N> { |
479 impl<F : Num, const N : usize> Loc<F, N> { |
|
480 /// Origin point |
447 pub const ORIGIN : Self = Loc([F::ZERO; N]); |
481 pub const ORIGIN : Self = Loc([F::ZERO; N]); |
|
482 } |
|
483 |
|
484 impl<F : Num + std::ops::Neg<Output=F>, const N : usize> Loc<F, N> { |
|
485 /// Reflects along the given coordinate |
|
486 pub fn reflect(mut self, i : usize) -> Self { |
|
487 self[i] = -self[i]; |
|
488 self |
|
489 } |
|
490 |
|
491 /// Reflects along the given coordinate sequences |
|
492 pub fn reflect_many<I : IntoIterator<Item=usize>>(mut self, idxs : I) -> Self { |
|
493 for i in idxs { |
|
494 self[i]=-self[i] |
|
495 } |
|
496 self |
|
497 } |
|
498 } |
|
499 |
|
500 impl<F : std::ops::Neg<Output=F>> Loc<F, 2> { |
|
501 /// Reflect x coordinate |
|
502 pub fn reflect_x(self) -> Self { |
|
503 let Loc([x, y]) = self; |
|
504 [-x, y].into() |
|
505 } |
|
506 |
|
507 /// Reflect y coordinate |
|
508 pub fn reflect_y(self) -> Self { |
|
509 let Loc([x, y]) = self; |
|
510 [x, -y].into() |
|
511 } |
|
512 } |
|
513 |
|
514 impl<F : Float> Loc<F, 2> { |
|
515 /// Rotate by angle φ |
|
516 pub fn rotate(self, φ : F) -> Self { |
|
517 let Loc([x, y]) = self; |
|
518 let sin_φ = φ.sin(); |
|
519 let cos_φ = φ.cos(); |
|
520 [cos_φ * x - sin_φ * y, |
|
521 sin_φ * x + cos_φ * y].into() |
|
522 } |
|
523 } |
|
524 |
|
525 impl<F : std::ops::Neg<Output=F>> Loc<F, 3> { |
|
526 /// Reflect x coordinate |
|
527 pub fn reflect_x(self) -> Self { |
|
528 let Loc([x, y, z]) = self; |
|
529 [-x, y, z].into() |
|
530 } |
|
531 |
|
532 /// Reflect y coordinate |
|
533 pub fn reflect_y(self) -> Self { |
|
534 let Loc([x, y, z]) = self; |
|
535 [x, -y, z].into() |
|
536 } |
|
537 |
|
538 /// Reflect y coordinate |
|
539 pub fn reflect_z(self) -> Self { |
|
540 let Loc([x, y, z]) = self; |
|
541 [x, y, -z].into() |
|
542 } |
|
543 } |
|
544 |
|
545 impl<F : Float> Loc<F, 3> { |
|
546 /// Rotate by angles (yaw, pitch, roll) |
|
547 pub fn rotate(self, Loc([φ, ψ, θ]) : Self) -> Self { |
|
548 let Loc([mut x, mut y, mut z]) = self; |
|
549 let sin_φ = φ.sin(); |
|
550 let cos_φ = φ.cos(); |
|
551 [x, y, z] = [cos_φ * x - sin_φ *y, |
|
552 sin_φ * x + cos_φ * y, |
|
553 z]; |
|
554 let sin_ψ = ψ.sin(); |
|
555 let cos_ψ = ψ.cos(); |
|
556 [x, y, z] = [cos_ψ * x + sin_ψ * z, |
|
557 y, |
|
558 -sin_ψ * x + cos_ψ * z]; |
|
559 let sin_θ = θ.sin(); |
|
560 let cos_θ = θ.cos(); |
|
561 [x, y, z] = [x, |
|
562 cos_θ * y - sin_θ * z, |
|
563 sin_θ * y + cos_θ * z]; |
|
564 [x, y, z].into() |
|
565 } |
448 } |
566 } |
449 |
567 |
450 impl<F : Float,const N : usize> StaticEuclidean<F> for Loc<F, N> { |
568 impl<F : Float,const N : usize> StaticEuclidean<F> for Loc<F, N> { |
451 #[inline] |
569 #[inline] |
452 fn origin() -> Self { |
570 fn origin() -> Self { |
453 Self::ORIGIN |
571 Self::ORIGIN |
454 } |
572 } |
455 } |
573 } |
456 |
574 |
|
575 |
|
576 /// The default norm for `Loc` is [`L2`]. |
|
577 impl<F : Float, const N : usize> Normed<F> for Loc<F, N> { |
|
578 type NormExp = L2; |
|
579 |
|
580 #[inline] |
|
581 fn norm_exponent(&self) -> Self::NormExp { |
|
582 L2 |
|
583 } |
|
584 |
|
585 // #[inline] |
|
586 // fn similar_origin(&self) -> Self { |
|
587 // [F::ZERO; N].into() |
|
588 // } |
|
589 |
|
590 #[inline] |
|
591 fn is_zero(&self) -> bool { |
|
592 self.norm2_squared() == F::ZERO |
|
593 } |
|
594 } |
|
595 |
|
596 impl<F : Float, const N : usize> HasDual<F> for Loc<F, N> { |
|
597 type DualSpace = Self; |
|
598 } |
|
599 |
457 impl<F : Float, const N : usize> Norm<F, L2> for Loc<F, N> { |
600 impl<F : Float, const N : usize> Norm<F, L2> for Loc<F, N> { |
458 #[inline] |
601 #[inline] |
459 fn norm(&self, _ : L2) -> F { self.norm2() } |
602 fn norm(&self, _ : L2) -> F { self.norm2() } |
460 } |
603 } |
461 |
604 |
462 impl<F : Float, const N : usize> Dist<F, L2> for Loc<F, N> { |
605 impl<F : Float, const N : usize> Dist<F, L2> for Loc<F, N> { |
463 #[inline] |
606 #[inline] |
464 fn dist(&self, other : &Self, _ : L2) -> F { self.dist2(other) } |
607 fn dist<I : Instance<Self>>(&self, other : I, _ : L2) -> F { self.dist2(other) } |
465 } |
608 } |
|
609 |
|
610 /* Implemented automatically as Euclidean. |
|
611 impl<F : Float, const N : usize> Projection<F, L2> for Loc<F, N> { |
|
612 #[inline] |
|
613 fn proj_ball_mut(&mut self, ρ : F, nrm : L2) { |
|
614 let n = self.norm(nrm); |
|
615 if n > ρ { |
|
616 *self *= ρ/n; |
|
617 } |
|
618 } |
|
619 }*/ |
466 |
620 |
467 impl<F : Float, const N : usize> Norm<F, L1> for Loc<F, N> { |
621 impl<F : Float, const N : usize> Norm<F, L1> for Loc<F, N> { |
468 /// This implementation is not stabilised as it's meant to be used for very small vectors. |
622 /// This implementation is not stabilised as it's meant to be used for very small vectors. |
469 /// Use [`nalgebra`] for larger vectors. |
623 /// Use [`nalgebra`] for larger vectors. |
470 #[inline] |
624 #[inline] |
534 fn fl_iter(self) -> Self::Iter { |
688 fn fl_iter(self) -> Self::Iter { |
535 self.iter() |
689 self.iter() |
536 } |
690 } |
537 } |
691 } |
538 |
692 |
539 impl<F : Num, const N : usize> AXPY<F, Loc<F, N>> for Loc<F, N> { |
693 |
540 |
694 impl<F : Num, const N : usize> Space for Loc<F, N> { |
541 #[inline] |
695 type Decomp = BasicDecomposition; |
542 fn axpy(&mut self, α : F, x : &Loc<F, N>, β : F) { |
696 } |
543 if β == F::ZERO { |
697 |
544 map2_mut(self, x, |yi, xi| { *yi = α * (*xi) }) |
698 impl<F : Float, const N : usize> Mapping<Loc<F, N>> for Loc<F, N> { |
545 } else { |
699 type Codomain = F; |
546 map2_mut(self, x, |yi, xi| { *yi = β * (*yi) + α * (*xi) }) |
700 |
547 } |
701 fn apply<I : Instance<Loc<F, N>>>(&self, x : I) -> Self::Codomain { |
548 } |
702 x.eval(|x̃| self.dot(x̃)) |
549 |
703 } |
550 #[inline] |
704 } |
551 fn copy_from(&mut self, x : &Loc<F, N>) { |
705 |
552 map2_mut(self, x, |yi, xi| *yi = *xi ) |
706 impl<F : Float, const N : usize> Linear<Loc<F, N>> for Loc<F, N> { } |
553 } |
707 |
554 } |
708 impl<F : Float, const N : usize> AXPY<F, Loc<F, N>> for Loc<F, N> { |
|
709 type Owned = Self; |
|
710 |
|
711 #[inline] |
|
712 fn axpy<I : Instance<Loc<F, N>>>(&mut self, α : F, x : I, β : F) { |
|
713 x.eval(|x̃| { |
|
714 if β == F::ZERO { |
|
715 map2_mut(self, x̃, |yi, xi| { *yi = α * (*xi) }) |
|
716 } else { |
|
717 map2_mut(self, x̃, |yi, xi| { *yi = β * (*yi) + α * (*xi) }) |
|
718 } |
|
719 }) |
|
720 } |
|
721 |
|
722 #[inline] |
|
723 fn copy_from<I : Instance<Loc<F, N>>>(&mut self, x : I) { |
|
724 x.eval(|x̃| map2_mut(self, x̃, |yi, xi| *yi = *xi )) |
|
725 } |
|
726 |
|
727 #[inline] |
|
728 fn similar_origin(&self) -> Self::Owned { |
|
729 Self::ORIGIN |
|
730 } |
|
731 |
|
732 #[inline] |
|
733 fn set_zero(&mut self) { |
|
734 *self = Self::ORIGIN; |
|
735 } |
|
736 } |
|
737 |