Fri, 28 Nov 2025 14:01:14 -0500
Add residual to DataTerm
| 105 | 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 | ||
| 109 | 8 | use super::{DifferentiableImpl, DifferentiableMapping, LipschitzDifferentiableImpl, Mapping}; |
| 105 | 9 | use crate::convex::ConvexMapping; |
|
110
a1278320be26
Use DynResult for Lipschitz factors and operator norm bounds
Tuomo Valkonen <tuomov@iki.fi>
parents:
109
diff
changeset
|
10 | use crate::error::DynResult; |
| 150 | 11 | use crate::instance::{ClosedSpace, Instance, Space}; |
| 109 | 12 | use crate::linops::{BoundedLinear, Linear, Preadjointable}; |
| 13 | use crate::norms::{Normed, L2}; | |
| 105 | 14 | use crate::types::Float; |
| 15 | use std::ops::Sub; | |
|
110
a1278320be26
Use DynResult for Lipschitz factors and operator norm bounds
Tuomo Valkonen <tuomov@iki.fi>
parents:
109
diff
changeset
|
16 | |
| 105 | 17 | //use serde::{Deserialize, Serialize}; |
| 18 | ||
| 111 | 19 | /// Functions of the form $g(Ax-b)$ for an operator $A$, data $b$, and fidelity $g$. |
| 105 | 20 | pub struct DataTerm< |
| 21 | F: Float, | |
| 22 | Domain: Space, | |
| 23 | A: Mapping<Domain>, | |
| 24 | G: Mapping<A::Codomain, Codomain = F>, | |
| 25 | > { | |
| 111 | 26 | // The operator A |
| 105 | 27 | opA: A, |
| 111 | 28 | // The data b |
| 105 | 29 | b: <A as Mapping<Domain>>::Codomain, |
| 111 | 30 | // The outer fidelity |
| 105 | 31 | g: G, |
| 32 | } | |
| 33 | ||
| 111 | 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 { | |
| 150 | 44 | DataTerm { opA: self.opA.clone(), b: self.b.clone(), g: self.g.clone() } |
| 111 | 45 | } |
| 46 | } | |
| 47 | ||
| 105 | 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 | } | |
| 191 | 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 | } | |
| 105 | 77 | } |
| 78 | ||
|
124
6aa955ad8122
Transpose loc parameters to allow f64 defaults
Tuomo Valkonen <tuomov@iki.fi>
parents:
111
diff
changeset
|
79 | //+ AdjointProductBoundedBy<RNDM<N, F>, P, FloatType = F>, |
| 105 | 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>, | |
| 150 | 87 | A::Codomain: ClosedSpace + for<'a> Sub<&'a A::Codomain, Output = A::Codomain>, |
| 105 | 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, | |
| 109 | 103 | X: Normed<F>, |
| 105 | 104 | A: Linear<X>, |
| 105 | G: ConvexMapping<A::Codomain, F>, | |
| 150 | 106 | A::Codomain: ClosedSpace + Normed<F> + for<'a> Sub<&'a A::Codomain, Output = A::Codomain>, |
| 105 | 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, | |
| 150 | 114 | Y: Space + Instance<Y> + for<'a> Sub<&'a Y, Output = Y>, |
| 105 | 115 | //<A as Mapping<X>>::Codomain: Euclidean<F>, |
| 116 | A: Linear<X, Codomain = Y> + Preadjointable<X, G::DerivativeDomain>, | |
| 150 | 117 | G::DerivativeDomain: Instance<G::DerivativeDomain>, |
| 118 | A::PreadjointCodomain: ClosedSpace, | |
| 105 | 119 | //<<A as Mapping<X>>::Codomain as Euclidean<F>>::Output: Instance<<A as Mapping<X>>::Codomain>, |
| 120 | G: DifferentiableMapping<Y, Codomain = F>, | |
| 121 | { | |
| 122 | type Derivative = A::PreadjointCodomain; | |
| 123 | ||
| 124 | fn differential_impl<I: Instance<X>>(&self, x: I) -> Self::Derivative { | |
| 125 | // TODO: possibly (if at all more effcient) use GEMV once generalised | |
| 126 | // to not require preallocation. However, Rust should be pretty efficient | |
| 127 | // at not doing preallocations or anything here, as the result of self.opA.apply() | |
| 128 | // can be consumed, so maybe GEMV is no use. | |
| 129 | //self.opA.preadjoint().apply(self.opA.apply(x) - self.b) | |
| 130 | self.opA | |
| 131 | .preadjoint() | |
| 132 | .apply(self.g.diff_ref().apply(self.opA.apply(x) - &self.b)) | |
| 133 | } | |
| 134 | } | |
| 135 | ||
| 109 | 136 | impl<'a, F, X, Y, A, G> LipschitzDifferentiableImpl<X, X::NormExp> for DataTerm<F, X, A, G> |
| 105 | 137 | where |
| 138 | F: Float, | |
|
128
f75bf34adda0
Switch from Cow to MyCow for DifferentiableImpl to avoid Clone trait bound
Tuomo Valkonen <tuomov@iki.fi>
parents:
124
diff
changeset
|
139 | X: Normed<F>, |
| 109 | 140 | Y: Normed<F>, |
|
128
f75bf34adda0
Switch from Cow to MyCow for DifferentiableImpl to avoid Clone trait bound
Tuomo Valkonen <tuomov@iki.fi>
parents:
124
diff
changeset
|
141 | A: BoundedLinear<X, X::NormExp, L2, F, Codomain = Y>, |
| 109 | 142 | G: Mapping<Y, Codomain = F> + LipschitzDifferentiableImpl<Y, Y::NormExp>, |
| 105 | 143 | Self: DifferentiableImpl<X>, |
| 144 | { | |
| 145 | type FloatType = F; | |
| 146 | ||
|
110
a1278320be26
Use DynResult for Lipschitz factors and operator norm bounds
Tuomo Valkonen <tuomov@iki.fi>
parents:
109
diff
changeset
|
147 | fn diff_lipschitz_factor(&self, seminorm: X::NormExp) -> DynResult<F> { |
|
a1278320be26
Use DynResult for Lipschitz factors and operator norm bounds
Tuomo Valkonen <tuomov@iki.fi>
parents:
109
diff
changeset
|
148 | Ok(self.opA.opnorm_bound(seminorm, L2)?.powi(2)) |
| 105 | 149 | } |
| 150 | } |