Serial Solver ExecutionΒΆ
When you evaluate performance, you may want to run the solver multiple times iteratively. Also, for some solvers, multiple short-timeout iterations are more likely to find a good solution than a single long-timeout iteration. The Amplify SDK allows running the same combinatorial optimization problem multiple times in a row with the same solver for such needs.
See also
Parallel Solver Execution may be a better fit for obtaining statistics from multiple runs for formulation, solver performance studies, etc.
Example of multiple runsΒΆ
First, create a model and solver client as in a usual solve() function execution.
from amplify import VariableGenerator, one_hot, AmplifyAEClient, solve
from datetime import timedelta
gen = VariableGenerator()
q = gen.array("Binary", 3)
objective = q[0] * q[1] - q[2]
constraint = one_hot(q)
model = objective + constraint
client = AmplifyAEClient()
# client.token = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
client.parameters.time_limit_ms = timedelta(milliseconds=1000)
By passing an integer as the num_solves keyword argument to solve(), the solver is executed iteratively for num_solves times.
result = solve(model, client, num_solves=3)
As in a usual solve() call, the solver returns an instance of the Result class. The return value contains the results of the num_solves iterations.
>>> len(result)
3
Using the best attribute, you can obtain the best solution among those returned by the num_solves runs.
>>> print(f"objective = {result.best.objective}, q = {q.evaluate(result.best.values)}")
objective = -1.0, q = [0. 0. 1.]
Fetching resultsΒΆ
When solve() is called with the num_solves keyword, the return value is an instance of the Result class, as usual.
The Amplify SDK determines the number of times of the solver executions by the num_solves attribute of the Result class. This attribute usually yields the same value as the value specified to solve() as the num_solves keyword argument. Still, it may be less than the value specified as the num_solves keyword argument, if some of the solver runs, fail for some reason.
>>> result.num_solves
3
Index or iteration access allows you to aggregate all the solutions the solver returns for the num_solves runs. By default, the Amplify SDK sorts them in order of preferred solution. There is no distinction regarding the number of executions the solution was found.
>>> print(f"objective = {result[0].objective}, q = {q.evaluate(result[0].values)}")
objective = -1.0, q = [0. 0. 1.]
Use the split property to get only the solutions returned by a particular run.
>>> first_result = result.split[0] # extract only the part of the `result` that was obtained at the first run
>>> type(first_result)
<class 'amplify.Result'>
>>> len(first_result)
1
>>> print(f"objective = {first_result.best.objective}, q = {q.evaluate(first_result.best.values)}")
objective = -1.0, q = [0. 0. 1.]
When num_solves is specified, solve() returns a Result object. The following are included for each of its properties. The split property on it gives a Result object that represents the result of the i-th run.
Property name |
||
|---|---|---|
The best solution for the |
The best solution for the |
|
All solutions obtained for the |
All solutions obtained for the |
|
Same as usual |
Same as usual |
|
Same as usual |
Same as usual |
|
Property obtained for the first run |
Property obtained for the |
|
The sum of the |
Property obtained for the |
|
The sum of the |
Property obtained for the |
|
Time from the start to the end of |
Time used for the |