Expand description
How long a solve has sat at a constraint violation it could not get below — the evidence a locally-infeasible verdict actually rests on.
The five reconstructed locally-infeasible gates in restoration
(pounce_restoration::resto_inner_solver::run_inner_resto) all claim
“the solve stalled at a violation it could not improve on”, and gh#661
is what happens when nothing measures that claim: they test only that
the recovered violation is large, which a diverging restoration
satisfies ever more emphatically the worse it gets.
The gh#661 divergence guard withholds those gates’ verdict when the restoration sub-solve ends far worse than the violation it was entered at. That guard needs an exemption for one shape of run: a solve that provably could not get below some floor, and whose restoration then blew up over a handful of iterations at the end. The blow-up is the tail of that trajectory, not a description of it.
gh#664 keyed that exemption on inner_iter_count >= 1000, which is a
proxy for “it ran out of room” and a doubly loose one. A count is not a
stall test — that substitution is the same class of error gh#661 fixed,
where a size stood in for a stall test. And the counter it read is
not what its comments claimed: the inner IPM’s iter_count is seeded
from the outer’s (IpRestoMinC_1Nrm.cpp:181), so 1019 on
issue_508_infeasible_gap_1em2 is 1015 outer iterations plus a
four-iteration sub-solve, not a sub-solve that ran a thousand times.
InfPrFloor measures the property directly, at the scope where the
long trajectory actually lives: the outer solve. It watches the
original NLP’s scaled primal infeasibility at each outer iterate and
counts how many of them sat within [FLOOR_BAND] of the floor the
count is being measured against. A solve still finding its way down
keeps clearing the band and restarting that count; one that is out of
room accumulates it.
Two choices in that sentence are load-bearing rather than simplifications, and both were arrived at by measuring:
Cumulative, not consecutive. A real trajectory pinned at a floor
does not sit there quietly. issue_508_infeasible_gap_1em2 returns to
1.0e-2 over and over across 1016 outer iterations while excursions to
9.56e1 break every run in between; simulated over its printed
iteration trace, its longest consecutive stay is 39 — against 19 for
pooling_rt2stp, which is feasible and must not be exempted. The
consecutive measure does not separate them at all. Time spent at the
floor does, by two orders of magnitude: 943 against 7.
Pinned reference, not running minimum. Measuring the band against the running minimum lets it chase the iterates, so a solve creeping downward by 0.9x per iteration is forever within a decade of its own previous best. Under that reading a 2000-iteration grind — which reduces the violation by eighty-eight orders of magnitude, i.e. is working — accumulates all 2000 and buys the exemption. Pinned, it accumulates one decade’s worth and resets.
Structs§
- InfPr
Floor - Running evidence, over one solve, that it found a floor on the constraint violation and could not get below it.