Skip to content

Commit 5b192e6

Browse files
Controlled.wires does not contain work wires (#5728)
**Context:** `Controlled.wires` contained `work_wires` that are typically used for the decomposition. This was inconsistent with the general usage of the attributes. The expected wires behaviour was in the `active_wires`. **Description of the Change:** `active_wires` was renamed to `wires`, and `work_wires` has been removed from the returned wires for `self.wires` **Benefits:** - More consistent behaviour of wiring attributes [[55898](https://app.shortcut.com/xanaduai/story/55898)] --------- Co-authored-by: Astral Cai <astral.cai@xanadu.ai>
1 parent c285b87 commit 5b192e6

File tree

5 files changed

+26
-28
lines changed

5 files changed

+26
-28
lines changed

doc/releases/changelog-dev.md

+4
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,10 @@
137137
returning a list of `QuantumTape`s and a post-processing function instead of simply the transformed circuit.
138138
[(#5693)](https://github.com/PennyLaneAI/pennylane/pull/5693)
139139

140+
* `Controlled.wires` does not include `self.work_wires` anymore. That can be accessed separately through `Controlled.work_wires`.
141+
Consequently, `Controlled.active_wires` has been removed in favour of the more common `Controlled.wires`.
142+
[(#5728)](https://github.com/PennyLaneAI/pennylane/pull/5728)
143+
140144
* `qml.QutritAmplitudeDamping` channel has been added, allowing for noise processes modelled by amplitude damping to be simulated on the `default.qutrit.mixed` device.
141145
[(#5503)](https://github.com/PennyLaneAI/pennylane/pull/5503)
142146

pennylane/drawer/tape_mpl.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ def _(op: ops.Toffoli, drawer, layer, _):
114114

115115
@_add_operation_to_drawer.register
116116
def _(op: ops.MultiControlledX, drawer, layer, _):
117-
drawer.CNOT(layer, op.active_wires, control_values=op.control_values)
117+
drawer.CNOT(layer, op.wires, control_values=op.control_values)
118118

119119

120120
@_add_operation_to_drawer.register

pennylane/ops/op_math/controlled.py

+7-14
Original file line numberDiff line numberDiff line change
@@ -517,14 +517,9 @@ def work_wires(self):
517517
"""Additional wires that can be used in the decomposition. Not modified by the operation."""
518518
return self.hyperparameters["work_wires"]
519519

520-
@property
521-
def active_wires(self):
522-
"""Wires modified by the operator. This is the control wires followed by the target wires."""
523-
return self.control_wires + self.target_wires
524-
525520
@property
526521
def wires(self):
527-
return self.control_wires + self.target_wires + self.work_wires
522+
return self.control_wires + self.target_wires
528523

529524
def map_wires(self, wire_map: dict):
530525
new_base = self.base.map_wires(wire_map=wire_map)
@@ -584,9 +579,7 @@ def matrix(self, wire_order=None):
584579
canonical_matrix = self._compute_matrix_from_base()
585580

586581
wire_order = wire_order or self.wires
587-
return qml.math.expand_matrix(
588-
canonical_matrix, wires=self.active_wires, wire_order=wire_order
589-
)
582+
return qml.math.expand_matrix(canonical_matrix, wires=self.wires, wire_order=wire_order)
590583

591584
# pylint: disable=arguments-differ
592585
def sparse_matrix(self, wire_order=None, format="csr"):
@@ -743,16 +736,16 @@ def _decompose_pauli_x_based_no_control_values(op: Controlled):
743736
"""Decomposes a PauliX-based operation"""
744737

745738
if isinstance(op.base, qml.PauliX) and len(op.control_wires) == 1:
746-
return [qml.CNOT(wires=op.active_wires)]
739+
return [qml.CNOT(wires=op.wires)]
747740

748741
if isinstance(op.base, qml.PauliX) and len(op.control_wires) == 2:
749-
return qml.Toffoli.compute_decomposition(wires=op.active_wires)
742+
return qml.Toffoli.compute_decomposition(wires=op.wires)
750743

751744
if isinstance(op.base, qml.CNOT) and len(op.control_wires) == 1:
752-
return qml.Toffoli.compute_decomposition(wires=op.active_wires)
745+
return qml.Toffoli.compute_decomposition(wires=op.wires)
753746

754747
return qml.MultiControlledX.compute_decomposition(
755-
wires=op.active_wires,
748+
wires=op.wires,
756749
work_wires=op.work_wires,
757750
)
758751

@@ -766,7 +759,7 @@ def _decompose_custom_ops(op: Controlled) -> List["operation.Operator"]:
766759
custom_key = (type(op.base), len(op.control_wires))
767760
if custom_key in ops_with_custom_ctrl_ops:
768761
custom_op_cls = ops_with_custom_ctrl_ops[custom_key]
769-
return custom_op_cls.compute_decomposition(*op.data, op.active_wires)
762+
return custom_op_cls.compute_decomposition(*op.data, op.wires)
770763
if isinstance(op.base, pauli_x_based_ctrl_ops):
771764
# has some special case handling of its own for further decomposition
772765
return _decompose_pauli_x_based_no_control_values(op)

pennylane/ops/op_math/controlled_ops.py

+10-9
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@ class ControlledQubitUnitary(ControlledOp):
4444
* ``control_wires``: wires that act as control for the operation
4545
* ``control_values``: the state on which to apply the controlled operation (see below)
4646
* ``target_wires``: the wires the unitary matrix will be applied to
47-
* ``active_wires``: Wires modified by the operator. This is the control wires followed
48-
by the target wires.
47+
* ``work_wires``: wires made use of during the decomposition of the operation into native operations
4948
5049
**Details:**
5150
@@ -66,6 +65,8 @@ class ControlledQubitUnitary(ControlledOp):
6665
control on (default is the all 1s state)
6766
unitary_check (bool): whether to check whether an array U is unitary when creating the
6867
operator (default False)
68+
work_wires (Union[Wires, Sequence[int], or int]): ancillary wire(s) that may be utilized in during
69+
the decomposition of the operator into native operations.
6970
7071
**Example**
7172
@@ -1028,7 +1029,7 @@ class MultiControlledX(ControlledOp):
10281029
name = "MultiControlledX"
10291030

10301031
def _flatten(self):
1031-
return (), (self.active_wires, tuple(self.control_values), self.work_wires)
1032+
return (), (self.wires, tuple(self.control_values), self.work_wires)
10321033

10331034
@classmethod
10341035
def _unflatten(cls, _, metadata):
@@ -1079,11 +1080,13 @@ def __init__(self, control_wires=None, wires=None, control_values=None, work_wir
10791080
)
10801081

10811082
def __repr__(self):
1082-
return f"MultiControlledX(wires={self.active_wires.tolist()}, control_values={self.control_values})"
1083+
return (
1084+
f"MultiControlledX(wires={self.wires.tolist()}, control_values={self.control_values})"
1085+
)
10831086

10841087
@property
10851088
def wires(self):
1086-
return self.active_wires
1089+
return self.control_wires + self.target_wires
10871090

10881091
# pylint: disable=unused-argument, arguments-differ
10891092
@staticmethod
@@ -1126,9 +1129,7 @@ def compute_matrix(control_wires, control_values=None, **kwargs):
11261129
def matrix(self, wire_order=None):
11271130
canonical_matrix = self.compute_matrix(self.control_wires, self.control_values)
11281131
wire_order = wire_order or self.wires
1129-
return qml.math.expand_matrix(
1130-
canonical_matrix, wires=self.active_wires, wire_order=wire_order
1131-
)
1132+
return qml.math.expand_matrix(canonical_matrix, wires=self.wires, wire_order=wire_order)
11321133

11331134
# pylint: disable=unused-argument, arguments-differ
11341135
@staticmethod
@@ -1189,7 +1190,7 @@ def compute_decomposition(wires=None, work_wires=None, control_values=None, **kw
11891190
return flips1 + decomp + flips2
11901191

11911192
def decomposition(self):
1192-
return self.compute_decomposition(self.active_wires, self.work_wires, self.control_values)
1193+
return self.compute_decomposition(self.wires, self.work_wires, self.control_values)
11931194

11941195

11951196
class CRX(ControlledOp):

tests/ops/op_math/test_controlled.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ def test_nonparametric_ops(self):
132132
assert op.base is self.temp_op
133133
assert op.hyperparameters["base"] is self.temp_op
134134

135-
assert op.wires == Wires((0, 1, "a", "aux"))
135+
assert op.wires == Wires((0, 1, "a"))
136136

137137
assert op.control_wires == Wires((0, 1))
138138
assert op.hyperparameters["control_wires"] == Wires((0, 1))
@@ -151,7 +151,7 @@ def test_nonparametric_ops(self):
151151
assert op.parameters == [] # pylint: disable=use-implicit-booleaness-not-comparison
152152
assert op.data == ()
153153

154-
assert op.num_wires == 4
154+
assert op.num_wires == 3
155155

156156
def test_default_control_values(self):
157157
"""Test assignment of default control_values."""
@@ -355,7 +355,7 @@ def test_map_wires(self):
355355
base = qml.IsingXX(1.234, wires=(0, 1))
356356
op = Controlled(base, (3, 4), work_wires="aux")
357357

358-
assert op.wires == Wires((3, 4, 0, 1, "aux"))
358+
assert op.wires == Wires((3, 4, 0, 1))
359359

360360
op = op.map_wires(wire_map={3: "a", 4: "b", 0: "c", 1: "d", "aux": "extra"})
361361

@@ -740,7 +740,7 @@ def test_aux_wires_included(self):
740740
work_wires="aux",
741741
)
742742
mat = op.matrix()
743-
assert mat.shape == (8, 8)
743+
assert mat.shape == (4, 4)
744744

745745
def test_wire_order(self):
746746
"""Test that the ``wire_order`` keyword argument alters the matrix as expected."""

0 commit comments

Comments
 (0)