Making a loop of solves faster¶
How to start each solve in a loop from the one before, and build many small
models faster. The reference is
Model.solve.
Carry on from the solve before¶
Build once, then update and solve:
import specsolve as sps
model = sps.build('dispatch.yaml', sources)
model.solve()
for numbers in steps:
result = model.update(numbers).solve()
Each solve carries on from the last while update keeps the loaded solver. An
update that moves a mask or a coordinate set loads the solver again
(Model.update).
start=None begins from nothing every time. The answer is the same either way.
Check that it pays¶
Run the loop once each way and read the clock:
for start in ('previous', None):
model = sps.build('dispatch.yaml', sources)
model.solve()
for numbers in steps:
model.update(numbers).solve(start=start)
print(start, model.diagnostics().seconds['solve'], model.diagnostics().loads)
Take the faster one. loads equal to the number of solves means each update
moved a mask.
Carrying on usually pays, and can lose by an order of magnitude. It cut wall time by 1.5× to 5.7× on the bench's dispatch and storage models (#1884). On a model whose presolve cracks the problem, it cost 76.6 s against 4.3 s on HiGHS, because a solver that carries on skips its presolve (#815).
To time a cold solve, close the model and build it again: start=None alone
leaves solver state behind.
A sweep¶
solve_over carries each slice on from the one
before it, except under an executor. start=None turns it off. Pass an earlier
sweep as start= to start each slice from the earlier slice of its key:
monday = sps.load_archive('runs/monday.zip').sweep
tuesday = sps.solve_over('dispatch.yaml', tuesday_sources, axis, start=monday)
Start from an earlier answer¶
A model that gains a row, such as a cutting-plane master that gains a cut, loads again. Start it from an earlier answer that carries its basis:
previous = master.solve(outputs={'basis'})
for cut in cuts:
previous = master.update(cut).solve(start=previous, outputs={'basis'})
Over a Benders run with HiGHS, this saved 66% to 73% of the simplex iterations (#1877).
Start a MIP from values¶
The solver takes the values as its first incumbent. Pass an earlier answer, or
values per variable under 'primal':
yesterday = sps.load_result('runs/monday')
today = sps.solve('commitment.yaml', sources, start=yesterday)
today = sps.solve('commitment.yaml', sources, start={'primal': {'on': 1.0}})
A start never changes the optimum.
Model.solve lists what each
solver takes.
Build many small models faster¶
specsolve builds a model of at most 250,000 columns and 250,000 rows on polars' in-memory engine. To move the rest of a process onto it too, such as a sweep of many small models and your own queries:
POLARS_ENGINE_AFFINITY=in-memory in the environment does the same, and the
workers of a parallel sweep inherit it.