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)] |
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 } |