Can't understand what the multiple outputs of the step_and_cost and step functions are

I am having trouble reading the optimiser documentation. I am trying to understand what the individual outputs of the step_and_cost and step functions are, however the documentation is not as easy to understand as I would like it to be. So here please ask what are the individual outputs of the step_and_cost and step functions and what data types. Also is it possible to read the gradient of each step, I failed to read the gradient of pennylane.

Hey @RX1! Thanks for the feedback :slight_smile:

Let’s look at an example. I’ve taken some of this code from our qubit rotation demo. I’ll define the usual things first (a differentiable function that relies on the output of a quantum circuit):

import pennylane as qml
from pennylane import numpy as np

dev1 = qml.device("lightning.qubit", wires=1)

@qml.qnode(dev1)
def circuit(params):
    qml.RX(params[0], wires=0)
    qml.RY(params[1], wires=0)
    return qml.expval(qml.PauliZ(0))

def cost(x):
    return circuit(x)

init_params = np.array([0.011, 0.012], requires_grad=True)
print(cost(init_params))
0.9998675058299389

According to the documentation, opt.step will just give you the parameters back as an output:

params = init_params

opt = qml.GradientDescentOptimizer(stepsize=0.4)

for _ in range(3):
    params = opt.step(cost, params)
    print(params)
[0.01539959 0.01679959]
[0.02155832 0.02351832]
[0.03017859 0.03292259]

opt.step_and_cost behaves somewhat similarly to opt.step, but outputs the cost function’s value before the parameters were updated.

params = init_params

opt = qml.GradientDescentOptimizer(stepsize=0.4)

for _ in range(3):
    params, cost_val = opt.step_and_cost(cost, params)
    print(params, cost_val)
[0.01539959 0.01679959] 0.9998675058299389
[0.02155832 0.02351832] 0.9997403354524409
[0.03017859 0.03292259] 0.9994911497625389

If you had multiple individual parameters in your circuit like the example below, then your output would correspond as follows

@qml.qnode(dev1)
def circuit(param1, param2):
    qml.RX(param1, wires=0)
    qml.RY(param2, wires=0)
    return qml.expval(qml.PauliZ(0))

def cost(x1, x2):
    return circuit(x1, x2)

init_x1, init_x2 = np.array([0.011], requires_grad=True), np.array([0.012], requires_grad=True)
print(cost(init_x1, init_x2))
0.9998675058299389
x1, x2 = init_x1, init_x2

opt = qml.GradientDescentOptimizer(stepsize=0.4)

for _ in range(3):
    x1, x2 = opt.step(cost, x1, x2)
    print(x1, x2)
[0.01539959] [0.01679959]
[0.02155832] [0.02351832]
[0.03017859] [0.03292259]

and with opt.step_and_cost, you just need to make sure that your parameters are inside of a tuple — the output of step_and_cost is still two things: your parameters and the cost function’s value before the parameters were updated.

x1, x2 = init_x1, init_x2

opt = qml.GradientDescentOptimizer(stepsize=0.4)

for _ in range(3):
    (x1, x2), cost_val = opt.step_and_cost(cost, x1, x2)
    print(x1, x2, cost_val)
[0.01539959] [0.01679959] 0.9998675058299389
[0.02155832] [0.02351832] 0.9997403354524409
[0.03017859] [0.03292259] 0.9994911497625389

Also is it possible to read the gradient of each step, I failed to read the gradient of pennylane.

It sure is! I encourage you to check out this section of the qubit rotation demo.

Hope this helps!

2 Likes