Skip to main content

pounce_algorithm/mu/
mod.rs

1//! Barrier-parameter update strategies — port of
2//! `Algorithm/IpMuUpdate.hpp`, `IpMonotoneMuUpdate.{hpp,cpp}`,
3//! `IpAdaptiveMuUpdate.{hpp,cpp}`, and the four oracle files
4//! (`IpMuOracle.hpp`, `IpLoqoMuOracle.cpp`, `IpProbingMuOracle.cpp`,
5//! `IpQualityFunctionMuOracle.cpp`).
6//!
7//! Phase 7 ships [`monotone::MonotoneMuUpdate`] (Fiacco-McCormick).
8//! Phase 10 adds the adaptive path and all four oracles.
9
10pub mod adaptive;
11pub mod monotone;
12pub mod oracle;
13pub mod r#trait;
14
15pub use r#trait::MuUpdate;
16
17use pounce_common::types::Number;
18
19/// `compl_inf_tol` expressed in the **internally scaled** space that μ lives
20/// in (pounce#257). Shared by both μ strategies; see
21/// [`monotone::MonotoneMuUpdate::scaled_compl_inf_tol`] for the full story.
22///
23/// `compl_inf_tol` is enforced on the **unscaled** complementarity, which is
24/// the scaled complementarity divided by the objective scaling factor — so in
25/// scaled units the tolerance is `compl_inf_tol · |df|`. The factor is signed
26/// (`obj_scaling_factor = -1` poses a maximization), so take its magnitude,
27/// and fall back to the unconverted tolerance when it is absent or degenerate.
28pub(crate) fn scaled_compl_inf_tol(compl_inf_tol: Number, obj_scaling_factor: Number) -> Number {
29    let df = obj_scaling_factor.abs();
30    if df.is_finite() && df > 0.0 {
31        compl_inf_tol * df
32    } else {
33        compl_inf_tol
34    }
35}
36
37/// `mu_min` capped so it can never block the termination certificate
38/// (pounce#266). Shared by both μ strategies; see
39/// [`monotone::MonotoneMuUpdate::certificate_safe_mu_min`] for the full story.
40///
41/// `mu_min` is an absolute constant in μ's scaled space. Left raw, once
42/// `compl_inf_tol·|df|/(barrier_tol_factor+1) < mu_min` the unscaled
43/// complementarity is pinned at `mu_min/|df| > compl_inf_tol` and the strict
44/// certificate becomes unreachable. The cap keeps `mu_min` inert exactly when
45/// it would cost the certificate, with the same headroom the monotone dynamic
46/// floor reserves. A floor that is too low only costs iterations; one that is
47/// too high costs the certificate.
48pub(crate) fn certificate_safe_mu_min(
49    mu_min: Number,
50    compl_inf_tol: Number,
51    barrier_tol_factor: Number,
52    obj_scaling_factor: Number,
53) -> Number {
54    mu_min.min(scaled_compl_inf_tol(compl_inf_tol, obj_scaling_factor) / (barrier_tol_factor + 1.0))
55}