pub struct DiffHandoff {
pub x: Vec<Number>,
pub obj_val: Number,
pub lambda: Vec<Number>,
pub mult_x_lower: Vec<Number>,
pub mult_x_upper: Vec<Number>,
pub active_constraints: Vec<bool>,
pub pinned_vars: Vec<bool>,
pub active_tol: Number,
}Expand description
Everything the implicit-function-theorem backward pass needs from a converged solve, in a solver-agnostic shape.
Producers (IPM-NLP, convex LP/QP, conic, and — for discopt — the
fixed-integer leaf of a branch-and-bound) emit this; consumers
(pounce.jax, pounce.torch, the C ABI, a future Rust autodiff
user, discopt across the solve_nlp seam) differentiate from it.
The multiplier sign / length conventions match the existing C ABI and
Python info dict (mult_g, mult_x_L, mult_x_U), so this is a
re-shape of data POUNCE already returns — not a new computation — plus
the precomputed active-set masks, which are the genuinely new part.
Fields§
§x: Vec<Number>Final primal iterate x* (length n_x).
obj_val: NumberObjective value f(x*).
lambda: Vec<Number>General-constraint multipliers λ (length n_g). The g/G/A
duals, depending on the solver; one name across all of them.
mult_x_lower: Vec<Number>Variable lower-bound multipliers z_L (length n_x).
mult_x_upper: Vec<Number>Variable upper-bound multipliers z_U (length n_x).
active_constraints: Vec<bool>Constraint rows in the differentiated KKT block: equalities
(always) plus inequalities whose |λ| > active_tol. Length n_g.
Inactive (slack) rows drop out of the backward block.
pinned_vars: Vec<bool>Variables pinned in the backward (dx/dp = 0): those with an
active bound (max(z_L, z_U) > active_tol) and — for a B&B leaf —
integer variables fixed at the optimum (see Self::pin).
Length n_x.
active_tol: NumberThe activity tolerance used to derive the masks above. Recorded so consumers and tests see the exact threshold.
Implementations§
Source§impl DiffHandoff
impl DiffHandoff
Sourcepub fn from_solution(
x: Vec<Number>,
obj_val: Number,
lambda: Vec<Number>,
mult_x_lower: Vec<Number>,
mult_x_upper: Vec<Number>,
equality_mask: &[bool],
active_tol: Number,
) -> Self
pub fn from_solution( x: Vec<Number>, obj_val: Number, lambda: Vec<Number>, mult_x_lower: Vec<Number>, mult_x_upper: Vec<Number>, equality_mask: &[bool], active_tol: Number, ) -> Self
Build a handoff from the raw converged solution and duals,
deriving the active-set masks with active_tol.
equality_mask[i] is true when constraint i is an equality
(g_l[i] == g_u[i]) — such rows are always active. Pass an empty
slice when there are no general constraints.
Sourcepub fn masks(
mult_x_lower: &[Number],
mult_x_upper: &[Number],
lambda: &[Number],
equality_mask: &[bool],
active_tol: Number,
) -> (Vec<bool>, Vec<bool>)
pub fn masks( mult_x_lower: &[Number], mult_x_upper: &[Number], lambda: &[Number], equality_mask: &[bool], active_tol: Number, ) -> (Vec<bool>, Vec<bool>)
Derive the active-set masks (pinned_vars, active_constraints) from
borrowed duals — the single active-set derivation, shared by
Self::from_solution and producers that want only the masks (e.g.
the Python info dict) without surrendering the solution vectors.
Keeping the rule here means “|mult| > active_tol, equalities always
active” lives in exactly one place.
pinned_vars[i] is true when variable i’s lower- or upper-bound
multiplier exceeds active_tol. active_constraints[i] is true for
an equality row (equality_mask[i]) or one whose |lambda[i]| > active_tol. equality_mask may be empty (no equalities known) or
length lambda.len().
Sourcepub fn from_sens_result(
res: &SensResult,
equality_mask: &[bool],
) -> Option<Self>
pub fn from_sens_result( res: &SensResult, equality_mask: &[bool], ) -> Option<Self>
Re-shape a SensResult from a converged solve into a
DiffHandoff, using DEFAULT_ACTIVE_TOL.
Returns None when the solve did not populate the duals
(mult_g / mult_x_l / mult_x_u) — i.e. it didn’t converge, or
the NLP didn’t expose user-space multipliers.
equality_mask is the caller’s g_l[i] == g_u[i] test, length
n_g. Pass the real mask whenever the problem has equality
constraints. Equality rows are always part of the differentiated
KKT block regardless of multiplier magnitude, and the mask is the
only way from_sens_result learns which rows those are — a
SensResult carries the constraint values (g) but not their
[g_l, g_u] bounds, so equalities can’t be recovered from it.
An empty slice means “no equality information”: a row then counts as
active only when |λ| > active_tol. That is correct only when
the problem has no equalities. ⚠ With equalities present it silently
drops any degenerate equality — one whose multiplier is ≈ 0
(redundant rows, or an equality not binding the optimum’s curvature)
— from the active set, yielding a wrong backward block and wrong
gradients. Dropping a row is the unsafe direction, so the empty
slice is a convenience for the no-equality case, not a safe default.
Sourcepub fn pin(&mut self, indices: &[Index])
pub fn pin(&mut self, indices: &[Index])
Additionally pin a set of variables — the seam discopt uses for a
branch-and-bound leaf: integer variables fixed at the optimum
differentiate exactly like active bounds (dx/dp = 0). Indices
out of range are ignored.
Sourcepub fn n_active_constraints(&self) -> usize
pub fn n_active_constraints(&self) -> usize
Count of active constraint rows.
Trait Implementations§
Source§impl Clone for DiffHandoff
impl Clone for DiffHandoff
Source§fn clone(&self) -> DiffHandoff
fn clone(&self) -> DiffHandoff
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreAuto Trait Implementations§
impl Freeze for DiffHandoff
impl RefUnwindSafe for DiffHandoff
impl Send for DiffHandoff
impl Sync for DiffHandoff
impl Unpin for DiffHandoff
impl UnsafeUnpin for DiffHandoff
impl UnwindSafe for DiffHandoff
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T, U> Imply<T> for U
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more