| |
1 /*! |
| |
2 General deata terms of the form $g(Ax-b)$ for an operator $A$ |
| |
3 to a [`Euclidean`] space, and a function g on that space. |
| |
4 */ |
| |
5 |
| |
6 #![allow(non_snake_case)] |
| |
7 |
| |
8 use super::{DifferentiableImpl, DifferentiableMapping, LipschitzDifferentiableImpl, Mapping}; |
| |
9 use crate::convex::ConvexMapping; |
| |
10 use crate::error::DynResult; |
| |
11 use crate::instance::{ClosedSpace, Instance, Space}; |
| |
12 use crate::linops::{BoundedLinear, Linear, Preadjointable}; |
| |
13 use crate::norms::{Normed, L2}; |
| |
14 use crate::types::Float; |
| |
15 use std::ops::Sub; |
| |
16 |
| |
17 //use serde::{Deserialize, Serialize}; |
| |
18 |
| |
19 /// Functions of the form $g(Ax-b)$ for an operator $A$, data $b$, and fidelity $g$. |
| |
20 pub struct DataTerm< |
| |
21 F: Float, |
| |
22 Domain: Space, |
| |
23 A: Mapping<Domain>, |
| |
24 G: Mapping<A::Codomain, Codomain = F>, |
| |
25 > { |
| |
26 // The operator A |
| |
27 opA: A, |
| |
28 // The data b |
| |
29 b: <A as Mapping<Domain>>::Codomain, |
| |
30 // The outer fidelity |
| |
31 g: G, |
| |
32 } |
| |
33 |
| |
34 // Derive has troubles with `b`. |
| |
35 impl<F, Domain, A, G> Clone for DataTerm<F, Domain, A, G> |
| |
36 where |
| |
37 F: Float, |
| |
38 Domain: Space, |
| |
39 A: Mapping<Domain> + Clone, |
| |
40 <A as Mapping<Domain>>::Codomain: Clone, |
| |
41 G: Mapping<A::Codomain, Codomain = F> + Clone, |
| |
42 { |
| |
43 fn clone(&self) -> Self { |
| |
44 DataTerm { opA: self.opA.clone(), b: self.b.clone(), g: self.g.clone() } |
| |
45 } |
| |
46 } |
| |
47 |
| |
48 #[allow(non_snake_case)] |
| |
49 impl<F: Float, Domain: Space, A: Mapping<Domain>, G: Mapping<A::Codomain, Codomain = F>> |
| |
50 DataTerm<F, Domain, A, G> |
| |
51 { |
| |
52 pub fn new(opA: A, b: A::Codomain, g: G) -> Self { |
| |
53 DataTerm { opA, b, g } |
| |
54 } |
| |
55 |
| |
56 pub fn operator(&self) -> &'_ A { |
| |
57 &self.opA |
| |
58 } |
| |
59 |
| |
60 pub fn data(&self) -> &'_ <A as Mapping<Domain>>::Codomain { |
| |
61 &self.b |
| |
62 } |
| |
63 |
| |
64 pub fn fidelity(&self) -> &'_ G { |
| |
65 &self.g |
| |
66 } |
| |
67 |
| |
68 /// Returns the residual $Ax-b$. |
| |
69 pub fn residual<'a, 'b>(&'b self, x: &'a Domain) -> <A as Mapping<Domain>>::Codomain |
| |
70 where |
| |
71 &'a Domain: Instance<Domain>, |
| |
72 <A as Mapping<Domain>>::Codomain: |
| |
73 Sub<&'b <A as Mapping<Domain>>::Codomain, Output = <A as Mapping<Domain>>::Codomain>, |
| |
74 { |
| |
75 self.opA.apply(x) - &self.b |
| |
76 } |
| |
77 } |
| |
78 |
| |
79 //+ AdjointProductBoundedBy<RNDM<N, F>, P, FloatType = F>, |
| |
80 |
| |
81 impl<F, X, A, G> Mapping<X> for DataTerm<F, X, A, G> |
| |
82 where |
| |
83 F: Float, |
| |
84 X: Space, |
| |
85 A: Mapping<X>, |
| |
86 G: Mapping<A::Codomain, Codomain = F>, |
| |
87 A::Codomain: ClosedSpace + for<'a> Sub<&'a A::Codomain, Output = A::Codomain>, |
| |
88 { |
| |
89 type Codomain = F; |
| |
90 |
| |
91 fn apply<I: Instance<X>>(&self, x: I) -> F { |
| |
92 // TODO: possibly (if at all more effcient) use GEMV once generalised |
| |
93 // to not require preallocation. However, Rust should be pretty efficient |
| |
94 // at not doing preallocations or anything here, as the result of self.opA.apply() |
| |
95 // can be consumed, so maybe GEMV is no use. |
| |
96 self.g.apply(self.opA.apply(x) - &self.b) |
| |
97 } |
| |
98 } |
| |
99 |
| |
100 impl<F, X, A, G> ConvexMapping<X, F> for DataTerm<F, X, A, G> |
| |
101 where |
| |
102 F: Float, |
| |
103 X: Normed<F>, |
| |
104 A: Linear<X>, |
| |
105 G: ConvexMapping<A::Codomain, F>, |
| |
106 A::Codomain: ClosedSpace + Normed<F> + for<'a> Sub<&'a A::Codomain, Output = A::Codomain>, |
| |
107 { |
| |
108 } |
| |
109 |
| |
110 impl<F, X, Y, A, G> DifferentiableImpl<X> for DataTerm<F, X, A, G> |
| |
111 where |
| |
112 F: Float, |
| |
113 X: Space, |
| |
114 Y: Space + Instance<Y> + for<'a> Sub<&'a Y, Output = Y>, |
| |
115 A: Linear<X, Codomain = Y> + Preadjointable<X, G::DerivativeDomain>, |
| |
116 G::DerivativeDomain: Instance<G::DerivativeDomain>, |
| |
117 A::PreadjointCodomain: ClosedSpace, |
| |
118 G: DifferentiableMapping<Y, Codomain = F>, |
| |
119 Self: Mapping<X, Codomain = F>, |
| |
120 { |
| |
121 type Derivative = A::PreadjointCodomain; |
| |
122 |
| |
123 fn differential_impl<I: Instance<X>>(&self, x: I) -> Self::Derivative { |
| |
124 // TODO: possibly (if at all more effcient) use GEMV once generalised |
| |
125 // to not require preallocation. However, Rust should be pretty efficient |
| |
126 // at not doing preallocations or anything here, as the result of self.opA.apply() |
| |
127 // can be consumed, so maybe GEMV is no use. |
| |
128 //self.opA.preadjoint().apply(self.opA.apply(x) - self.b) |
| |
129 self.opA |
| |
130 .preadjoint() |
| |
131 .apply(self.g.differential(self.opA.apply(x) - &self.b)) |
| |
132 } |
| |
133 |
| |
134 fn apply_and_differential_impl<I: Instance<X>>(&self, x: I) -> (F, Self::Derivative) { |
| |
135 let j = self.opA.apply(x) - &self.b; |
| |
136 let (v, d) = self.g.apply_and_differential(j); |
| |
137 (v, self.opA.preadjoint().apply(d)) |
| |
138 } |
| |
139 } |
| |
140 |
| |
141 impl<'a, F, X, Y, A, G> LipschitzDifferentiableImpl<X, X::NormExp> for DataTerm<F, X, A, G> |
| |
142 where |
| |
143 F: Float, |
| |
144 X: Normed<F>, |
| |
145 Y: Normed<F>, |
| |
146 A: BoundedLinear<X, X::NormExp, L2, F, Codomain = Y>, |
| |
147 G: Mapping<Y, Codomain = F> + LipschitzDifferentiableImpl<Y, Y::NormExp>, |
| |
148 Self: DifferentiableImpl<X>, |
| |
149 { |
| |
150 type FloatType = F; |
| |
151 |
| |
152 fn diff_lipschitz_factor(&self, seminorm: X::NormExp) -> DynResult<F> { |
| |
153 Ok(self.opA.opnorm_bound(seminorm, L2)?.powi(2)) |
| |
154 } |
| |
155 } |