Expand description
Interior-point → active-set handover inside one solve (gh#1054).
§Why
On large collocation models the interior-point method gets close fast and
then spends a long tail meeting tol: the reporter of gh#1054 measured the
objective within 0.1 % of final after ~40 % of the iterations, the rest
going to a tail where the dual infeasibility oscillates and the filter
backtracks. Near the solution the active set is already identified, and an
SQP step on it converges in a few iterations. Running the two as separate
solves needs a working set between them, and getting one at that size was
the blocker: post-convergence crossover ran after the tail it was meant to
avoid.
§What it does
With ipm_sqp_handover=yes, the interior-point loop offers each iterate
that has not converged to HandoverTrigger::should_try. When the
trigger fires, the iterate goes through the same bridge as post-convergence
crossover (crate::crossover::run): identify the active set from the
multipliers against the slacks, take one QP step over it, and fall back to
the full active-set SQP from that working set. The point that comes back
replaces the interior iterate only if it meets the stopping tolerances,
and the solve then ends Solve_Succeeded. Otherwise the interior
iteration continues from exactly where it was: nothing it carries is
touched by a declined attempt, so the fallback the request asked for is
the interior method itself, not a restart.
§When it fires
Either test, whichever comes first:
- tolerance: the overall NLP error is at or below
ipm_sqp_handover_tol(a loosenedtol); - stall:
ipm_sqp_handover_stall_iter > 0and the best error seen has not halved in that many iterations.
A declined attempt re-arms only after the error has fallen another order
of magnitude below the error at that attempt, or a full stall window has
passed without one, and at most ipm_sqp_handover_max_attempts attempts
are made. Without the back-off, a model whose active set is not yet
identifiable would pay an SQP attempt on every remaining iteration.
§Identification
At an interior iterate a bound’s slack s and multiplier z satisfy
s·z ≈ μ, so an active bound has s ≈ μ/z and an inactive one
z ≈ μ/s; √μ separates the two. The primal tolerance handed to the
classifier is therefore max(crossover_primal_tol, √μ): the crossover
default (1e-6) is sized for a converged iterate, and at a loosened
tolerance it would call every active bound inactive. A misclassified row
costs pivots in the QP, not correctness.
Structs§
- Handover
Options - Tuning, read from the
ipm_sqp_handover*options. - Handover
Report - What the handover did on the most recent solve. Retrieved from
crate::application::IpoptApplication::handover_report. - Handover
Trigger - Decides, iteration by iteration, whether to attempt a handover. See the module docs for the rules.
Functions§
- identification_
primal_ tol - The primal tolerance for classifying the active set at an interior
iterate whose barrier parameter is
mu. See the module docs.