Second order differentiation using adjoint differentiation

Hi,

I noticed that calculating qml.jacobian(qml.grad(circuit)) using (lightning.qubit, diff_method=‘adjoint’) is not possible but using other diff methods is possible. But other diff methods are quite slow. Can someone please let me know if this is the case or am i making mistake.

Thanks

Hey @Abhishek! Welcome to the forum :smiley:!

Tagging @mlxd here because he’s the expert, but pennylane-lightning does support adjoint differentiation — there are caveats! For details, see here.

TLDR, adjoint differentiation has the following restrictions:

  • As it requires knowledge of the statevector, only statevector simulator devices can be used.
  • Only expectation values are supported as measurements.
  • Does not work for parametrized observables like Hamiltonian or Hermitian.

So, here is an example of pennylane-lightning in action with adjoint differentiation and the object you want calculated!

import pennylane as qml
from pennylane import numpy as np

dev_lightning = qml.device('lightning.qubit', wires=2)
dev = qml.device('default.qubit', wires=2)

@qml.qnode(dev_lightning, diff_method="adjoint")
def circuit_adjoint(a):
    qml.RX(a[0], wires=0)
    qml.CNOT(wires=(0,1))
    qml.RY(a[1], wires=1)
    qml.RZ(a[2], wires=1)
    return qml.expval(qml.PauliX(wires=1))

x = np.array([0.1, 0.2, 0.3], requires_grad=True)
print(qml.jacobian(qml.grad(circuit_adjoint))(x))

I’m not sure if you actually meant to calculate qml.jacobian(qml.grad(circuit_adjoint))(x). In this example, the output seems to be independent of the input. Nevertheless, it runs! If you meant to just calculate, say, the Jacobian, it works as well :).

1 Like

Hi @isaacdevlugt , thanks for your support. I slightly modified your example and shown the output below,

import pennylane as qml
from pennylane import numpy as np
dev_lightning = qml.device('lightning.qubit', wires=2)

@qml.qnode(dev_lightning, diff_method="adjoint", max_diff=2)
def circuit_adjoint(a):
    qml.RX(a[0], wires=0)
    qml.RZ(a[1], wires=1)
    qml.CNOT(wires=(0,1))
    return qml.expval(qml.PauliZ(wires=0))

x = np.array([0.1, 0.2], requires_grad=True)

image

qml.jacobian(qml.grad(circuit_adjoint))(x)
array([[0., 0.],
       [0., 0.]])
qml.gradients.param_shift_hessian(circuit_adjoint)(x)
tensor([[-9.95004165e-01, -8.32667268e-17],
        [-8.32667268e-17,  1.11022302e-16]], requires_grad=True)

Now change the diff_method to ‘best’ and rerun the same lines.

qml.jacobian(qml.grad(circuit_adjoint))(x)
array([[-9.95004165e-01, -8.32667268e-17],
       [-8.32667268e-17, -1.11022302e-16]])

Please compare with above output from parameter shift hessian.

By the way, do you have some idea about dask parallelizing. I have posted something there. It would be great if you can share your views.

Thanks :smile:

Hi @Abhishek
Just jumping in for a comment. The adjoint diff method in lightning.qubit only supports evaluation of Jacobian expectation values. If you would like to evaluate the Hessian, it is best to use the parameter shift method you have already discovered, since param-shift will have all the necessary backend support for the higher-order derivative.

Hope this helps.

Hi @mlxd … Thanks for your support.