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