Serial Solver Execution¶
You may want to run the solver several times iteratively when evaluating performance. Also, some solvers are more likely to find a good solution by running several iterations with a short timeout than by running a single iteration with a long timeout.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 more appropriate 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, FixstarsClient, 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 = FixstarsClient()
# client.token = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
client.parameters.timeout = 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, and 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.]
The following are included for each property of the Result
object returned by solve()
when num_solves
is specified, and the Result
object represents the result of the i
-th run obtained by using the split
property on it.
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 |