pub struct IpoptCalculatedQuantities {
pub s_max: Number,
pub kappa_d: Number,
pub slack_move: Number,
/* private fields */
}Expand description
Calculated-quantities object. Holds shared handles on data and the
NLP; per-quantity caches live in RefCells here.
Fields§
§s_max: NumberOptimality scaling cap from IpOptErrorConvCheck defaults.
kappa_d: NumberDamping coefficient for the bound-multiplier complementarity
term (kappa_d in upstream’s RegisterOptions).
slack_move: NumberCorrection size for very small slacks (slack_move option,
default mach_eps^{3/4}). Drives calculate_safe_slack’s
upper cap on the moved bound — port of upstream’s slack_move_
(IpIpoptCalculatedQuantities.cpp:525).
Implementations§
Source§impl IpoptCalculatedQuantities
impl IpoptCalculatedQuantities
pub fn new(data: IpoptDataHandle, nlp: Rc<RefCell<dyn IpoptNlp>>) -> Self
pub fn data(&self) -> &IpoptDataHandle
pub fn nlp(&self) -> &Rc<RefCell<dyn IpoptNlp>>
pub fn curr_slack_x_l(&self) -> Rc<dyn Vector>
pub fn curr_slack_x_u(&self) -> Rc<dyn Vector>
pub fn curr_slack_s_l(&self) -> Rc<dyn Vector>
pub fn curr_slack_s_u(&self) -> Rc<dyn Vector>
pub fn trial_slack_x_l(&self) -> Rc<dyn Vector>
pub fn trial_slack_x_u(&self) -> Rc<dyn Vector>
pub fn trial_slack_s_l(&self) -> Rc<dyn Vector>
pub fn trial_slack_s_u(&self) -> Rc<dyn Vector>
Sourcepub fn adjusted_trial_bounds(&self) -> Option<AdjustedBounds>
pub fn adjusted_trial_bounds(&self) -> Option<AdjustedBounds>
Compute the four trial slacks with safe-slack flooring and, if any
component was corrected, the adjusted variable bounds that make the
trial slacks exactly representable. Port of the bound-adjustment
block in IpoptAlgorithm::AcceptTrialPoint
(IpIpoptAlg.cpp:664-706): new_x_L = Px_L^T x - safe_slack_x_L,
new_x_U = Px_U^T x + safe_slack_x_U, likewise for s/d.
Returns None when no slack needed correcting.
pub fn curr_grad_f(&self) -> Rc<dyn Vector>
pub fn trial_grad_f(&self) -> Rc<dyn Vector>
pub fn curr_c(&self) -> Rc<dyn Vector>
pub fn trial_c(&self) -> Rc<dyn Vector>
pub fn curr_d(&self) -> Rc<dyn Vector>
pub fn trial_d(&self) -> Rc<dyn Vector>
pub fn curr_jac_c(&self) -> Rc<dyn Matrix>
pub fn curr_jac_d(&self) -> Rc<dyn Matrix>
pub fn curr_exact_hessian(&self) -> Rc<dyn SymMatrix>
Sourcepub fn curr_d_minus_s(&self) -> Rc<dyn Vector>
pub fn curr_d_minus_s(&self) -> Rc<dyn Vector>
curr_d - s — port of IpIpoptCalculatedQuantities.cpp:1185-1206.
pub fn trial_d_minus_s(&self) -> Rc<dyn Vector>
Sourcepub fn curr_jac_c_t_times_vec(&self, vec: &dyn Vector) -> Rc<dyn Vector>
pub fn curr_jac_c_t_times_vec(&self, vec: &dyn Vector) -> Rc<dyn Vector>
J_c^T y_c — for a generic vec argument
(IpIpoptCalculatedQuantities.cpp:1373-1404).
Sourcepub fn curr_jac_d_t_times_vec(&self, vec: &dyn Vector) -> Rc<dyn Vector>
pub fn curr_jac_d_t_times_vec(&self, vec: &dyn Vector) -> Rc<dyn Vector>
J_d^T y_d for arbitrary vec.
pub fn curr_jac_c_t_times_curr_y_c(&self) -> Rc<dyn Vector>
pub fn curr_jac_d_t_times_curr_y_d(&self) -> Rc<dyn Vector>
Sourcepub fn curr_jac_c_times_vec(&self, vec: &dyn Vector) -> Rc<dyn Vector>
pub fn curr_jac_c_times_vec(&self, vec: &dyn Vector) -> Rc<dyn Vector>
J_c v — IpIpoptCalculatedQuantities.cpp:1303-1321.
Sourcepub fn curr_jac_d_times_vec(&self, vec: &dyn Vector) -> Rc<dyn Vector>
pub fn curr_jac_d_times_vec(&self, vec: &dyn Vector) -> Rc<dyn Vector>
J_d v — IpIpoptCalculatedQuantities.cpp:1323-1343.
Sourcepub fn curr_grad_lag_x(&self) -> Rc<dyn Vector>
pub fn curr_grad_lag_x(&self) -> Rc<dyn Vector>
∇_x L = ∇f(x) + J_c^T y_c + J_d^T y_d - P_L z_L + P_U z_U
per IpIpoptCalculatedQuantities.cpp:1993-2030.
Sourcepub fn curr_grad_lag_s(&self) -> Rc<dyn Vector>
pub fn curr_grad_lag_s(&self) -> Rc<dyn Vector>
∇_s L = -y_d - P_L v_L + P_U v_U
(IpIpoptCalculatedQuantities.cpp:2069-2098).
pub fn curr_compl_x_l(&self) -> Rc<dyn Vector>
pub fn curr_compl_x_u(&self) -> Rc<dyn Vector>
pub fn curr_compl_s_l(&self) -> Rc<dyn Vector>
pub fn curr_compl_s_u(&self) -> Rc<dyn Vector>
Sourcepub fn curr_relaxed_compl_x_l(&self) -> Rc<dyn Vector>
pub fn curr_relaxed_compl_x_l(&self) -> Rc<dyn Vector>
s_L .* z_L - mu — relaxed complementarity used in the KKT
RHS. IpIpoptCalculatedQuantities.cpp:2406-2430.
pub fn curr_relaxed_compl_x_u(&self) -> Rc<dyn Vector>
pub fn curr_relaxed_compl_s_l(&self) -> Rc<dyn Vector>
pub fn curr_relaxed_compl_s_u(&self) -> Rc<dyn Vector>
pub fn curr_sigma_x(&self) -> Rc<dyn Vector>
pub fn curr_sigma_s(&self) -> Rc<dyn Vector>
pub fn curr_f(&self) -> Number
Sourcepub fn unscaled_curr_f(&self) -> Number
pub fn unscaled_curr_f(&self) -> Number
Unscaled objective at the current iterate. curr_f returns the
internally scaled value (f · df_); upstream IPOPT prints the
unscaled objective in its iteration log, so this divides the
scaling back out. Mirrors IpoptCalculatedQuantities:: unscaled_curr_f. A zero factor (scaling never determined) is
treated as the identity.
Sourcepub fn curr_unscaled_dual_infeasibility_max(&self) -> Number
pub fn curr_unscaled_dual_infeasibility_max(&self) -> Number
Max-norm dual infeasibility in the unscaled (user-original)
space. Self::curr_dual_infeasibility_max is evaluated in the
internally-scaled NLP space (objective × df, constraints × dc);
because POUNCE applies no variable scaling, every term of the
Lagrangian gradient ∇f + Jᵀy − z carries the same objective
factor df, so the unscaling is a single divide by
df = obj_scaling_factor. A zero or unit factor returns the scaled
value unchanged — the common no-scaling path stays division-free.
Sourcepub fn curr_unscaled_complementarity_max(&self) -> Number
pub fn curr_unscaled_complementarity_max(&self) -> Number
Max-norm complementarity in the unscaled space. Each bound block
s · z scales uniformly by df: the slack’s dc/dd factor and
the multiplier’s df/dc (df/dd) factor cancel in the product,
leaving df. So this is the scaled max-norm divided by df. See
Self::curr_unscaled_dual_infeasibility_max.
Sourcepub fn curr_unscaled_primal_infeasibility_max(&self) -> Number
pub fn curr_unscaled_primal_infeasibility_max(&self) -> Number
Max-norm primal infeasibility in the unscaled space. Unlike the
dual/complementarity terms the constraint scaling is per-row
(c_scaled = dc ⊙ c_user, (d−s)_scaled = dd ⊙ (d−s)_user), so each
block is unscaled element-by-element before the max-norm. When no row
scaling is active (c_scale_vec/d_scale_vec both None — the
common case) this is exactly Self::curr_primal_infeasibility_max.
Sourcepub fn curr_unscaled_nlp_constraint_violation_max(&self) -> Number
pub fn curr_unscaled_nlp_constraint_violation_max(&self) -> Number
Max-norm constraint violation of the original NLP, in user units:
|c_i| over the equality block and max(0, d_l_i − d_i, d_i − d_u_i)
over the inequality block. This is what upstream’s
inf_pr_output = original — its default — prints in the inf_pr
column, and what its end-of-run “Constraint violation” line reports.
Mirrors IpIpoptCalculatedQuantities.cpp:unscaled_curr_nlp_constraint_violation.
Deliberately not Self::curr_primal_infeasibility_max, which is
max(‖c‖_∞, ‖d − s‖_∞) — the violation of the internal slack
reformulation. The two diverge whenever the slack drifts from d(x):
s is confined to [d_l, d_u], so d = s + (d − s) with d − s > 0
clears a lower bound however large that gap grows. On a model that is
all inequalities the gap is the whole number — on Mittelmann’s
robot_a POUNCE reported 2.79e4 at an iterate where Ipopt reported
0.00e+00 and every original row was in fact satisfied (pounce#476).
Display only. The filter’s theta, the barrier-parameter strategies
and the convergence test all stay on the internal measure — that split
is upstream’s, not a shortcut.
Judged against the declared bounds where the NLP tracks them, so
the bound_relax_factor widening cannot forgive a violation the user
would still see (same reasoning as
Self::relative_d_infeasibility_max).
Sourcepub fn curr_relative_primal_infeasibility_max(&self) -> Number
pub fn curr_relative_primal_infeasibility_max(&self) -> Number
Largest primal infeasibility of a constraint row relative to that
row’s own magnitude — |c_i| / |b_i| over the equality block and
dist(d_i, [d_l_i, d_u_i]) / max(|d_l_i|, |d_u_i|) over the
inequality block, whichever is worse.
See Self::relative_d_infeasibility_max and
Self::relative_c_infeasibility_max for the two blocks; both use
the row’s declared magnitude, never a live or relaxed stand-in,
and both abstain (contribute nothing) on a row that has no declared
magnitude to be relative to.
Sourcepub fn relative_c_infeasibility_max(&self) -> Number
pub fn relative_c_infeasibility_max(&self) -> Number
The equality-block half of
Self::curr_relative_primal_infeasibility_max: max_i |c_i| / |b_i|,
where b_i is the row’s declared right-hand side
(IpoptNlp::declared_c_rhs).
POUNCE folds g_i(x) == b_i into c_i(x) = 0, so |c_i| is the
violation and by itself carries no magnitude to be judged against —
which is why every runtime feasibility decision on an equality row was
an absolute one, and why down-scaling such a row shrank |c_i| under
the absolute tolerance and flipped a true infeasibility verdict to
Solve_Succeeded (gh #390, residual of #387). Dividing by the pre-fold
RHS restores it: s·g(x) == s·b has residual s·(g(x) − b) and RHS
s·b, so the ratio is the same at every s — the point.
Both numerator and denominator are taken in the internally-scaled
space, so the solver’s own row scaling dc_i cancels too.
A homogeneous row (b_i = 0) contributes nothing. It has no
declared magnitude to be relative to, and needs none: s·g(x) == 0 is
the same row at every s, so the absolute test is already invariant
there. Dividing by zero — or by a fabricated floor — would turn
float-noise residuals into 100% “violations” on the single most common
equality row there is. Non-finite entries likewise contribute nothing:
an unjudgeable row must not fabricate a relative verdict. When the NLP
does not track the RHS at all (declared_c_rhs is None — e.g. the
restoration NLP, whose c block is not the user’s rows), the whole
block abstains.
“Homogeneous” is judged numerically, not by b_i == 0 exactly: a
row abstains once |b_i| sinks under its own
noise floor. An exact-zero test is the
right idea measured with the wrong instrument — a converter that emits
2^-53 where the model says 0 (Maros-Mészáros QSC*/QSCFXM*, and
every one of the 15 problems in gh #446, carry equality rows with an
RHS at 1e-17–1e-16) declares a magnitude that is pure rounding
residue — a target no iterate could be positioned finely enough to hit.
|c_i| cannot be driven below the same floor either, so the
ratio was noise over noise: QSCSD1 read 81× violated at a converged KKT
point whose absolute violation was 9.2e-15, which vetoed its success
certificate and then armed the rapid-infeasibility pre-filter — a
feasible convex QP reported Converged to a point of local infeasibility. Comparing against the row’s own noise floor keeps the
scale invariance that is the whole point of the measure (both sides
carry dc_i), which an absolute cutoff on |b_i| would have thrown
away.
Sourcepub fn curr_primal_infeasibility_above_noise(&self, kappa: Number) -> Number
pub fn curr_primal_infeasibility_above_noise(&self, kappa: Number) -> Number
Self::curr_primal_infeasibility_max — max(‖c‖_∞, ‖d − s‖_∞) —
counting each row only where its residual rises above the finest value
that residual can take in floating point (gh #528).
Both residuals are differences of quantities the row’s own size:
c_i = g_i(x) − b_i and d_i − s_i with s_i confined to d_i’s
bounds. A difference of doubles of magnitude m is quantised in units
of eps · m, so no iterate can place either residual strictly between
0 and eps · m_i — it lands on an exact 0 or on a multiple of the
quantum, and which of the two is arithmetic luck. Once eps · m_i
exceeds tol that luck decides whether a fully converged solve gets a
certificate: on gh #528’s LPs (|b| ~ 1e8, so one ulp is 1.5e-8
against the default tol = 1e-8) the KKT error was pinned one ulp
above the tolerance at the exact optimum, the solve kept iterating at a
point it could not improve, and it exited
Search_Direction_Becomes_Too_Small with the right answer in hand.
The floor per row is the larger of two irreducible effects, both
carrying [ROW_NOISE_KAPPA] for the same reason
Self::row_noise_floor does (accumulation over the row’s nonzeros
and the linear solve’s conditioning):
- Placing
x—Self::row_noise_floor,eps · ‖x‖_∞passed through the row atmax_j |∂g_i/∂x_j|. A row whose Jacobian is empty getsINFINITYthere, meaning “abstain”, which is the safe direction for the relative measures that floor was written for and the unsafe one here — silencing a constant row0 = bwould forgive a genuine infeasibility outright. Non-finite floors are therefore read as0: such a row is judged on its residual alone. - Forming the residual —
eps · m_i, withm_ithe magnitude of the quantities subtracted: the declared right-hand side|b_i|on the equality block (the valuec_iwas formed against), andmax(|d_i|, |s_i|)on the inequality block. A block with no declared magnitude to hand (the restoration NLP’sc, whose rows are not the user’s) contributes nothing here and is left to the placement floor.
A row’s residual is counted in full or not at all, matching how
Self::relative_c_infeasibility_max and
Self::relative_d_infeasibility_max use their floors: the question
is whether the row says anything, not how much of it to subtract.
Sourcepub fn relative_d_infeasibility_max(&self) -> Number
pub fn relative_d_infeasibility_max(&self) -> Number
The inequality-block half of
Self::curr_relative_primal_infeasibility_max:
max_i |d_i − s_i| / max(|d_i|, |d_l_i|, |d_u_i|).
This is the scale-free companion to
Self::curr_unscaled_primal_infeasibility_max. An absolute violation
measure cannot tell “satisfied” from “violated by 10% of everything the
row is” once the row’s numbers are small — x >= 0.7 written as
1e-12·x >= 0.7e-12 has an absolute violation of 1e-13 at x = 0.6,
under every absolute tolerance, while the row is violated by a seventh
of its own right-hand side. The ratio is 0.14 at every writing of the
row.
Computed entirely in the internally-scaled space: numerator and
denominator both carry the row scaling dd_i, so it cancels and the
ratio is invariant under it — which is the point.
The magnitude comes from the row’s declared bounds only — the
current value d_i is deliberately excluded. On an active row the
value converges to the bound, so for a zero-bound row (g(x) >= 0,
ubiquitous) both the violation and |d_i| go to zero together and
their ratio hovers near 1 at a perfectly converged point — including
|d_i| made HS13’s genuine solution read as 100% violated and vetoed
its certificate. A zero bound also needs no relative treatment in the
first place: s·g >= 0 is the same row at every s, so the absolute
test is already invariant there. Rows whose bounds are all zero or
non-finite therefore contribute nothing (the relative measure
abstains) — “zero” measured against the row’s own
noise floor rather than exactly, for the
reason spelled out on Self::relative_c_infeasibility_max: a
converter that writes 2^-53 where the model says 0 otherwise hands
a bound made of rounding residue to a measure that then reads the row
as 100% violated. QPILOTNO carries 43 such inequality bounds at
1e-17–1e-15, and one of them — a row sitting at exactly d(x) = 0
against a declared bound of 1.1e-16 — pinned rel_viol at 1.0 for
the whole run and drove the gh #446 local-infeasibility verdict from
this block. Equality rows are judged by
Self::relative_c_infeasibility_max, which plumbs the pre-fold RHS
back to supply the magnitude the fold into c(x) = 0 erased.
The violation judged is the distance of d(x) outside the declared
box — NOT the lifted residual |d − s| the absolute measure uses.
|d − s| only bounds the true violation from above: mid-solve the
slack lags d while d is comfortably inside its bounds, so a
slack-lag of 1% of a small row’s magnitude read as “violated” at
points that are genuinely feasible. That armed the rapid-infeasibility
pre-filter at degenerate QP endgames, where the no-descent
confirmation is vacuous (the violation is already ~0, so no materially
less-violating point exists) — and 18 feasible CUTEr QPs were reported
locally infeasible. Measured, not hypothetical.
Sourcepub fn obj_scaling_factor(&self) -> Number
pub fn obj_scaling_factor(&self) -> Number
The objective scaling factor df currently in force (1.0 when no
objective scaling is active).
Exposed because the termination logic must be able to tell an honest certificate from one an extreme scale has masked (gh #200): the scale factor itself is the discriminating signal, not the error.
Sourcepub fn computed_obj_scaling_factor(&self) -> Number
pub fn computed_obj_scaling_factor(&self) -> Number
The solver-computed part of the objective scale — see
IpoptNlp::computed_obj_scaling_factor. The masked-certificate test
keys on this, not on the product, so a user who deliberately scales a
well-conditioned objective down is not second-guessed.
Sourcepub fn curr_unscaled_nlp_error(&self) -> Number
pub fn curr_unscaled_nlp_error(&self) -> Number
Overall unscaled max-norm KKT error — max of the unscaled dual
infeasibility, primal infeasibility, and complementarity. This is the
honest “distance from a KKT point in the user’s own units”, as
opposed to Self::curr_nlp_error, which additionally applies the
s_d/s_c optimality scaling. Used by the status-fidelity gate and
surfaced to callers that must independently verify a returned
certificate (pounce#173).
pub fn trial_f(&self) -> Number
pub fn curr_barrier_obj(&self) -> Number
pub fn trial_barrier_obj(&self) -> Number
Sourcepub fn curr_grad_barrier_obj_x(&self) -> Rc<dyn Vector>
pub fn curr_grad_barrier_obj_x(&self) -> Rc<dyn Vector>
Gradient of the barrier objective wrt x:
∇_x φ = ∇f(x) − μ · [P_L · (1/s_L) − P_U · (1/s_U)] + damping
Mirrors IpIpoptCalculatedQuantities.cpp:CalcGradBarrierObjectiveX.
Sourcepub fn curr_grad_barrier_obj_s(&self) -> Rc<dyn Vector>
pub fn curr_grad_barrier_obj_s(&self) -> Rc<dyn Vector>
Gradient of the barrier objective wrt s:
∇_s φ = − μ · [P_L · (1/s_s_L) − P_U · (1/s_s_U)] + damping
Sourcepub fn curr_grad_barr_t_delta(
&self,
delta_x: &dyn Vector,
delta_s: &dyn Vector,
) -> Number
pub fn curr_grad_barr_t_delta( &self, delta_x: &dyn Vector, delta_s: &dyn Vector, ) -> Number
Directional derivative of the barrier objective along (δx, δs):
gradBarrTDelta = ∇_x φ · δx + ∇_s φ · δs. Port of
IpIpoptCalculatedQuantities.cpp:CurrGradBarrTDelta (called
IpCq().curr_gradBarrTDelta() in upstream after the search dir
has been computed).
Sourcepub fn curr_dwd(&self, delta_x: &dyn Vector, delta_s: &dyn Vector) -> Number
pub fn curr_dwd(&self, delta_x: &dyn Vector, delta_s: &dyn Vector) -> Number
δᵀ(W + Σ_x + δ_pert_x I)δ_x + δ_sᵀ(Σ_s + δ_pert_s I)δ_s —
the quadratic-model term used by IpPenaltyLSAcceptor.cpp: InitThisLineSearch:101-129. Reads W and the active PD
perturbations from crate::ipopt_data::IpoptData.
Returns 0 if the result would be negative (matching upstream’s
if dWd <= 0 then dWd = 0 guard at line 133).
pub fn curr_constraint_violation(&self) -> Number
Sourcepub fn constraint_violation_rows(&self) -> usize
pub fn constraint_violation_rows(&self) -> usize
Number of constraint rows backing the 1-norm above, i.e.
dim(c) + dim(d - s). Upstream never needs this because it
treats theta as a bare scalar, but any threshold expressed in
theta units is a sum over this many rows — a theta of T
is a mean per-row residual of T / rows. The filter acceptor
uses it to floor the theta_max reference so the ceiling means
the same thing on a 10-row and a 50 000-row model.
pub fn trial_constraint_violation(&self) -> Number
Sourcepub fn curr_primal_infeasibility_max(&self) -> Number
pub fn curr_primal_infeasibility_max(&self) -> Number
Max-norm primal infeasibility — max(||c||_∞, ||d − s||_∞). Used
by the iteration output’s inf_pr column when
inf_pr_output == INTERNAL. Mirrors
IpIpoptCalculatedQuantities.cpp:CurrPrimalInfeasibility(NORM_MAX).
Sourcepub fn curr_dual_infeasibility_max(&self) -> Number
pub fn curr_dual_infeasibility_max(&self) -> Number
Max-norm dual infeasibility — max(||∇_x L||_∞, ||∇_s L||_∞).
Mirrors IpIpoptCalculatedQuantities.cpp:CurrDualInfeasibility(NORM_MAX).
Sourcepub fn curr_dual_infeasibility_scale_max(&self) -> Number
pub fn curr_dual_infeasibility_scale_max(&self) -> Number
Magnitude of the largest term the Lagrangian gradient is assembled
from — the scale Self::curr_dual_infeasibility_max is a residual
of (gh #532).
D = max( ‖∇f‖_∞ , ‖J_cᵀ y_c‖_∞ , ‖J_dᵀ y_d‖_∞ ,
‖P_L z_L‖_∞ , ‖P_U z_U‖_∞ ,
‖y_d‖_∞ , ‖P_L v_L‖_∞ , ‖P_U v_U‖_∞ )∇L is the sum of exactly these terms, so dual_inf / D is the
fraction of them that failed to cancel: 1 at a point where nothing
cancelled (min -exp(x) s.t. x >= 0 running away, ∇f = −8.8e47 with
no multiplier to meet it), and ~eps at a point where the cancellation
was as complete as the arithmetic allows. That ratio is the
scale-invariant statement of stationarity: it is unchanged by
multiplying the objective — and hence every multiplier — by a positive
constant, which is the map an absolute bound on dual_inf is not
invariant under.
The projections are applied rather than assumed away: P_L/P_U are
0/1 expansion matrices in the main NLP, where the scatter leaves the
max-norm alone, but the term’s own norm is what this measures and the
restoration NLP supplies its own operators.
No has_valid_numbers sweep, unlike Self::curr_nlp_error (gh #292):
amax drops NaN, so a NaN gradient reads here as a finite scale. That
cannot launder anything, because the only caller pairs this with the
aggregate nlp_err <= tol test, and nlp_err carries that sweep — a
NaN anywhere in ∇L makes it NaN, and NaN <= tol is false.
Repeats the ∇f and the two transpose products
Self::curr_grad_lag_x already performs on the same iterate, plus
four scatters. The evaluations themselves hit OrigIpoptNLP’s
per-iterate caches, so the marginal cost is the products — but it is
still a second pass, and the caller reads this only where a termination
certificate is otherwise on the table. See
OptErrorConvCheck::dual_inf_bound.
Sourcepub fn curr_unscaled_dual_infeasibility_scale_max(&self) -> Number
pub fn curr_unscaled_dual_infeasibility_scale_max(&self) -> Number
Self::curr_dual_infeasibility_scale_max in the unscaled
(user-original) space. Every term of the scaled Lagrangian gradient is
df times its user-space counterpart — ∇f_scaled = df·∇f,
J_cᵀ_scaled y_c_scaled = Jᵀ(dc ⊙ y_c_scaled) = df·Jᵀ y_c since
dc ⊙ y_scaled = df·y_user, and likewise for the bound blocks, POUNCE
applying no variable scaling — so the unscaling is the single divide by
|df| that Self::curr_unscaled_dual_infeasibility_max performs on
the residual, term for term and row scaling included. Magnitude, for
the reason documented there: df is signed, a max-norm is not.
Sourcepub fn infeasibility_descent_available(&self) -> bool
pub fn infeasibility_descent_available(&self) -> bool
Scaled stationarity of the infeasibility measure ½‖(c, d−s)‖²
— ‖J_cᵀ c + J_dᵀ (d−s)‖_∞ / max(1, ‖(c, d−s)‖_∞). The
numerator is the x-gradient of the squared constraint
violation; a value near zero with the violation itself bounded
away from zero marks an iterate converging to a stationary
point of the infeasibility — i.e. a locally infeasible problem.
No linear solve: two transpose-products. Mirrors the gradient
term behind Ipopt’s IpRestoConvCheck.cpp LOCALLY_INFEASIBLE
test, applied here in the main loop.
Does a short step along −∇θ actually reduce the constraint violation?
LocalInfeasibility asserts the iterate has converged to a stationary
point of the constraint violation — that no local move reduces it. That
is a checkable claim, and this checks it directly instead of trusting a
threshold on a proxy.
Why a probe rather than a better proxy: the detector’s surrogate is
‖Jᵀc‖ / max(1, ‖c‖) against an absolute tolerance, and no variant of it
separates the cases. Measured over 800 MINLPLib models plus targeted
infeasible problems, the scaled form produces a confirmed false verdict
(HS13 from x₀ = (1e4, 1e4), where the constraint scaling dc ≈ 3.3e-7
drives the surrogate to 5e-14 at a point whose violation is 0.51); the
unscaled form needs a tolerance ≥ 1e-2 to fire at all, which introduces
new false infeasibility on 3+ corpus models while still losing 2 correct
detections; and a scale-invariant ‖Jᵀc‖ / ‖c‖² is not separable even on
the targeted set. A single absolute threshold on a surrogate cannot do
this job.
Comparing θ at two points is scale-free by construction — the row
scaling cancels out of the ratio — so this needs no calibration at all.
Costs one eval_c/eval_d pair per probed step, and runs only where the
detector was about to fire (both gates already passed for a full streak),
which is rare. Steps are clamped to the variable bounds, so descent that
only exists outside the box is correctly not counted — that direction
would suppress a correct infeasibility verdict.
Returns true when descent is available, i.e. the iterate is not
stationary and LocalInfeasibility must not be declared.
pub fn curr_infeasibility_stationarity(&self) -> Number
Sourcepub fn curr_avrg_compl(&self) -> Number
pub fn curr_avrg_compl(&self) -> Number
(z_L · s_L + z_U · s_U + v_L · s_L^d + v_U · s_U^d) / N
where N is the total number of bound multipliers
(IpIpoptCalculatedQuantities.cpp:3553-3606).
Sourcepub fn curr_complementarity_min(&self) -> Number
pub fn curr_complementarity_min(&self) -> Number
min_i (s_i · z_i) over all four bound complementarity blocks.
Mirrors IpIpoptCalculatedQuantities.cpp:CurrComplxMin
(lines 3608-3640) — the smallest pairwise product s · z,
signalling how close the iterate is to the central path.
Empty bound sets contribute +∞; returns 0 if no bounds at
all.
Sourcepub fn curr_complementarity_max(&self) -> Number
pub fn curr_complementarity_max(&self) -> Number
Max-norm of the unbarriered complementarity blocks
max_i |s_i · z_i| across all four (x_L, x_U, s_L, s_U)
pairs. Mirrors upstream
IpIpoptCalculatedQuantities.cpp:CurrComplementarity(0., NORM_MAX)
— used by OptimalityErrorConvergenceCheck to gate the
per-component compl_inf_tol test independently of the scaled
scalar curr_nlp_error.
Sourcepub fn curr_centrality_measure(&self) -> Number
pub fn curr_centrality_measure(&self) -> Number
Centrality measure ξ = min_i(s_i z_i) / avrg(s · z). Mirrors
IpIpoptCalculatedQuantities.cpp:CurrCentralityMeasure. Used
by crate::mu::oracle::loqo::LoqoMuOracle to bias σ toward
the central path when the iterate is unbalanced. Returns 1.0
(perfectly central) when there are no bound multipliers.
Sourcepub fn curr_barrier_error(&self) -> Number
pub fn curr_barrier_error(&self) -> Number
Barriered KKT error E_μ(x,y,z) — port of
IpIpoptCalculatedQuantities.cpp:CurrBarrierError. Same as
Self::curr_nlp_error but uses the relaxed complementarity
s ⊙ z − μ so the residual is zero when the iterate sits on the
μ-perturbed central path. The monotone barrier-update strategy
reduces μ only once this error drops below
barrier_tol_factor · μ.
Sourcepub fn curr_nlp_error(&self) -> Number
pub fn curr_nlp_error(&self) -> Number
Optimality-scaled max-norm KKT error — port of
IpIpoptCalculatedQuantities.cpp:3050-3104.
E = max( ||∇_x L, ∇_s L||_∞ / s_d ,
||c, d − s||_∞ ,
||compl||_∞ / s_c )where s_d / s_c are the asum-based scalings from
ComputeOptimalityErrorScaling (see §4 of MAIN_LOOP.md).
Uses mu_target = 0 (the unbarriered KKT residual). The
barriered variant is curr_barrier_error (TODO in Phase 7).
Sourcepub fn curr_nlp_error_above_primal_noise(&self, kappa: Number) -> Number
pub fn curr_nlp_error_above_primal_noise(&self, kappa: Number) -> Number
Self::curr_nlp_error with the primal-infeasibility term replaced by
Self::curr_primal_infeasibility_above_noise — i.e. counting a
constraint row’s residual only where it rises above the finest value
that row’s residual can take in floating point (gh #528).
Never larger than Self::curr_nlp_error, and equal to it whenever no
row is at its own resolution limit — which is every problem whose data
is O(1), so the common path is unchanged. It exists because the other
two terms of the KKT error are already normalised (s_d, s_c) while
the primal one is a bare absolute residual: ‖c‖_∞ and ‖d − s‖_∞ are
quantised in units of eps · the rows’ own magnitude, so on a model
whose constraint values reach ~1e8 the smallest nonzero value the
term can take already exceeds the default tol = 1e-8. Judging that
term absolutely there asks for a residual no iterate can represent.
Read only by the strict convergence gate, which pairs it with the
unscaled constr_viol_tol test on the full, unfloored residual — so
what this admits is bounded by the user’s own feasibility tolerance,
never by the noise floor alone.
kappa is the safety factor on the per-row floor —
[ROW_NOISE_KAPPA] by default, from the primal_noise_floor_kappa
option. 0 switches the floor off entirely, making this identical
to Self::curr_nlp_error and the strict gate bit-for-bit upstream’s.
pub fn trial_jac_c(&self) -> Rc<dyn Matrix>
pub fn trial_jac_d(&self) -> Rc<dyn Matrix>
Sourcepub fn trial_grad_lag_x(&self) -> Rc<dyn Vector>
pub fn trial_grad_lag_x(&self) -> Rc<dyn Vector>
∇_x L at the trial iterate — analog of Self::curr_grad_lag_x.
Sourcepub fn trial_grad_lag_s(&self) -> Rc<dyn Vector>
pub fn trial_grad_lag_s(&self) -> Rc<dyn Vector>
∇_s L at the trial iterate — analog of Self::curr_grad_lag_s.
pub fn trial_compl_x_l(&self) -> Rc<dyn Vector>
pub fn trial_compl_x_u(&self) -> Rc<dyn Vector>
pub fn trial_compl_s_l(&self) -> Rc<dyn Vector>
pub fn trial_compl_s_u(&self) -> Rc<dyn Vector>
Sourcepub fn curr_primal_dual_system_error(&self, mu: Number) -> Number
pub fn curr_primal_dual_system_error(&self, mu: Number) -> Number
Unscaled primal-dual KKT system error at the current iterate —
port of
IpIpoptCalculatedQuantities.cpp:curr_primal_dual_system_error.
Each block uses the 1-norm scaled by its entry count; the result
is the sum of the dual-infeasibility, primal-infeasibility, and
complementarity terms. Used by the soft restoration phase’s
sufficient-reduction test.
Sourcepub fn trial_primal_dual_system_error(&self, mu: Number) -> Number
pub fn trial_primal_dual_system_error(&self, mu: Number) -> Number
Unscaled primal-dual KKT system error at the trial iterate —
trial-side analog of Self::curr_primal_dual_system_error.
Sourcepub fn curr_grad_lag_with_damping_x(&self) -> Rc<dyn Vector>
pub fn curr_grad_lag_with_damping_x(&self) -> Rc<dyn Vector>
curr_grad_lag_x plus the kappa_d · μ · (Px_L · 1 − Px_U · 1)
damping term on singly-bounded primals — port of
IpIpoptCalculatedQuantities.cpp:2131-2180. When kappa_d == 0
returns the un-damped gradient.
pub fn curr_grad_lag_with_damping_s(&self) -> Rc<dyn Vector>
Sourcepub fn grad_kappa_times_damping_x(&self) -> Rc<dyn Vector>
pub fn grad_kappa_times_damping_x(&self) -> Rc<dyn Vector>
kappa_d · (P_L · damping_l − P_U · damping_u) in the full x
space — port of IpIpoptCalculatedQuantities.cpp::grad_kappa_times_damping_x
(lines 912-949). Unlike curr_grad_lag_with_damping_x this does
NOT include grad_lag_x and is NOT scaled by mu; the centering
RHS in the quality-function oracle multiplies the returned vector
by -avrg_compl per upstream IpQualityFunctionMuOracle.cpp:229.
pub fn grad_kappa_times_damping_s(&self) -> Rc<dyn Vector>
Sourcepub fn aff_step_alpha_primal_max(
&self,
delta_aff: &IteratesVector,
tau: Number,
) -> Number
pub fn aff_step_alpha_primal_max( &self, delta_aff: &IteratesVector, tau: Number, ) -> Number
Max primal step that keeps s + α · Δs > 0 for the four slack
blocks (x_L, x_U, s_L, s_U), bounded by the fraction-to-the-
boundary parameter τ ∈ (0, 1]. Mirrors
CalcFracToBound against the projected step P_L^T Δx,
−P_U^T Δx, P_L^T Δs, −P_U^T Δs.
Sourcepub fn aff_step_alpha_dual_max(
&self,
delta_aff: &IteratesVector,
tau: Number,
) -> Number
pub fn aff_step_alpha_dual_max( &self, delta_aff: &IteratesVector, tau: Number, ) -> Number
Max dual step that keeps z + α · Δz > 0 (and same for v).
Sourcepub fn aff_step_compl_avrg(
&self,
delta_aff: &IteratesVector,
alpha_primal: Number,
alpha_dual: Number,
) -> Number
pub fn aff_step_compl_avrg( &self, delta_aff: &IteratesVector, alpha_primal: Number, alpha_dual: Number, ) -> Number
Predicted average complementarity after the affine step:
(1/N) · Σ (s + α_pri · Δs) · (z + α_du · Δz) summed over the
four bound blocks. Returns 0 when there are no bounds.
Auto Trait Implementations§
impl !Freeze for IpoptCalculatedQuantities
impl !RefUnwindSafe for IpoptCalculatedQuantities
impl !Send for IpoptCalculatedQuantities
impl !Sync for IpoptCalculatedQuantities
impl !UnwindSafe for IpoptCalculatedQuantities
impl Unpin for IpoptCalculatedQuantities
impl UnsafeUnpin for IpoptCalculatedQuantities
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
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