| 1 /*! |
1 /*! |
| 2 Euclidean spaces. |
2 Euclidean spaces. |
| 3 */ |
3 */ |
| 4 |
4 |
| 5 use std::ops::{Mul, MulAssign, Div, DivAssign, Add, Sub, AddAssign, SubAssign, Neg}; |
5 use crate::instance::Instance; |
| |
6 use crate::linops::{VectorSpace, AXPY}; |
| |
7 use crate::norms::{HasDual, Norm, Normed, Reflexive, L2}; |
| 6 use crate::types::*; |
8 use crate::types::*; |
| 7 use crate::instance::Instance; |
|
| 8 use crate::norms::{HasDual, Reflexive}; |
|
| 9 |
9 |
| |
10 pub mod wrap; |
| |
11 |
| |
12 // TODO: Euclidean & EuclideanMut |
| |
13 // |
| 10 /// Space (type) with Euclidean and vector space structure |
14 /// Space (type) with Euclidean and vector space structure |
| 11 /// |
15 /// |
| 12 /// The type should implement vector space operations (addition, subtraction, scalar |
16 /// The type should implement vector space operations (addition, subtraction, scalar |
| 13 /// multiplication and scalar division) along with their assignment versions, as well |
17 /// multiplication and scalar division) along with their assignment versions, as well |
| 14 /// as an inner product. |
18 /// as an inner product. |
| 15 pub trait Euclidean<F : Float> : HasDual<F, DualSpace=Self> + Reflexive<F> |
19 // TODO: remove F parameter, use VectorSpace::Field |
| 16 + Mul<F, Output=<Self as Euclidean<F>>::Output> + MulAssign<F> |
20 pub trait Euclidean<F: Float = f64>: |
| 17 + Div<F, Output=<Self as Euclidean<F>>::Output> + DivAssign<F> |
21 VectorSpace<Field = F, PrincipalV = Self::PrincipalE> + Reflexive<F, DualSpace = Self::PrincipalE> |
| 18 + Add<Self, Output=<Self as Euclidean<F>>::Output> |
|
| 19 + Sub<Self, Output=<Self as Euclidean<F>>::Output> |
|
| 20 + for<'b> Add<&'b Self, Output=<Self as Euclidean<F>>::Output> |
|
| 21 + for<'b> Sub<&'b Self, Output=<Self as Euclidean<F>>::Output> |
|
| 22 + AddAssign<Self> + for<'b> AddAssign<&'b Self> |
|
| 23 + SubAssign<Self> + for<'b> SubAssign<&'b Self> |
|
| 24 + Neg<Output=<Self as Euclidean<F>>::Output> |
|
| 25 { |
22 { |
| 26 type Output : Euclidean<F>; |
23 /// Principal form of the space; always equal to [`crate::linops::Space::Principal`] and |
| |
24 /// [`VectorSpace::PrincipalV`], but with more traits guaranteed. |
| |
25 type PrincipalE: ClosedEuclidean<F>; |
| 27 |
26 |
| 28 // Inner product |
27 // Inner product |
| 29 fn dot<I : Instance<Self>>(&self, other : I) -> F; |
28 fn dot<I: Instance<Self>>(&self, other: I) -> F; |
| 30 |
29 |
| 31 /// Calculate the square of the 2-norm, $\frac{1}{2}\\|x\\|_2^2$, where `self` is $x$. |
30 /// Calculate the square of the 2-norm, $\frac{1}{2}\\|x\\|_2^2$, where `self` is $x$. |
| 32 /// |
31 /// |
| 33 /// This is not automatically implemented to avoid imposing |
32 /// This is not automatically implemented to avoid imposing |
| 34 /// `for <'a> &'a Self : Instance<Self>` trait bound bloat. |
33 /// `for <'a> &'a Self : Instance<Self>` trait bound bloat. |
| 36 |
35 |
| 37 /// Calculate the square of the 2-norm divided by 2, $\frac{1}{2}\\|x\\|_2^2$, |
36 /// Calculate the square of the 2-norm divided by 2, $\frac{1}{2}\\|x\\|_2^2$, |
| 38 /// where `self` is $x$. |
37 /// where `self` is $x$. |
| 39 #[inline] |
38 #[inline] |
| 40 fn norm2_squared_div2(&self) -> F { |
39 fn norm2_squared_div2(&self) -> F { |
| 41 self.norm2_squared()/F::TWO |
40 self.norm2_squared() / F::TWO |
| 42 } |
41 } |
| 43 |
42 |
| 44 /// Calculate the 2-norm $‖x‖_2$, where `self` is $x$. |
43 /// Calculate the 2-norm $‖x‖_2$, where `self` is $x$. |
| 45 #[inline] |
44 #[inline] |
| 46 fn norm2(&self) -> F { |
45 fn norm2(&self) -> F { |
| 47 self.norm2_squared().sqrt() |
46 self.norm2_squared().sqrt() |
| 48 } |
47 } |
| 49 |
48 |
| 50 /// Calculate the 2-distance squared $\\|x-y\\|_2^2$, where `self` is $x$. |
49 /// Calculate the 2-distance squared $\\|x-y\\|_2^2$, where `self` is $x$. |
| 51 fn dist2_squared<I : Instance<Self>>(&self, y : I) -> F; |
50 fn dist2_squared<I: Instance<Self>>(&self, y: I) -> F; |
| 52 |
51 |
| 53 /// Calculate the 2-distance $\\|x-y\\|_2$, where `self` is $x$. |
52 /// Calculate the 2-distance $\\|x-y\\|_2$, where `self` is $x$. |
| 54 #[inline] |
53 #[inline] |
| 55 fn dist2<I : Instance<Self>>(&self, y : I) -> F { |
54 fn dist2<I: Instance<Self>>(&self, y: I) -> F { |
| 56 self.dist2_squared(y).sqrt() |
55 self.dist2_squared(y).sqrt() |
| 57 } |
56 } |
| 58 |
57 |
| 59 /// Projection to the 2-ball. |
58 /// Projection to the 2-ball. |
| 60 #[inline] |
59 #[inline] |
| 61 fn proj_ball2(mut self, ρ : F) -> Self { |
60 fn proj_ball2(self, ρ: F) -> Self::PrincipalV { |
| 62 self.proj_ball2_mut(ρ); |
|
| 63 self |
|
| 64 } |
|
| 65 |
|
| 66 /// In-place projection to the 2-ball. |
|
| 67 #[inline] |
|
| 68 fn proj_ball2_mut(&mut self, ρ : F) { |
|
| 69 let r = self.norm2(); |
61 let r = self.norm2(); |
| 70 if r>ρ { |
62 if r > ρ { |
| 71 *self *= ρ/r |
63 self * (ρ / r) |
| |
64 } else { |
| |
65 self.into_owned() |
| 72 } |
66 } |
| 73 } |
67 } |
| 74 } |
68 } |
| 75 |
69 |
| |
70 pub trait ClosedEuclidean<F: Float = f64>: |
| |
71 Instance<Self> + Euclidean<F, PrincipalE = Self> |
| |
72 { |
| |
73 } |
| |
74 impl<F: Float, X: Instance<X> + Euclidean<F, PrincipalE = Self>> ClosedEuclidean<F> for X {} |
| |
75 |
| |
76 // TODO: remove F parameter, use AXPY::Field |
| |
77 pub trait EuclideanMut<F: Float = f64>: Euclidean<F> + AXPY<Field = F> { |
| |
78 /// In-place projection to the 2-ball. |
| |
79 #[inline] |
| |
80 fn proj_ball2_mut(&mut self, ρ: F) { |
| |
81 let r = self.norm2(); |
| |
82 if r > ρ { |
| |
83 *self *= ρ / r |
| |
84 } |
| |
85 } |
| |
86 } |
| |
87 |
| |
88 impl<X, F: Float> EuclideanMut<F> for X where X: Euclidean<F> + AXPY<Field = F> {} |
| |
89 |
| 76 /// Trait for [`Euclidean`] spaces with dimensions known at compile time. |
90 /// Trait for [`Euclidean`] spaces with dimensions known at compile time. |
| 77 pub trait StaticEuclidean<F : Float> : Euclidean<F> { |
91 pub trait StaticEuclidean<F: Float = f64>: Euclidean<F> { |
| 78 /// Returns the origin |
92 /// Returns the origin |
| 79 fn origin() -> <Self as Euclidean<F>>::Output; |
93 fn origin() -> <Self as Euclidean<F>>::PrincipalE; |
| 80 } |
94 } |
| |
95 |
| |
96 macro_rules! scalar_euclidean { |
| |
97 ($f:ident) => { |
| |
98 impl VectorSpace for $f { |
| |
99 type Field = $f; |
| |
100 type PrincipalV = $f; |
| |
101 |
| |
102 #[inline] |
| |
103 fn similar_origin(&self) -> Self::PrincipalV { |
| |
104 0.0 |
| |
105 } |
| |
106 } |
| |
107 impl AXPY for $f { |
| |
108 #[inline] |
| |
109 fn axpy<I: Instance<$f>>(&mut self, α: $f, x: I, β: $f) { |
| |
110 *self = β * *self + α * x.own() |
| |
111 } |
| |
112 |
| |
113 #[inline] |
| |
114 fn set_zero(&mut self) { |
| |
115 *self = 0.0 |
| |
116 } |
| |
117 } |
| |
118 |
| |
119 impl Norm<L2, $f> for $f { |
| |
120 fn norm(&self, _p: L2) -> $f { |
| |
121 self.abs() |
| |
122 } |
| |
123 } |
| |
124 |
| |
125 impl Normed<$f> for $f { |
| |
126 type NormExp = L2; |
| |
127 |
| |
128 fn norm_exponent(&self) -> Self::NormExp { |
| |
129 L2 |
| |
130 } |
| |
131 } |
| |
132 |
| |
133 impl HasDual<$f> for $f { |
| |
134 type DualSpace = $f; |
| |
135 |
| |
136 #[inline] |
| |
137 fn dual_origin(&self) -> $f { |
| |
138 0.0 |
| |
139 } |
| |
140 } |
| |
141 |
| |
142 impl Euclidean<$f> for $f { |
| |
143 type PrincipalE = $f; |
| |
144 |
| |
145 #[inline] |
| |
146 fn dot<I: Instance<Self>>(&self, other: I) -> $f { |
| |
147 *self * other.own() |
| |
148 } |
| |
149 |
| |
150 #[inline] |
| |
151 fn norm2_squared(&self) -> $f { |
| |
152 *self * *self |
| |
153 } |
| |
154 |
| |
155 #[inline] |
| |
156 fn dist2_squared<I: Instance<Self>>(&self, y: I) -> $f { |
| |
157 let d = *self - y.own(); |
| |
158 d * d |
| |
159 } |
| |
160 } |
| |
161 }; |
| |
162 } |
| |
163 |
| |
164 scalar_euclidean!(f64); |
| |
165 scalar_euclidean!(f32); |