pounce_sensitivity/lib.rs
1//! Sensitivity analysis for POUNCE — port of upstream Ipopt's `contrib/sIPOPT/`.
2//!
3//! # Status
4//!
5//! Phases A–C complete. Wired today:
6//!
7//! * [`schur_data::IndexSchurData`] + [`p_calculator::IndexPCalculator`]:
8//! row-selector representation of the perturbation matrix `B`.
9//! * [`backsolver::DenseLuBacksolver`] + [`PdSensBacksolver`]: backsolves
10//! against the converged KKT factor (test / live IPM, respectively).
11//! * [`schur_driver::DenseGenSchurDriver`]: dense Schur-complement
12//! factor `S = -B K⁻¹ Bᵀ` with parallel right-hand-side solves.
13//! * [`step_calc::StdStepCalc`] + [`sens_app::SensApplication`]:
14//! high-level `parametric_step(Δp, dx)` and
15//! [`reduced_hessian::compute_reduced_hessian`] entry points.
16//! * [`SensSolve`] / [`SensResult`]: one-call builder (covers the
17//! `on_converged` plumbing typically required to wire the above into
18//! an `IpoptApplication`).
19//!
20//! Verified against upstream sIPOPT 3.14.19's `parametric_cpp` golden
21//! output to 1e-8 (see `tests/parametric_cpp.rs`); the standalone
22//! `pounce_sens` AMPL driver in `pounce-cli` matches `sensitivity_amplsolver`'s
23//! `_sens_sol` output on representative .nl problems.
24//!
25//! **Phase D progress** (per [pounce#7](https://github.com/jkitchin/pounce/issues/7)):
26//!
27//! * **Fixed-variable lifting** ✔ — `pounce_sens` handles `n_x != n_full`
28//! via the `IpoptNlp::full_x_to_var_x` / `var_x_to_full_x` /
29//! `full_g_to_c_block` trait methods (which delegate to
30//! `BoundClassification.x_not_fixed_map` / `c_map`).
31//! * **Reduced-Hessian eigendecomposition** ✔ — pure-Rust cyclic Jacobi
32//! in [`pounce_linalg::symmetric_eigen`] (shared with the convex QP
33//! sensitivity path); surfaced via
34//! [`SensApplication::compute_reduced_hessian_eigen`],
35//! [`SensSolve::with_reduced_hessian_eigen`], the `pounce_sens
36//! --rh-eigendecomp` flag, and the Python `solve_with_sens(rh_eigendecomp=True)`
37//! kwarg.
38//! * **`sens_boundcheck` bound projection** ✔ (single-pass clamp) —
39//! [`boundcheck::clamp_step_to_bounds`] /
40//! [`boundcheck::clamp_with_nlp`] project the perturbed step onto
41//! `[x_l, x_u]` after the linear solve. Surfaced via
42//! [`SensSolve::with_boundcheck`], `pounce_sens --sens-boundcheck`,
43//! and the Python `solve_with_sens(sens_boundcheck=True)` kwarg.
44//! Upstream's iterative Schur refinement (re-factorize on each
45//! violation) is **not** ported — see [`boundcheck`] module docs.
46//!
47//! # Algorithmic reference
48//!
49//! Pirnay, H., López-Negrete, R., and Biegler, L.T. (2012).
50//! *Optimal sensitivity based on IPOPT.*
51//! Mathematical Programming Computation, **4**(4), 307–331.
52//! [DOI: 10.1007/s12532-012-0043-2](https://doi.org/10.1007/s12532-012-0043-2).
53//!
54//! Verified 2026-05-14 via Crossref: title, authors (Hans Pirnay; Rodrigo
55//! López-Negrete; Lorenz T. Biegler), MPC volume 4 issue 4 pp 307–331.
56//!
57//! # Upstream source mirror
58//!
59//! Port targets `ref/Ipopt/contrib/sIPOPT/src/` in this repo
60//! (EPL-2.0, © Hans Pirnay 2009–2011 per the file headers). Each
61//! public item in this crate documents the upstream symbol it mirrors
62//! with file path and (where stable) line numbers.
63
64#![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used))]
65
66pub mod algorithm_backsolver;
67pub mod backsolver;
68pub mod boundcheck;
69pub mod convenience;
70pub mod diff_handoff;
71pub mod p_calculator;
72pub mod reduced_hessian;
73pub mod schur_data;
74pub mod schur_driver;
75pub mod sens_app;
76pub mod solver;
77pub mod step_calc;
78mod vec_util;
79
80pub use algorithm_backsolver::PdSensBacksolver;
81pub use backsolver::{DenseLuBacksolver, SensBacksolver};
82pub use convenience::{SensResult, SensSolve};
83pub use diff_handoff::{DiffHandoff, DEFAULT_ACTIVE_TOL};
84// Hoisted to pounce-linalg so the convex QP sensitivity path can share it;
85// re-exported here to preserve `pounce_sensitivity::symmetric_eigen`.
86pub use p_calculator::{IndexPCalculator, PCalculator};
87pub use pounce_linalg::symmetric_eigen;
88pub use reduced_hessian::compute_reduced_hessian;
89pub use schur_data::{IndexSchurData, SchurData};
90pub use schur_driver::{DenseGenSchurDriver, SchurDriver};
91pub use sens_app::{register_options, SensApplication, SensOptions};
92pub use solver::{ConvergedState, Solver, SolverError};
93pub use step_calc::{SensStepCalc, StdStepCalc, WithBacksolver};