qml.RotosolveOptimizer¶

class RotosolveOptimizer[source]

Bases: object

The Rotosolve optimizer minimizes an objective function with respect to the parameters of a quantum circuit without the need for calculating the gradient of the function. The algorithm works by updating the parameters $$\theta = \theta_1, \dots, \theta_D$$ one at a time according to a closed-form expression for the optimal value of the $$d^{th}$$ parameter $$\theta^*_d$$ when the other parameters are fixed:

$\theta^*_d = \underset{\theta_d}{\text{argmin}}\left<H\right>_{\theta_d} = -\frac{\pi}{2} - \text{arctan2}\left(2\left<H\right>_{\theta_d=0} - \left<H\right>_{\theta_d=\pi/2} - \left<H\right>_{\theta_d=-\pi/2}, \left<H\right>_{\theta_d=\pi/2} - \left<H\right>_{\theta_d=-\pi/2}\right),$

where $$\left<H\right>_{\theta_d}$$ is the expectation value of the objective function optimized over the parameter $$\theta_d$$. $$\text{arctan2}(x, y)$$ computes the element-wise arc tangent of $$x/y$$ choosing the quadrant correctly, avoiding, in particular, division-by-zero when $$y = 0$$.

The algorithm is described in further detail in Ostaszewski et al. (2019)

Example:

Initialize the optimizer, set the initial values of x to be used and set the number of steps to optimize over.

>>> opt = qml.optimize.RotosolveOptimizer()
>>> x = [0.3, 0.7]
>>> n_steps = 10


Set up the PennyLane circuit using the default.qubit as simulator device.

>>> dev = qml.device("default.qubit", shots=None, wires=2)
... @qml.qnode(dev)
... def circuit(params):
...     qml.RX(params[0], wires=0)
...     qml.RY(params[1], wires=1)
...     qml.CNOT(wires=[0, 1])
...     return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliX(1))


Define a cost function (that takes a list of values as input and return a single value) based on the above circuit.

>>> def cost(x):
...     Z_1, X_2 = circuit(x)
...     return 0.2 * Z_1 + 0.5 * X_2


Run the optimization step-by-step for n_steps steps.

>>> cost_rotosolve = []
>>> for _ in range(n_steps):
...     cost_rotosolve.append(cost(x))
...     x = opt.step(cost, x)


The optimized values for x should now be stored in x and steps-vs-cost can be seen by plotting cost_rotosel.

 step(objective_fn, *args, **kwargs) Update args with one step of the optimizer. step_and_cost(objective_fn, *args, **kwargs) Update args with one step of the optimizer and return the corresponding objective function value prior to the step.
step(objective_fn, *args, **kwargs)[source]

Update args with one step of the optimizer.

Parameters
• objective_fn (function) – the objective function for optimization. It should take a sequence of the values *args and a list of the gates generators as inputs, and return a single value.

• *args – variable length sequence containing the initial values of the variables to be optimized over or a single float with the initial value.

• **kwargs – variable length keyword arguments for the objective function.

Returns

the new variable values $$x^{(t+1)}$$. If single arg is provided, list [array] is replaced by array.

Return type

list [array]

step_and_cost(objective_fn, *args, **kwargs)[source]

Update args with one step of the optimizer and return the corresponding objective function value prior to the step.

Parameters
• objective_fn (function) – The objective function for optimization. It should take a sequence of the values *args as inputs, and return a single value.

• *args – variable length argument list containing the initial values of the variables to be optimized over or a single float with the initial value

• **kwargs – variable length dictionary of keywords for the objective function

Returns

the new variable values $$x^{(t+1)}$$ and the objective function output prior to the step. If single arg is provided, list [array] is replaced by array.

Return type

tuple[list [array], float]