Skip to main content

pounce_sens_core/
lib.rs

1//! Engine-agnostic core of POUNCE's sensitivity layer.
2//!
3//! This crate holds the parts of the sIPOPT port that do not know which
4//! solver produced the KKT system they are reading. Everything here is
5//! written against one trait — [`backsolver::SensBacksolver`], whose whole
6//! required surface is `dim()` and `solve(rhs, lhs)` — so any engine that can
7//! back-solve against its converged factor gets the parametric machinery for
8//! free:
9//!
10//! * [`boundcheck`] — fix-relax refinement, path following through
11//!   active-set breakpoints, and the directional derivative at a kink.
12//! * [`rowlimit`] — an observer block that gives a limit written as a
13//!   *constraint row* the primal coordinate `boundcheck` decides in,
14//!   for engines whose KKT has no slack block (gh#929).
15//! * [`sens_app`] — the sIPOPT `SensApplication` driver, the reduced-Hessian
16//!   entry point, and the option registrations.
17//! * [`p_calculator`], [`schur_data`], [`schur_driver`], [`step_calc`],
18//!   [`reduced_hessian`] — the `P = K⁻¹A` / Schur-complement stack.
19//!
20//! Two consumers exist in tree. `pounce-sensitivity` implements the trait over
21//! the NLP filter-IPM's KKT factor (`PdSensBacksolver`); `pounce-convex`
22//! implements it over the convex active-set KKT. Neither depends on the other,
23//! and this crate depends on neither — it needs only `pounce-common` and
24//! `pounce-linalg`.
25//!
26//! # What deliberately did not move here
27//!
28//! Two parts of the NLP arm's sensitivity layer are genuinely engine-coupled
29//! and stay in `pounce-sensitivity`, so this boundary is a decision rather
30//! than an oversight:
31//!
32//! * **The corrector.** Its entry points take the concrete `PdSensBacksolver`
33//!   and reach for `activity_handles()`, `offsets_public()`, `block_dims()`,
34//!   `pack_natural()` and `corrector_sigma()` — none of which are on the
35//!   trait, and several of which only mean anything for the filter-IPM's
36//!   eight-block compound iterate.
37//! * **Activity classification's plumbing.** It reads the filter-IPM's own
38//!   iterate (`z_l`, `z_u`, `v_l`, `v_u`) out of an `IpoptData` handle. Its
39//!   pure decision rule is portable and is expected to land here later; the
40//!   plumbing around it is not.
41//!
42//! Generalizing either would mean abstracting `IpoptData` / `CalculatedQuantities`
43//! access behind another trait, which is a larger project than this crate.
44//!
45//! # Provenance
46//!
47//! Port of upstream Ipopt's `contrib/sIPOPT/` (Pirnay, López-Negrete &
48//! Biegler 2012, DOI [10.1007/s12532-012-0043-2]). The module names mirror
49//! upstream's file names so the two can be read side by side.
50//!
51//! [10.1007/s12532-012-0043-2]: https://doi.org/10.1007/s12532-012-0043-2
52
53#![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used))]
54
55pub mod activity_kernel;
56pub mod backsolver;
57pub mod boundcheck;
58pub mod p_calculator;
59pub mod reduced_hessian;
60pub mod rowlimit;
61pub mod schur_data;
62pub mod schur_driver;
63pub mod sens_app;
64pub mod step_calc;
65
66// Root re-exports. These are not a convenience: several moved modules refer to
67// each other through the crate root (`&dyn crate::SchurData` appears in
68// `schur_driver`'s public trait signature), so removing one of these breaks
69// compilation rather than merely lengthening a path. `pounce-sensitivity`
70// re-exports the same names, which is what keeps its published API unchanged
71// across this extraction.
72pub use backsolver::{DenseLuBacksolver, SensBacksolver};
73pub use p_calculator::{IndexPCalculator, PCalculator};
74pub use reduced_hessian::compute_reduced_hessian;
75pub use schur_data::{IndexSchurData, SchurData};
76pub use schur_driver::{DenseGenSchurDriver, SchurDriver};
77pub use sens_app::{SensApplication, SensOptions, register_options};
78pub use step_calc::{SensStepCalc, StdStepCalc, WithBacksolver};