Skip to content

Commit

Permalink
Merge branch 'master' into templates_toplvl
Browse files Browse the repository at this point in the history
  • Loading branch information
antalszava committed Oct 22, 2021
2 parents 99903de + eefa9c9 commit 79f306b
Show file tree
Hide file tree
Showing 14 changed files with 83 additions and 575 deletions.
12 changes: 8 additions & 4 deletions doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -567,17 +567,21 @@

<h3>Breaking changes</h3>

- The input signature of an `expand_fn` used in a `batch_transform`
* The `qml.inv` function has been removed, `qml.adjoint` should be used
instead.
[(#1778)](https://github.com/PennyLaneAI/pennylane/pull/1778)

* The input signature of an `expand_fn` used in a `batch_transform`
now **must** have the same signature as the provided `transform_fn`,
and vice versa.
[(#1721)](https://github.com/PennyLaneAI/pennylane/pull/1721)

- The expansion rule in the `qml.metric_tensor` transform has been changed.
* The expansion rule in the `qml.metric_tensor` transform has been changed.
[(#1721)](https://github.com/PennyLaneAI/pennylane/pull/1721)

If `hybrid=False`, the changed expansion rule might lead to a changed output.

- The `qml.metric_tensor` keyword argument `diag_approx` is deprecated.
* The `qml.metric_tensor` keyword argument `diag_approx` is deprecated.
Approximations can be controlled with the more fine-grained `approx`
keyword argument, with `approx="block-diag"` (the default) reproducing
the old behaviour.
Expand All @@ -594,7 +598,7 @@
`requires_grad=False` was explicitly set.
[(#1638)](https://github.com/PennyLaneAI/pennylane/pull/1638)

- The operation `qml.Interferometer` has been renamed `qml.InterferometerUnitary` in order to
* The operation `qml.Interferometer` has been renamed `qml.InterferometerUnitary` in order to
distinguish it from the template `qml.templates.Interferometer`.
[(#1714)](https://github.com/PennyLaneAI/pennylane/pull/1714)

Expand Down
1 change: 0 additions & 1 deletion pennylane/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@
quantum_monte_carlo,
apply_controlled_Q,
)
from pennylane.utils import inv
from pennylane.vqe import ExpvalCost, VQECost

# QueuingContext and collections needs to be imported after all other pennylane imports
Expand Down
2 changes: 1 addition & 1 deletion pennylane/templates/decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def bell_state_preparation(wires):
@qml.qnode(dev)
def circuit():
qml.inv(bell_state_preparation(wires=[0, 1]))
qml.adjoint(bell_state_preparation)(wires=[0, 1])
return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1))
Args:
Expand Down
122 changes: 0 additions & 122 deletions pennylane/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import itertools
import numbers
from operator import matmul
import warnings

import numpy as np
import scipy
Expand Down Expand Up @@ -305,127 +304,6 @@ def pauli_eigs(n):
return np.concatenate([pauli_eigs(n - 1), -pauli_eigs(n - 1)])


def inv(operation_list):
"""Invert a list of operations or a :doc:`template </introduction/templates>`.
If the inversion happens inside a QNode, the operations are removed and requeued
in the reversed order for proper inversion.
.. warning::
Use of :func:`~.inv()` is deprecated and should be replaced with
:func:`~.adjoint()`.
**Example:**
The following example illuminates the inversion of a template:
.. code-block:: python3
@qml.template
def ansatz(weights, wires):
for idx, wire in enumerate(wires):
qml.RX(weights[idx], wires=[wire])
for idx in range(len(wires) - 1):
qml.CNOT(wires=[wires[idx], wires[idx + 1]])
dev = qml.device('default.qubit', wires=2)
@qml.qnode(dev)
def circuit(weights):
qml.inv(ansatz(weights, wires=[0, 1]))
return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1))
We may also invert an operation sequence:
.. code-block:: python3
dev = qml.device('default.qubit', wires=2)
@qml.qnode(dev)
def circuit1():
qml.T(wires=[0]).inv()
qml.Hadamard(wires=[0]).inv()
qml.S(wires=[0]).inv()
return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1))
@qml.qnode(dev)
def circuit2():
qml.inv([qml.S(wires=[0]), qml.Hadamard(wires=[0]), qml.T(wires=[0])])
return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1))
Double checking that both circuits produce the same output:
>>> ZZ1 = circuit1()
>>> ZZ2 = circuit2()
>>> assert ZZ1 == ZZ2
True
Args:
operation_list (Iterable[~.Operation]): An iterable of operations
Returns:
List[~.Operation]: The inverted list of operations
"""

warnings.warn(
"Use of qml.inv() is deprecated and should be replaced with qml.adjoint().",
UserWarning,
)
if isinstance(operation_list, qml.operation.Operation):
operation_list = [operation_list]
elif operation_list is None:
raise ValueError(
"None was passed as an argument to inv. "
"This could happen if inversion of a template without the template decorator is attempted."
)
elif callable(operation_list):
raise ValueError(
"A function was passed as an argument to inv. "
"This could happen if inversion of a template function is attempted. "
"Please use inv on the function including its arguments, as in inv(template(args))."
)
elif isinstance(operation_list, qml.tape.QuantumTape):
new_tape = operation_list.adjoint()
return new_tape

elif not isinstance(operation_list, Iterable):
raise ValueError("The provided operation_list is not iterable.")

non_ops = [
(idx, op)
for idx, op in enumerate(operation_list)
if not isinstance(op, qml.operation.Operation)
]

if non_ops:
string_reps = [" operation_list[{}] = {}".format(idx, op) for idx, op in non_ops]
raise ValueError(
"The given operation_list does not only contain Operations."
+ "The following elements of the iterable were not Operations:"
+ ",".join(string_reps)
)

for op in operation_list:
try:
# remove the queued operation to be inverted
# from the existing queuing context
qml.QueuingContext.remove(op)
except KeyError:
# operation to be inverted does not
# exist on the queuing context
pass

def qfunc():
for o in operation_list:
o.queue()

with qml.tape.QuantumTape() as tape:
qml.adjoint(qfunc)()

return tape


def expand(matrix, original_wires, expanded_wires):
r"""Expand a an operator matrix to more wires.
Expand Down
7 changes: 4 additions & 3 deletions tests/devices/test_default_qubit.py
Original file line number Diff line number Diff line change
Expand Up @@ -2173,10 +2173,11 @@ def test_s_inverse():
expected = np.array([1.0, -1.0j]) / np.sqrt(2)
assert np.allclose(dev.state, expected, atol=tol, rtol=0)

def test_inverse_S_decomposition(self, tol, monkeypatch):
def test_inverse_S_decomposition(self, tol, mocker, monkeypatch):
"""Test that applying the inverse of the S gate
works when the inverse S gate is decomposed"""
dev = qml.device("default.qubit", wires=1)
spy = mocker.spy(dev, "execute")

patched_operations = dev.operations.copy()
patched_operations.remove("S")
Expand All @@ -2189,7 +2190,7 @@ def test_s():
return qml.probs(wires=0)

test_s()
operations = test_s.qtape.operations
operations = spy.call_args[0][0].operations
assert "S" not in [i.name for i in operations]
assert "PhaseShift" in [i.name for i in operations]

Expand All @@ -2203,7 +2204,7 @@ def test_s_inverse():
return qml.probs(wires=0)

test_s_inverse()
operations = test_s_inverse.qtape.operations
operations = spy.call_args[0][0].operations
assert "S.inv" not in [i.name for i in operations]
assert "PhaseShift" in [i.name for i in operations]

Expand Down
6 changes: 2 additions & 4 deletions tests/gradients/test_finite_difference.py
Original file line number Diff line number Diff line change
Expand Up @@ -566,12 +566,11 @@ def test_torch(self, approx_order, strategy, tol):
"""Tests that the output of the finite-difference transform
can be differentiated using Torch, yielding second derivatives."""
torch = pytest.importorskip("torch")
from pennylane.interfaces.torch import TorchInterface

dev = qml.device("default.qubit.torch", wires=2)
params = torch.tensor([0.543, -0.654], dtype=torch.float64, requires_grad=True)

with TorchInterface.apply(qml.tape.QubitParamShiftTape()) as tape:
with qml.tape.JacobianTape() as tape:
qml.RX(params[0], wires=[0])
qml.RY(params[1], wires=[1])
qml.CNOT(wires=[0, 1])
Expand Down Expand Up @@ -601,7 +600,6 @@ def test_jax(self, approx_order, strategy, tol):
can be differentiated using JAX, yielding second derivatives."""
jax = pytest.importorskip("jax")
from jax import numpy as jnp
from pennylane.interfaces.jax import JAXInterface
from jax.config import config

config.update("jax_enable_x64", True)
Expand All @@ -610,7 +608,7 @@ def test_jax(self, approx_order, strategy, tol):
params = jnp.array([0.543, -0.654])

def cost_fn(x):
with JAXInterface.apply(qml.tape.QubitParamShiftTape()) as tape:
with qml.tape.JacobianTape() as tape:
qml.RX(x[0], wires=[0])
qml.RY(x[1], wires=[1])
qml.CNOT(wires=[0, 1])
Expand Down
6 changes: 2 additions & 4 deletions tests/gradients/test_parameter_shift.py
Original file line number Diff line number Diff line change
Expand Up @@ -940,12 +940,11 @@ def test_torch(self, tol):
"""Tests that the output of the finite-difference transform
can be differentiated using Torch, yielding second derivatives."""
torch = pytest.importorskip("torch")
from pennylane.interfaces.torch import TorchInterface

dev = qml.device("default.qubit.torch", wires=2)
params = torch.tensor([0.543, -0.654], dtype=torch.float64, requires_grad=True)

with TorchInterface.apply(qml.tape.QubitParamShiftTape()) as tape:
with qml.tape.JacobianTape() as tape:
qml.RX(params[0], wires=[0])
qml.RY(params[1], wires=[1])
qml.CNOT(wires=[0, 1])
Expand All @@ -970,7 +969,6 @@ def test_jax(self, tol):
can be differentiated using JAX, yielding second derivatives."""
jax = pytest.importorskip("jax")
from jax import numpy as jnp
from pennylane.interfaces.jax import JAXInterface
from jax.config import config

config.update("jax_enable_x64", True)
Expand All @@ -979,7 +977,7 @@ def test_jax(self, tol):
params = jnp.array([0.543, -0.654])

def cost_fn(x):
with JAXInterface.apply(qml.tape.QubitParamShiftTape()) as tape:
with qml.tape.JacobianTape() as tape:
qml.RX(x[0], wires=[0])
qml.RY(x[1], wires=[1])
qml.CNOT(wires=[0, 1])
Expand Down
35 changes: 22 additions & 13 deletions tests/gradients/test_parameter_shift_cv.py
Original file line number Diff line number Diff line change
Expand Up @@ -966,19 +966,19 @@ def test_autograd_gradient(self, tol):
"""Tests that the output of the parameter-shift CV transform
can be differentiated using autograd, yielding second derivatives."""
dev = qml.device("default.gaussian", wires=1)
from pennylane.interfaces.autograd import AutogradInterface

r = 0.12
phi = 0.105

def cost_fn(x):
with AutogradInterface.apply(qml.tape.CVParamShiftTape()) as tape:
with qml.tape.JacobianTape() as tape:
qml.Squeezing(x[0], 0, wires=0)
qml.Rotation(x[1], wires=0)
qml.var(qml.X(wires=[0]))

tapes, fn = param_shift_cv(tape, dev)
return fn([t.execute(dev) for t in tapes])[0, 1]
jac = fn(qml.execute(tapes, dev, param_shift_cv, gradient_kwargs={"dev": dev}))
return jac[0, 2]

params = np.array([r, phi], requires_grad=True)
grad = qml.jacobian(cost_fn)(params)
Expand All @@ -991,19 +991,23 @@ def test_tf(self, tol):
"""Tests that the output of the parameter-shift CV transform
can be executed using TF"""
tf = pytest.importorskip("tensorflow")
from pennylane.interfaces.tf import TFInterface

dev = qml.device("default.gaussian", wires=1)
params = tf.Variable([0.543, -0.654], dtype=tf.float64)

with tf.GradientTape() as t:
with TFInterface.apply(qml.tape.CVParamShiftTape()) as tape:
with qml.tape.JacobianTape() as tape:
qml.Squeezing(params[0], 0, wires=0)
qml.Rotation(params[1], wires=0)
qml.var(qml.X(wires=[0]))

tapes, fn = qml.gradients.param_shift_cv(tape, dev)
jac = fn([tp.execute(dev) for tp in tapes])
tape.trainable_params = {0, 2}
tapes, fn = param_shift_cv(tape, dev)
jac = fn(
qml.execute(
tapes, dev, param_shift_cv, gradient_kwargs={"dev": dev}, interface="tf"
)
)
res = jac[0, 1]

r, phi = 1.0 * params
Expand All @@ -1026,18 +1030,20 @@ def test_torch(self, tol):
"""Tests that the output of the parameter-shift CV transform
can be executed using Torch."""
torch = pytest.importorskip("torch")
from pennylane.interfaces.torch import TorchInterface

dev = qml.device("default.gaussian", wires=1)
params = torch.tensor([0.543, -0.654], dtype=torch.float64, requires_grad=True)

with TorchInterface.apply(qml.tape.CVParamShiftTape()) as tape:
with qml.tape.JacobianTape() as tape:
qml.Squeezing(params[0], 0, wires=0)
qml.Rotation(params[1], wires=0)
qml.var(qml.X(wires=[0]))

tape.trainable_params = {0, 2}
tapes, fn = qml.gradients.param_shift_cv(tape, dev)
jac = fn([t.execute(dev) for t in tapes])
jac = fn(
qml.execute(tapes, dev, param_shift_cv, gradient_kwargs={"dev": dev}, interface="torch")
)

r, phi = params.detach().numpy()

Expand All @@ -1062,7 +1068,6 @@ def test_jax(self, tol):
can be differentiated using JAX, yielding second derivatives."""
jax = pytest.importorskip("jax")
from jax import numpy as jnp
from pennylane.interfaces.jax import JAXInterface
from jax.config import config

config.update("jax_enable_x64", True)
Expand All @@ -1071,14 +1076,18 @@ def test_jax(self, tol):
params = jnp.array([0.543, -0.654])

def cost_fn(x):
with JAXInterface.apply(qml.tape.CVParamShiftTape()) as tape:
with qml.tape.JacobianTape() as tape:
qml.Squeezing(params[0], 0, wires=0)
qml.Rotation(params[1], wires=0)
qml.var(qml.X(wires=[0]))

tape.trainable_params = {0, 2}
tapes, fn = qml.gradients.param_shift_cv(tape, dev)
jac = fn([t.execute(dev) for t in tapes])
jac = fn(
qml.execute(
tapes, dev, param_shift_cv, gradient_kwargs={"dev": dev}, interface="jax"
)
)
return jac

r, phi = params
Expand Down
Loading

0 comments on commit 79f306b

Please sign in to comment.