qml.adjoint_metric_tensor

adjoint_metric_tensor(circuit, device=None, hybrid=True)[source]

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.

Note

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.

Parameters
  • circuit (QuantumTape or QNode) – Circuit to compute the metric tensor of

  • device (Device) – Device to use for the adjoint method

  • hybrid (bool) – Whether to take classical preprocessing into account. Ignored if circuit is a tape.

Returns

the metric tensor of the tape with respect to its trainable parameters. Dimensions are (tape.num_params, tape.num_params).

Return type

array

See also

metric_tensor() for hardware-compatible metric tensor computations.

Example

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.PauliZ(0) @ qml.PauliZ(1)), qml.expval(qml.PauliY(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
0.019
>>> mt_fn_2 = qml.metric_tensor(circuit)
>>> start_time = time.process_time()
>>> mt = mt_fn_2(weights)
>>> time.process_time() - start_time
0.025

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.