Skip to content

Commit

Permalink
Controlled.wires does not contain work wires (#5728)
Browse files Browse the repository at this point in the history
**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 <[email protected]>
  • Loading branch information
Shiro-Raven and astralcai authored May 28, 2024
1 parent c285b87 commit 5b192e6
Show file tree
Hide file tree
Showing 5 changed files with 26 additions and 28 deletions.
4 changes: 4 additions & 0 deletions doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@
returning a list of `QuantumTape`s and a post-processing function instead of simply the transformed circuit.
[(#5693)](https://github.com/PennyLaneAI/pennylane/pull/5693)

* `Controlled.wires` does not include `self.work_wires` anymore. That can be accessed separately through `Controlled.work_wires`.
Consequently, `Controlled.active_wires` has been removed in favour of the more common `Controlled.wires`.
[(#5728)](https://github.com/PennyLaneAI/pennylane/pull/5728)

* `qml.QutritAmplitudeDamping` channel has been added, allowing for noise processes modelled by amplitude damping to be simulated on the `default.qutrit.mixed` device.
[(#5503)](https://github.com/PennyLaneAI/pennylane/pull/5503)

Expand Down
2 changes: 1 addition & 1 deletion pennylane/drawer/tape_mpl.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def _(op: ops.Toffoli, drawer, layer, _):

@_add_operation_to_drawer.register
def _(op: ops.MultiControlledX, drawer, layer, _):
drawer.CNOT(layer, op.active_wires, control_values=op.control_values)
drawer.CNOT(layer, op.wires, control_values=op.control_values)


@_add_operation_to_drawer.register
Expand Down
21 changes: 7 additions & 14 deletions pennylane/ops/op_math/controlled.py
Original file line number Diff line number Diff line change
Expand Up @@ -517,14 +517,9 @@ def work_wires(self):
"""Additional wires that can be used in the decomposition. Not modified by the operation."""
return self.hyperparameters["work_wires"]

@property
def active_wires(self):
"""Wires modified by the operator. This is the control wires followed by the target wires."""
return self.control_wires + self.target_wires

@property
def wires(self):
return self.control_wires + self.target_wires + self.work_wires
return self.control_wires + self.target_wires

def map_wires(self, wire_map: dict):
new_base = self.base.map_wires(wire_map=wire_map)
Expand Down Expand Up @@ -584,9 +579,7 @@ def matrix(self, wire_order=None):
canonical_matrix = self._compute_matrix_from_base()

wire_order = wire_order or self.wires
return qml.math.expand_matrix(
canonical_matrix, wires=self.active_wires, wire_order=wire_order
)
return qml.math.expand_matrix(canonical_matrix, wires=self.wires, wire_order=wire_order)

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

if isinstance(op.base, qml.PauliX) and len(op.control_wires) == 1:
return [qml.CNOT(wires=op.active_wires)]
return [qml.CNOT(wires=op.wires)]

if isinstance(op.base, qml.PauliX) and len(op.control_wires) == 2:
return qml.Toffoli.compute_decomposition(wires=op.active_wires)
return qml.Toffoli.compute_decomposition(wires=op.wires)

if isinstance(op.base, qml.CNOT) and len(op.control_wires) == 1:
return qml.Toffoli.compute_decomposition(wires=op.active_wires)
return qml.Toffoli.compute_decomposition(wires=op.wires)

return qml.MultiControlledX.compute_decomposition(
wires=op.active_wires,
wires=op.wires,
work_wires=op.work_wires,
)

Expand All @@ -766,7 +759,7 @@ def _decompose_custom_ops(op: Controlled) -> List["operation.Operator"]:
custom_key = (type(op.base), len(op.control_wires))
if custom_key in ops_with_custom_ctrl_ops:
custom_op_cls = ops_with_custom_ctrl_ops[custom_key]
return custom_op_cls.compute_decomposition(*op.data, op.active_wires)
return custom_op_cls.compute_decomposition(*op.data, op.wires)
if isinstance(op.base, pauli_x_based_ctrl_ops):
# has some special case handling of its own for further decomposition
return _decompose_pauli_x_based_no_control_values(op)
Expand Down
19 changes: 10 additions & 9 deletions pennylane/ops/op_math/controlled_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ class ControlledQubitUnitary(ControlledOp):
* ``control_wires``: wires that act as control for the operation
* ``control_values``: the state on which to apply the controlled operation (see below)
* ``target_wires``: the wires the unitary matrix will be applied to
* ``active_wires``: Wires modified by the operator. This is the control wires followed
by the target wires.
* ``work_wires``: wires made use of during the decomposition of the operation into native operations
**Details:**
Expand All @@ -66,6 +65,8 @@ class ControlledQubitUnitary(ControlledOp):
control on (default is the all 1s state)
unitary_check (bool): whether to check whether an array U is unitary when creating the
operator (default False)
work_wires (Union[Wires, Sequence[int], or int]): ancillary wire(s) that may be utilized in during
the decomposition of the operator into native operations.
**Example**
Expand Down Expand Up @@ -1028,7 +1029,7 @@ class MultiControlledX(ControlledOp):
name = "MultiControlledX"

def _flatten(self):
return (), (self.active_wires, tuple(self.control_values), self.work_wires)
return (), (self.wires, tuple(self.control_values), self.work_wires)

@classmethod
def _unflatten(cls, _, metadata):
Expand Down Expand Up @@ -1079,11 +1080,13 @@ def __init__(self, control_wires=None, wires=None, control_values=None, work_wir
)

def __repr__(self):
return f"MultiControlledX(wires={self.active_wires.tolist()}, control_values={self.control_values})"
return (
f"MultiControlledX(wires={self.wires.tolist()}, control_values={self.control_values})"
)

@property
def wires(self):
return self.active_wires
return self.control_wires + self.target_wires

# pylint: disable=unused-argument, arguments-differ
@staticmethod
Expand Down Expand Up @@ -1126,9 +1129,7 @@ def compute_matrix(control_wires, control_values=None, **kwargs):
def matrix(self, wire_order=None):
canonical_matrix = self.compute_matrix(self.control_wires, self.control_values)
wire_order = wire_order or self.wires
return qml.math.expand_matrix(
canonical_matrix, wires=self.active_wires, wire_order=wire_order
)
return qml.math.expand_matrix(canonical_matrix, wires=self.wires, wire_order=wire_order)

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

def decomposition(self):
return self.compute_decomposition(self.active_wires, self.work_wires, self.control_values)
return self.compute_decomposition(self.wires, self.work_wires, self.control_values)


class CRX(ControlledOp):
Expand Down
8 changes: 4 additions & 4 deletions tests/ops/op_math/test_controlled.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def test_nonparametric_ops(self):
assert op.base is self.temp_op
assert op.hyperparameters["base"] is self.temp_op

assert op.wires == Wires((0, 1, "a", "aux"))
assert op.wires == Wires((0, 1, "a"))

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

assert op.num_wires == 4
assert op.num_wires == 3

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

assert op.wires == Wires((3, 4, 0, 1, "aux"))
assert op.wires == Wires((3, 4, 0, 1))

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

Expand Down Expand Up @@ -740,7 +740,7 @@ def test_aux_wires_included(self):
work_wires="aux",
)
mat = op.matrix()
assert mat.shape == (8, 8)
assert mat.shape == (4, 4)

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

0 comments on commit 5b192e6

Please sign in to comment.