hadamard_grad(tape, argnum=None, aux_wire=None, device_wires=None)

Transform a QNode to compute the Hadamard test gradient of all gates with respect to their inputs.

Parameters
• tape (pennylane.QNode or QuantumTape) – quantum tape or QNode to differentiate

• argnum (int or list[int] or None) – Trainable tape parameter indices to differentiate with respect to. If not provided, the derivatives with respect to all trainable parameters are returned.

• aux_wire (pennylane.wires.Wires) – Auxiliary wire to be used for the Hadamard tests. If None (the default), a suitable wire is inferred from the wires used in the original circuit and device_wires.

• device_wires (pennylane.wires.Wires) – Wires of the device that are going to be used for the gradient. Facilitates finding a default for aux_wire if aux_wire is None.

Returns

• If the input is a QNode, an object representing the Jacobian (function) of the QNode that can be executed to obtain the Jacobian. The type of the Jacobian returned is either a tensor, a tuple or a nested tuple depending on the nesting structure of the original QNode output.

• If the input is a tape, a tuple containing a list of generated tapes, together with a post-processing function to be applied to the results of the evaluated tapes in order to obtain the Jacobian.

Return type

function or tuple[list[QuantumTape], function]

For a variational evolution $$U(\mathbf{p}) \vert 0\rangle$$ with $$N$$ parameters $$\mathbf{p}$$, consider the expectation value of an observable $$O$$:

$f(\mathbf{p}) = \langle \hat{O} \rangle(\mathbf{p}) = \langle 0 \vert U(\mathbf{p})^\dagger \hat{O} U(\mathbf{p}) \vert 0\rangle.$

$\frac{\partial f}{\partial \mathbf{p}} = -2 \Im[\bra{0} \hat{O} G \ket{0}] = i \left(\bra{0} \hat{O} G \ket{ 0} - \bra{0} G\hat{O} \ket{0}\right) = -2 \bra{+}\bra{0} ctrl-G^{\dagger} (\hat{Y} \otimes \hat{O}) ctrl-G \ket{+}\ket{0}$

Here, $$G$$ is the generator of the unitary $$U$$.

Example

This gradient transform can be applied directly to QNode objects:

>>> dev = qml.device("default.qubit", wires=2)
>>> @qml.qnode(dev)
... def circuit(params):
...     qml.RX(params, wires=0)
...     qml.RY(params, wires=0)
...     qml.RX(params, wires=0)
...     return qml.expval(qml.PauliZ(0))
>>> params = np.array([0.1, 0.2, 0.3], requires_grad=True)


This quantum gradient transform can also be applied to low-level QuantumTape objects. This will result in no implicit quantum device evaluation. Instead, the processed tapes, and post-processing function, which together define the gradient are directly returned:

>>> ops = [qml.RX(p, wires=0) for p in params]
>>> measurements = [qml.expval(qml.PauliZ(0))]
>>> tape = qml.tape.QuantumTape(ops, measurements)
[<QuantumTape: wires=[0, 1], params=3>,
<QuantumTape: wires=[0, 1], params=3>,
<QuantumTape: wires=[0, 1], params=3>]


This can be useful if the underlying circuits representing the gradient computation need to be analyzed.

The output tapes can then be evaluated and post-processed to retrieve the gradient:

>>> dev = qml.device("default.qubit", wires=2)
(array(-0.3875172), array(-0.18884787), array(-0.38355704))


This transform can be registered directly as the quantum gradient transform to use during autodifferentiation:

>>> dev = qml.device("default.qubit", wires=3)
... def circuit(params):
...     qml.RX(params, wires=0)
...     qml.RY(params, wires=0)
...     qml.RX(params, wires=0)
...     return qml.expval(qml.PauliZ(0))
>>> params = jax.numpy.array([0.1, 0.2, 0.3])
>>> jax.jacobian(circuit)(params)
[-0.3875172  -0.18884787 -0.38355704]


If you use custom wires on your device, you need to pass an auxiliary wire.

>>> dev_wires = ("a", "c")
>>> dev = qml.device("default.qubit", wires=dev_wires)
>>> @qml.qnode(dev, interface="jax", diff_method="hadamard", aux_wire="c", device_wires=dev_wires)
>>> def circuit(params):
...    qml.RX(params, wires="a")
...    qml.RY(params, wires="a")
...    qml.RX(params, wires="a")
...    return qml.expval(qml.PauliZ("a"))
>>> params = jax.numpy.array([0.1, 0.2, 0.3])
>>> jax.jacobian(circuit)(params)
[-0.3875172  -0.18884787 -0.38355704]


Note

hadamard_grad will decompose the operations that are not in the list of supported operations.

• pennylane.RX

• pennylane.RY

• pennylane.RZ

• pennylane.Rot

• pennylane.PhaseShift

• pennylane.U1

• pennylane.CRX

• pennylane.CRY

• pennylane.CRZ

• pennylane.IsingXX

• pennylane.IsingYY

• pennylane.IsingZZ

The expansion will fail if a suitable decomposition in terms of supported operation is not found. The number of trainable parameters may increase due to the decomposition.