Skip to content

Commit de04d85

Browse files
authored
Stop queueing from within qml.pauli.PauliWord.operation and qml.pauli.PauliSentence.operation (#8136)
**Context:** Queuing behaviour is inconsistent due to caching, see bug report. **Description of the Change:** Stop queuing altogether from within the mentioned methods. **Benefits:** Consistent queuing behaviour. **Possible Drawbacks:** Stopping the queuing system for each call to `.operation` might be a performance regression (because in many cases there was no queuing anyways, so now we just do extra work of toggling the recording status, I suppose). **Related GitHub Issues:** Fixes #8135 [sc-98282]
1 parent 08d58a7 commit de04d85

File tree

4 files changed

+56
-17
lines changed

4 files changed

+56
-17
lines changed

doc/releases/changelog-dev.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,10 @@
501501

502502
<h3>Breaking changes 💔</h3>
503503

504+
* The methods :meth:`~.pauli.PauliWord.operation` and :meth:`~.pauli.PauliSentence.operation`
505+
no longer queue any operators.
506+
[(#8136)](https://github.com/PennyLaneAI/pennylane/pull/8136)
507+
504508
* `qml.sample` no longer has singleton dimensions squeezed out for single shots or single wires. This cuts
505509
down on the complexity of post-processing due to having to handle single shot and single wire cases
506510
separately. The return shape will now *always* be `(shots, num_wires)`.
@@ -878,6 +882,11 @@
878882

879883
<h3>Bug fixes 🐛</h3>
880884

885+
* Fixes a bug that made the queueing behaviour of :meth:`~.pauli.PauliWord.operation` and
886+
:meth:`~.pauli.PauliSentence.operation` dependent on the global state of a program due to
887+
a caching issue.
888+
[(#8135)](https://github.com/PennyLaneAI/pennylane/pull/8135)
889+
881890
* A more informative error is raised when extremely deep circuits are attempted to be drawn.
882891
[(#8139)](https://github.com/PennyLaneAI/pennylane/pull/8139)
883892

pennylane/pauli/pauli_arithmetic.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import pennylane as qml
2323
from pennylane import math
2424
from pennylane.ops import Identity, PauliX, PauliY, PauliZ, Prod, SProd, Sum
25+
from pennylane.queuing import QueuingManager
2526
from pennylane.typing import TensorLike
2627
from pennylane.wires import Wires, WiresLike
2728

@@ -504,6 +505,7 @@ def _get_csr_indices(self, wire_order):
504505
current_size *= 2
505506
return indices
506507

508+
@QueuingManager.stop_recording()
507509
def operation(self, wire_order: WiresLike = ()):
508510
"""Returns a native PennyLane :class:`~pennylane.operation.Operation` representing the PauliWord."""
509511
if len(self) == 0:
@@ -1002,6 +1004,7 @@ def _sum_different_structure_pws(indices, data):
10021004
matrix.eliminate_zeros()
10031005
return matrix
10041006

1007+
@QueuingManager.stop_recording()
10051008
def operation(self, wire_order: WiresLike = ()):
10061009
"""Returns a native PennyLane :class:`~pennylane.operation.Operation` representing the PauliSentence."""
10071010
if len(self) == 0:

tests/ops/functions/test_commutator.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,45 +58,54 @@ def test_alias():
5858

5959
def test_no_recording_in_context():
6060
"""Test that commutator is not recorded"""
61-
with qml.tape.QuantumTape() as tape:
61+
with qml.queuing.AnnotatedQueue() as q1:
6262
a = qml.PauliX(0) # gets recorded
6363
b = qml.PauliY(0) # gets recorded
6464
comm = qml.commutator(a, b)
6565

66-
with qml.tape.QuantumTape() as tape2:
66+
with qml.queuing.AnnotatedQueue() as q2:
6767
qml.PauliX(0)
6868
qml.PauliY(0)
6969

70-
qml.assert_equal(tape, tape2)
70+
expected = [qml.X(0), qml.Y(0)]
71+
for op1, op2, exp_op in zip(q1.queue, q2.queue, expected, strict=True):
72+
qml.assert_equal(op1, exp_op)
73+
qml.assert_equal(op2, exp_op)
7174

7275

7376
def test_no_recording_in_context_with_pauli():
7477
"""Test that commutator is not recorded while one of the ops is a Pauli"""
75-
with qml.tape.QuantumTape() as tape:
78+
with qml.queuing.AnnotatedQueue() as q1:
7679
a = qml.PauliX(0) # gets recorded
7780
b = PauliWord({0: "Y"}) # does not get recorded
7881
comm = qml.commutator(a, b)
7982

80-
with qml.tape.QuantumTape() as tape2:
83+
with qml.queuing.AnnotatedQueue() as q2:
8184
qml.PauliX(0)
8285

83-
qml.assert_equal(tape, tape2)
86+
expected = [qml.X(0)]
87+
for op1, op2, exp_op in zip(q1.queue, q2.queue, expected, strict=True):
88+
qml.assert_equal(op1, exp_op)
89+
qml.assert_equal(op2, exp_op)
8490

8591

8692
def test_recording_wanted():
8793
"""Test that commutator can be correctly recorded with qml.apply still"""
88-
with qml.tape.QuantumTape() as tape:
94+
with qml.queuing.AnnotatedQueue() as q1:
8995
a = qml.PauliX(0)
9096
b = qml.PauliY(0)
9197
comm = qml.commutator(a, b)
9298
qml.apply(comm)
9399

94-
with qml.tape.QuantumTape() as tape2:
100+
with qml.queuing.AnnotatedQueue() as q2:
95101
qml.PauliX(0)
96102
qml.PauliY(0)
97103
qml.s_prod(2j, qml.PauliZ(0))
98104

99-
qml.assert_equal(tape, tape2)
105+
expected = [qml.X(0), qml.Y(0), 2j * qml.Z(0)]
106+
for op1, op2, exp_op in zip(q1.queue, q2.queue, expected, strict=True):
107+
qml.assert_equal(op1, exp_op)
108+
qml.assert_equal(op2, exp_op)
100109

101110

102111
class TestcommPauli:

tests/pauli/test_pauli_arithmetic.py

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,10 @@ def test_to_mat_format(self, pw, wire_order, true_matrix):
393393
@pytest.mark.parametrize("pw, op", tup_pw_operation)
394394
def test_operation(self, pw, op):
395395
"""Test that a PauliWord can be cast to a PL operation."""
396-
pw_op = pw.operation()
396+
with qml.queuing.AnnotatedQueue() as q:
397+
pw_op = pw.operation()
398+
assert len(q.queue) == 0
399+
397400
if len(pw) > 1:
398401
for pw_factor, op_factor in zip(pw_op.operands, op.operands):
399402
assert pw_factor.name == op_factor.name
@@ -404,14 +407,18 @@ def test_operation(self, pw, op):
404407

405408
def test_operation_empty(self):
406409
"""Test that an empty PauliWord with wire_order returns Identity."""
407-
op = PauliWord({}).operation(wire_order=[0, 1])
410+
with qml.queuing.AnnotatedQueue() as q:
411+
op = PauliWord({}).operation(wire_order=[0, 1])
412+
assert len(q.queue) == 0
408413
id = qml.Identity(wires=[0, 1])
409414
assert op.name == id.name
410415
assert op.wires == id.wires
411416

412417
def test_operation_empty_nowires(self):
413418
"""Test that an empty PauliWord is cast to qml.Identity() operation."""
414-
res = pw4.operation()
419+
with qml.queuing.AnnotatedQueue() as q:
420+
res = pw4.operation()
421+
assert len(q.queue) == 0
415422
assert res == qml.Identity()
416423

417424
def test_pickling(self):
@@ -888,7 +895,9 @@ def _compare_ops(op1, op2):
888895
assert op1.name == op2.name
889896
assert op1.wires == op2.wires
890897

891-
ps_op = ps.operation()
898+
with qml.queuing.AnnotatedQueue() as q:
899+
ps_op = ps.operation()
900+
assert len(q.queue) == 0
892901
if len(ps) > 1:
893902
for ps_summand, op_summand in zip(ps_op.operands, op.operands):
894903
assert ps_summand.scalar == op_summand.scalar
@@ -902,7 +911,9 @@ def _compare_ops(op1, op2):
902911
def test_operation_with_identity(self):
903912
"""Test that a PauliSentence with an empty PauliWord can be cast to
904913
operation correctly."""
905-
full_ps_op = ps3.operation()
914+
with qml.queuing.AnnotatedQueue() as q:
915+
full_ps_op = ps3.operation()
916+
assert len(q.queue) == 0
906917
full_op = qml.sum(
907918
-0.5 * qml.prod(qml.PauliZ(wires=0), qml.PauliZ(wires="b"), qml.PauliZ(wires="c")),
908919
qml.s_prod(1, qml.Identity(wires=[0, "b", "c"])),
@@ -924,7 +935,9 @@ def test_operation_with_identity(self):
924935

925936
def test_operation_empty(self):
926937
"""Test that an empty PauliSentence with wire_order returns Identity."""
927-
op = ps5.operation(wire_order=[0, 1])
938+
with qml.queuing.AnnotatedQueue() as q:
939+
op = ps5.operation(wire_order=[0, 1])
940+
assert len(q.queue) == 0
928941
id = qml.s_prod(0.0, qml.Identity(wires=[0, 1]))
929942

930943
assert op.name == id.name
@@ -933,9 +946,14 @@ def test_operation_empty(self):
933946
def test_operation_empty_nowires(self):
934947
"""Test that a ValueError is raised if an empty PauliSentence is
935948
cast to a PL operation."""
936-
res1 = ps4.operation()
949+
with qml.queuing.AnnotatedQueue() as q:
950+
res1 = ps4.operation()
951+
assert len(q.queue) == 0
937952
assert res1 == qml.Identity()
938-
res2 = ps5.operation()
953+
954+
with qml.queuing.AnnotatedQueue() as q:
955+
res2 = ps5.operation()
956+
assert len(q.queue) == 0
939957
assert res2 == qml.s_prod(0, qml.Identity())
940958

941959
def test_operation_wire_order(self):

0 commit comments

Comments
 (0)