Implements the adjoint method outlined in Jones to compute the metric tensor.

A forward pass followed by intermediate partial backwards passes are used to evaluate the metric tensor in \(\mathcal{O}(p^2)\) operations, where \(p\) is the number of trainable operations, using 4 state vectors.


The adjoint metric tensor method has the following restrictions:

  • Currently only "default.qubit" with shots=None is supported.

  • We assume the circuit to be composed of unitary gates only and rely on the generator property of the gates to be implemented. Note also that this makes the metric tensor strictly real-valued.


tape (QNode or QuantumTape) – Circuit to compute the metric tensor of


The transformed circuit as described in qml.transform. Executing this circuit will provide the metric tensor in the form of a tensor. Dimensions are (tape.num_params, tape.num_params).

Return type

qnode (QNode) or tuple[List[QuantumTape], function]

See also

metric_tensor() for hardware-compatible metric tensor computations.


Consider the following QNode:

dev = qml.device("default.qubit", wires=3)

@qml.qnode(dev, interface="autograd")
def circuit(weights):
    qml.RX(weights[0], wires=0)
    qml.RY(weights[1], wires=0)
    qml.CNOT(wires=[0, 1])
    qml.RZ(weights[2], wires=1)
    qml.RZ(weights[3], wires=0)
    return qml.expval(qml.Z(0) @ qml.Z(1)), qml.expval(qml.Y(1))

We can use the adjoint_metric_tensor transform to generate a new function that returns the metric tensor of this QNode:

>>> mt_fn = qml.adjoint_metric_tensor(circuit)
>>> weights = np.array([0.1, 0.2, 0.4, 0.5], requires_grad=True)
>>> mt_fn(weights)
tensor([[ 0.25  ,  0.    , -0.0497, -0.0497],
        [ 0.    ,  0.2475,  0.0243,  0.0243],
        [-0.0497,  0.0243,  0.0123,  0.0123],
        [-0.0497,  0.0243,  0.0123,  0.0123]], requires_grad=True)

This approach has the benefit of being significantly faster than the hardware-ready metric_tensor function:

>>> import time
>>> start_time = time.process_time()
>>> mt = mt_fn(weights)
>>> time.process_time() - start_time
>>> mt_fn_2 = qml.metric_tensor(circuit)
>>> start_time = time.process_time()
>>> mt = mt_fn_2(weights)
>>> time.process_time() - start_time

This speedup becomes more drastic for larger circuits. The drawback of the adjoint method is that it is only available on simulators and without shot simulations.