adjoint_metric_tensor(tape)[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

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

Returns

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]

metric_tensor() for hardware-compatible metric tensor computations.

Example

Consider the following QNode:

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

def circuit(weights):
qml.RX(weights, wires=0)
qml.RY(weights, wires=0)
qml.CNOT(wires=[0, 1])
qml.RZ(weights, wires=1)
qml.RZ(weights, 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],


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.