Repository: PennyLaneAI/pennylane
Pull request #6835 (2025-01-15 16:07:24)

Remove string support for `control_values` of `MutliControlledX`
**Context:**
In PL0.36 we deprecated the string type support for argument `control_values` of the operator `MultiControlledX` initialization, e.g. `control_values='01'` shall not be allowed anymore. In this PR we would like to remove this entirely with a strict type check.

**Description of the Change:**
 - Added a type check with `isinstance` to raise `ValueError` if the `control_values` does not follow the designed pattern.
 - Added a type hint at correpsonding position within the init func signature
 - Removed a test that presumes the validity of str input
 - Alter the deprecation check into error check

**Benefits:**
Less redundancy

**Possible Drawbacks:**
Potential existence of uncovered indirect usage of string input in `MultiControlledX`

**Related GitHub Issues:**
[sc-81527]

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/6835?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 99.59%. Comparing base [(`14c61a6`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/14c61a6d2b252dbf19d9771d61ca5498f8ce03fb?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) to head [(`b994bbf`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/b994bbf01fa270912ff604458dfbc55d620f3dda?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).
> Report is 4 commits behind head on master.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #6835      +/-   ##
==========================================
- Coverage   99.60%   99.59%   -0.01%     
==========================================
  Files         476      476              
  Lines       45215    45183      -32     
==========================================
- Hits        45035    45001      -34     
- Misses        180      182       +2     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/6835?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).

JerryChen97: Spotted deprecated usages in lightning https://github.com/PennyLaneAI/pennylane-lightning/pull/1040/files 
I guess we better hold on until that side things are fixed
JerryChen97: Another open PR from qml used deprecated interface, although it seems long time unmaintained. https://github.com/PennyLaneAI/qml/pull/386#discussion_r1917355704
lillian542: > Another open PR from qml used deprecated interface, although it seems long time unmaintained. https://github.com/PennyLaneAI/qml/pull/386#discussion_r1917355704

That demo hasn't been touched in 2 years, no need to worry about it :)
JerryChen97: Lightning fix merged. We set this removal as auto-merge now.

Patch:
diff --git a/doc/development/deprecations.rst b/doc/development/deprecations.rst
index dd6ba3076..ff6048e18 100644
--- a/doc/development/deprecations.rst
+++ b/doc/development/deprecations.rst
@@ -25,11 +25,6 @@ Pending deprecations
 
   - Deprecated in v0.36
 
-* ``MultiControlledX`` is the only controlled operation that still supports specifying control
-  values with a bit string. In the future, it will no longer accepts strings as control values.
-
-  - Deprecated in v0.36
-
 Completed removal of legacy operator arithmetic
 -----------------------------------------------
 
@@ -58,6 +53,11 @@ for details on how to port your legacy code to the new system. The following fun
 Completed deprecation cycles
 ----------------------------
 
+* ``MultiControlledX`` no longer accepts strings as control values.
+
+  - Deprecated in v0.36
+  - Removed in v0.41
+
 * The input argument ``control_wires`` of ``MultiControlledX`` has been removed.
 
   - Deprecated in v0.22
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index bebe15e58..af5aa2278 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -46,6 +46,9 @@
 
 <h3>Breaking changes 💔</h3>
 
+* `MultiControlledX` no longer accepts strings as control values.
+  [(#6835)](https://github.com/PennyLaneAI/pennylane/pull/6835)
+
 * The input argument `control_wires` of `MultiControlledX` has been removed.
   [(#6832)](https://github.com/PennyLaneAI/pennylane/pull/6832)
 
diff --git a/pennylane/ops/op_math/controlled_ops.py b/pennylane/ops/op_math/controlled_ops.py
index 7106fc046..e04188997 100644
--- a/pennylane/ops/op_math/controlled_ops.py
+++ b/pennylane/ops/op_math/controlled_ops.py
@@ -18,6 +18,7 @@ This submodule contains controlled operators based on the ControlledOp class.
 import warnings
 from collections.abc import Iterable
 from functools import lru_cache
+from typing import List, Union
 
 import numpy as np
 from scipy.linalg import block_diag
@@ -1154,23 +1155,31 @@ class MultiControlledX(ControlledOp):
             *wires, n_wires=len(wires), control_values=control_values, work_wires=work_wires
         )
 
+    @staticmethod
+    def _validate_control_values(control_values):
+        if control_values is not None:
+            if not (
+                isinstance(control_values, (bool, int))
+                or (
+                    (
+                        isinstance(control_values, (list, tuple))
+                        and all(isinstance(val, (bool, int)) for val in control_values)
+                    )
+                )
+            ):
+                raise ValueError(f"control_values must be boolean or int. Got: {control_values}")
+
     # pylint: disable=too-many-arguments
     def __init__(
         self,
         wires: WiresLike = (),
-        control_values=None,
+        control_values: Union[bool, List[bool], int, List[int]] = None,
         work_wires: WiresLike = (),
     ):
         wires = Wires(() if wires is None else wires)
         work_wires = Wires(() if work_wires is None else work_wires)
 
-        # First raise deprecation warnings regardless of the validity of other arguments
-        if isinstance(control_values, str):
-            warnings.warn(
-                "Specifying control values using a bitstring is deprecated, and will not be "
-                "supported in future releases, Use a list of booleans or integers instead.",
-                qml.PennyLaneDeprecationWarning,
-            )
+        self._validate_control_values(control_values)
 
         if len(wires) == 0:
             raise ValueError("Must specify the wires where the operation acts on")


Test patch:
diff --git a/tests/ops/qubit/test_non_parametric_ops.py b/tests/ops/qubit/test_non_parametric_ops.py
index b34d94ba9..453b7c408 100644
--- a/tests/ops/qubit/test_non_parametric_ops.py
+++ b/tests/ops/qubit/test_non_parametric_ops.py
@@ -606,11 +606,11 @@ class TestMultiControlledX:
 
     X = np.array([[0, 1], [1, 0]])
 
-    def test_str_control_values_deprecation(self):
+    def test_str_control_values_error(self):
         """Tests that control_values specified with a bit string is deprecated."""
-        with pytest.warns(
-            qml.PennyLaneDeprecationWarning,
-            match="Specifying control values using a bitstring is deprecated",
+        with pytest.raises(
+            ValueError,
+            match="control_values must be boolean or int",
         ):
             _ = qml.MultiControlledX(wires=[0, 1, 2], control_values="01")
 
@@ -631,26 +631,6 @@ class TestMultiControlledX:
         with pytest.raises(ValueError, match=error_message):
             _ = qml.MultiControlledX(wires=wires, control_values=control_values)
 
-    @pytest.mark.parametrize(
-        "wires, control_values, error_message",
-        [
-            ([0, 1, 2], "ab", "String of control values can contain only '0' or '1'."),
-            (
-                [0, 1, 2],
-                "011",
-                "Length of control values must equal number of control wires.",
-            ),
-        ],
-    )
-    def test_invalid_str_control_values(self, wires, control_values, error_message):
-        """Tests control_values specified with invalid strings"""
-        with pytest.warns(
-            qml.PennyLaneDeprecationWarning,
-            match="Specifying control values using a bitstring is deprecated",
-        ):
-            with pytest.raises(ValueError, match=error_message):
-                _ = qml.MultiControlledX(wires=wires, control_values=control_values)
-
     def test_decomposition_not_enough_wires(self):
         """Test that the decomposition raises an error if the number of wires is lower than two"""
         with pytest.raises(ValueError, match="Wrong number of wires"):