diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index 10d9c3c0ec3..4411b25e69c 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -102,15 +102,19 @@
Bug fixes 🐛
+* Use vanilla NumPy arrays in `test_projector_expectation` to avoid differentiating `qml.Projector` with respect to the state attribute.
+ [(#5683)](https://github.com/PennyLaneAI/pennylane/pull/5683)
+
* `qml.Projector` is now compatible with jax-jit.
[(#5595)](https://github.com/PennyLaneAI/pennylane/pull/5595)
* Finite shot circuits with a `qml.probs` measurement, both with a `wires` or `op` argument, can now be compiled with `jax.jit`.
[(#5619)](https://github.com/PennyLaneAI/pennylane/pull/5619)
-* `param_shift`, `finite_diff`, `compile`, `merge_rotations`, and `transpile` now all work
- with circuits with non-commuting measurements.
+* `param_shift`, `finite_diff`, `compile`, `insert`, `merge_rotations`, and `transpile` now
+ all work with circuits with non-commuting measurements.
[(#5424)](https://github.com/PennyLaneAI/pennylane/pull/5424)
+ [(#5681)](https://github.com/PennyLaneAI/pennylane/pull/5681)
* A correction is added to `bravyi_kitaev` to call the correct function for a FermiSentence input.
[(#5671)](https://github.com/PennyLaneAI/pennylane/pull/5671)
diff --git a/pennylane/devices/tests/test_compare_default_qubit.py b/pennylane/devices/tests/test_compare_default_qubit.py
index 728950b80a7..05b2b6509e8 100755
--- a/pennylane/devices/tests/test_compare_default_qubit.py
+++ b/pennylane/devices/tests/test_compare_default_qubit.py
@@ -12,6 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
"""Tests that a device gives the same output as the default device."""
+import numpy as np
+
# pylint: disable=no-self-use,no-member
import pytest
from flaky import flaky
@@ -90,10 +92,10 @@ def workload():
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1],
- pnp.array([1, 1, 0, 0]) / pnp.sqrt(2),
- pnp.array([0, 1, 0, 1]) / pnp.sqrt(2),
- pnp.array([1, 1, 1, 0]) / pnp.sqrt(3),
- pnp.array([1, 1, 1, 1]) / 2,
+ np.array([1, 1, 0, 0]) / np.sqrt(2),
+ np.array([0, 1, 0, 1]) / np.sqrt(2),
+ np.array([1, 1, 1, 0]) / np.sqrt(3),
+ np.array([1, 1, 1, 1]) / 2,
],
)
def test_projector_expectation(self, device, state, tol, benchmark):
diff --git a/pennylane/transforms/insert_ops.py b/pennylane/transforms/insert_ops.py
index 9d221079734..17a81beeec8 100644
--- a/pennylane/transforms/insert_ops.py
+++ b/pennylane/transforms/insert_ops.py
@@ -89,7 +89,6 @@ def insert(
Raises:
- QuantumFunctionError: if some observables in the tape are not qubit-wise commuting
ValueError: if a single operation acting on multiple wires is passed to ``op``
ValueError: if the requested ``position`` argument is not ``'start'``, ``'end'`` or
``'all'`` OR PennyLane Operation
@@ -100,9 +99,11 @@ def insert(
.. code-block:: python3
+ from functools import partial
+
dev = qml.device("default.mixed", wires=2)
- @partial(qml.transforms.insert, qml.AmplitudeDamping, 0.2, position="end")
+ @partial(qml.transforms.insert, op=qml.AmplitudeDamping, op_args=0.2, position="end")
@qml.qnode(dev)
def f(w, x, y, z):
qml.RX(w, wires=0)
@@ -141,7 +142,7 @@ def op(x, y, wires):
dev = qml.device("default.qubit", wires=2)
@qml.qnode(dev)
- @qml.transforms.insert(op, [0.2, 0.3], position="end")
+ @partial(qml.transforms.insert, op=op, op_args=[0.2, 0.3], position="end")
def f(w, x, y, z):
qml.RX(w, wires=0)
qml.RY(x, wires=1)
@@ -175,7 +176,7 @@ def f(w, x, y, z):
We can add the :class:`~.AmplitudeDamping` channel to the end of the circuit using:
>>> from pennylane.transforms import insert
- >>> noisy_tape = insert(tape, qml.AmplitudeDamping, 0.05, position="end")
+ >>> [noisy_tape], _ = insert(tape, qml.AmplitudeDamping, 0.05, position="end")
>>> print(qml.drawer.tape_text(noisy_tape, decimals=2))
0: ──RX(0.90)─╭●──RY(0.50)──AmplitudeDamping(0.05)─┤ ╭
1: ──RY(0.40)─╰X──RX(0.60)──AmplitudeDamping(0.05)─┤ ╰
@@ -210,17 +211,18 @@ def f(w, x, y, z):
>>> qnode_noisy(0.9, 0.4, 0.5, 0.6)
tensor(0.72945434, requires_grad=True)
"""
- # decompose templates and their adjoints (which fixes a bug in the tutorial_error_mitigation demo)
- # TODO: change this to be cleaner and more robust
- try:
- tape = tape.expand(
- stop_at=lambda op: not hasattr(qml.templates, op.name) and not isinstance(op, Adjoint)
- )
- except qml.QuantumFunctionError as e:
- raise qml.QuantumFunctionError(
- "The insert transform cannot transform a circuit measuring non-commuting observables. "
- "Consider wrapping the gates in their own function and transforming only that function."
- ) from e
+
+ # decompose templates and their adjoints to fix a bug in the tutorial_error_mitigation demo
+ def stop_at(obj):
+ if not isinstance(obj, qml.operation.Operator):
+ return True
+ if not obj.has_decomposition:
+ return True
+ return not (hasattr(qml.templates, obj.name) or isinstance(obj, Adjoint))
+
+ error_type = (qml.operation.DecompositionUndefinedError,)
+ decompose = qml.devices.preprocess.decompose
+ [tape], _ = decompose(tape, stopping_condition=stop_at, name="insert", error=error_type)
if not isinstance(op, FunctionType) and op.num_wires != 1:
raise ValueError("Only single-qubit operations can be inserted into the circuit")
diff --git a/requirements-ci.txt b/requirements-ci.txt
index dafa106ffe4..9f7c3b39c77 100644
--- a/requirements-ci.txt
+++ b/requirements-ci.txt
@@ -8,7 +8,7 @@ autograd
toml
appdirs
semantic_version
-autoray
+autoray>=0.6.1,<0.6.10
matplotlib
requests
tomli # Drop once minimum Python version is 3.11
diff --git a/requirements.txt b/requirements.txt
index a734df40bba..ab01652d0e9 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -9,7 +9,7 @@ autograd~=1.4
toml~=0.10
appdirs~=1.4
semantic_version~=2.10
-autoray>=0.6.1
+autoray>=0.6.1,<0.6.10
matplotlib~=3.5
opt_einsum~=3.3
requests~=2.31.0
diff --git a/tests/transforms/test_insert_ops.py b/tests/transforms/test_insert_ops.py
index 96e207f8c8d..a8c33f6cb6a 100644
--- a/tests/transforms/test_insert_ops.py
+++ b/tests/transforms/test_insert_ops.py
@@ -602,28 +602,32 @@ def f2(w1, w2):
assert np.allclose(f1(w1, w2), f2(w1, w2))
-def test_insert_decorator_causes_custom_insert_error_non_qwc_obs():
+def test_insert_transform_works_with_non_qwc_obs():
"""Test that the insert transform catches and reports errors from the enclosed function."""
- # pylint: disable=unused-argument
-
- def noise(noise_param, wires):
+ def op(noise_param, wires):
+ # pylint: disable=unused-argument
qml.CRX(noise_param, wires=[0, 1])
qml.CNOT(wires=[1, 0])
- dev = qml.device("default.mixed", wires=2)
+ dev = qml.device("default.qubit", wires=2)
@qml.qnode(dev)
- @partial(insert, op=noise, op_args=0.3, position="all")
+ @partial(insert, op=op, op_args=0.3, position="all")
def noisy_circuit(circuit_param):
qml.RY(circuit_param, wires=0)
qml.Hadamard(wires=0)
qml.T(wires=0)
return qml.expval(qml.PauliX(0)), qml.expval(qml.PauliY(0)), qml.expval(qml.PauliZ(0))
- # This tape's expansion fails, but shouldn't cause a downstream IndexError. See issue #3103
- with pytest.raises(
- qml.QuantumFunctionError,
- match="The insert transform cannot transform a circuit measuring non-commuting observables",
- ):
- noisy_circuit(0.4)
+ @qml.qnode(dev)
+ def explicit_circuit(circuit_param):
+ qml.RY(circuit_param, wires=0)
+ op(0.3, None)
+ qml.Hadamard(wires=0)
+ op(0.3, None)
+ qml.T(wires=0)
+ op(0.3, None)
+ return qml.expval(qml.PauliX(0)), qml.expval(qml.PauliY(0)), qml.expval(qml.PauliZ(0))
+
+ assert np.allclose(noisy_circuit(0.4), explicit_circuit(0.4))