Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove hardware argument in qml.qinfo.quantum_fisher #2695

Merged
merged 8 commits into from
Jun 15, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 22 additions & 12 deletions pennylane/qinfo/transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,8 +400,8 @@ def wrapper(*args, **kwargs):
return wrapper


def quantum_fisher(qnode, *args, hardware=False, **kwargs):
r"""Returns a function that computes the quantum fisher information matrix (QFIM) of a given :class:`.QNode` or quantum tape.
def quantum_fisher(qnode, *args, **kwargs):
r"""Returns a function that computes the quantum fisher information matrix (QFIM) of a given :class:`.QNode`.

Given a parametrized quantum state :math:`|\psi(\bm{\theta})\rangle`, the quantum fisher information matrix (QFIM) quantifies how changes to the parameters :math:`\bm{\theta}`
are reflected in the quantum state. The metric used to induce the QFIM is the fidelity :math:`f = |\langle \psi | \psi' \rangle|^2` between two (pure) quantum states.
Expand All @@ -418,16 +418,16 @@ def quantum_fisher(qnode, *args, hardware=False, **kwargs):
:func:`~.pennylane.metric_tensor`, :func:`~.pennylane.adjoint_metric_tensor`, :func:`~.pennylane.qinfo.transforms.classical_fisher`

Args:
qnode (:class:`.QNode` or qml.QuantumTape): A :class:`.QNode` or quantum tape that may have arbitrary return types.
hardware (bool): Indicate if execution needs to be hardware compatible (True)
qnode (:class:`.QNode`): A :class:`.QNode` that may have arbitrary return types.
args: In case finite shots are used, further arguments according to :func:`~.pennylane.metric_tensor` may be passed.
Qottmann marked this conversation as resolved.
Show resolved Hide resolved

Returns:
func: The function that computes the quantum fisher information matrix.

.. note::

``quantum_fisher`` coincides with the ``metric_tensor`` with a prefactor of :math:`4`. In case of ``hardware=True``, the hardware compatible transform :func:`~.pennylane.metric_tensor` is used.
In case of ``hardware=False``, :func:`~.pennylane.adjoint_metric_tensor` is used. Please refer to their respective documentations for details on the arguments.
``quantum_fisher`` coincides with the ``metric_tensor`` with a prefactor of :math:`4`. In case a device with finite shots is used, the hardware compatible transform :func:`~.pennylane.metric_tensor` is used.
In case of a device with ``shots=None``, :func:`~.pennylane.adjoint_metric_tensor` is used. Please refer to their respective documentations for details on the arguments.
Qottmann marked this conversation as resolved.
Show resolved Hide resolved

**Example**

Expand Down Expand Up @@ -463,16 +463,26 @@ def circ(params):
>>> q_nat_grad = qfim @ grad
[ 0.59422561 -0.02615095 -0.03989212]

When using real hardware with finite shots, we have to specify ``hardware=True`` in order to compute the QFIM.
Additionally, we need to provide a device that has a spare wire for the Hadamard test, otherwise it will just be able to compute the block diagonal terms.
When using real hardware or finite shots, ``quantum_fisher`` is internally calling :func:`~.pennylane.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)
>>> circ = qml.QNode(circ, dev)
>>> qfim = qml.qinfo.quantum_fisher(circ, hardware=True)(params)
>>> @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.qinfo.quantum_fisher(circ)(params)

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

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

"""
# TODO: ``hardware`` argument will be obsolete in future releases when ``shots`` can be inferred.
if hardware:

if qnode.device.shots is not None:
Qottmann marked this conversation as resolved.
Show resolved Hide resolved

def wrapper(*args0, **kwargs0):
return 4 * metric_tensor(qnode, *args, **kwargs)(*args0, **kwargs0)
Expand Down
14 changes: 8 additions & 6 deletions tests/qinfo/test_fisher.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,23 +155,25 @@ def test_quantum_fisher_info(
n_wires = 2

dev = qml.device("default.qubit", wires=n_wires)
dev_hard = qml.device("default.qubit", wires=n_wires + 1, shots=1000)

@qml.qnode(dev)
def circ(params):
def qfunc(params):
qml.RX(params[0], wires=0)
qml.RX(params[1], wires=0)
qml.CNOT(wires=(0, 1))
return qml.state()

params = pnp.random.random(2)

QFIM_hard = quantum_fisher(circ, hardware=True)(params)
QFIM1_hard = 4.0 * qml.metric_tensor(circ)(params)
circ_hard = qml.QNode(qfunc, dev_hard)
QFIM_hard = quantum_fisher(circ_hard)(params)
QFIM1_hard = 4.0 * qml.metric_tensor(circ_hard)(params)

QFIM = quantum_fisher(circ, hardware=False)(params)
circ = qml.QNode(qfunc, dev)
QFIM = quantum_fisher(circ)(params)
QFIM1 = 4.0 * qml.adjoint_metric_tensor(circ)(params)
assert np.allclose(QFIM, QFIM1)
assert np.allclose(QFIM_hard, QFIM1_hard)
assert np.allclose(QFIM_hard, QFIM1_hard, atol=1e-1)


class TestInterfacesClassicalFisher:
Expand Down