Skip to content

Commit

Permalink
Daily rc sync to master 2022-06-16 (#2725)
Browse files Browse the repository at this point in the history
* Update phase decomp test (#2697)

* Update interfaces.rst (#2698)

* Allow templates to be decomposed (#2704)

* Allow templates to be decomposed

* Add TODO to clean

* Update changelog

* Fix pylint error

Co-authored-by: Edward Jiang <edward.jiang@resident.xanadu.ai>

* Deprecate `qml.ExpvalCost` (#2571)

* Add UserWarning to ExpvalCost and catch said warning in all tests containing ExpvalCost

* added warning in doc string

* added to the changelog in 0.23.1

* Update pennylane/vqe/vqe.py

Co-authored-by: Josh Izaac <josh146@gmail.com>

* Update pennylane/vqe/vqe.py

Co-authored-by: Josh Izaac <josh146@gmail.com>

* Update pennylane/vqe/vqe.py

Co-authored-by: Josh Izaac <josh146@gmail.com>

* Update pennylane/vqe/vqe.py

Co-authored-by: Josh Izaac <josh146@gmail.com>

* Update tests/optimize/test_qng.py

Co-authored-by: Josh Izaac <josh146@gmail.com>

* Update tests/qchem/of_tests/test_convert.py

Co-authored-by: Josh Izaac <josh146@gmail.com>

* Update tests/test_qaoa.py

Co-authored-by: Josh Izaac <josh146@gmail.com>

* Update tests/test_vqe.py

Co-authored-by: Josh Izaac <josh146@gmail.com>

* Update tests/transforms/test_adjoint_metric_tensor.py

Co-authored-by: Josh Izaac <josh146@gmail.com>

* Update tests/optimize/test_optimize_shot_adapative.py

Co-authored-by: Josh Izaac <josh146@gmail.com>

* overlooked to catch the new warning in test_dipole_of

* switched to the right changelog

Co-authored-by: Qottmann <qottmann@LT0290.ad.icfo.net>
Co-authored-by: Josh Izaac <josh146@gmail.com>
Co-authored-by: Romain Moyard <rmoyard@gmail.com>

* Update JAX jit forward mode forward evaluation (#2700)

* update logic

* update logic

* test case

* test assertion

Co-authored-by: Romain Moyard <rmoyard@gmail.com>

* Improve ising gates documentation (#2711)

* Update doc

* Update

* Update pennylane/ops/qubit/parametric_ops.py

Co-authored-by: antalszava <antalszava@gmail.com>

* Apply suggestions from code review

* CHange

* Typo

Co-authored-by: antalszava <antalszava@gmail.com>

* Support classical fisher gradients when using Autograd (#2688)

* Support classical fisher gradients when using Autograd

* Update doc/releases/changelog-dev.md

* added test for jax and autograd

* black

* added xfail tests

* added comment in doc string about torch and tf not diffable

Co-authored-by: Qottmann <korbinian.kottmann@gmail.com>
Co-authored-by: Romain Moyard <rmoyard@gmail.com>

* Support classical Fisher gradients when using TF and torch (#2710)

* Support classical fisher gradients when using Autograd

* Update doc/releases/changelog-dev.md

* added test for jax and autograd

* black

* added xfail tests

* added comment in doc string about torch and tf not diffable

* Fix differentiability for tensorflow and torch

* Remove warning

* Fix TF not iterable error

* Trigger CI

* added example in doc-string about diffability

* close-block becomes code-block lol

* Update pennylane/qinfo/transforms.py

Co-authored-by: Josh Izaac <josh146@gmail.com>

* Change pytest mark

Co-authored-by: Josh Izaac <josh146@gmail.com>
Co-authored-by: Qottmann <korbinian.kottmann@gmail.com>
Co-authored-by: Romain Moyard <rmoyard@gmail.com>

* Remove `hardware` argument in `qml.qinfo.quantum_fisher` (#2695)

* removed hardware argument, adapted doc string

* updated tests

* black

* Update pennylane/qinfo/transforms.py

Co-authored-by: Josh Izaac <josh146@gmail.com>

* Update pennylane/qinfo/transforms.py

Co-authored-by: Josh Izaac <josh146@gmail.com>

* add default.qubit condition and change doc string

Co-authored-by: Josh Izaac <josh146@gmail.com>
Co-authored-by: Romain Moyard <rmoyard@gmail.com>

* Add `qinfo` measurements in supported configurations docs (#2712)

* Add documentation for new qinfo measurements

* Add docs for finite diff

* Fix JAX tests

* Change param shift to red

* Move entropy param shift tests

Co-authored-by: Romain Moyard <rmoyard@gmail.com>

* Use access_state (#2719)

Co-authored-by: Romain Moyard <rmoyard@gmail.com>

* exclude files from pr

Co-authored-by: antalszava <antalszava@gmail.com>
Co-authored-by: Edward Jiang <34989448+eddddddy@users.noreply.github.com>
Co-authored-by: Edward Jiang <edward.jiang@resident.xanadu.ai>
Co-authored-by: Korbinian Kottmann <43949391+Qottmann@users.noreply.github.com>
Co-authored-by: Qottmann <qottmann@LT0290.ad.icfo.net>
Co-authored-by: Josh Izaac <josh146@gmail.com>
Co-authored-by: Romain Moyard <rmoyard@gmail.com>
Co-authored-by: Qottmann <korbinian.kottmann@gmail.com>
Co-authored-by: GitHub Actions Bot <>
  • Loading branch information
9 people committed Jun 16, 2022
1 parent 127391e commit b6fc538
Show file tree
Hide file tree
Showing 8 changed files with 445 additions and 140 deletions.
111 changes: 56 additions & 55 deletions doc/introduction/interfaces.rst

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion pennylane/gradients/parameter_shift.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import numpy as np

import pennylane as qml
from pennylane.measurements import State, VnEntropy, MutualInfo

from .gradient_transform import (
gradient_transform,
Expand Down Expand Up @@ -614,7 +615,7 @@ def param_shift(
[ 0.69916862 0.34072424 0.69202359]]
"""

if any(m.return_type is qml.measurements.State for m in tape.measurements):
if any(m.return_type in [State, VnEntropy, MutualInfo] for m in tape.measurements):
raise ValueError(
"Computing the gradient of circuits that return the state is not supported."
)
Expand Down
60 changes: 47 additions & 13 deletions pennylane/ops/qubit/parametric_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -2306,13 +2306,21 @@ class IsingXX(Operation):
r"""
Ising XX coupling gate
.. math:: XX(\phi) = \begin{bmatrix}
.. math:: XX(\phi) = \exp(-i \frac{\theta}{2} (X \otimes X)) =
\begin{bmatrix} =
\cos(\phi / 2) & 0 & 0 & -i \sin(\phi / 2) \\
0 & \cos(\phi / 2) & -i \sin(\phi / 2) & 0 \\
0 & -i \sin(\phi / 2) & \cos(\phi / 2) & 0 \\
-i \sin(\phi / 2) & 0 & 0 & \cos(\phi / 2)
\end{bmatrix}.
.. note::
Special cases of using the :math:`XX` operator include:
* :math:`XX(0) = I`;
* :math:`XX(\pi) = i (X \otimes X)`.
**Details:**
* Number of wires: 2
Expand Down Expand Up @@ -2429,13 +2437,21 @@ class IsingYY(Operation):
r"""
Ising YY coupling gate
.. math:: \mathtt{YY}(\phi) = \begin{bmatrix}
\cos(\phi / 2) & 0 & 0 & i \sin(\phi / 2) \\
0 & \cos(\phi / 2) & -i \sin(\phi / 2) & 0 \\
0 & -i \sin(\phi / 2) & \cos(\phi / 2) & 0 \\
i \sin(\phi / 2) & 0 & 0 & \cos(\phi / 2)
.. math:: \mathtt{YY}(\phi) = \exp(-i \frac{\theta}{2} (Y \otimes Y)) =
\begin{bmatrix}
\cos(\phi / 2) & 0 & 0 & i \sin(\phi / 2) \\
0 & \cos(\phi / 2) & -i \sin(\phi / 2) & 0 \\
0 & -i \sin(\phi / 2) & \cos(\phi / 2) & 0 \\
i \sin(\phi / 2) & 0 & 0 & \cos(\phi / 2)
\end{bmatrix}.
.. note::
Special cases of using the :math:`YY` operator include:
* :math:`YY(0) = I`;
* :math:`YY(\pi) = i (Y \otimes Y)`.
**Details:**
* Number of wires: 2
Expand Down Expand Up @@ -2551,13 +2567,22 @@ class IsingZZ(Operation):
r"""
Ising ZZ coupling gate
.. math:: ZZ(\phi) = \begin{bmatrix}
e^{-i \phi / 2} & 0 & 0 & 0 \\
0 & e^{i \phi / 2} & 0 & 0 \\
0 & 0 & e^{i \phi / 2} & 0 \\
0 & 0 & 0 & e^{-i \phi / 2}
.. math:: ZZ(\phi) = \exp(-i \frac{\theta}{2} (Z \otimes Z) =
\begin{bmatrix}
e^{-i \phi / 2} & 0 & 0 & 0 \\
0 & e^{i \phi / 2} & 0 & 0 \\
0 & 0 & e^{i \phi / 2} & 0 \\
0 & 0 & 0 & e^{-i \phi / 2}
\end{bmatrix}.
.. note::
Special cases of using the :math:`ZZ` operator include:
* :math:`ZZ(0) = I`;
* :math:`ZZ(\pi) = - (Z \otimes Z)`;
* :math:`ZZ(2\pi) = - I`;
**Details:**
* Number of wires: 2
Expand Down Expand Up @@ -2702,15 +2727,24 @@ def pow(self, z):

class IsingXY(Operation):
r"""
Ising XY coupling gate
Ising (XX + YY) coupling gate
.. math:: \mathtt{XY}(\phi) = \begin{bmatrix}
.. math:: \mathtt{XY}(\phi) = \exp(-i \frac{\theta}{4} (X \otimes X + Y \otimes Y) =
\begin{bmatrix}
1 & 0 & 0 & 0 \\
0 & \cos(\phi / 2) & i \sin(\phi / 2) & 0 \\
0 & i \sin(\phi / 2) & \cos(\phi / 2) & 0 \\
0 & 0 & 0 & 1
\end{bmatrix}.
.. note::
Special cases of using the :math:`XY` operator include:
* :math:`XY(0) = I`;
* :math:`XY(\frac{\pi}{2}) = \sqrt{iSWAP}`;
* :math:`XY(\pi) = iSWAP`;
**Details:**
* Number of wires: 2
Expand Down
81 changes: 57 additions & 24 deletions pennylane/qinfo/transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
# pylint: disable=import-outside-toplevel, not-callable
import functools
import pennylane as qml

from pennylane.transforms import batch_transform, metric_tensor, adjoint_metric_tensor
from pennylane.devices import DefaultQubit


def reduced_dm(qnode, wires):
Expand Down Expand Up @@ -181,8 +183,8 @@ def _torch_jac(circ):
def wrapper(*args, **kwargs):
loss = functools.partial(circ, **kwargs)
if len(args) > 1:
return torch.autograd.functional.jacobian(loss, (args))
return torch.autograd.functional.jacobian(loss, *args)
return torch.autograd.functional.jacobian(loss, args, create_graph=True)
return torch.autograd.functional.jacobian(loss, *args, create_graph=True)

return wrapper

Expand All @@ -195,7 +197,7 @@ def _tf_jac(circ):
def wrapper(*args, **kwargs):
with tf.GradientTape() as tape:
loss = circ(*args, **kwargs)
return tape.jacobian(loss, (args))
return tape.jacobian(loss, args)

return wrapper

Expand All @@ -207,12 +209,12 @@ def _compute_cfim(p, dp):
# Exclude values where p=0 and calculate 1/p
nonzeros_p = qml.math.where(p > 0, p, qml.math.ones_like(p))
one_over_p = qml.math.where(p > 0, qml.math.ones_like(p), qml.math.zeros_like(p))
one_over_p = qml.math.divide(one_over_p, nonzeros_p)
one_over_p = one_over_p / nonzeros_p

# Multiply dp and p
# Note that casting and being careful about dtypes is necessary as interfaces
# typically treat derivatives (dp) with float32, while standard execution (p) comes in float64
dp = qml.math.cast(dp, dtype=p.dtype)
dp = qml.math.cast_like(dp, p)
dp = qml.math.reshape(
dp, (len(p), -1)
) # Squeeze does not work, as you could have shape (num_probs, num_params) with num_params = 1
Expand Down Expand Up @@ -266,9 +268,6 @@ def classical_fisher(qnode, argnums=0):
returns a matrix of size ``(len(params), len(params))``. For multiple differentiable arguments ``x, y, z``,
it returns a list of sizes ``[(len(x), len(x)), (len(y), len(y)), (len(z), len(z))]``.
.. warning::
The ``classical_fisher()`` matrix is currently not differentiable.
.. seealso:: :func:`~.pennylane.metric_tensor`, :func:`~.pennylane.qinfo.transforms.quantum_fisher`
Expand Down Expand Up @@ -361,6 +360,29 @@ def circ(params):
>>> print(rescaled_grad)
[-0.66772533 -0.16618756 -0.05865127 -0.06696078]
The ``classical_fisher`` matrix itself is again differentiable:
.. code-block:: python
@qml.qnode(dev)
def circ(params):
qml.RX(qml.math.cos(params[0]), wires=0)
qml.RX(qml.math.cos(params[0]), wires=1)
qml.RX(qml.math.cos(params[1]), wires=0)
qml.RX(qml.math.cos(params[1]), wires=1)
return qml.probs(wires=range(2))
params = pnp.random.random(2)
>>> print(qml.qinfo.classical_fisher(circ)(params))
(tensor([[0.13340679, 0.03650311],
[0.03650311, 0.00998807]], requires_grad=True)
>>> print(qml.jacobian(qml.qinfo.classical_fisher(circ))(params))
array([[[9.98030491e-01, 3.46944695e-18],
[1.36541817e-01, 5.15248592e-01]],
[[1.36541817e-01, 5.15248592e-01],
[2.16840434e-18, 2.81967252e-01]]]))
"""
new_qnode = _make_probs(qnode, post_processing_fn=lambda x: qml.math.squeeze(qml.math.stack(x)))

Expand All @@ -385,23 +407,23 @@ def wrapper(*args, **kwargs):
p = new_qnode(*args, **kwargs)

# In case multiple variables are used, we create a list of cfi matrices
if isinstance(j, tuple) and len(j) > 1:
if isinstance(j, tuple):
res = []
for j_i in j:
if interface == "tf":
j_i = qml.math.transpose(qml.math.cast(j_i, dtype=p.dtype))

res.append(_compute_cfim(p, j_i))

if len(j) == 1:
return res[0]

return res

return _compute_cfim(p, j)

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 +440,17 @@ 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.
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`. Internally, :func:`~.pennylane.adjoint_metric_tensor` is used when executing on a device with
exact expectations (``shots=None``) that inherits from ``"default.qubit"``. In all other cases, i.e. if a device with finite shots is used, the hardware compatible transform :func:`~.pennylane.metric_tensor` is used.
Please refer to their respective documentations for details on the arguments.
**Example**
Expand Down Expand Up @@ -463,16 +486,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 and isinstance(qnode.device, DefaultQubit):

def wrapper(*args0, **kwargs0):
return 4 * metric_tensor(qnode, *args, **kwargs)(*args0, **kwargs0)
Expand Down
44 changes: 38 additions & 6 deletions tests/docs/test_supported_confs.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,15 @@
import pennylane as qml
from pennylane import numpy as np
from pennylane import QuantumFunctionError
from pennylane.measurements import State, Probability, Expectation, Variance, Sample
from pennylane.measurements import (
State,
Probability,
Expectation,
Variance,
Sample,
VnEntropy,
MutualInfo,
)

pytestmark = pytest.mark.all_interfaces

Expand Down Expand Up @@ -67,9 +75,20 @@
"Hermitian", # non-standard variant of expectation values
"Projector", # non-standard variant of expectation values
Variance,
VnEntropy,
MutualInfo,
]

grad_return_cases = ["StateCost", "DensityMatrix", Expectation, "Hermitian", "Projector", Variance]
grad_return_cases = [
"StateCost",
"DensityMatrix",
Expectation,
"Hermitian",
"Projector",
Variance,
VnEntropy,
MutualInfo,
]


def get_qnode(interface, diff_method, return_type, shots, wire_specs):
Expand Down Expand Up @@ -116,6 +135,11 @@ def circuit(x):
return qml.expval(qml.Projector(np.array([1]), wires=single_meas_wire))
elif return_type == Variance:
return qml.var(qml.PauliZ(wires=single_meas_wire))
elif return_type == VnEntropy:
return qml.vn_entropy(wires=single_meas_wire)
elif return_type == MutualInfo:
wires1 = [w for w in wire_labels if w != single_meas_wire]
return qml.mutual_info(wires0=[single_meas_wire], wires1=wires1)

return circuit

Expand Down Expand Up @@ -283,6 +307,8 @@ def test_none_all(self, diff_method, return_type, shots, wire_specs):
"Hermitian",
"Projector",
Variance,
VnEntropy,
MutualInfo,
],
)
@pytest.mark.parametrize("wire_specs", wire_specs_list)
Expand All @@ -303,7 +329,9 @@ def test_all_backprop_finite_shots(self, interface, return_type, wire_specs):
circuit = get_qnode(interface, "backprop", return_type, 100, wire_specs)

@pytest.mark.parametrize("interface", diff_interfaces)
@pytest.mark.parametrize("return_type", ["StateCost", "DensityMatrix", Probability, Variance])
@pytest.mark.parametrize(
"return_type", ["StateCost", "DensityMatrix", Probability, Variance, VnEntropy, MutualInfo]
)
@pytest.mark.parametrize("shots", shots_list)
@pytest.mark.parametrize("wire_specs", wire_specs_list)
def test_all_adjoint_nonexp(self, interface, return_type, shots, wire_specs):
Expand Down Expand Up @@ -348,7 +376,8 @@ def test_all_adjoint_exp(self, interface, return_type, shots, wire_specs):

@pytest.mark.parametrize("interface", diff_interfaces)
@pytest.mark.parametrize(
"return_type", [Probability, Expectation, "Hermitian", "Projector", Variance]
"return_type",
[Probability, Expectation, "Hermitian", "Projector", Variance],
)
@pytest.mark.parametrize("shots", shots_list)
@pytest.mark.parametrize("wire_specs", wire_specs_list)
Expand All @@ -362,7 +391,9 @@ def test_all_paramshift_nonstate(self, interface, return_type, shots, wire_specs
grad = compute_gradient(x, interface, circuit, return_type)

@pytest.mark.parametrize("interface", diff_interfaces)
@pytest.mark.parametrize("return_type", ["StateCost", "StateVector", "DensityMatrix"])
@pytest.mark.parametrize(
"return_type", ["StateCost", "StateVector", "DensityMatrix", VnEntropy, MutualInfo]
)
@pytest.mark.parametrize("shots", shots_list)
@pytest.mark.parametrize("wire_specs", wire_specs_list)
def test_all_paramshift_state(self, interface, return_type, shots, wire_specs):
Expand All @@ -378,7 +409,8 @@ def test_all_paramshift_state(self, interface, return_type, shots, wire_specs):

@pytest.mark.parametrize("interface", diff_interfaces)
@pytest.mark.parametrize(
"return_type", [Probability, Expectation, "Hermitian", "Projector", Variance]
"return_type",
[Probability, Expectation, "Hermitian", "Projector", Variance, VnEntropy, MutualInfo],
)
@pytest.mark.parametrize("shots", shots_list)
@pytest.mark.parametrize("wire_specs", wire_specs_list)
Expand Down
Loading

0 comments on commit b6fc538

Please sign in to comment.