38 } |
39 } |
39 |
40 |
40 /// Returns the ∞-norm minimal subdifferential of $x ↦ x^⊤Ax - g^⊤ x + λ\|x\|₁$ at $x$. |
41 /// Returns the ∞-norm minimal subdifferential of $x ↦ x^⊤Ax - g^⊤ x + λ\|x\|₁$ at $x$. |
41 /// |
42 /// |
42 /// `v` will be modified and cannot be trusted to contain useful values afterwards. |
43 /// `v` will be modified and cannot be trusted to contain useful values afterwards. |
43 #[replace_float_literals(F::cast_from(literal).to_nalgebra_mixed())] |
44 #[replace_float_literals(F::cast_from(literal))] |
44 fn min_subdifferential<F : Float + ToNalgebraRealField>( |
45 fn min_subdifferential<F : Float + nalgebra::RealField>( |
45 v : &mut DVector<F::MixedType>, |
46 v : &mut DVector<F>, |
46 mA : &DMatrix<F::MixedType>, |
47 mA : &DMatrix<F>, |
47 x : &DVector<F::MixedType>, |
48 x : &DVector<F>, |
48 g : &DVector<F::MixedType>, |
49 g : &DVector<F>, |
49 λ : F::MixedType |
50 λ : F |
50 ) -> F { |
51 ) -> F { |
51 v.copy_from(g); |
52 v.copy_from(g); |
52 mA.gemv(v, 1.0, x, -1.0); // v = Ax - g |
53 mA.gemv(v, 1.0, x, -1.0); // v = Ax - g |
53 let mut val = 0.0; |
54 let mut val = 0.0; |
54 for (&v_i, &x_i) in izip!(v.iter(), x.iter()) { |
55 for (&v_i, &x_i) in izip!(v.iter(), x.iter()) { |
55 // The subdifferential at x is $Ax - g + λ ∂‖·‖₁(x)$. |
56 let (mut lb, mut ub) = (v_i, v_i); |
56 val = val.max(match x_i.partial_cmp(&0.0) { |
57 match x_i.partial_cmp(&0.0) { |
57 Some(Greater) => v_i + λ, |
58 Some(Greater) => { lb += λ; ub += λ }, |
58 Some(Less) => v_i - λ, |
59 Some(Less) => { lb -= λ; ub -= λ }, |
59 Some(Equal) => soft_thresholding(v_i, λ), |
60 Some(Equal) => { lb -= λ; ub += λ }, |
60 None => F::MixedType::nan(), |
61 None => {}, |
61 }) |
62 } |
|
63 val = max_interval_dist_to_zero(val, lb, ub); |
62 } |
64 } |
63 F::from_nalgebra_mixed(val) |
65 val |
64 } |
66 } |
65 |
67 |
66 |
68 |
67 /// Forward-backward splitting implementation of [`quadratic_unconstrained`]. |
69 /// Forward-backward splitting implementation of [`quadratic_unconstrained`]. |
68 /// For detailed documentation of the inputs and outputs, refer to there. |
70 /// For detailed documentation of the inputs and outputs, refer to there. |
240 iters += 1; |
242 iters += 1; |
241 |
243 |
242 // 4. Report solution quality |
244 // 4. Report solution quality |
243 state.if_verbose(|| { |
245 state.if_verbose(|| { |
244 // Calculate subdifferential at the FB step `x` that hasn't yet had `s` yet added. |
246 // Calculate subdifferential at the FB step `x` that hasn't yet had `s` yet added. |
245 min_subdifferential(&mut v, mA, x, g, λ) |
247 F::from_nalgebra_mixed(min_subdifferential(&mut v, mA, x, g, λ)) |
246 }) |
248 }) |
247 }); |
249 }); |
248 |
250 |
249 res.map(|_| iters) |
251 res.map(|_| iters) |
250 } |
252 } |
251 |
253 |
252 /// This function applies an iterative method for the solution of the problem |
254 /// This function applies an iterative method for the solution of the problem |
253 /// <div>$$ |
255 /// <div>$$ |
254 /// \min_{x ∈ ℝ^n} \frac{1}{2} x^⊤Ax - g^⊤ x + λ\|x\|₁ + c. |
256 /// \min_{x ∈ ℝ^n} \frac{1}{2} x^⊤Ax - g^⊤ x + λ\|x\|₁. |
255 /// $$</div> |
257 /// $$</div> |
256 /// Semismooth Newton or forward-backward are supported based on the setting in `method`. |
258 /// Semismooth Newton or forward-backward are supported based on the setting in `method`. |
257 /// The parameter `mA` is matrix $A$, and `g` and `λ` are as in the mathematical formulation. |
259 /// The parameter `mA` is matrix $A$, and `g` and `λ` are as in the mathematical formulation. |
258 /// The constant $c$ does not need to be provided. The step length parameter is `τ` while |
260 /// The constant $c$ does not need to be provided. The step length parameter is `τ` while |
259 /// `x` contains the initial iterate and on return the final one. The `iterator` controls |
261 /// `x` contains the initial iterate and on return the final one. The `iterator` controls |
260 /// stopping. The “verbose” value output by all methods is the $ℓ\_∞$ distance of some |
262 /// stopping. The “verbose” value output by all methods is the $ℓ\_∞$ distance of some |
261 /// subdifferential of the objective to zero. |
263 /// subdifferential of the objective to zero. |
262 /// |
264 /// |
263 /// This function returns the number of iterations taken. |
265 /// This function returns the number of iterations taken. |
264 pub fn quadratic_unconstrained<F, I>( |
266 pub fn quadratic_unconstrained<F, I>( |
265 method : InnerMethod, |
|
266 mA : &DMatrix<F::MixedType>, |
267 mA : &DMatrix<F::MixedType>, |
267 g : &DVector<F::MixedType>, |
268 g : &DVector<F::MixedType>, |
268 //c_ : F, |
|
269 λ : F, |
269 λ : F, |
270 x : &mut DVector<F::MixedType>, |
270 x : &mut DVector<F::MixedType>, |
271 τ : F, |
271 mA_normest : F, |
|
272 inner : &InnerSettings<F>, |
272 iterator : I |
273 iterator : I |
273 ) -> usize |
274 ) -> usize |
274 where F : Float + ToNalgebraRealField, |
275 where F : Float + ToNalgebraRealField, |
275 I : AlgIteratorFactory<F> |
276 I : AlgIteratorFactory<F> |
276 { |
277 { |
|
278 let inner_τ = inner.fb_τ0 / mA_normest; |
277 |
279 |
278 match method { |
280 match inner.method { |
279 InnerMethod::FB => |
281 InnerMethod::FB | InnerMethod::Default => |
280 quadratic_unconstrained_fb(mA, g, λ, x, τ, iterator), |
282 quadratic_unconstrained_fb(mA, g, λ, x, inner_τ, iterator), |
281 InnerMethod::SSN => |
283 InnerMethod::SSN => |
282 quadratic_unconstrained_ssn(mA, g, λ, x, τ, iterator).unwrap_or_else(|e| { |
284 quadratic_unconstrained_ssn(mA, g, λ, x, inner_τ, iterator).unwrap_or_else(|e| { |
283 println!("{}", format!("{e}. Using FB fallback.").red()); |
285 println!("{}", format!("{e}. Using FB fallback.").red()); |
284 let ins = InnerSettings::<F>::default(); |
286 let ins = InnerSettings::<F>::default(); |
285 quadratic_unconstrained_fb(mA, g, λ, x, τ, ins.iterator_options) |
287 quadratic_unconstrained_fb(mA, g, λ, x, inner_τ, ins.iterator_options) |
286 }) |
288 }), |
|
289 InnerMethod::PDPS => unimplemented!(), |
287 } |
290 } |
288 } |
291 } |