Warm-starting a re-solve¶
How to start each solve in a loop from the work the solve before it did. It
suits a loop whose solves differ by a small step: a rolling horizon, a myopic
pathway, a search that inches. The reference is
Model.solve.
Keep the solver's progress¶
Build once, then solve each update with keep='progress':
import specsolve as sps
model = sps.build('dispatch.yaml', sources)
model.solve()
for numbers in steps:
result = model.update(numbers).solve(keep='progress')
print(result.kept) # progress
kept says
what the solve actually kept. The first solve of a model keeps 'nothing',
because no work came before it. An update that moves a mask or a coordinate
set also gives 'nothing': the columns change, so the model is loaded again
(Model.update).
The default, keep='solver', reuses the loaded model and discards the work.
The answer is the same under every keep; only the time changes.
Check that it pays¶
Run the loop once with each keep= and read the clock the package keeps:
for keep in ('solver', 'progress'):
model = sps.build('dispatch.yaml', sources)
for numbers in steps:
assert model.update(numbers).solve(keep=keep).kept in {keep, 'nothing'}
print(keep, model.diagnostics().seconds['solve'])
Take the faster one. 'nothing' on every iteration means each update moved a
mask and the model was rebuilt, so the loop is paying for the build, not the
solve.
keep='progress' can lose by an order of magnitude and win by a factor of
two, so measure rather than guess. Over six updates on HiGHS
(#815), carrying the solver's
work cost 76.6 s against 4.3 s on a dispatch model whose presolve cracks
the problem outright, an 18× loss, and 111.2 s against 213.9 s on a
storage model whose cyclic recurrence presolve cannot crack, a 1.9× win. It
pays where the model is hard for its solver's preprocessing and consecutive
solves differ by a small step. The answer does not change either way: across
both models the objectives agreed to 2e-15 relative. No solver option reaches
the same thing; on both solvers that ship, an option asking for it did not
produce it (#815).
Warm-start a sweep¶
solve_over takes the same keep=, and carries
from one slice to the next:
axis = sps.EachWindow('hour', steps=24, lookahead=24, into='t')
sweep = sps.solve_over('horizon.yaml', sources, axis, keep='progress')
Under executor=, every slice is a first solve and keeps 'nothing'
(running slices in parallel).
Time a cold solve¶
Pass keep='nothing'. It discards the held solver before the load, so no
basis, incumbent or solver-internal state survives. A benchmark needs that, and
so does comparing two sets of solver_options.
What a rebuild loses¶
A rebuild carries no progress. A cutting-plane master re-solved after gaining a cut has gained a row, and a basis spans the model it was read from. #382 tracks that case.