src/subproblem.rs

changeset 2
7a953a87b6c1
parent 0
eb3c7813b67a
equal deleted inserted replaced
0:eb3c7813b67a 2:7a953a87b6c1
13 AlgIteratorState, 13 AlgIteratorState,
14 AlgIteratorOptions, 14 AlgIteratorOptions,
15 Verbose, 15 Verbose,
16 Step, 16 Step,
17 }; 17 };
18 use alg_tools::linops::GEMV; 18 use alg_tools::linops::{GEMV, Adjointable, AXPY};
19 use alg_tools::nalgebra_support::ToNalgebraRealField; 19 use alg_tools::nalgebra_support::ToNalgebraRealField;
20 use alg_tools::norms::{Projection, Linfinity};
21 use alg_tools::euclidean::Euclidean;
20 22
21 use crate::types::*; 23 use crate::types::*;
22 24
23 /// Method for solving finite-dimensional subproblems 25 /// Method for solving finite-dimensional subproblems
24 #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)] 26 #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)]
97 let λ = λ_.to_nalgebra_mixed(); 99 let λ = λ_.to_nalgebra_mixed();
98 let τ = τ_.to_nalgebra_mixed(); 100 let τ = τ_.to_nalgebra_mixed();
99 let τλ = τ * λ; 101 let τλ = τ * λ;
100 let mut v = DVector::zeros(x.len()); 102 let mut v = DVector::zeros(x.len());
101 let mut iters = 0; 103 let mut iters = 0;
104
105 assert!(λ > 0.0);
102 106
103 iterator.iterate(|state| { 107 iterator.iterate(|state| {
104 // Replace `x` with $x - τ[Ax-g]= [x + τg]- τAx$ 108 // Replace `x` with $x - τ[Ax-g]= [x + τg]- τAx$
105 v.copy_from(g); // v = g 109 v.copy_from(g); // v = g
106 v.axpy(1.0, x, τ); // v = x + τ*g 110 v.axpy(1.0, x, τ); // v = x + τ*g
216 let τλ = τ * λ; 220 let τλ = τ * λ;
217 let mut inact : Vec<bool> = Vec::from_iter(std::iter::repeat(false).take(n)); 221 let mut inact : Vec<bool> = Vec::from_iter(std::iter::repeat(false).take(n));
218 let mut s = DVector::zeros(0); 222 let mut s = DVector::zeros(0);
219 let mut decomp = nalgebra::linalg::LU::new(DMatrix::zeros(0, 0)); 223 let mut decomp = nalgebra::linalg::LU::new(DMatrix::zeros(0, 0));
220 let mut iters = 0; 224 let mut iters = 0;
225
226 assert!(λ > 0.0);
221 227
222 let res = iterator.iterate_fallible(|state| { 228 let res = iterator.iterate_fallible(|state| {
223 // 1. Perform delayed SSN-update based on previously computed step on active 229 // 1. Perform delayed SSN-update based on previously computed step on active
224 // coordinates. The step is delayed to the beginning of the loop because 230 // coordinates. The step is delayed to the beginning of the loop because
225 // the SSN step may violate constraints, so we arrange `x` to contain at the 231 // the SSN step may violate constraints, so we arrange `x` to contain at the
369 let ins = InnerSettings::<F>::default(); 375 let ins = InnerSettings::<F>::default();
370 quadratic_nonneg_fb(mA, g, λ, x, τ, ins.iterator_options) 376 quadratic_nonneg_fb(mA, g, λ, x, τ, ins.iterator_options)
371 }) 377 })
372 } 378 }
373 } 379 }
380
381 /// PDPSimplementation of [`l1_nonneg`].
382 /// For detailed documentation of the inputs and outputs, refer to there.
383 #[replace_float_literals(F::cast_from(literal))]
384 pub fn l1_nonneg_pdps<F, I, A, Y>(
385 mA : &A,
386 b : &Y,
387 λ : F,
388 x : &mut DVector<F>,
389 τ : F,
390 σ : F,
391 iterator : I
392 ) -> usize
393 where F : Float + ToNalgebraRealField,
394 I : AlgIteratorFactory<F>,
395 Y : Euclidean<F, Output = Y> + Projection<F, Linfinity> + AXPY<F>,
396 A : GEMV<F, DVector<F>, Codomain=Y>
397 + Adjointable<DVector<F>, Y>,
398 for<'a> A::Adjoint<'a> : GEMV<F, Y, Codomain=DVector<F>>
399 {
400 let mAt = mA.adjoint();
401 let mut xprev = x.clone();
402 let τλ = τ * λ;
403 let mut v = DVector::zeros(x.len());
404 let mut y = b.similar_origin();
405 let mut iters = 0;
406
407 assert!(λ > 0.0);
408
409 iterator.iterate(|state| {
410 // Replace `x` with $x - τA^*y
411 xprev.copy_from(x);
412 mAt.gemv(x, -τ, &y, 1.0);
413 // Calculate the proximal map
414 x.iter_mut().for_each(|x_i| *x_i = nonneg_soft_thresholding(*x_i, τλ));
415 // Calculate v = 2x - xprev
416 v.copy_from(x);
417 v.axpy(-1.0, &xprev, 2.0);
418 // Calculate y = y + σ(A v - b)
419 y.axpy(-σ, b, 1.0);
420 mA.gemv(&mut y, σ, &v, 1.0);
421 y.proj_ball_mut(1.0, Linfinity);
422
423 iters +=1;
424
425 state.if_verbose(|| {
426 // The subdifferential of the objective is $A^* ∂\|.\|(Ax - g) + λ + ∂δ_{≥ 0}(x)$.
427 // We return the minimal ∞-norm over all subderivatives.
428 v.copy_from(b); // d = g
429 mA.gemv(&mut ỹ, 1.0, x, -1.0); // d = Ax - b
430 let mut val = 0.0;
431 izip!(ỹ.iter(), x.iter()).for_each(|(&ỹ_i, &x_i)| {
432 use std::cmp::Ordering::*;
433 let tmp = match ỹ_i.partial_cmp(&0.0) {
434 None => F::INFINITY,
435 Some(c) => match (c, x_i > 0.0) {
436 (Greater, true) => 1.0 + λ,
437 (Greater, false) | (Equal, false) => 0.0,
438 (Less, true) => -1.0 + λ,
439 (Less, false) => 0.0.min(-1.0 + λ),
440 (Equal, true) => 0.0.max(-1.0 + λ), // since λ > 0
441 }
442 };
443 val = val.max(tmp);
444 });
445 val
446 })
447 });
448
449 iters
450 }
451
452 /// Method for L1 weight optimisation
453 #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)]
454 #[allow(dead_code)]
455 pub enum L1InnerMethod {
456 /// PDPS
457 PDPS,
458 }
459
460 /// This function applies an iterative method for the solution of the L1 non-negativity
461 /// constrained problem
462 /// <div>$$
463 /// \min_{x ∈ ℝ^n} \|Ax - b\|_1 + λ{\vec 1}^⊤ x + δ_{≥ 0}(x).
464 /// $$</div>
465 ///
466 /// This function returns the number of iterations taken.
467 pub fn l1_nonneg<F, I>(
468 method : L1InnerMethod,
469 mA : &DMatrix<F::MixedType>,
470 b : &DVector<F::MixedType>,
471 λ : F,
472 σ : F,
473 x : &mut DVector<F::MixedType>,
474 τ : F,
475 iterator : I
476 ) -> usize
477 where F : Float + ToNalgebraRealField,
478 I : AlgIteratorFactory<F> {
479
480 match method {
481 L1InnerMethod::PDPS => l1_nonneg_pdps(mA, b, λ, x, τ, σ, iterator)
482 }
483 }

mercurial