Repository: PennyLaneAI/pennylane
Pull request #5067 (2024-01-15 20:34:06)

Deprecate Operator.validate_subspace(subspace)
This PR relocates the `Operator.validate_subspace()`  method  to the `qml.ops.qutrit.parametric_ops` module.

### Context:
`Operator.validate_subspace(subspace)` is only employed under a specific set of qutrit operators and is therefore not needed as an official part of the Operator interface.

Alex-Preciado: [sc-51601]

Patch:
diff --git a/doc/development/deprecations.rst b/doc/development/deprecations.rst
index ecb027b9a..12354d71b 100644
--- a/doc/development/deprecations.rst
+++ b/doc/development/deprecations.rst
@@ -9,6 +9,13 @@ deprecations are listed below.
 Pending deprecations
 --------------------
 
+* The method ``Operator.validate_subspace(subspace)``, only employed under a specific set of qutrit
+  operators, has been relocated to the ``qml.ops.qutrit.parametric_ops`` module and will be removed
+  from the ``Operator`` class.
+
+  - Deprecated in v0.35
+  - Will be removed in v0.36
+
 * The private functions ``_pauli_mult``, ``_binary_matrix`` and ``_get_pauli_map`` from the
   ``pauli`` module have been deprecated, as they are no longer used anywhere and the same
   functionality can be achieved using newer features in the ``pauli`` module.
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index bcae207fa..80b6ad285 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -72,6 +72,10 @@
 
 <h3>Deprecations 👋</h3>
 
+* `Operator.validate_subspace(subspace)` has been relocated to the `qml.ops.qutrit.parametric_ops`
+  module and will be removed from the Operator class in an upcoming release.
+  [(#5067)](https://github.com/PennyLaneAI/pennylane/pull/5067)
+
 * Matrix and tensor products between `PauliWord` and `PauliSentence` instances are done using the `@` operator, `*` will be used only for scalar multiplication.
   [(#4989)](https://github.com/PennyLaneAI/pennylane/pull/4989)
 
@@ -125,11 +129,12 @@ This release contains contributions from (in alphabetical order):
 
 Abhishek Abhishek,
 Astral Cai,
-Pablo Antonio Moreno Casares,
 Isaac De Vlugt,
 Korbinian Kottmann,
 Christina Lee,
 Xiaoran Li,
+Pablo Antonio Moreno Casares,
 Lee J. O'Riordan,
 Mudit Pandey,
+Alex Preciado,
 Matthew Silverman.
diff --git a/pennylane/operation.py b/pennylane/operation.py
index ee3ecefa6..cea249de0 100644
--- a/pennylane/operation.py
+++ b/pennylane/operation.py
@@ -1149,19 +1149,18 @@ class Operator(abc.ABC):
 
         Args:
             subspace (tuple[int]): Subspace to check for correctness
-        """
-        if not hasattr(subspace, "__iter__") or len(subspace) != 2:
-            raise ValueError(
-                "The subspace must be a sequence with two unique elements from the set {0, 1, 2}."
-            )
 
-        if not all(s in {0, 1, 2} for s in subspace):
-            raise ValueError("Elements of the subspace must be 0, 1, or 2.")
+        .. warning::
 
-        if subspace[0] == subspace[1]:
-            raise ValueError("Elements of subspace list must be unique.")
+            ``Operator.validate_subspace(subspace)`` has been relocated to the ``qml.ops.qutrit.parametric_ops`` module and will be removed from the Operator class in an upcoming release.
+        """
+
+        warnings.warn(
+            "Operator.validate_subspace(subspace) has been relocated to the qml.ops.qutrit.parametric_ops module and will be removed from the Operator class in an upcoming release.",
+            qml.PennyLaneDeprecationWarning,
+        )
 
-        return tuple(sorted(subspace))
+        return qml.ops.qutrit.validate_subspace(subspace)
 
     @property
     def num_params(self):
diff --git a/pennylane/ops/qutrit/non_parametric_ops.py b/pennylane/ops/qutrit/non_parametric_ops.py
index 7727ad62d..483b49370 100644
--- a/pennylane/ops/qutrit/non_parametric_ops.py
+++ b/pennylane/ops/qutrit/non_parametric_ops.py
@@ -20,6 +20,7 @@ import numpy as np
 
 from pennylane.operation import Operation, AdjointUndefinedError
 from pennylane.wires import Wires
+from .parametric_ops import validate_subspace
 
 OMEGA = np.exp(2 * np.pi * 1j / 3)
 ZETA = OMEGA ** (1 / 3)  # ZETA will be used as a phase for later non-parametric operations
@@ -466,7 +467,7 @@ class THadamard(Operation):
         return base_label or "TH"
 
     def __init__(self, wires, subspace=None):
-        self._subspace = Operation.validate_subspace(subspace) if subspace is not None else None
+        self._subspace = validate_subspace(subspace) if subspace is not None else None
         self._hyperparameters = {
             "subspace": self.subspace,
         }
diff --git a/pennylane/ops/qutrit/parametric_ops.py b/pennylane/ops/qutrit/parametric_ops.py
index e7776c0e5..1f0761a54 100644
--- a/pennylane/ops/qutrit/parametric_ops.py
+++ b/pennylane/ops/qutrit/parametric_ops.py
@@ -24,6 +24,29 @@ from pennylane.operation import Operation
 stack_last = functools.partial(qml.math.stack, axis=-1)
 
 
+def validate_subspace(subspace):
+    """Validate the subspace for qutrit operations.
+
+    This method determines whether a given subspace for qutrit operations
+    is defined correctly or not. If not, a ``ValueError`` is thrown.
+
+    Args:
+        subspace (tuple[int]): Subspace to check for correctness
+    """
+    if not hasattr(subspace, "__iter__") or len(subspace) != 2:
+        raise ValueError(
+            "The subspace must be a sequence with two unique elements from the set {0, 1, 2}."
+        )
+
+    if not all(s in {0, 1, 2} for s in subspace):
+        raise ValueError("Elements of the subspace must be 0, 1, or 2.")
+
+    if subspace[0] == subspace[1]:
+        raise ValueError("Elements of subspace list must be unique.")
+
+    return tuple(sorted(subspace))
+
+
 class TRX(Operation):
     r"""
     The single qutrit X rotation
@@ -91,7 +114,7 @@ class TRX(Operation):
         return qml.s_prod(-0.5, qml.GellMann(self.wires, index=self._index_dict[self.subspace]))
 
     def __init__(self, phi, wires, subspace=(0, 1), id=None):
-        self._subspace = self.validate_subspace(subspace)
+        self._subspace = validate_subspace(subspace)
         self._hyperparameters = {
             "subspace": self._subspace,
         }
@@ -235,7 +258,7 @@ class TRY(Operation):
         return qml.s_prod(-0.5, qml.GellMann(self.wires, index=self._index_dict[self.subspace]))
 
     def __init__(self, phi, wires, subspace=(0, 1), id=None):
-        self._subspace = self.validate_subspace(subspace)
+        self._subspace = validate_subspace(subspace)
         self._hyperparameters = {
             "subspace": self._subspace,
         }
@@ -382,7 +405,7 @@ class TRZ(Operation):
         return qml.dot(coeffs, obs)
 
     def __init__(self, phi, wires, subspace=(0, 1), id=None):
-        self._subspace = self.validate_subspace(subspace)
+        self._subspace = validate_subspace(subspace)
         self._hyperparameters = {
             "subspace": self._subspace,
         }


Test patch:
diff --git a/tests/ops/qutrit/test_qutrit_parametric_ops.py b/tests/ops/qutrit/test_qutrit_parametric_ops.py
index 3c44dd28b..5a3f66a5d 100644
--- a/tests/ops/qutrit/test_qutrit_parametric_ops.py
+++ b/tests/ops/qutrit/test_qutrit_parametric_ops.py
@@ -24,6 +24,7 @@ from gate_data import TSHIFT, TCLOCK
 from pennylane import numpy as npp
 import pennylane as qml
 from pennylane.wires import Wires
+from pennylane.ops.qutrit import validate_subspace
 
 
 PARAMETRIZED_OPERATIONS = [
@@ -408,6 +409,24 @@ def test_control_wires(op, control_wires):
     assert op.control_wires == control_wires
 
 
+qutrit_subspace_error_data = [
+    ([1, 1], "Elements of subspace list must be unique."),
+    ([1, 2, 3], "The subspace must be a sequence with"),
+    ([3, 1], "Elements of the subspace must be 0, 1, or 2."),
+    ([3, 3], "Elements of the subspace must be 0, 1, or 2."),
+    ([1], "The subspace must be a sequence with"),
+    (0, "The subspace must be a sequence with two unique"),
+]
+
+
+@pytest.mark.parametrize("subspace, err_msg", qutrit_subspace_error_data)
+def test_qutrit_subspace_op_errors(subspace, err_msg):
+    """Test that the correct errors are raised when subspace is incorrectly defined"""
+
+    with pytest.raises(ValueError, match=err_msg):
+        _ = validate_subspace(subspace)
+
+
 @pytest.mark.parametrize(
     "op, obs, grad_fn",
     [
diff --git a/tests/test_operation.py b/tests/test_operation.py
index e20903c95..32f2e1a4d 100644
--- a/tests/test_operation.py
+++ b/tests/test_operation.py
@@ -44,22 +44,14 @@ CNOT_broadcasted = np.tensordot([1.4], CNOT, axes=0)
 I_broadcasted = I[pnp.newaxis]
 
 
-qutrit_subspace_error_data = [
-    ([1, 1], "Elements of subspace list must be unique."),
-    ([1, 2, 3], "The subspace must be a sequence with"),
-    ([3, 1], "Elements of the subspace must be 0, 1, or 2."),
-    ([3, 3], "Elements of the subspace must be 0, 1, or 2."),
-    ([1], "The subspace must be a sequence with"),
-    (0, "The subspace must be a sequence with two unique"),
-]
-
+def test_validate_subspace_is_deprecated():
+    """Test that Operator.validate_subspace() is deprecated"""
 
-@pytest.mark.parametrize("subspace, err_msg", qutrit_subspace_error_data)
-def test_qutrit_subspace_op_errors(subspace, err_msg):
-    """Test that the correct errors are raised when subspace is incorrectly defined"""
-
-    with pytest.raises(ValueError, match=err_msg):
-        _ = Operator.validate_subspace(subspace)
+    with pytest.warns(
+        expected_warning=qml.PennyLaneDeprecationWarning,
+        match=r"Operator\.validate_subspace\(subspace\)",
+    ):
+        _ = Operator.validate_subspace([0, 1])
 
 
 class TestOperatorConstruction: