Restarting from the Best Point Found
Note
This page starts the new optimization where the previous one ended, under the same configuration. What the results at that point still cover when the configuration changes between the optimizations is described in Reusing Results at the Starting Point.
The full script for this example is examples/restart.py. It restarts the same optimization several times, each time starting from the best point the previous optimization found.
Why restart?
A single optimization can stop before truly converging — for example
because it hit its iteration limit while still improving. Restarting
runs optimize again, using the previous result as the
new start point. Since
each call to optimize is independent, this is a loop in your own code;
ropt needs nothing special to support it.
Seeing every evaluation
The best point of one optimization is all you need to start the next. To also
see every evaluation across the whole sequence of restarts, pass a
result callback: it is called with each
FunctionResults as that evaluation finishes.
A callback belongs to the optimization it is given to, so appending to one list
is what carries the results across the restarts:
A handler, given with handlers=, receives the same results but is an
object with state of its own. One handler can be given to several optimizations,
including optimizations that execute concurrently, and does more than pass
results on:
HistoryHandler stores every result it receives,
and DataFrameHandler collects them into named
DataFrames. Result Handlers covers them.
Restart in a loop
Each iteration runs one optimization, starting from the previous best point,
and appends its results to reported:
x0 = INITIAL_VALUES
f0: FunctionResults | None = None
g0: GradientResults | None = None
for _ in range(RESTARTS):
result = optimize(CONFIG, x0, rosenbrock, result_callback=reported.append, f0=f0, g0=g0)
assert result.results is not None
x0 = result.results.variables # restart from the best point found so far
f0, g0 = result.results, result.gradient
result.results.variables is the best point the optimization found — feeding
it back in as x0 is the entire restart mechanism. After the loop, reported
holds every evaluation from every restart, not just the last one:
print(f"evaluations collected across all restarts: {len(reported)}")
print(f"best objective after {RESTARTS} restarts: {result.results.target_objective}")
Restarting from the last point
To restart from the last valid point evaluated rather than the best point,
create a ResultsHandler with what="last" and
pass it to optimize in handlers=[handler]. After the optimization,
check that handler.result is not None, then use
handler.result.variables as the next start point. Failed evaluations
are not retained, so this may not be the last point attempted.
Not evaluating the restart point again
The loop passes result.results as f0 and result.gradient as g0 to
reuse evaluations already performed at the restart point. See
Reusing Results at the Starting Point for gradient
availability and the rules for reuse when the configuration changes.
See also
- Restarting concurrent, rather than sequential, optimizations collects into the same handler: Result Handlers.