Expand description
Warm-start iterate initializer — port of
IpWarmStartIterateInitializer.{hpp,cpp}. Used when a previous
solve has left a trial point that should be reused.
There are two callers we serve:
- A full primal-dual warm restart installed via
Application::set_warm_start_iterateand consumed by the nextoptimize_tnlp(e.g. the debuggerresolvere-solve):data.curralready carries the previous solve’s iterate, so we keep it, clamp multipliers, and optionally overridemu. - First solves from
OptimizeTNLPthat opt intowarm_start_init_point=yesto forward user-supplied primal/dual seeds viaTNLP::get_starting_point. Heredata.currcarries only dim metadata (uninitialized vectors); we pull seeds from the NLP, push primals/slacks into the bound interior with warm-startbound_push/bound_frac, and then apply the same multiplier clamps.
Wired options today: bound_push, bound_frac,
slack_bound_push, slack_bound_frac, mult_bound_push,
mult_init_max, target_mu. mult_bound_push floors the four
bound-multiplier blocks (mirroring upstream’s ElementWiseMax
with warm_start_mult_bound_push): a user-seeded z = 0 would
otherwise start the barrier on its boundary.
§Residual-adaptive recentering (gh#606)
warm_start_recentering=residual (the default) adds a pass over
the supplied point before the clamps: measure what was actually
handed in, reconstruct what is missing, and choose μ from the
measurement rather than from a universal constant.
-
Measure.
inf_prcomes first because it is the one residual that does not depend on the duals, so it is meaningful even when every multiplier block is absent. It is reported rather than acted on (step 4 explains why). -
Reconstruct bound multipliers. An entry that arrives as exactly
0(or NaN) is not a legal barrier multiplier — the barrier needsz > 0— so it was never a seed. Upstream floors it at the constantwarm_start_mult_bound_push; here it takesμ̂ / slackinstead, the same complementarity relation the solver is about to enforce. Seeded (strictly positive) entries are left alone.This step needs no dual to work from — only the slacks the supplied point already determines — so it runs even for a caller who seeded nothing but
x(gh#622). That case used to fall through to the constant, and the constant it fell through to waswarm_start_mult_bound_push: 1e-3 by default, and 1e-9 under the tightened pushespounce.WarmStartships, i.e. a start declaring every bound inactive. Against the pre-gh#622 behaviour, filling it properly is worth 49 -> 44 iterations at horizon 5 and 67 -> 55 at horizon 20 on that issue’s receding-horizon family, and takes an HS071 restart from a transferred point with no duals from 11 iterations to 7.It costs one iteration in one place: HS071 restarted from its own solution under
warm_start_recentering=none, 3 -> 4, where the kill switch keeps the constant fill and the constant is nowbound_mult_init_valrather than a bound-multiplier push so small it read as “inactive” and happened to be right about a solution whose bounds mostly are. Under the default the same restart is 3 -> 2. -
Reconstruct equality multipliers. A
yblock that is identically zero is likewise unseeded, and is re-derived from stationarity by the same regularized least-squares augmented solve the cold path uses (LeastSquareMults) — now with the reconstructedzin its right-hand side, so the estimate is not forced to absorb the bound multipliers.Unlike step 2, this one completes a partial seed and is gated on [
any_dual_seeded]: from a point alone it is the cold path’s estimate wearing the warm path’s barrier, measured overbenchmarks/warmstartat 1102 -> 1211 iterations across 27 parametric paths. Step 4 is gated the same way and for the same reason. -
Choose μ. With the point complete, μ is raised to the measured
avrg_complwhen that overshoots whatmu_initasked for by more than [MU_ESCALATION_TRIGGER], clamped to[MU_FLOOR, MU_CEILING]. A KKT-quality point measures its own converged complementarity and keeps it; a stale one, whose multipliers and slacks no longer pair up, measures a large one and gets a correspondingly loose barrier — the “safely fall back to stronger recentering” half. The primal and dual residuals are deliberately not in that max; see [final_mu].warm_start_target_mustill wins outright when set.
warm_start_recentering=none restores the pre-gh#606 behaviour
exactly: constant floor, zero-filled y, μ untouched. It is the
kill switch for this whole block.
Every branch above records what it did on
WarmStartDiagnostics, which lands on IpoptData and is
readable afterwards through
IpoptApplication::warm_start_diagnostics().
Structs§
- Warm
Start Diagnostics - What the warm-start initializer accepted, reconstructed, or discarded, and the residuals it based those calls on (gh#606).
- Warm
Start Iterate Initializer
Enums§
- Block
Verdict - What happened to one multiplier block of the supplied warm point.