Repository: PennyLaneAI/pennylane

Issue:
[BUG] `CSWAP` and `CRot` are missing its `control_wires` attribute
`qml.CSWAP` and `qml.CRot`  are both controlled operations, but neither define their `control_wires` attribute.


Discussion:

Resolving patch:
diff --git a/pennylane/ops/qubit/non_parametric_ops.py b/pennylane/ops/qubit/non_parametric_ops.py
--- a/pennylane/ops/qubit/non_parametric_ops.py
+++ b/pennylane/ops/qubit/non_parametric_ops.py
@@ -611,10 +611,6 @@ def adjoint(self):
     def _controlled(self, wire):
         CSWAP(wires=wire + self.wires)
 
-    @property
-    def control_wires(self):
-        return Wires(self.wires[:2])
-
 
 class ISWAP(Operation):
     r"""ISWAP(wires)
@@ -791,6 +787,10 @@ def decomposition(wires):
     def adjoint(self):
         return CSWAP(wires=self.wires)
 
+    @property
+    def control_wires(self):
+        return Wires(self.wires[0])
+
 
 class Toffoli(Operation):
     r"""Toffoli(wires)
diff --git a/pennylane/ops/qubit/parametric_ops.py b/pennylane/ops/qubit/parametric_ops.py
--- a/pennylane/ops/qubit/parametric_ops.py
+++ b/pennylane/ops/qubit/parametric_ops.py
@@ -1143,6 +1143,10 @@ def adjoint(self):
         phi, theta, omega = self.parameters
         return CRot(-omega, -theta, -phi, wires=self.wires)
 
+    @property
+    def control_wires(self):
+        return Wires(self.wires[0])
+
 
 class U1(Operation):
     r"""U1(phi)


Test patch:
diff --git a/tests/ops/qubit/test_matrix_ops.py b/tests/ops/qubit/test_matrix_ops.py
--- a/tests/ops/qubit/test_matrix_ops.py
+++ b/tests/ops/qubit/test_matrix_ops.py
@@ -410,3 +410,16 @@ def test_label(op, label):
     assert op.label(decimals=5) == label
     op.inv()
     assert op.label() == label + "⁻¹"
+
+
+control_data = [
+    (qml.QubitUnitary(X, wires=0), Wires([])),
+    (qml.DiagonalQubitUnitary([1, 1], wires=1), Wires([])),
+    (qml.ControlledQubitUnitary(X, control_wires=0, wires=1), Wires([0])),
+]
+
+
+@pytest.mark.parametrize("op, control_wires", control_data)
+def test_control_wires(op, control_wires):
+    """Test ``control_wires`` attribute for matrix operations."""
+    assert op.control_wires == control_wires
diff --git a/tests/ops/qubit/test_non_parametric_ops.py b/tests/ops/qubit/test_non_parametric_ops.py
--- a/tests/ops/qubit/test_non_parametric_ops.py
+++ b/tests/ops/qubit/test_non_parametric_ops.py
@@ -738,3 +738,29 @@ def test_label_method(op, label1, label2):
 
     op.inv()
     assert op.label() == label2
+
+
+control_data = [
+    (qml.Hadamard(0), Wires([])),
+    (qml.PauliX(0), Wires([])),
+    (qml.PauliY(0), Wires([])),
+    (qml.S(wires=0), Wires([])),
+    (qml.T(wires=0), Wires([])),
+    (qml.SX(wires=0), Wires([])),
+    (qml.SWAP(wires=(0, 1)), Wires([])),
+    (qml.ISWAP(wires=(0, 1)), Wires([])),
+    (qml.SISWAP(wires=(0, 1)), Wires([])),
+    (qml.CNOT(wires=(0, 1)), Wires(0)),
+    (qml.CZ(wires=(0, 1)), Wires(0)),
+    (qml.CY(wires=(0, 1)), Wires(0)),
+    (qml.CSWAP(wires=(0, 1, 2)), Wires([0])),
+    (qml.Toffoli(wires=(0, 1, 2)), Wires([0, 1])),
+    (qml.MultiControlledX(control_wires=[0, 1, 2, 3], wires=4), Wires([0, 1, 2, 3])),
+]
+
+
+@pytest.mark.parametrize("op, control_wires", control_data)
+def test_control_wires(op, control_wires):
+    """Test ``control_wires`` attribute for non-parametrized operations."""
+
+    assert op.control_wires == control_wires
diff --git a/tests/ops/qubit/test_parametric_ops.py b/tests/ops/qubit/test_parametric_ops.py
--- a/tests/ops/qubit/test_parametric_ops.py
+++ b/tests/ops/qubit/test_parametric_ops.py
@@ -1506,3 +1506,32 @@ def test_string_parameter(self):
 
         op3 = qml.Rot("x", "y", "z", wires=0)
         assert op3.label(decimals=0) == "Rot\n(x,y,z)"
+
+
+control_data = [
+    (qml.Rot(1, 2, 3, wires=0), Wires([])),
+    (qml.RX(1.23, wires=0), Wires([])),
+    (qml.RY(1.23, wires=0), Wires([])),
+    (qml.MultiRZ(1.234, wires=(0, 1, 2)), Wires([])),
+    (qml.PauliRot(1.234, "IXY", wires=(0, 1, 2)), Wires([])),
+    (qml.PhaseShift(1.234, wires=0), Wires([])),
+    (qml.U1(1.234, wires=0), Wires([])),
+    (qml.U2(1.234, 2.345, wires=0), Wires([])),
+    (qml.U3(1.234, 2.345, 3.456, wires=0), Wires([])),
+    (qml.IsingXX(1.234, wires=(0, 1)), Wires([])),
+    (qml.IsingYY(1.234, wires=(0, 1)), Wires([])),
+    (qml.IsingZZ(1.234, wires=(0, 1)), Wires([])),
+    ### Controlled Ops
+    (qml.ControlledPhaseShift(1.234, wires=(0, 1)), Wires(0)),
+    (qml.CPhase(1.234, wires=(0, 1)), Wires(0)),
+    (qml.CRX(1.234, wires=(0, 1)), Wires(0)),
+    (qml.CRY(1.234, wires=(0, 1)), Wires(0)),
+    (qml.CRZ(1.234, wires=(0, 1)), Wires(0)),
+    (qml.CRot(1.234, 2.2345, 3.456, wires=(0, 1)), Wires(0)),
+]
+
+
+@pytest.mark.parametrize("op, control_wires", control_data)
+def test_control_wires(op, control_wires):
+    """Test the ``control_wires`` attribute for parametrized operations."""
+    assert op.control_wires == control_wires