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