src/subproblem.rs

Tue, 29 Nov 2022 15:36:12 +0200

author
Tuomo Valkonen <tuomov@iki.fi>
date
Tue, 29 Nov 2022 15:36:12 +0200
changeset 2
7a953a87b6c1
parent 0
eb3c7813b67a
permissions
-rw-r--r--

fubar

0
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
1 //! Iterative algorithms for solving finite-dimensional subproblems.
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
2
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
3 use serde::{Serialize, Deserialize};
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
4 use nalgebra::{DVector, DMatrix};
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
5 use numeric_literals::replace_float_literals;
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
6 use itertools::{izip, Itertools};
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
7 use colored::Colorize;
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
8
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
9 use alg_tools::iter::Mappable;
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
10 use alg_tools::error::NumericalError;
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
11 use alg_tools::iterate::{
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
12 AlgIteratorFactory,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
13 AlgIteratorState,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
14 AlgIteratorOptions,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
15 Verbose,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
16 Step,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
17 };
2
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
18 use alg_tools::linops::{GEMV, Adjointable, AXPY};
0
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
19 use alg_tools::nalgebra_support::ToNalgebraRealField;
2
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
20 use alg_tools::norms::{Projection, Linfinity};
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
21 use alg_tools::euclidean::Euclidean;
0
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
22
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
23 use crate::types::*;
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
24
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
25 /// Method for solving finite-dimensional subproblems
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
26 #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)]
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
27 #[allow(dead_code)]
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
28 pub enum InnerMethod {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
29 /// Forward-backward
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
30 FB,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
31 /// Semismooth Newton
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
32 SSN,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
33 }
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
34
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
35 /// Settings for the solution of finite-dimensional subproblems
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
36 #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)]
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
37 pub struct InnerSettings<F : Float> {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
38 /// Method
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
39 pub method : InnerMethod,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
40 /// Proportional step length (∈ [0, 1) for `InnerMethod::FB`).
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
41 pub τ0 : F,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
42 /// Fraction of `tolerance` given to inner algorithm
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
43 pub tolerance_mult : F,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
44 /// Iterator options
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
45 #[serde(flatten)]
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
46 pub iterator_options : AlgIteratorOptions,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
47 }
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
48
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
49 #[replace_float_literals(F::cast_from(literal))]
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
50 impl<F : Float> Default for InnerSettings<F> {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
51 fn default() -> Self {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
52 InnerSettings {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
53 τ0 : 0.99,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
54 iterator_options : AlgIteratorOptions {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
55 // max_iter cannot be very small, as initially FB needs many iterations, although
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
56 // on later invocations even one or two tends to be enough
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
57 max_iter : 2000,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
58 // verbose_iter affects testing of sufficient convergence, so we set it to
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
59 // a small value…
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
60 verbose_iter : Verbose::Every(1),
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
61 // … but don't print out anything
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
62 quiet : true,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
63 .. Default::default()
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
64 },
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
65 method : InnerMethod::FB,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
66 tolerance_mult : 0.01,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
67 }
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
68 }
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
69 }
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
70
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
71 /// Compute the proximal operator of $x \mapsto x + \delta\_{[0, \infty)}$, i.e.,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
72 /// the non-negativity contrained soft-thresholding operator.
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
73 #[inline]
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
74 #[replace_float_literals(F::cast_from(literal))]
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
75 fn nonneg_soft_thresholding<F : Float>(v : F, λ : F) -> F {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
76 (v - λ).max(0.0)
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
77 }
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
78
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
79 /// Forward-backward splitting implementation of [`quadratic_nonneg`].
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
80 /// For detailed documentation of the inputs and outputs, refer to there.
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
81 ///
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
82 /// The `λ` component of the model is handled in the proximal step instead of the gradient step
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
83 /// for potential performance improvements.
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
84 #[replace_float_literals(F::cast_from(literal).to_nalgebra_mixed())]
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
85 pub fn quadratic_nonneg_fb<F, I>(
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
86 mA : &DMatrix<F::MixedType>,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
87 g : &DVector<F::MixedType>,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
88 //c_ : F,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
89 λ_ : F,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
90 x : &mut DVector<F::MixedType>,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
91 τ_ : F,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
92 iterator : I
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
93 ) -> usize
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
94 where F : Float + ToNalgebraRealField,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
95 I : AlgIteratorFactory<F>
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
96 {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
97 let mut xprev = x.clone();
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
98 //let c = c_.to_nalgebra_mixed();
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
99 let λ = λ_.to_nalgebra_mixed();
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
100 let τ = τ_.to_nalgebra_mixed();
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
101 let τλ = τ * λ;
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
102 let mut v = DVector::zeros(x.len());
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
103 let mut iters = 0;
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
104
2
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
105 assert!(λ > 0.0);
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
106
0
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
107 iterator.iterate(|state| {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
108 // Replace `x` with $x - τ[Ax-g]= [x + τg]- τAx$
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
109 v.copy_from(g); // v = g
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
110 v.axpy(1.0, x, τ); // v = x + τ*g
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
111 v.sygemv(-τ, mA, x, 1.0); // v = [x + τg]- τAx
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
112 let backup = state.if_verbose(|| {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
113 xprev.copy_from(x)
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
114 });
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
115 // Calculate the proximal map
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
116 x.iter_mut().zip(v.iter()).for_each(|(x_i, &v_i)| {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
117 *x_i = nonneg_soft_thresholding(v_i, τλ);
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
118 });
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
119
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
120 iters +=1;
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
121
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
122 backup.map(|_| {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
123 // The subdifferential of the objective is $Ax - g + λ + ∂ δ_{≥ 0}(x)$.
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
124 // We return the minimal ∞-norm over all subderivatives.
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
125 v.copy_from(g); // d = g
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
126 mA.gemv(&mut v, 1.0, x, -1.0); // d = Ax - g
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
127 let mut val = 0.0;
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
128 for (&v_i, &x_i) in izip!(v.iter(), x.iter()) {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
129 let d = v_i + λ;
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
130 if x_i > 0.0 || d < 0.0 {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
131 val = val.max(d.abs());
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
132 }
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
133 }
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
134 F::from_nalgebra_mixed(val)
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
135 })
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
136 });
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
137
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
138 iters
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
139 }
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
140
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
141 /// Semismooth Newton implementation of [`quadratic_nonneg`].
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
142 ///
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
143 /// For detailed documentation of the inputs, refer to there.
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
144 /// This function returns the number of iterations taken if there was no inversion failure,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
145 ///
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
146 /// ## Method derivation
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
147 ///
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
148 /// **The below may look like garbage. Sorry, but rustdoc is obsolete rubbish
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
149 /// that doesn't directly support by-now standard-in-markdown LaTeX math. Instead it
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
150 /// forces one into unreliable KaTeX autorender postprocessing andescape hell and that
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
151 /// it doesn't even process correctly.**
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
152 ///
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
153 /// <p>
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
154 /// For the objective
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
155 /// $$
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
156 /// J(x) = \frac{1}{2} x^⊤Ax - g^⊤ x + λ{\vec 1}^⊤ x + c + δ_{≥ 0}(x),
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
157 /// $$
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
158 /// we have the optimality condition
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
159 /// $$
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
160 /// x - \mathop{\mathrm{prox}}_{τλ{\vec 1}^⊤ + δ_{≥ 0}}(x - τ[Ax-g^⊤]) = 0,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
161 /// $$
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
162 /// which we write as
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
163 /// $$
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
164 /// x - [G ∘ F](x)=0
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
165 /// $$
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
166 /// for
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
167 /// $$
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
168 /// G(x) = \mathop{\mathrm{prox}}_{λ{\vec 1}^⊤ + δ_{≥ 0}}
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
169 /// \quad\text{and}\quad
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
170 /// F(x) = x - τ Ax + τ g^⊤
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
171 /// $$
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
172 /// We can use Newton derivative chain rule to compute
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
173 /// $D_N[G ∘ F](x) = D_N G(F(x)) D_N F(x)$, where
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
174 /// $D_N F(x) = \mathop{\mathrm{Id}} - τ A$,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
175 /// and $[D_N G(F(x))]_i = 1$ for inactive coordinates and $=0$ for active coordinates.
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
176 /// </p>
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
177 ///
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
178 /// <p>
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
179 /// The method itself involves solving $D_N[Id - G ∘ F](x^k) s^k = - [Id - G ∘ F](x^k)$ and
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
180 /// updating $x^{k+1} = x^k + s^k$. Consequently
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
181 /// $$
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
182 /// s^k - D_N G(F(x^k)) [s^k - τ As^k] = - x^k + [G ∘ F](x^k)
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
183 /// $$
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
184 /// For $𝒜$ the set of active coordinates and $ℐ$ the set of inactive coordinates, this
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
185 /// expands as
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
186 /// $$
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
187 /// [τ A_{ℐ × ℐ}]s^k_ℐ = - x^k_ℐ + [G ∘ F](x^k)_ℐ - [τ A_{ℐ × 𝒜}]s^k_𝒜
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
188 /// $$
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
189 /// and
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
190 /// $$
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
191 /// s^k_𝒜 = - x^k_𝒜 + [G ∘ F](x^k)_𝒜.
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
192 /// $$
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
193 /// Thus on $𝒜$ the update $[x^k + s^k]_𝒜 = [G ∘ F](x^k)_𝒜$ is just the forward-backward update.
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
194 /// </p>
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
195 ///
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
196 /// <p>
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
197 /// We need to detect stopping by a subdifferential and return $x$ satisfying $x ≥ 0$,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
198 /// which is in general not true for the SSN. We therefore use that $[G ∘ F](x^k)$ is a valid
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
199 /// forward-backward step.
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
200 /// </p>
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
201 #[replace_float_literals(F::cast_from(literal).to_nalgebra_mixed())]
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
202 pub fn quadratic_nonneg_ssn<F, I>(
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
203 mA : &DMatrix<F::MixedType>,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
204 g : &DVector<F::MixedType>,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
205 //c_ : F,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
206 λ_ : F,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
207 x : &mut DVector<F::MixedType>,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
208 τ_ : F,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
209 iterator : I
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
210 ) -> Result<usize, NumericalError>
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
211 where F : Float + ToNalgebraRealField,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
212 I : AlgIteratorFactory<F>
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
213 {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
214 let n = x.len();
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
215 let mut xprev = x.clone();
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
216 let mut v = DVector::zeros(n);
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
217 //let c = c_.to_nalgebra_mixed();
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
218 let λ = λ_.to_nalgebra_mixed();
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
219 let τ = τ_.to_nalgebra_mixed();
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
220 let τλ = τ * λ;
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
221 let mut inact : Vec<bool> = Vec::from_iter(std::iter::repeat(false).take(n));
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
222 let mut s = DVector::zeros(0);
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
223 let mut decomp = nalgebra::linalg::LU::new(DMatrix::zeros(0, 0));
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
224 let mut iters = 0;
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
225
2
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
226 assert!(λ > 0.0);
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
227
0
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
228 let res = iterator.iterate_fallible(|state| {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
229 // 1. Perform delayed SSN-update based on previously computed step on active
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
230 // coordinates. The step is delayed to the beginning of the loop because
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
231 // the SSN step may violate constraints, so we arrange `x` to contain at the
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
232 // end of the loop the valid FB step that forms part of the SSN step
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
233 let mut si = s.iter();
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
234 for (&ast, x_i, xprev_i) in izip!(inact.iter(), x.iter_mut(), xprev.iter_mut()) {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
235 if ast {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
236 *x_i = *xprev_i + *si.next().unwrap()
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
237 }
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
238 *xprev_i = *x_i;
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
239 }
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
240
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
241 //xprev.copy_from(x);
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
242
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
243 // 2. Calculate FB step.
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
244 // 2.1. Replace `x` with $x⁻ - τ[Ax⁻-g]= [x⁻ + τg]- τAx⁻$
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
245 x.axpy(τ, g, 1.0); // x = x⁻ + τ*g
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
246 x.sygemv(-τ, mA, &xprev, 1.0); // x = [x⁻ + τg]- τAx⁻
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
247 // 2.2. Calculate prox and set of active coordinates at the same time
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
248 let mut act_changed = false;
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
249 let mut n_inact = 0;
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
250 for (x_i, ast) in izip!(x.iter_mut(), inact.iter_mut()) {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
251 if *x_i > τλ {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
252 *x_i -= τλ;
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
253 if !*ast {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
254 act_changed = true;
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
255 *ast = true;
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
256 }
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
257 n_inact += 1;
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
258 } else {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
259 *x_i = 0.0;
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
260 if *ast {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
261 act_changed = true;
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
262 *ast = false;
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
263 }
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
264 }
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
265 }
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
266
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
267 // *** x now contains forward-backward step ***
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
268
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
269 // 3. Solve SSN step `s`.
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
270 // 3.1 Construct [τ A_{ℐ × ℐ}] if the set of inactive coordinates has changed.
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
271 if act_changed {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
272 let decomp_iter = inact.iter().cartesian_product(inact.iter()).zip(mA.iter());
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
273 let decomp_constr = decomp_iter.filter_map(|((&i_inact, &j_inact), &mAij)| {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
274 //(i_inact && j_inact).then_some(mAij * τ)
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
275 (i_inact && j_inact).then_some(mAij) // 🔺 below matches removal of τ
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
276 });
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
277 let mat = DMatrix::from_iterator(n_inact, n_inact, decomp_constr);
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
278 decomp = nalgebra::linalg::LU::new(mat);
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
279 }
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
280
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
281 // 3.2 Solve `s` = $s_ℐ^k$ from
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
282 // $[τ A_{ℐ × ℐ}]s^k_ℐ = - x^k_ℐ + [G ∘ F](x^k)_ℐ - [τ A_{ℐ × 𝒜}]s^k_𝒜$.
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
283 // With current variable setup we have $[G ∘ F](x^k) = $`x` and $x^k = x⁻$ = `xprev`,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
284 // so the system to solve is $[τ A_{ℐ × ℐ}]s^k_ℐ = (x-x⁻)_ℐ - [τ A_{ℐ × 𝒜}](x-x⁻)_𝒜$
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
285 // The matrix $[τ A_{ℐ × ℐ}]$ we have already LU-decomposed above into `decomp`.
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
286 s = if n_inact > 0 {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
287 // 3.2.1 Construct `rhs` = $(x-x⁻)_ℐ - [τ A_{ℐ × 𝒜}](x-x⁻)_𝒜$
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
288 let inactfilt = inact.iter().copied();
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
289 let rhs_iter = izip!(x.iter(), xprev.iter(), mA.row_iter()).filter_zip(inactfilt);
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
290 let rhs_constr = rhs_iter.map(|(&x_i, &xprev_i, mAi)| {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
291 // Calculate row i of [τ A_{ℐ × 𝒜}]s^k_𝒜 = [τ A_{ℐ × 𝒜}](x-xprev)_𝒜
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
292 let actfilt = inact.iter().copied().map(std::ops::Not::not);
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
293 let actit = izip!(x.iter(), xprev.iter(), mAi.iter()).filter_zip(actfilt);
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
294 let actpart = actit.map(|(&x_j, &xprev_j, &mAij)| {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
295 mAij * (x_j - xprev_j)
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
296 }).sum();
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
297 // Subtract it from [x-prev]_i
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
298 //x_i - xprev_i - τ * actpart
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
299 (x_i - xprev_i) / τ - actpart // 🔺 change matches removal of τ above
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
300 });
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
301 let mut rhs = DVector::from_iterator(n_inact, rhs_constr);
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
302 assert_eq!(rhs.len(), n_inact);
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
303 // Solve the system
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
304 if !decomp.solve_mut(&mut rhs) {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
305 return Step::Failure(NumericalError(
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
306 "Failed to solve linear system for subproblem SSN."
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
307 ))
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
308 }
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
309 rhs
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
310 } else {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
311 DVector::zeros(0)
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
312 };
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
313
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
314 iters += 1;
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
315
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
316 // 4. Report solution quality
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
317 state.if_verbose(|| {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
318 // Calculate subdifferential at the FB step `x` that hasn't yet had `s` yet added.
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
319 // The subdifferential of the objective is $Ax - g + λ + ∂ δ_{≥ 0}(x)$.
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
320 // We return the minimal ∞-norm over all subderivatives.
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
321 v.copy_from(g); // d = g
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
322 mA.gemv(&mut v, 1.0, x, -1.0); // d = Ax - g
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
323 let mut val = 0.0;
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
324 for (&v_i, &x_i) in izip!(v.iter(), x.iter()) {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
325 let d = v_i + λ;
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
326 if x_i > 0.0 || d < 0.0 {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
327 val = val.max(d.abs());
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
328 }
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
329 }
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
330 F::from_nalgebra_mixed(val)
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
331 })
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
332 });
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
333
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
334 res.map(|_| iters)
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
335 }
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
336
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
337 /// This function applies an iterative method for the solution of the quadratic non-negativity
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
338 /// constrained problem
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
339 /// <div>$$
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
340 /// \min_{x ∈ ℝ^n} \frac{1}{2} x^⊤Ax - g^⊤ x + λ{\vec 1}^⊤ x + c + δ_{≥ 0}(x).
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
341 /// $$</div>
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
342 /// Semismooth Newton or forward-backward are supported based on the setting in `method`.
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
343 /// The parameter `mA` is matrix $A$, and `g` and `λ` are as in the mathematical formulation.
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
344 /// The constant $c$ does not need to be provided. The step length parameter is `τ` while
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
345 /// `x` contains the initial iterate and on return the final one. The `iterator` controls
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
346 /// stopping. The “verbose” value output by all methods is the $ℓ\_∞$ distance of some
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
347 /// subdifferential of the objective to zero.
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
348 ///
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
349 /// Interior point methods could offer a further alternative, for example, the one in:
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
350 ///
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
351 /// * Valkonen T. - _A method for weighted projections to the positive definite
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
352 /// cone_, <https://doi.org/10.1080/02331934.2014.929680>.
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
353 ///
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
354 /// This function returns the number of iterations taken.
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
355 pub fn quadratic_nonneg<F, I>(
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
356 method : InnerMethod,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
357 mA : &DMatrix<F::MixedType>,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
358 g : &DVector<F::MixedType>,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
359 //c_ : F,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
360 λ : F,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
361 x : &mut DVector<F::MixedType>,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
362 τ : F,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
363 iterator : I
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
364 ) -> usize
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
365 where F : Float + ToNalgebraRealField,
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
366 I : AlgIteratorFactory<F>
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
367 {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
368
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
369 match method {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
370 InnerMethod::FB =>
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
371 quadratic_nonneg_fb(mA, g, λ, x, τ, iterator),
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
372 InnerMethod::SSN =>
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
373 quadratic_nonneg_ssn(mA, g, λ, x, τ, iterator).unwrap_or_else(|e| {
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
374 println!("{}", format!("{e}. Using FB fallback.").red());
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
375 let ins = InnerSettings::<F>::default();
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
376 quadratic_nonneg_fb(mA, g, λ, x, τ, ins.iterator_options)
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
377 })
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
378 }
eb3c7813b67a Initial version
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
379 }
2
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
380
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
381 /// PDPSimplementation of [`l1_nonneg`].
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
382 /// For detailed documentation of the inputs and outputs, refer to there.
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
383 #[replace_float_literals(F::cast_from(literal))]
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
384 pub fn l1_nonneg_pdps<F, I, A, Y>(
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
385 mA : &A,
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
386 b : &Y,
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
387 λ : F,
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
388 x : &mut DVector<F>,
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
389 τ : F,
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
390 σ : F,
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
391 iterator : I
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
392 ) -> usize
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
393 where F : Float + ToNalgebraRealField,
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
394 I : AlgIteratorFactory<F>,
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
395 Y : Euclidean<F, Output = Y> + Projection<F, Linfinity> + AXPY<F>,
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
396 A : GEMV<F, DVector<F>, Codomain=Y>
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
397 + Adjointable<DVector<F>, Y>,
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
398 for<'a> A::Adjoint<'a> : GEMV<F, Y, Codomain=DVector<F>>
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
399 {
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
400 let mAt = mA.adjoint();
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
401 let mut xprev = x.clone();
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
402 let τλ = τ * λ;
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
403 let mut v = DVector::zeros(x.len());
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
404 let mut y = b.similar_origin();
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
405 let mut iters = 0;
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
406
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
407 assert!(λ > 0.0);
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
408
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
409 iterator.iterate(|state| {
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
410 // Replace `x` with $x - τA^*y
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
411 xprev.copy_from(x);
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
412 mAt.gemv(x, -τ, &y, 1.0);
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
413 // Calculate the proximal map
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
414 x.iter_mut().for_each(|x_i| *x_i = nonneg_soft_thresholding(*x_i, τλ));
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
415 // Calculate v = 2x - xprev
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
416 v.copy_from(x);
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
417 v.axpy(-1.0, &xprev, 2.0);
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
418 // Calculate y = y + σ(A v - b)
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
419 y.axpy(-σ, b, 1.0);
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
420 mA.gemv(&mut y, σ, &v, 1.0);
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
421 y.proj_ball_mut(1.0, Linfinity);
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
422
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
423 iters +=1;
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
424
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
425 state.if_verbose(|| {
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
426 // The subdifferential of the objective is $A^* ∂\|.\|(Ax - g) + λ + ∂δ_{≥ 0}(x)$.
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
427 // We return the minimal ∞-norm over all subderivatives.
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
428 v.copy_from(b); // d = g
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
429 mA.gemv(&mut ỹ, 1.0, x, -1.0); // d = Ax - b
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
430 let mut val = 0.0;
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
431 izip!(ỹ.iter(), x.iter()).for_each(|(&ỹ_i, &x_i)| {
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
432 use std::cmp::Ordering::*;
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
433 let tmp = match ỹ_i.partial_cmp(&0.0) {
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
434 None => F::INFINITY,
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
435 Some(c) => match (c, x_i > 0.0) {
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
436 (Greater, true) => 1.0 + λ,
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
437 (Greater, false) | (Equal, false) => 0.0,
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
438 (Less, true) => -1.0 + λ,
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
439 (Less, false) => 0.0.min(-1.0 + λ),
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
440 (Equal, true) => 0.0.max(-1.0 + λ), // since λ > 0
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
441 }
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
442 };
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
443 val = val.max(tmp);
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
444 });
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
445 val
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
446 })
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
447 });
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
448
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
449 iters
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
450 }
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
451
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
452 /// Method for L1 weight optimisation
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
453 #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)]
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
454 #[allow(dead_code)]
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
455 pub enum L1InnerMethod {
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
456 /// PDPS
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
457 PDPS,
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
458 }
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
459
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
460 /// This function applies an iterative method for the solution of the L1 non-negativity
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
461 /// constrained problem
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
462 /// <div>$$
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
463 /// \min_{x ∈ ℝ^n} \|Ax - b\|_1 + λ{\vec 1}^⊤ x + δ_{≥ 0}(x).
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
464 /// $$</div>
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
465 ///
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
466 /// This function returns the number of iterations taken.
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
467 pub fn l1_nonneg<F, I>(
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
468 method : L1InnerMethod,
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
469 mA : &DMatrix<F::MixedType>,
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
470 b : &DVector<F::MixedType>,
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
471 λ : F,
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
472 σ : F,
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
473 x : &mut DVector<F::MixedType>,
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
474 τ : F,
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
475 iterator : I
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
476 ) -> usize
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
477 where F : Float + ToNalgebraRealField,
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
478 I : AlgIteratorFactory<F> {
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
479
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
480 match method {
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
481 L1InnerMethod::PDPS => l1_nonneg_pdps(mA, b, λ, x, τ, σ, iterator)
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
482 }
Tuomo Valkonen <tuomov@iki.fi>
parents: 0
diff changeset
483 }

mercurial