pub struct ConstraintKktDiagnostics {
pub n_constraints: usize,
pub n_active: usize,
pub primal_feasibility: f64,
pub dual_feasibility: f64,
pub complementarity: f64,
pub stationarity: f64,
pub active_tolerance: f64,
pub working_set_rank_deficient: bool,
pub cone_projection_refused: bool,
pub gradient_scale: f64,
}Expand description
KKT diagnostics for inequality-constrained Newton subproblems.
Constraints are represented as A * beta >= b in the same coefficient
coordinate system as the returned beta.
Invariants (held by all producers; not enforced at consumer boundary):
n_active <= n_constraints(a row cannot be active twice).- All four residual components (
primal_feasibility,dual_feasibility,complementarity,stationarity) are>= 0.0and finite. active_tolerance >= 0.0and finite.
Fields§
§n_constraints: usizeNumber of inequality rows.
n_active: usizeNumber of rows considered active (slack <= active_tolerance).
primal_feasibility: f64Maximum primal feasibility violation: max_i max(0, b_i - a_i^T beta).
dual_feasibility: f64Maximum dual feasibility violation: max_i max(0, -lambda_i).
complementarity: f64Maximum complementarity residual: max_i |lambda_i * slack_i|.
stationarity: f64Stationarity residual: ||grad - A^T lambda||_inf.
active_tolerance: f64Tolerance used to classify active constraints from slacks.
working_set_rank_deficient: booltrue when the active rows are linearly dependent (rank(A_active) < n_active) — a degenerate boundary face. On such a face the active-row
multipliers are non-unique and the strict stationarity tolerance is
unreachable by construction. The inner active-set solver certifies these
iterates via its ACTIVE_SET_KKT_DEGENERATE_STATIONARITY_TOL relaxation;
the outer validation gate must consult this flag to apply the matching
relaxation, or it will refuse a legitimately-converged constrained
optimum and abort the REML startup loop.
NOTE: B-spline shape=concave/shape=convex faces are not degenerate
— that path reparameterizes curvature into independent coordinate lower
bounds γ_j ≥ 0 (full-rank active subsets), so this flag stays false
for them. Their cold-start fragility is a seed problem fixed by the
strictly-interior seed, not by this relaxation.
cone_projection_refused: booltrue when the cone projector REFUSED rather than returning
multipliers, so lambda was never computed and stationarity is the
raw gradient by default rather than by measurement.
Without this, a refusal is indistinguishable from an answer of exactly
zero: both leave lambda = 0, both make stationarity == ‖grad‖∞, and
both print stat_rel = 1.000e0. #2601 reports precisely that on a
fully active convexity face, and the two readings call for opposite
responses — a genuine λ = 0 says the point is not stationary, while a
refusal says nothing was measured at all.
gradient_scale: f64Inf-norm of the (raw, unprojected) gradient at beta, ‖gradient‖∞ —
the natural scale of the stationarity residual. A converged constrained
optimum drives stationarity = ‖grad − Aᵀλ‖∞ to zero relative to this
scale, not to a fixed absolute floor: the profiled REML latent objective
carries an O(n) gradient magnitude even at a genuine stationary point
(issue #879), so a bare absolute stationarity gate is unreachable there
by construction. The inner active-set solver already certifies
convergence on the scale-invariant ratio
stationarity / max(gradient_scale, 1) (its stationarity_rel path
against ACTIVE_SET_KKT_STATIONARITY_TOL); the outer validation gate
crate::estimate::reml::outer_eval::enforce_constraint_kkt consults this
field to apply the identical relative test, so the two stop on the same
contract instead of the gate spuriously aborting a constrained optimum
the solver legitimately reached (issue #989). Defaults to 0.0 when
deserialized from a model saved before this field existed, which makes
max(gradient_scale, 1) = 1 and recovers the bare absolute test.
Implementations§
Source§impl ConstraintKktDiagnostics
impl ConstraintKktDiagnostics
Sourcepub fn cone_projection_note(&self) -> &'static str
pub fn cone_projection_note(&self) -> &'static str
The note a rendered KKT verdict must carry when the cone projector REFUSED instead of returning multipliers.
stationarity is ‖grad − Aᵀλ‖∞. On a refusal λ was never computed,
so the number reported in that slot is the raw gradient BY DEFAULT rather
than by measurement — and two failures with opposite remedies then render
identically: a computed λ = 0 says this point is not stationary, a
refusal says nothing was projected at all.
Self::cone_projection_refused has recorded which since #2601, and
until this accessor existed every reader of the numbers dropped it — the
outer REML gate’s ParameterConstraintViolation and the post-fit
feasibility audit’s KKT[...] suffix both rendered stat with no way to
say whether it had been projected. A flag that cannot change what anyone
sees is not a diagnostic.
Returns the separator too, so a non-refusal contributes nothing rather than a dangling one.
Trait Implementations§
Source§impl Clone for ConstraintKktDiagnostics
impl Clone for ConstraintKktDiagnostics
Source§fn clone(&self) -> ConstraintKktDiagnostics
fn clone(&self) -> ConstraintKktDiagnostics
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for ConstraintKktDiagnostics
impl Debug for ConstraintKktDiagnostics
Source§impl<'de> Deserialize<'de> for ConstraintKktDiagnostics
impl<'de> Deserialize<'de> for ConstraintKktDiagnostics
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Auto Trait Implementations§
impl Freeze for ConstraintKktDiagnostics
impl RefUnwindSafe for ConstraintKktDiagnostics
impl Send for ConstraintKktDiagnostics
impl Sync for ConstraintKktDiagnostics
impl Unpin for ConstraintKktDiagnostics
impl UnsafeUnpin for ConstraintKktDiagnostics
impl UnwindSafe for ConstraintKktDiagnostics
Blanket Implementations§
impl<T> Allocation for T
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<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<T> DistributionExt for Twhere
T: ?Sized,
impl<T> DistributionExt for Twhere
T: ?Sized,
impl<T, U> Imply<T> for U
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 moreSource§impl<T> Pointable for T
impl<T> Pointable for T
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
Source§fn to_subset(&self) -> Option<SS>
fn to_subset(&self) -> Option<SS>
self from the equivalent element of its
superset. Read moreSource§fn is_in_subset(&self) -> bool
fn is_in_subset(&self) -> bool
self is actually part of its subset T (and can be converted to it).Source§fn to_subset_unchecked(&self) -> SS
fn to_subset_unchecked(&self) -> SS
self.to_subset but without any property checks. Always succeeds.Source§fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
self to the equivalent element of its superset.