src/subproblem/nonneg.rs

branch
dev
changeset 34
efa60bc4f743
parent 24
d29d1fcf5423
child 39
6316d68b58af
--- a/src/subproblem/nonneg.rs	Tue Aug 01 10:32:12 2023 +0300
+++ b/src/subproblem/nonneg.rs	Thu Aug 29 00:00:00 2024 -0500
@@ -6,6 +6,7 @@
 use numeric_literals::replace_float_literals;
 use itertools::{izip, Itertools};
 use colored::Colorize;
+use std::cmp::Ordering::*;
 
 use alg_tools::iter::Mappable;
 use alg_tools::error::NumericalError;
@@ -22,38 +23,42 @@
     InnerMethod,
     InnerSettings
 };
+use super::l1squared_nonneg::max_interval_dist_to_zero;
 
 /// Compute the proximal operator of $x \mapsto x + \delta\_{[0, \infty)}$, i.e.,
 /// the non-negativity contrained soft-thresholding operator.
 #[inline]
 #[replace_float_literals(F::cast_from(literal))]
-fn nonneg_soft_thresholding<F : Float>(v : F, λ : F) -> F {
+pub(super) fn nonneg_soft_thresholding<F : Float>(v : F, λ : F) -> F {
     (v - λ).max(0.0)
 }
 
-/// Returns the ∞-norm minimal subdifferential of $x ↦ x^⊤Ax - g^⊤ x + λ\vec 1^⊤ x δ_{≥ 0}(x)$
+/// Returns the ∞-norm minimal subdifferential of $x ↦ x^⊤Ax/2 - g^⊤ x + λ\vec 1^⊤ x + δ_{≥ 0}(x)$
 /// at $x$.
 ///
 /// `v` will be modified and cannot be trusted to contain useful values afterwards.
-#[replace_float_literals(F::cast_from(literal).to_nalgebra_mixed())]
-fn min_subdifferential<F : Float + ToNalgebraRealField>(
-    v : &mut DVector<F::MixedType>,
-    mA : &DMatrix<F::MixedType>,
-    x : &DVector<F::MixedType>,
-    g : &DVector<F::MixedType>,
-    λ : F::MixedType
+#[replace_float_literals(F::cast_from(literal))]
+fn min_subdifferential<F : Float + nalgebra::RealField>(
+    v : &mut DVector<F>,
+    mA : &DMatrix<F>,
+    x : &DVector<F>,
+    g : &DVector<F>,
+    λ : F
 ) -> F {
     v.copy_from(g);
     mA.gemv(v, 1.0, x, -1.0);   // v =  Ax - g
     let mut val = 0.0;
     for (&v_i, &x_i) in izip!(v.iter(), x.iter()) {
-        // The subdifferential of the objective is $Ax - g + λ + ∂ δ_{≥ 0}(x)$.
-        let d = v_i + λ;
-        if x_i > 0.0 || d < 0.0 {
-            val = val.max(d.abs());
+        let (mut lb, mut ub) = (v_i, v_i);
+        match x_i.partial_cmp(&0.0) {
+            Some(Greater) => { lb += λ; ub += λ },
+            // Less should not happen
+            Some(Less|Equal) => { lb = F::NEG_INFINITY; ub += λ },
+            None => {},
         }
+        val = max_interval_dist_to_zero(val, lb, ub);
     }
-    F::from_nalgebra_mixed(val)
+    val
 }
 
 /// Forward-backward splitting implementation of [`quadratic_nonneg`].
@@ -98,7 +103,7 @@
         iters +=1;
 
         backup.map(|_| {
-            min_subdifferential(&mut v, mA, x, g, λ)
+            F::from_nalgebra_mixed(min_subdifferential(&mut v, mA, x, g, λ))
         })
     });
 
@@ -281,7 +286,7 @@
         // 4. Report solution quality
         state.if_verbose(|| {
             // Calculate subdifferential at the FB step `x` that hasn't yet had `s` yet added.
-            min_subdifferential(&mut v, mA, x, g, λ)
+            F::from_nalgebra_mixed(min_subdifferential(&mut v, mA, x, g, λ))
         })
     });
 
@@ -291,7 +296,7 @@
 /// This function applies an iterative method for the solution of the quadratic non-negativity
 /// constrained problem
 /// <div>$$
-///     \min_{x ∈ ℝ^n} \frac{1}{2} x^⊤Ax - g^⊤ x + λ{\vec 1}^⊤ x + c + δ_{≥ 0}(x).
+///     \min_{x ∈ ℝ^n} \frac{1}{2} x^⊤Ax - g^⊤ x + λ{\vec 1}^⊤ x + δ_{≥ 0}(x).
 /// $$</div>
 /// Semismooth Newton or forward-backward are supported based on the setting in `method`.
 /// The parameter `mA` is matrix $A$, and `g` and `λ` are as in the mathematical formulation.
@@ -307,27 +312,28 @@
 ///
 /// This function returns the number of iterations taken.
 pub fn quadratic_nonneg<F, I>(
-    method : InnerMethod,
     mA : &DMatrix<F::MixedType>,
     g : &DVector<F::MixedType>,
-    //c_ : F,
     λ : F,
     x : &mut DVector<F::MixedType>,
-    τ : F,
+    mA_normest : F,
+    inner : &InnerSettings<F>,
     iterator : I
 ) -> usize
 where F : Float + ToNalgebraRealField,
       I : AlgIteratorFactory<F>
 {
-    
-    match method {
-        InnerMethod::FB =>
-            quadratic_nonneg_fb(mA, g, λ, x, τ, iterator),
+    let inner_τ = inner.fb_τ0 / mA_normest;
+
+    match inner.method {
+        InnerMethod::FB | InnerMethod::Default =>
+            quadratic_nonneg_fb(mA, g, λ, x, inner_τ, iterator),
         InnerMethod::SSN =>
-            quadratic_nonneg_ssn(mA, g, λ, x, τ, iterator).unwrap_or_else(|e| {
+            quadratic_nonneg_ssn(mA, g, λ, x, inner_τ, iterator).unwrap_or_else(|e| {
                 println!("{}", format!("{e}. Using FB fallback.").red());
                 let ins = InnerSettings::<F>::default();
-                quadratic_nonneg_fb(mA, g, λ, x, τ, ins.iterator_options)
-            })
+                quadratic_nonneg_fb(mA, g, λ, x, inner_τ, ins.iterator_options)
+            }),
+        InnerMethod::PDPS => unimplemented!(),
     }
 }

mercurial