qml.gradients.quantum_fisher

quantum_fisher(tape, device, *args, **kwargs)[source]

Returns a function that computes the quantum fisher information matrix (QFIM) of a given QNode.

Given a parametrized quantum state \(|\psi(\bm{\theta})\rangle\), the quantum fisher information matrix (QFIM) quantifies how changes to the parameters \(\bm{\theta}\) are reflected in the quantum state. The metric used to induce the QFIM is the fidelity \(f = |\langle \psi | \psi' \rangle|^2\) between two (pure) quantum states. This leads to the following definition of the QFIM (see eq. (27) in arxiv:2103.15191):

\[\text{QFIM}_{i, j} = 4 \text{Re}\left[ \langle \partial_i \psi(\bm{\theta}) | \partial_j \psi(\bm{\theta}) \rangle - \langle \partial_i \psi(\bm{\theta}) | \psi(\bm{\theta}) \rangle \langle \psi(\bm{\theta}) | \partial_j \psi(\bm{\theta}) \rangle \right]\]

with short notation \(| \partial_j \psi(\bm{\theta}) \rangle := \frac{\partial}{\partial \theta_j}| \psi(\bm{\theta}) \rangle\).

See also

metric_tensor(), adjoint_metric_tensor(), classical_fisher()

Parameters
  • tape (QNode or QuantumTape or Callable) – A quantum circuit that may have arbitrary return types.

  • *args – In case finite shots are used, further arguments according to metric_tensor() may be passed.

Returns

The transformed circuit as described in qml.transform. Executing this circuit will provide the quantum Fisher information in the form of a tensor.

Return type

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

Note

quantum_fisher coincides with the metric_tensor with a prefactor of \(4\). Internally, adjoint_metric_tensor() is used when executing on "default.qubit" with exact expectations (shots=None). In all other cases, e.g. if a device with finite shots is used, the hardware-compatible transform metric_tensor() is used, which may require an additional wire on the device. Please refer to the respective documentations for details.

Example

The quantum Fisher information matrix (QIFM) can be used to compute the natural gradient for Quantum Natural Gradient Descent. A typical scenario is optimizing the expectation value of a Hamiltonian:

from pennylane import numpy as np

n_wires = 2

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

H = 1.*qml.X(0) @ qml.X(1) - 0.5 * qml.Z(1)

@qml.qnode(dev)
def circ(params):
    qml.RY(params[0], wires=1)
    qml.CNOT(wires=(1,0))
    qml.RY(params[1], wires=1)
    qml.RZ(params[2], wires=1)
    return qml.expval(H)

params = np.array([0.5, 1., 0.2], requires_grad=True)

The natural gradient is then simply the QFIM multiplied by the gradient:

>>> grad = qml.grad(circ)(params)
>>> grad
array([ 0.59422561, -0.02615095, -0.05146226])
>>> qfim = qml.gradients.quantum_fisher(circ)(params)
>>> qfim
tensor([[1.        , 0.        , 0.        ],
        [0.        , 1.        , 0.        ],
        [0.        , 0.        , 0.77517241]], requires_grad=True)
>>> qfim @ grad
tensor([ 0.59422561, -0.02615095, -0.03989212], requires_grad=True)

When using real hardware or finite shots, quantum_fisher is internally calling metric_tensor(). To obtain the full QFIM, we need an auxilary wire to perform the Hadamard test.

>>> dev = qml.device("default.qubit", wires=n_wires+1, shots=1000)
>>> @qml.qnode(dev)
... def circ(params):
...     qml.RY(params[0], wires=1)
...     qml.CNOT(wires=(1,0))
...     qml.RY(params[1], wires=1)
...     qml.RZ(params[2], wires=1)
...     return qml.expval(H)
>>> qfim = qml.gradients.quantum_fisher(circ)(params)

Alternatively, we can fall back on the block-diagonal QFIM without the additional wire.

>>> qfim = qml.gradients.quantum_fisher(circ, approx="block-diag")(params)