pub enum SecondOpinionTrigger {
LocalInfeasibility,
InvalidNumber,
RestorationFailure,
StepComputationError,
IterationLimit,
}Expand description
Which failure opened the ladder. Not every rung is evidence about every
failure: an Invalid_Number_Detected is a statement about the callbacks
at a point, and re-running the same callbacks at the same point under a
different linear-solver scaling or a different barrier strategy evaluates
the same non-finite quantity again. Only the rung that moves the point
applies there.
Variants§
LocalInfeasibility
Infeasible_Problem_Detected — a local statement about a nonconvex
problem, which every rung is evidence against.
InvalidNumber
Invalid_Number_Detected — a NaN or infinity out of the model.
RestorationFailure
Restoration_Failed — the restoration phase could not find a point
the filter would accept. Like the two above and unlike a budget exit,
this is a statement about the trajectory the solve happened to take,
not about the model; see SecondOpinionTrigger::for_status.
StepComputationError
Error_In_Step_Computation — the step computation broke down: the
KKT system could not be made to yield a usable step, even after
regularization. Like a restoration failure and unlike a budget exit,
this is a statement about the trajectory the solve happened to take,
so a different barrier schedule is evidence against it.
Measured on deb7 (found via gh#960): the wasm32 build, whose factorization
rounds differently from the native one, follows the native trajectory
for 83 iterations, diverges by one ulp in inf_du at iteration 84,
and ends Error_In_Step_Computation at 160 where native solves in
147. Under mu_strategy=adaptive the same binary and the same model
reach the same optimum as native — Solve_Succeeded, 131 iterations,
objective 97.559938 — so the rescue was already in the ladder and
only the trigger was missing. This opens only that rung: on the
same case feral_scaling=mc64 still fails at 154 iterations,
feral_increase_quality=no at 201, and start_point_perturbation
spends 324 iterations to fail anyway.
IterationLimit
Maximum_Iterations_Exceeded — and only when the solve escalated
the linear solver’s factorization quality at least once (gh#857).
This is the one trigger that is not a property of the verdict alone,
and the exception it carves out of the paragraph on
SecondOpinionTrigger::for_status is narrow on purpose. A budget
exit normally wants a bigger budget, not a different trajectory —
but a feral_increase_quality escalation changes which pivots are
taken and never steps back down, so when one fired, the wall the
solve hit may be the escalated trajectory’s rather than the model’s,
and a bigger budget re-runs the same wall. On
square_flowsheet_resto’s lbfgs leg the escalated path reaches 3000
iterations and the un-escalated one converges in 178.
The escalation count is what keeps this from opening a ladder on
every budget exit; it comes from the quality_escalations statistic
and is checked in second_opinion_rungs, not here — for_status
only sees a status. A solve that escalated zero times produces an
empty rung list and the driver returns before it narrates anything.
Implementations§
Source§impl SecondOpinionTrigger
impl SecondOpinionTrigger
Sourcepub fn for_status(status: ApplicationReturnStatus) -> Option<Self>
pub fn for_status(status: ApplicationReturnStatus) -> Option<Self>
Which ladder, if any, a finished solve’s verdict opens.
Only these three statuses open one. In particular an iteration- or time-limit exit does not: the answer there is a bigger budget, and a re-solve from a different trajectory would burn the same budget again to reach the same wall.
Restoration_Failed is on the list for the same reason the other two
are (gh#815): it is a report about the path, not about the model.
The restoration phase failing to find a filter-acceptable point says
the iterate reached somewhere the sub-problem could not work from, and
a different starting point is a different sub-problem. It is not a
budget exit — pounce stops far short of max_iter — so “give it more
iterations” is not the available answer, which is precisely the
distinction the paragraph above draws. Measured on the gh#815 square
flowsheet family: both failing members exit Restoration_Failed, no
ladder ran, and rung 3 alone recovers both to Optimal Solution Found — one of them (f100) to an optimum Ipopt itself misses.
Only rung 3 opens on this trigger; rungs 1 and 2 stay gated on
SecondOpinionTrigger::LocalInfeasibility, so a restoration failure
costs exactly one extra solve. That is the measured ordering, not
caution for its own sake: over the KRONOS corpus the displaced start
recovered 13 of 15 where mu_strategy=adaptive recovered 4 (see
start_point_retry’s option text).
Sourcepub fn verdict(self) -> &'static str
pub fn verdict(self) -> &'static str
The verdict alone, with no claim about how the solve got there —
for a console line that does not depend on an escalation having
happened (the square-system stage opens on a budget exit that never
escalated, where Self::describe’s wording would be false).
Sourcepub fn describe(self) -> &'static str
pub fn describe(self) -> &'static str
The word for this trigger in the option rungs’ console line. For
SecondOpinionTrigger::IterationLimit it names the escalation,
which is true there: no option rung opens on a budget exit that did
not escalate.
Trait Implementations§
Source§impl Clone for SecondOpinionTrigger
impl Clone for SecondOpinionTrigger
impl Copy for SecondOpinionTrigger
Source§impl Debug for SecondOpinionTrigger
impl Debug for SecondOpinionTrigger
impl Eq for SecondOpinionTrigger
Source§impl PartialEq for SecondOpinionTrigger
impl PartialEq for SecondOpinionTrigger
impl StructuralPartialEq for SecondOpinionTrigger
Auto Trait Implementations§
impl Freeze for SecondOpinionTrigger
impl RefUnwindSafe for SecondOpinionTrigger
impl Send for SecondOpinionTrigger
impl Sync for SecondOpinionTrigger
impl Unpin for SecondOpinionTrigger
impl UnsafeUnpin for SecondOpinionTrigger
impl UnwindSafe for SecondOpinionTrigger
Blanket Implementations§
impl<T> Boilerplate 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
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