Skip to content

Commit 85ff62b

Browse files
authored
Allow tuple-valued control_values with qml.ctrl on a Controlled instance. (#5725)
**Context:** By default, `control_values` of `Controlled` instances are lists. `qml.defer_measurements` passes a tuple as `control_values` to `ctrl`. `create_controlled_op`, called by `ctrl`, tries to merge `control_values` of the base operation and the provided `control_values`. The consequence is an attempted addition of `list` and `tuple`, if `qml.defer_measurements` hits a `qml.cond` of a `Controlled` instance, leading to #5717 **Description of the Change:** This PR fixes the above problem in two (redundant!) ways: - tuples are allowed to be passed as `control_values` to `ctrl`, which casts them to a `list`. - `defer_measurements` passes a `list` itself. Clearly those changes are redundant for the bug #5717, with the former also providing users with the option of using `tuple`s for `control_values`. We could skip the change to `defer_measurements`, thus. **Benefits:** **Possible Drawbacks:** **Related GitHub Issues:** Fixes #5717 [sc-63645]
1 parent fdca352 commit 85ff62b

File tree

5 files changed

+36
-3
lines changed

5 files changed

+36
-3
lines changed

doc/releases/changelog-dev.md

+6
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99

1010
<h3>Improvements 🛠</h3>
1111

12+
* `ctrl` now works with tuple-valued `control_values` when applied to any already controlled operation.
13+
[(#5725)](https://github.com/PennyLaneAI/pennylane/pull/5725)
14+
1215
* Add support for 3 new pytest markers: `unit`, `integration` and `system`.
1316
[(#5517)](https://github.com/PennyLaneAI/pennylane/pull/5517)
1417

@@ -141,6 +144,9 @@
141144

142145
<h3>Bug fixes 🐛</h3>
143146

147+
* `qml.cond` can now be applied to `ControlledOp` operations when deferring measurements.
148+
[(#5725)](https://github.com/PennyLaneAI/pennylane/pull/5725)
149+
144150
* The legacy `Tensor` class can now handle a `Projector` with abstract tracer input.
145151
[(#5720)](https://github.com/PennyLaneAI/pennylane/pull/5720)
146152

pennylane/ops/op_math/controlled.py

+2
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ def create_controlled_op(op, control, control_values=None, work_wires=None):
140140
control_values = [control_values]
141141
elif control_values is None:
142142
control_values = [True] * len(control)
143+
elif isinstance(control_values, tuple):
144+
control_values = list(control_values)
143145

144146
ctrl_op = _try_wrap_in_custom_ctrl_op(
145147
op, control, control_values=control_values, work_wires=work_wires

tests/ops/op_math/test_controlled.py

+11
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,17 @@ def test_zero_one_control_values(self):
163163
op = Controlled(self.temp_op, (0, 1), control_values=[0, 1])
164164
assert op.control_values == [False, True]
165165

166+
@pytest.mark.parametrize("control_values", [True, False, 0, 1])
167+
def test_scalar_control_values(self, control_values):
168+
"""Test assignment of provided control_values."""
169+
op = Controlled(self.temp_op, 0, control_values=control_values)
170+
assert op.control_values == [control_values]
171+
172+
def test_tuple_control_values(self):
173+
"""Test assignment of provided control_values."""
174+
op = Controlled(self.temp_op, (0, 1), control_values=(0, 1))
175+
assert op.control_values == [False, True]
176+
166177
def test_non_boolean_control_values(self):
167178
"""Test control values are converted to booleans."""
168179
op = Controlled(self.temp_op, (0, 1, 2), control_values=["", None, 5])

tests/ops/op_math/test_controlled_ops.py

+12
Original file line numberDiff line numberDiff line change
@@ -737,3 +737,15 @@ def test_controlled_method(base, cbase):
737737
"""Tests the _controlled method for parametric ops."""
738738
# pylint: disable=protected-access
739739
assert qml.equal(base._controlled("a"), cbase)
740+
741+
742+
@pytest.mark.parametrize(
743+
"control, control_values",
744+
[([0, 1], [True, False]), ([10, "a"], (0, 0)), ([2], 1), (2, (True,))],
745+
)
746+
@pytest.mark.parametrize(
747+
"base_op", [qml.CRX(0.2, [21, 22]), qml.CNOT([21, 22]), qml.CPhase(0.6, [21, 22])]
748+
)
749+
def test_controlling_a_controlled_operation(control, control_values, base_op):
750+
"""Test that a controlled op can be controlled again."""
751+
qml.ctrl(base_op, control=control, control_values=control_values)

tests/transforms/test_defer_measurements.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -1063,7 +1063,7 @@ def qnode():
10631063
)
10641064
def test_cond_qfunc(self, device):
10651065
"""Test that a qfunc can also used with qml.cond."""
1066-
dev = qml.device(device, wires=3)
1066+
dev = qml.device(device, wires=4)
10671067

10681068
r = 2.324
10691069

@@ -1074,20 +1074,22 @@ def normal_circuit(rads):
10741074
qml.CNOT(wires=[0, 1])
10751075
qml.CRY(rads, wires=[0, 1])
10761076
qml.CZ(wires=[0, 1])
1077-
return qml.probs(wires=1)
1077+
qml.ctrl(qml.CRX, control=0, control_values=[1])(0.5, [1, 2])
1078+
return qml.probs(wires=[1, 2])
10781079

10791080
def f(x):
10801081
qml.PauliX(1)
10811082
qml.RY(x, wires=1)
10821083
qml.PauliZ(1)
1084+
qml.CRX(0.5, [1, 2])
10831085

10841086
@qml.defer_measurements
10851087
@qml.qnode(dev)
10861088
def quantum_control_circuit(r):
10871089
qml.Hadamard(0)
10881090
m_0 = qml.measure(0)
10891091
qml.cond(m_0, f)(r)
1090-
return qml.probs(wires=1)
1092+
return qml.probs(wires=[1, 2])
10911093

10921094
exp = normal_circuit(r)
10931095
cond_probs = quantum_control_circuit(r)

0 commit comments

Comments
 (0)