1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
//! NLP sensitivity, parametric warm starts, and reduced Hessians — the
//! `pounce-sensitivity` port of Ipopt's sIPOPT, re-exported (feature
//! `sensitivity`).
//!
//! ```toml
//! [dependencies]
//! pounce-rs = { version = "0.9", features = ["sensitivity"] }
//! ```
//!
//! The problem shape sIPOPT assumes: the TNLP declares equality constraints
//! of the form `g_i(x) − p_i = 0`, and the 0-based row indices `i` of those
//! constraints are the *pins*. [`SensSolve`] solves the NLP once and then,
//! from the converged KKT factor, returns a first-order predictor
//! `Δx ≈ (∂x*/∂p)·Δp` for a perturbation of the pinned parameters — no
//! re-solve, one Schur complement.
//!
//! ```no_run
//! use pounce_rs::prelude::*;
//! use pounce_rs::sensitivity::SensSolve;
//! use std::cell::RefCell;
//! use std::rc::Rc;
//!
//! # fn demo(tnlp: Rc<RefCell<dyn TNLP>>) {
//! let mut app = IpoptApplication::new();
//! app.initialize().unwrap();
//!
//! let result = SensSolve::new(vec![2, 3]) // pinned constraint rows
//! .with_deltas(vec![-0.5, 0.0]) // Δp
//! .with_reduced_hessian()
//! .run(&mut app, tnlp);
//!
//! assert_eq!(result.status, ApplicationReturnStatus::SolveSucceeded);
//! let dx = result.dx.expect("Δx populated when with_deltas was set");
//! # }
//! ```
//!
//! A sensitivity-stage failure is reported through [`SensResult::error`], not
//! through `status` — the underlying solve can converge while the post-solve
//! step fails (a pin that isn't an equality, a Schur setup error). Both
//! "sensitivity failed" and "sensitivity not requested" leave the outputs
//! `None`, so `error` is the only signal that separates them.
//!
//! [`compute_reduced_hessian`] gives the curvature on the null space of the
//! active constraints directly, and
//! [`SensSolve::with_reduced_hessian_eigen`] adds its eigendecomposition via
//! the shared [`symmetric_eigen`]. Over parameter-pin rows both report
//! **`−H_R`**, not `H_R` — so the ascending spectrum runs stiffest-first, and
//! an identifiability read has to negate. gh#937;
//! [`SensResult::reduced_hessian`] carries the reasoning.
//!
//! For the long form — [`SensApplication`] driven by an explicit
//! [`IndexSchurData`] / [`PdSensBacksolver`] / [`DenseGenSchurDriver`] stack —
//! every piece is re-exported below, and [`pounce_sensitivity`] itself is
//! re-exported for anything not listed.
//!
//! # The diagnostics, not just the drivers
//!
//! The facade used to name only the things you *call* — [`SensSolve`],
//! [`Solver`], the Schur stack — and none of the things they *return*. A
//! caller could reach `Solver::classify_activity` through the facade and then
//! had to name its result type through `pounce_sensitivity` directly, which is
//! the dependency the facade exists to remove. Every result and diagnostic
//! type on the session API is re-exported here now:
//!
//! * [`ActivityReport`] — what each bound is doing at the optimum. Read
//! [`pounce_sensitivity::activity`]'s note on `AMBIGUOUS` before acting on a
//! status: it is not "probably not a kink", and the reduced-curvature
//! accessors ([`Solver::reduced_activity`], [`Solver::reduced_row_activity`])
//! are what settle it.
//! * [`RefineStop`] — why `mode = fix_relax` stopped refining;
//! [`PathSegment`] — one leg of a path-following step;
//! [`WeakBound`] / [`BoundMultiplier`] / [`BoundRow`] — the bound-geometry
//! records `boundcheck` reads and returns.
//! * [`CorrectorReport`] — what the corrector's iterations did, including the
//! residual split that says whether they helped.
//! * [`SensOptionOverrides`] — the option layer between an `OptionsList` and
//! the step.
//! * [`VarX`] / [`FullX`] / [`VarToFull`] / [`FullXSlice`] — the index
//! newtypes. `FullX` has no public constructor by design: it is obtainable
//! only by putting a `VarX` through a `VarToFull`, so "this number is in
//! full-x" is asserted once per map rather than once per read.
pub use ;
/// Result and diagnostic types from the session API.
pub use ActivityReport;
pub use BoundRow;
pub use ;
pub use CorrectorReport;
pub use ;
pub use SensOptionOverrides;
/// The underlying crate, for anything not surfaced above.
pub use pounce_sensitivity;