src/subproblem/nonneg.rs

branch
dev
changeset 34
efa60bc4f743
parent 24
d29d1fcf5423
child 39
6316d68b58af
equal deleted inserted replaced
33:aec67cdd6b14 34:efa60bc4f743
4 4
5 use nalgebra::{DVector, DMatrix}; 5 use nalgebra::{DVector, DMatrix};
6 use numeric_literals::replace_float_literals; 6 use numeric_literals::replace_float_literals;
7 use itertools::{izip, Itertools}; 7 use itertools::{izip, Itertools};
8 use colored::Colorize; 8 use colored::Colorize;
9 use std::cmp::Ordering::*;
9 10
10 use alg_tools::iter::Mappable; 11 use alg_tools::iter::Mappable;
11 use alg_tools::error::NumericalError; 12 use alg_tools::error::NumericalError;
12 use alg_tools::iterate::{ 13 use alg_tools::iterate::{
13 AlgIteratorFactory, 14 AlgIteratorFactory,
20 use crate::types::*; 21 use crate::types::*;
21 use super::{ 22 use super::{
22 InnerMethod, 23 InnerMethod,
23 InnerSettings 24 InnerSettings
24 }; 25 };
26 use super::l1squared_nonneg::max_interval_dist_to_zero;
25 27
26 /// Compute the proximal operator of $x \mapsto x + \delta\_{[0, \infty)}$, i.e., 28 /// Compute the proximal operator of $x \mapsto x + \delta\_{[0, \infty)}$, i.e.,
27 /// the non-negativity contrained soft-thresholding operator. 29 /// the non-negativity contrained soft-thresholding operator.
28 #[inline] 30 #[inline]
29 #[replace_float_literals(F::cast_from(literal))] 31 #[replace_float_literals(F::cast_from(literal))]
30 fn nonneg_soft_thresholding<F : Float>(v : F, λ : F) -> F { 32 pub(super) fn nonneg_soft_thresholding<F : Float>(v : F, λ : F) -> F {
31 (v - λ).max(0.0) 33 (v - λ).max(0.0)
32 } 34 }
33 35
34 /// Returns the ∞-norm minimal subdifferential of $x ↦ x^⊤Ax - g^⊤ x + λ\vec 1^⊤ x δ_{≥ 0}(x)$ 36 /// Returns the ∞-norm minimal subdifferential of $x ↦ x^⊤Ax/2 - g^⊤ x + λ\vec 1^⊤ x + δ_{≥ 0}(x)$
35 /// at $x$. 37 /// at $x$.
36 /// 38 ///
37 /// `v` will be modified and cannot be trusted to contain useful values afterwards. 39 /// `v` will be modified and cannot be trusted to contain useful values afterwards.
38 #[replace_float_literals(F::cast_from(literal).to_nalgebra_mixed())] 40 #[replace_float_literals(F::cast_from(literal))]
39 fn min_subdifferential<F : Float + ToNalgebraRealField>( 41 fn min_subdifferential<F : Float + nalgebra::RealField>(
40 v : &mut DVector<F::MixedType>, 42 v : &mut DVector<F>,
41 mA : &DMatrix<F::MixedType>, 43 mA : &DMatrix<F>,
42 x : &DVector<F::MixedType>, 44 x : &DVector<F>,
43 g : &DVector<F::MixedType>, 45 g : &DVector<F>,
44 λ : F::MixedType 46 λ : F
45 ) -> F { 47 ) -> F {
46 v.copy_from(g); 48 v.copy_from(g);
47 mA.gemv(v, 1.0, x, -1.0); // v = Ax - g 49 mA.gemv(v, 1.0, x, -1.0); // v = Ax - g
48 let mut val = 0.0; 50 let mut val = 0.0;
49 for (&v_i, &x_i) in izip!(v.iter(), x.iter()) { 51 for (&v_i, &x_i) in izip!(v.iter(), x.iter()) {
50 // The subdifferential of the objective is $Ax - g + λ + ∂ δ_{≥ 0}(x)$. 52 let (mut lb, mut ub) = (v_i, v_i);
51 let d = v_i + λ; 53 match x_i.partial_cmp(&0.0) {
52 if x_i > 0.0 || d < 0.0 { 54 Some(Greater) => { lb += λ; ub += λ },
53 val = val.max(d.abs()); 55 // Less should not happen
56 Some(Less|Equal) => { lb = F::NEG_INFINITY; ub += λ },
57 None => {},
54 } 58 }
59 val = max_interval_dist_to_zero(val, lb, ub);
55 } 60 }
56 F::from_nalgebra_mixed(val) 61 val
57 } 62 }
58 63
59 /// Forward-backward splitting implementation of [`quadratic_nonneg`]. 64 /// Forward-backward splitting implementation of [`quadratic_nonneg`].
60 /// For detailed documentation of the inputs and outputs, refer to there. 65 /// For detailed documentation of the inputs and outputs, refer to there.
61 /// 66 ///
96 }); 101 });
97 102
98 iters +=1; 103 iters +=1;
99 104
100 backup.map(|_| { 105 backup.map(|_| {
101 min_subdifferential(&mut v, mA, x, g, λ) 106 F::from_nalgebra_mixed(min_subdifferential(&mut v, mA, x, g, λ))
102 }) 107 })
103 }); 108 });
104 109
105 iters 110 iters
106 } 111 }
279 iters += 1; 284 iters += 1;
280 285
281 // 4. Report solution quality 286 // 4. Report solution quality
282 state.if_verbose(|| { 287 state.if_verbose(|| {
283 // Calculate subdifferential at the FB step `x` that hasn't yet had `s` yet added. 288 // Calculate subdifferential at the FB step `x` that hasn't yet had `s` yet added.
284 min_subdifferential(&mut v, mA, x, g, λ) 289 F::from_nalgebra_mixed(min_subdifferential(&mut v, mA, x, g, λ))
285 }) 290 })
286 }); 291 });
287 292
288 res.map(|_| iters) 293 res.map(|_| iters)
289 } 294 }
290 295
291 /// This function applies an iterative method for the solution of the quadratic non-negativity 296 /// This function applies an iterative method for the solution of the quadratic non-negativity
292 /// constrained problem 297 /// constrained problem
293 /// <div>$$ 298 /// <div>$$
294 /// \min_{x ∈ ℝ^n} \frac{1}{2} x^⊤Ax - g^⊤ x + λ{\vec 1}^⊤ x + c + δ_{≥ 0}(x). 299 /// \min_{x ∈ ℝ^n} \frac{1}{2} x^⊤Ax - g^⊤ x + λ{\vec 1}^⊤ x + δ_{≥ 0}(x).
295 /// $$</div> 300 /// $$</div>
296 /// Semismooth Newton or forward-backward are supported based on the setting in `method`. 301 /// Semismooth Newton or forward-backward are supported based on the setting in `method`.
297 /// The parameter `mA` is matrix $A$, and `g` and `λ` are as in the mathematical formulation. 302 /// The parameter `mA` is matrix $A$, and `g` and `λ` are as in the mathematical formulation.
298 /// The constant $c$ does not need to be provided. The step length parameter is `τ` while 303 /// The constant $c$ does not need to be provided. The step length parameter is `τ` while
299 /// `x` contains the initial iterate and on return the final one. The `iterator` controls 304 /// `x` contains the initial iterate and on return the final one. The `iterator` controls
305 /// * Valkonen T. - _A method for weighted projections to the positive definite 310 /// * Valkonen T. - _A method for weighted projections to the positive definite
306 /// cone_, <https://doi.org/10.1080/02331934.2014.929680>. 311 /// cone_, <https://doi.org/10.1080/02331934.2014.929680>.
307 /// 312 ///
308 /// This function returns the number of iterations taken. 313 /// This function returns the number of iterations taken.
309 pub fn quadratic_nonneg<F, I>( 314 pub fn quadratic_nonneg<F, I>(
310 method : InnerMethod,
311 mA : &DMatrix<F::MixedType>, 315 mA : &DMatrix<F::MixedType>,
312 g : &DVector<F::MixedType>, 316 g : &DVector<F::MixedType>,
313 //c_ : F,
314 λ : F, 317 λ : F,
315 x : &mut DVector<F::MixedType>, 318 x : &mut DVector<F::MixedType>,
316 τ : F, 319 mA_normest : F,
320 inner : &InnerSettings<F>,
317 iterator : I 321 iterator : I
318 ) -> usize 322 ) -> usize
319 where F : Float + ToNalgebraRealField, 323 where F : Float + ToNalgebraRealField,
320 I : AlgIteratorFactory<F> 324 I : AlgIteratorFactory<F>
321 { 325 {
322 326 let inner_τ = inner.fb_τ0 / mA_normest;
323 match method { 327
324 InnerMethod::FB => 328 match inner.method {
325 quadratic_nonneg_fb(mA, g, λ, x, τ, iterator), 329 InnerMethod::FB | InnerMethod::Default =>
330 quadratic_nonneg_fb(mA, g, λ, x, inner_τ, iterator),
326 InnerMethod::SSN => 331 InnerMethod::SSN =>
327 quadratic_nonneg_ssn(mA, g, λ, x, τ, iterator).unwrap_or_else(|e| { 332 quadratic_nonneg_ssn(mA, g, λ, x, inner_τ, iterator).unwrap_or_else(|e| {
328 println!("{}", format!("{e}. Using FB fallback.").red()); 333 println!("{}", format!("{e}. Using FB fallback.").red());
329 let ins = InnerSettings::<F>::default(); 334 let ins = InnerSettings::<F>::default();
330 quadratic_nonneg_fb(mA, g, λ, x, τ, ins.iterator_options) 335 quadratic_nonneg_fb(mA, g, λ, x, inner_τ, ins.iterator_options)
331 }) 336 }),
337 InnerMethod::PDPS => unimplemented!(),
332 } 338 }
333 } 339 }

mercurial