| 159 }); |
159 }); |
| 160 |
160 |
| 161 iters |
161 iters |
| 162 } |
162 } |
| 163 |
163 |
| |
164 /// Alternative PDPS implementation of [`l1squared_unconstrained`]. |
| |
165 /// For detailed documentation of the inputs and outputs, refer to there. |
| |
166 /// |
| |
167 /// By not dualising the 1-norm, this should produce more sparse solutions than |
| |
168 /// [`l1squared_unconstrained_pdps`]. |
| |
169 /// |
| |
170 /// The `λ` component of the model is handled in the proximal step instead of the gradient step |
| |
171 /// for potential performance improvements. |
| |
172 /// The parameter `θ` is used to multiply the rescale the operator (identity) of the PDPS model. |
| |
173 /// We rewrite |
| |
174 /// <div>$$ |
| |
175 /// \begin{split} |
| |
176 /// & \min_{x ∈ ℝ^n} \frac{β}{2} |x-y|_1^2 - g^⊤ x + λ\|x\|₁ \\ |
| |
177 /// & = \min_{x ∈ ℝ^n} \max_{w} ⟨θ w, x⟩ - g^⊤ x + λ\|x\|₁ |
| |
178 /// - \left(x ↦ \frac{β}{2θ} |x-y|_1^2 \right)^*(w). |
| |
179 /// \end{split} |
| |
180 /// $$</div> |
| |
181 #[replace_float_literals(F::cast_from(literal).to_nalgebra_mixed())] |
| |
182 pub fn l1squared_unconstrained_pdps_alt<F, I>( |
| |
183 y : &DVector<F::MixedType>, |
| |
184 g : &DVector<F::MixedType>, |
| |
185 λ_ : F, |
| |
186 β_ : F, |
| |
187 x : &mut DVector<F::MixedType>, |
| |
188 τ_ : F, |
| |
189 σ_ : F, |
| |
190 θ_ : F, |
| |
191 iterator : I |
| |
192 ) -> usize |
| |
193 where F : Float + ToNalgebraRealField, |
| |
194 I : AlgIteratorFactory<F> |
| |
195 { |
| |
196 let λ = λ_.to_nalgebra_mixed(); |
| |
197 let τ = τ_.to_nalgebra_mixed(); |
| |
198 let σ = σ_.to_nalgebra_mixed(); |
| |
199 let θ = θ_.to_nalgebra_mixed(); |
| |
200 let β = β_.to_nalgebra_mixed(); |
| |
201 let σθ = σ*θ; |
| |
202 let τθ = τ*θ; |
| |
203 let mut w = DVector::zeros(x.len()); |
| |
204 let mut tmp = DVector::zeros(x.len()); |
| |
205 let mut xprev = x.clone(); |
| |
206 let mut iters = 0; |
| |
207 |
| |
208 iterator.iterate(|state| { |
| |
209 // Primal step: x^{k+1} = soft_τλ(x^k - τ(θ w^k -g)) |
| |
210 x.axpy(-τθ, &w, 1.0); |
| |
211 x.axpy(τ, g, 1.0); |
| |
212 x.apply(|x_i| *x_i = soft_thresholding(*x_i, τ*λ)); |
| |
213 |
| |
214 // Dual step: with g(x) = (β/(2θ))‖x-y‖₁² and q = w^k + σ(2x^{k+1}-x^k), |
| |
215 // we compute w^{k+1} = prox_{σg^*}(q) for |
| |
216 // = q - σ prox_{g/σ}(q/σ) |
| |
217 // = q - σ prox_{(β/(2θσ))‖.-y‖₁²}(q/σ) |
| |
218 // = σ(q/σ - prox_{(β/(2θσ))‖.-y‖₁²}(q/σ)) |
| |
219 // where q/σ = w^k/σ + (2x^{k+1}-x^k), |
| |
220 w /= σ; |
| |
221 w.axpy(2.0, x, 1.0); |
| |
222 w.axpy(-1.0, &xprev, 1.0); |
| |
223 xprev.copy_from(&w); // use xprev as temporary variable |
| |
224 l1squared_prox(&mut tmp, &mut xprev, y, β/σθ); |
| |
225 w -= &xprev; |
| |
226 w *= σ; |
| |
227 xprev.copy_from(x); |
| |
228 |
| |
229 iters += 1; |
| |
230 |
| |
231 state.if_verbose(|| { |
| |
232 F::from_nalgebra_mixed(min_subdifferential(y, x, g, λ, β)) |
| |
233 }) |
| |
234 }); |
| |
235 |
| |
236 iters |
| |
237 } |
| |
238 |
| 164 |
239 |
| 165 /// This function applies an iterative method for the solution of the problem |
240 /// This function applies an iterative method for the solution of the problem |
| 166 /// <div>$$ |
241 /// <div>$$ |
| 167 /// \min_{x ∈ ℝ^n} \frac{β}{2} |x-y|_1^2 - g^⊤ x + λ\|x\|₁. |
242 /// \min_{x ∈ ℝ^n} \frac{β}{2} |x-y|_1^2 - g^⊤ x + λ\|x\|₁. |
| 168 /// $$</div> |
243 /// $$</div> |
| 180 iterator : I |
255 iterator : I |
| 181 ) -> usize |
256 ) -> usize |
| 182 where F : Float + ToNalgebraRealField, |
257 where F : Float + ToNalgebraRealField, |
| 183 I : AlgIteratorFactory<F> |
258 I : AlgIteratorFactory<F> |
| 184 { |
259 { |
| 185 // Estimate of ‖K‖ for K=Id. |
260 // Estimate of ‖K‖ for K=θ Id. |
| 186 let normest = 1.0; |
261 let inner_θ = 1.0; |
| |
262 let normest = inner_θ; |
| 187 |
263 |
| 188 let (inner_τ, inner_σ) = (inner.pdps_τσ0.0 / normest, inner.pdps_τσ0.1 / normest); |
264 let (inner_τ, inner_σ) = (inner.pdps_τσ0.0 / normest, inner.pdps_τσ0.1 / normest); |
| 189 |
265 |
| 190 match inner.method { |
266 match inner.method { |
| 191 InnerMethod::PDPS => |
267 InnerMethod::PDPS => |
| 192 l1squared_unconstrained_pdps(y, g, λ, β, x, inner_τ, inner_σ, iterator), |
268 l1squared_unconstrained_pdps_alt(y, g, λ, β, x, inner_τ, inner_σ, inner_θ, iterator), |
| 193 other => unimplemented!("${other:?} is unimplemented"), |
269 other => unimplemented!("${other:?} is unimplemented"), |
| 194 } |
270 } |
| 195 } |
271 } |