Expand description
When the tiny-step stop may stop waiting for the multiplier step.
Upstream stops at a tiny step only after the multiplier step is also
below tiny_step_y_tol (IpBacktrackingLineSearch.cpp), so that a
primal iterate which has stopped moving is not abandoned while its
multipliers are still converging. At a point whose multipliers are not
unique they need not converge: they run away, the multiplier step stays
large, and the solve repeats the same iterate until max_iter.
TinyStepDualStall bounds that wait by dual progress. Fed one
observation per tiny primal step, it releases the stop once
max_iters consecutive tiny steps have gone by without the scaled dual
infeasibility falling below 0.99 times its best value in the run.
Multipliers that are converging reduce the dual infeasibility and reset
the count, so they are never cut short; a settled multiplier step or a
non-tiny step ends the run.
The case: the phase-change flash example
(pounce.examples.flash_mpcc.declare) at 332 K, started from 331 K’s
all-vapor answer, sat for 3 000 iterations at a primal step of 5.6e-16
with the dual infeasibility constant at 1.3e29, and the adaptive-mu
re-solve then finished it in 14 (arm64 macOS). With the default bound the
base attempt ends after 43 and the same re-solve takes it from there. On
x86_64 Linux the re-solve stalls as well, under either rule, and the
driver’s l1 penalty answers: the same point, in 86 iterations against
6 012 (python/tests/test_flash_mpcc.py, which asserts only what both
platforms share).