Skip to main content

Module warm_start

Module warm_start 

Source
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_iterate and consumed by the next optimize_tnlp (e.g. the debugger resolve re-solve): data.curr already carries the previous solve’s iterate, so we keep it, clamp multipliers, and optionally override mu.
  • First solves from OptimizeTNLP that opt into warm_start_init_point=yes to forward user-supplied primal/dual seeds via TNLP::get_starting_point. Here data.curr carries only dim metadata (uninitialized vectors); we pull seeds from the NLP, push primals/slacks into the bound interior with warm-start bound_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.

  1. Measure. inf_pr comes 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).

  2. Reconstruct bound multipliers. An entry that arrives as exactly 0 (or NaN) is not a legal barrier multiplier — the barrier needs z > 0 — so it was never a seed. Upstream floors it at the constant warm_start_mult_bound_push; here it takes μ̂ / slack instead, 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 was warm_start_mult_bound_push: 1e-3 by default, and 1e-9 under the tightened pushes pounce.WarmStart ships, 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 now bound_mult_init_val rather 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.

  3. Reconstruct equality multipliers. A y block 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 reconstructed z in 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 over benchmarks/warmstart at 1102 -> 1211 iterations across 27 parametric paths. Step 4 is gated the same way and for the same reason.

  4. Choose μ. With the point complete, μ is raised to the measured avrg_compl when that overshoots what mu_init asked 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_mu still 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§

WarmStartDiagnostics
What the warm-start initializer accepted, reconstructed, or discarded, and the residuals it based those calls on (gh#606).
WarmStartIterateInitializer

Enums§

BlockVerdict
What happened to one multiplier block of the supplied warm point.