| |
1 /*! |
| |
2 Quadratic functions of the form $\frac{1}{2}\|Ax-b\|_2^2$ for an operator $A$ |
| |
3 to a [`Euclidean`] space. |
| |
4 */ |
| |
5 |
| |
6 #![allow(non_snake_case)] |
| |
7 |
| |
8 use super::{DifferentiableImpl, Differential, Lipschitz, Mapping}; |
| |
9 use crate::convex::ConvexMapping; |
| |
10 use crate::euclidean::Euclidean; |
| |
11 use crate::instance::{Instance, Space}; |
| |
12 use crate::linops::{BoundedLinear, Linear, Preadjointable}; |
| |
13 use crate::norms::{Norm, NormExponent, L2}; |
| |
14 use crate::types::Float; |
| |
15 use std::marker::PhantomData; |
| |
16 |
| |
17 /// Functions of the form $\frac{1}{2}\|Ax-b\|_2^2$ for an operator $A$ |
| |
18 /// to a [`Euclidean`] space. |
| |
19 #[derive(Clone, Copy)] |
| |
20 pub struct Quadratic<'a, F: Float, Domain: Space, A: Mapping<Domain>> { |
| |
21 opA: &'a A, |
| |
22 b: &'a <A as Mapping<Domain>>::Codomain, |
| |
23 _phantoms: PhantomData<F>, |
| |
24 } |
| |
25 |
| |
26 #[allow(non_snake_case)] |
| |
27 impl<'a, F: Float, Domain: Space, A: Mapping<Domain>> Quadratic<'a, F, Domain, A> { |
| |
28 pub fn new(opA: &'a A, b: &'a A::Codomain) -> Self { |
| |
29 Quadratic { |
| |
30 opA, |
| |
31 b, |
| |
32 _phantoms: PhantomData, |
| |
33 } |
| |
34 } |
| |
35 |
| |
36 pub fn operator(&self) -> &'a A { |
| |
37 self.opA |
| |
38 } |
| |
39 |
| |
40 pub fn data(&self) -> &'a <A as Mapping<Domain>>::Codomain { |
| |
41 self.b |
| |
42 } |
| |
43 } |
| |
44 |
| |
45 //+ AdjointProductBoundedBy<RNDM<F, N>, P, FloatType = F>, |
| |
46 |
| |
47 impl<'a, F: Float, X: Space, A: Mapping<X>> Mapping<X> for Quadratic<'a, F, X, A> |
| |
48 where |
| |
49 A::Codomain: Euclidean<F>, |
| |
50 { |
| |
51 type Codomain = F; |
| |
52 |
| |
53 fn apply<I: Instance<X>>(&self, x: I) -> F { |
| |
54 // TODO: possibly (if at all more effcient) use GEMV once generalised |
| |
55 // to not require preallocation. However, Rust should be pretty efficient |
| |
56 // at not doing preallocations or anything here, as the result of self.opA.apply() |
| |
57 // can be consumed, so maybe GEMV is no use. |
| |
58 (self.opA.apply(x) - self.b).norm2_squared() / F::TWO |
| |
59 } |
| |
60 } |
| |
61 |
| |
62 impl<'a, F: Float, X: Space, A: Linear<X>> ConvexMapping<X, F> for Quadratic<'a, F, X, A> where |
| |
63 A::Codomain: Euclidean<F> |
| |
64 { |
| |
65 } |
| |
66 |
| |
67 impl<'a, F, X, A> DifferentiableImpl<X> for Quadratic<'a, F, X, A> |
| |
68 where |
| |
69 F: Float, |
| |
70 X: Space, |
| |
71 <A as Mapping<X>>::Codomain: Euclidean<F>, |
| |
72 A: Linear<X> + Preadjointable<X>, |
| |
73 <<A as Mapping<X>>::Codomain as Euclidean<F>>::Output: Instance<<A as Mapping<X>>::Codomain>, |
| |
74 { |
| |
75 type Derivative = A::PreadjointCodomain; |
| |
76 |
| |
77 fn differential_impl<I: Instance<X>>(&self, x: I) -> Self::Derivative { |
| |
78 // TODO: possibly (if at all more effcient) use GEMV once generalised |
| |
79 // to not require preallocation. However, Rust should be pretty efficient |
| |
80 // at not doing preallocations or anything here, as the result of self.opA.apply() |
| |
81 // can be consumed, so maybe GEMV is no use. |
| |
82 self.opA.preadjoint().apply(self.opA.apply(x) - self.b) |
| |
83 } |
| |
84 } |
| |
85 |
| |
86 impl<'a, 'b, F, X, ExpX, A> Lipschitz<ExpX> for Differential<'b, X, Quadratic<'a, F, X, A>> |
| |
87 where |
| |
88 F: Float, |
| |
89 X: Space + Clone + Norm<F, ExpX>, |
| |
90 ExpX: NormExponent, |
| |
91 A: Clone + BoundedLinear<X, ExpX, L2, F>, |
| |
92 { |
| |
93 type FloatType = F; |
| |
94 |
| |
95 fn lipschitz_factor(&self, seminorm: ExpX) -> Option<F> { |
| |
96 Some((*self.g).opA.opnorm_bound(seminorm, L2).powi(2)) |
| |
97 } |
| |
98 } |