---
file_format: mystnb
kernelspec:
name: python3
mystnb:
execution_mode: 'inline'
---
# Execution Time information
The Amplify SDK provides an interface to obtain information of various execution times when solving combinatorial optimization problems.
```{image} ../_images/timing_light.drawio.svg
:width: 70%
:align: center
:class: only-light
```
```{image} ../_images/timing_dark.drawio.svg
:width: 70%
:align: center
:class: only-dark
```
## Types of available time information
The following table shows the types of execution time information that you can obtain as {py:class}`datetime.timedelta` objects.
```{list-table}
* - {py:attr}`amplify.Result.total_time`
- The total time taken by {py:func}`amplify.solve`.
* - {py:attr}`amplify.Result.response_time`
- The time between sending the query to the solver and receiving the response.
* - {py:attr}`amplify.Result.execution_time`
- The time the solver used to solve the problem.
* - {py:attr}`amplify.Result.Solution.time`
- For each solution the solver returns, this is the time from the start of the solver's search until the solver obtains the solution.
```
## Example of obtaining execution time
An example of obtaining execution time is shown below. First, we execute the {py:func}`~amplify.solve` function to obtain a {py:class}`~amplify.Result` object.
```{testcode}
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)
result = solve(model, client)
```
The {py:attr}`~amplify.Result.total_time` attribute of {py:class}`~amplify.Result` represents the time from the start to the end of the {py:func}`~amplify.solve` function. This time includes the response time of the solver, plus the processing time taken by the Amplify SDK to perform various conversions and create the query data to pass to the solver.
```{doctest}
>>> result.total_time # doctest: +SKIP
datetime.timedelta(seconds=1, microseconds=870877)
```
The {py:attr}`~amplify.Result.response_time` attribute of {py:class}`~amplify.Result` represents the time between sending the request to the solver in the {py:func}`~amplify.solve` function and receiving the response.
```{doctest}
>>> result.response_time # doctest: +SKIP
datetime.timedelta(seconds=1, microseconds=865211)
```
The {py:attr}`~amplify.Result.execution_time` attribute of {py:class}`~amplify.Result` represents the time taken by the solver to find the solution. This time is usually the value contained in the response from the solver.
```{doctest}
>>> result.execution_time # doctest: +SKIP
datetime.timedelta(microseconds=980543)
```
For each solution ({py:class}`~amplify.Result.Solution`) in the {py:class}`~amplify.Result`, the time between starting the solver search and finding the solution can be retrieved using the {py:attr}`~amplify.Result.Solution.time` attribute. This is usually the value contained in the solver response, but if the solver does not return such information, it will be the same value as the {py:attr}`~amplify.Result.execution_time` attribute.
```{doctest}
>>> result.best.time # doctest: +SKIP
datetime.timedelta(microseconds=27925)
```
When the {py:func}`~amplify.solve` function is run with the `dry_run` option, {py:attr}`~amplify.Result.total_time` is approximately equal to the time it took the Amplify SDK to convert the model and generate the query data. Also, {py:attr}`~amplify.Result.response_time` and {py:attr}`~amplify.Result.execution_time` will be zero.
```{doctest}
>>> dry_run_result = solve(model, client, dry_run=True)
>>> dry_run_result.total_time # doctest: +SKIP
datetime.timedelta(microseconds=59)
```
## Plotting the time obtaining the solution
For a solver configured to return multiple solutions, we will show you how to plot the time the solver obtained each solution. Such a plot allows us to see the solver's solutions at a given time during the solver execution. You can use the convergence of the solution to determine if the run time is too short or too long.
For example, the following figure shows how Amplify Annealing Engine (AE) updates the solution objective values for a 50-city random traveling salesperson problem.
The first step is to create the model. See "[](tsp.ipynb)" for details on the formulation.
```{code-cell}
---
tags: [remove-input, remove-output]
---
import numpy as np
from amplify import VariableGenerator, einsum, one_hot
N = 50
np.random.seed(12345)
x = np.random.rand(N)
y = np.random.rand(N)
d = (
(x[:, np.newaxis] - x[np.newaxis, :]) ** 2
+ (y[:, np.newaxis] - y[np.newaxis, :]) ** 2
) ** 0.5
gen = VariableGenerator()
q = gen.array("Binary", N + 1, N)
q[-1, :] = q[0, :]
objective = einsum("ij,ki,kj->", d, q[:-1], q[1:])
constraints = one_hot(q[:-1], axis=1) + one_hot(q[:-1], axis=0)
model = objective + 0.5 * d.max() * constraints
```
```{testcode}
import numpy as np
from amplify import VariableGenerator, einsum, one_hot
N = 50
x = np.random.rand(N)
y = np.random.rand(N)
d = (
(x[:, np.newaxis] - x[np.newaxis, :]) ** 2
+ (y[:, np.newaxis] - y[np.newaxis, :]) ** 2
) ** 0.5
gen = VariableGenerator()
q = gen.array("Binary", N + 1, N)
q[-1, :] = q[0, :]
objective = einsum("ij,ki,kj->", d, q[:-1], q[1:])
constraints = one_hot(q[:-1], axis=1) + one_hot(q[:-1], axis=0)
model = objective + d.max() * constraints
```
Now, we set the solver client. By default, Amplify AE returns only the best solution found, so we configure it to return all solutions found during the search.
```{code-cell}
---
tags: [remove-input, remove-output]
---
from amplify import FixstarsClient
client = FixstarsClient()
client.parameters.timeout = 1000
client.parameters.outputs.num_outputs = 0
```
```{testcode}
from amplify import FixstarsClient
client = FixstarsClient()
client.parameters.timeout = 1000 # Timeout is 1000 ms
client.parameters.outputs.num_outputs = 0 # Return all solutions found
```
Run the solver and plot the times and objective function values of all solutions obtained.
```{code-cell}
---
tags: [remove-input, remove-output]
---
from amplify import solve
import matplotlib.pyplot as plt
while True:
result = solve(model, client)
if result.best.objective <= 5.55:
break
fig = plt.figure(1)
times = [solution.time.total_seconds() for solution in result]
objective_values = [solution.objective for solution in result]
plt.scatter(times, objective_values)
plt.plot(times, objective_values)
plt.xlabel("elapsed time in seconds")
plt.ylabel("objective value")
plt.grid(True)
```
```{testcode}
from amplify import solve
import matplotlib.pyplot as plt
# Run the solver
result = solve(model, client)
# Get time and objective function values for each solution
times = [solution.time.total_seconds() for solution in result]
objective_values = [solution.objective for solution in result]
# Plot
plt.scatter(times, objective_values)
plt.plot(times, objective_values)
plt.xlabel("elapsed time in seconds")
plt.ylabel("objective value")
plt.grid(True)
```
```{eval:figure} fig
:width: 50%
History of solutions obtained by Amplify AE for the 50-city traveling salesperson problem
```
You can retrieve the initial and best solutions of the search and plot against each other as follows.
```{code-cell}
---
tags: [remove-input, remove-output]
---
def tsp_plot(q_values, x, y):
route_x = q_values @ x
route_y = q_values @ y
fig = plt.figure()
plt.scatter(x, y)
plt.plot(route_x, route_y)
return fig
# Plot of the initial solution
fig_initial = tsp_plot(q.evaluate(result[-1].values), x, y)
init_sec = round(result[-1].time.total_seconds(), 2)
# Plot of the best solution
fig_last = tsp_plot(q.evaluate(result[0].values), x, y)
last_sec = round(result[0].time.total_seconds(), 2)
```
```{testcode}
def tsp_plot(q_values, x, y):
route_x = q_values @ x
route_y = q_values @ y
plt.scatter(x, y)
plt.plot(route_x, route_y)
plt.show()
# Plot of the initial solution
tsp_plot(q.evaluate(result[-1].values), x, y)
# Plot of the best solution
tsp_plot(q.evaluate(result[0].values), x, y)
```
`````{grid} 1 1 2 2
````{grid-item}
```{eval:figure} fig_initial
Path ({eval}`init_sec` seconds) of the initial solution
```
````
````{grid-item}
```{eval:figure} fig_last
Path ({eval}`last_sec` seconds) of the best solution
```
````
`````