Repository: PennyLaneAI/pennylane
Pull request #6026 (2024-07-22 18:15:00)

Deprecate max expansion from `qml.device` and `qml.QNode`
**Context:**

`max_expansion` only serves to prevent decompositions from reach the gateset they need to reach. Therefore, we are deprecating it in 0.38 and removing it in 0.39.

**Description of the Change:**

The max_expansion is deprecated from `qml.QNode` and the analogous `decomp_depth` is deprecated from `qml.device`

**Benefits:**

Specifying this only prevents the decomposition for reaching the target gateset, leading to things falling over uncontrollably later. Deprecating it (and later removing it) will avoid this.


**Possible Drawbacks:**

Users currently utilizing this argument must update their code after 0.39.

[sc-64693]

github-actions[bot]: Hello. You may have forgotten to update the changelog!
Please edit [`doc/releases/changelog-dev.md`](/PennyLaneAI/pennylane/blob/master/doc/releases/changelog-dev.md) with:
* A one-to-two sentence description of the change. You may include a small working example for new features.
* A link back to this PR.
* Your name (or GitHub username) in the contributors section.
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/6026?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.65%. Comparing base [(`ee4e7de`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/ee4e7de113497cc25279babb556c151463cd0155?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) to head [(`0641603`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/064160306a28a1c3eff7b4c4d6db8c914eaab21e?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).
> Report is 1 commits behind head on master.

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


```diff
@@            Coverage Diff             @@
##           master    #6026      +/-   ##
==========================================
- Coverage   99.66%   99.65%   -0.01%     
==========================================
  Files         427      427              
  Lines       41060    40772     -288     
==========================================
- Hits        40922    40632     -290     
- Misses        138      140       +2     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/6026?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).


Patch:
diff --git a/doc/development/deprecations.rst b/doc/development/deprecations.rst
index b45525a08..a1ef7ce72 100644
--- a/doc/development/deprecations.rst
+++ b/doc/development/deprecations.rst
@@ -9,6 +9,16 @@ deprecations are listed below.
 Pending deprecations
 --------------------
 
+* The ``decomp_depth`` argument in ``qml.device`` is deprecated. 
+
+  - Deprecated in v0.38
+  - Will be removed in v0.39
+
+* The ``max_expansion`` argument in ``qml.QNode`` is deprecated. 
+
+  - Deprecated in v0.38
+  - Will be removed in v0.39
+
 * The functions ``qml.transforms.sum_expand`` and ``qml.transforms.hamiltonian_expand`` are deprecated.
   Instead, ``qml.transforms.split_non_commuting`` can be used for equivalent behaviour.
 
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index b4e808eb0..94cd3ed86 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -87,6 +87,12 @@
 
 <h3>Deprecations 👋</h3>
 
+* The `decomp_depth` argument in `qml.device` has been deprecated.
+  [(#6026)](https://github.com/PennyLaneAI/pennylane/pull/6026)
+
+* The `max_expansion` argument in `qml.QNode` has been deprecated.
+  [(#6026)](https://github.com/PennyLaneAI/pennylane/pull/6026)
+
 * The `expansion_strategy` attribute in the `QNode` class is deprecated.
   [(#5989)](https://github.com/PennyLaneAI/pennylane/pull/5989)
 
diff --git a/pennylane/__init__.py b/pennylane/__init__.py
index 04b7e4f24..58cf18f38 100644
--- a/pennylane/__init__.py
+++ b/pennylane/__init__.py
@@ -18,6 +18,7 @@ PennyLane can be directly imported.
 from importlib import reload, metadata
 from sys import version_info
 
+import warnings
 import numpy as _np
 
 from semantic_version import SimpleSpec, Version
@@ -262,6 +263,10 @@ def device(name, *args, **kwargs):
         decomp_depth (int): For when custom decompositions are specified,
             the maximum expansion depth used by the expansion function.
 
+    .. warning::
+
+        The ``decomp_depth`` argument is deprecated and will be removed in version 0.39.
+
     All devices must be loaded by specifying their **short-name** as listed above,
     followed by the **wires** (subsystems) you wish to initialize. The ``wires``
     argument can be an integer, in which case the wires of the device are addressed
@@ -393,7 +398,15 @@ def device(name, *args, **kwargs):
         # Pop the custom decomposition keyword argument; we will use it here
         # only and not pass it to the device.
         custom_decomps = kwargs.pop("custom_decomps", None)
-        decomp_depth = kwargs.pop("decomp_depth", 10)
+        decomp_depth = kwargs.pop("decomp_depth", None)
+
+        if decomp_depth is not None:
+            warnings.warn(
+                "The decomp_depth argument is deprecated and will be removed in version 0.39. ",
+                PennyLaneDeprecationWarning,
+            )
+        else:
+            decomp_depth = 10
 
         kwargs.pop("config", None)
         options.update(kwargs)
@@ -414,6 +427,7 @@ def device(name, *args, **kwargs):
 
         # Once the device is constructed, we set its custom expansion function if
         # any custom decompositions were specified.
+
         if custom_decomps is not None:
             if isinstance(dev, pennylane.devices.LegacyDevice):
                 custom_decomp_expand_fn = pennylane.transforms.create_decomp_expand_fn(
diff --git a/pennylane/capture/capture_qnode.py b/pennylane/capture/capture_qnode.py
index d0faf65b6..0b7ecb676 100644
--- a/pennylane/capture/capture_qnode.py
+++ b/pennylane/capture/capture_qnode.py
@@ -15,6 +15,7 @@
 This submodule defines a capture compatible call to QNodes.
 """
 
+import warnings
 from copy import copy
 from dataclasses import asdict
 from functools import lru_cache, partial
@@ -29,7 +30,7 @@ except ImportError:
 
 
 def _get_shapes_for(*measurements, shots=None, num_device_wires=0):
-    if jax.config.jax_enable_x64:
+    if jax.config.jax_enable_x64:  # pylint: disable=no-member
         dtype_map = {
             float: jax.numpy.float64,
             int: jax.numpy.int64,
@@ -65,7 +66,13 @@ def _get_qnode_prim():
         def qfunc(*inner_args):
             return jax.core.eval_jaxpr(qfunc_jaxpr.jaxpr, qfunc_jaxpr.consts, *inner_args)
 
-        qnode = qml.QNode(qfunc, device, **qnode_kwargs)
+        with warnings.catch_warnings():
+            warnings.filterwarnings(
+                action="ignore",
+                message=r"The max_expansion argument is deprecated and will be removed in version 0.39.",
+                category=qml.PennyLaneDeprecationWarning,
+            )
+            qnode = qml.QNode(qfunc, device, **qnode_kwargs)
         return qnode._impl_call(*args, shots=shots)  # pylint: disable=protected-access
 
     # pylint: disable=unused-argument
diff --git a/pennylane/workflow/qnode.py b/pennylane/workflow/qnode.py
index 7dab18e3c..09c667bdd 100644
--- a/pennylane/workflow/qnode.py
+++ b/pennylane/workflow/qnode.py
@@ -239,7 +239,12 @@ class QNode:
             on supported options for your chosen gradient transform.
 
     .. warning::
-        The ``expansion_strategy`` attribute is deprecated and will be removed in version 0.39.
+
+        The ``expansion_strategy`` argument is deprecated and will be removed in version 0.39.
+
+    .. warning::
+
+        The ``max_expansion`` argument is deprecated and will be removed in version 0.39.
 
     **Example**
 
@@ -452,7 +457,7 @@ class QNode:
         interface="auto",
         diff_method="best",
         expansion_strategy=None,
-        max_expansion=10,
+        max_expansion=None,
         grad_on_execution="best",
         cache="auto",
         cachesize=10000,
@@ -462,6 +467,15 @@ class QNode:
         mcm_method=None,
         **gradient_kwargs,
     ):
+        # Moving it here since the old default value is checked on debugging
+        if max_expansion is not None:
+            warnings.warn(
+                "The max_expansion argument is deprecated and will be removed in version 0.39. ",
+                qml.PennyLaneDeprecationWarning,
+            )
+        else:
+            max_expansion = 10
+
         if logger.isEnabledFor(logging.DEBUG):
             logger.debug(
                 """Creating QNode(func=%s, device=%s, interface=%s, diff_method=%s, expansion_strategy=%s, max_expansion=%s, grad_on_execution=%s, cache=%s, cachesize=%s, max_diff=%s, gradient_kwargs=%s""",


Test patch:
diff --git a/tests/test_device.py b/tests/test_device.py
index 1639f76ed..2c15a7992 100644
--- a/tests/test_device.py
+++ b/tests/test_device.py
@@ -1069,6 +1069,15 @@ class TestDeviceInit:
 
         assert dev.shots == 22
 
+    def test_decomp_depth_is_deprecated(self):
+        """Test that a warning is raised when using the deprecated decomp_depth argument"""
+
+        with pytest.warns(
+            qml.PennyLaneDeprecationWarning,
+            match="The decomp_depth argument is deprecated",
+        ):
+            qml.device("default.qubit", decomp_depth=1)
+
 
 class TestBatchExecution:
     """Tests for the batch_execute method."""
diff --git a/tests/test_qnode.py b/tests/test_qnode.py
index 71e99e705..3251a7250 100644
--- a/tests/test_qnode.py
+++ b/tests/test_qnode.py
@@ -101,6 +101,15 @@ class TestInitialization:
 
         assert f.execute_kwargs["cache"] is True
 
+    def test_max_expansion_is_deprecated(self):
+        """Test that a warning is raised when using the deprecated max_expansion argument"""
+        dev = qml.device("default.qubit", wires=1)
+        with pytest.warns(
+            qml.PennyLaneDeprecationWarning,
+            match="The max_expansion argument is deprecated",
+        ):
+            QNode(dummyfunc, dev, max_expansion=10)
+
 
 # pylint: disable=too-many-public-methods
 class TestValidation:
diff --git a/tests/transforms/test_tape_expand.py b/tests/transforms/test_tape_expand.py
index 56fee6948..5d7b59a67 100644
--- a/tests/transforms/test_tape_expand.py
+++ b/tests/transforms/test_tape_expand.py
@@ -17,6 +17,8 @@ Unit tests for tape expansion stopping criteria and expansion functions.
 # pylint: disable=too-few-public-methods, invalid-unary-operand-type, no-member,
 # pylint: disable=arguments-differ, arguments-renamed,
 
+import warnings
+
 import numpy as np
 import pytest
 
@@ -24,15 +26,6 @@ import pennylane as qml
 from pennylane.wires import Wires
 
 
-@pytest.fixture(scope="function", autouse=True)
-def capture_warnings(recwarn):
-    yield
-    if len(recwarn) > 0:
-        for w in recwarn:
-            assert isinstance(w.message, qml.PennyLaneDeprecationWarning)
-            assert "'expansion_strategy' attribute is deprecated" in str(w.message)
-
-
 class TestCreateExpandFn:
     """Test creating expansion functions from stopping criteria."""
 
@@ -438,6 +431,21 @@ def custom_basic_entangler_layers(weights, wires, **kwargs):
 class TestCreateCustomDecompExpandFn:
     """Tests for the custom_decomps argument for devices"""
 
+    @pytest.fixture(scope="function", autouse=True)
+    def capture_warnings(self):
+        with pytest.warns(
+            qml.PennyLaneDeprecationWarning,
+        ) as record:
+            yield
+
+            assert any(
+                "'expansion_strategy' attribute is deprecated" in str(w.message) for w in record
+            )
+
+        for w in record:
+            if "'expansion_strategy' attribute is deprecated" not in str(w.message):
+                warnings.warn(w.message, w.category)
+
     @pytest.mark.parametrize("device_name", ["default.qubit", "default.qubit.legacy"])
     def test_string_and_operator_allowed(self, device_name):
         """Test that the custom_decomps dictionary accepts both strings and operator classes as keys."""
@@ -544,7 +552,12 @@ class TestCreateCustomDecompExpandFn:
         """Test that specifying a single custom decomposition works as expected."""
 
         custom_decomps = {"Hadamard": custom_hadamard, "CNOT": custom_cnot}
-        decomp_dev = qml.device(device_name, wires=2, custom_decomps=custom_decomps, decomp_depth=0)
+        with pytest.warns(
+            qml.PennyLaneDeprecationWarning, match="The decomp_depth argument is deprecated"
+        ):
+            decomp_dev = qml.device(
+                device_name, wires=2, custom_decomps=custom_decomps, decomp_depth=0
+            )
 
         @qml.qnode(decomp_dev, expansion_strategy="device")
         def circuit():
@@ -721,13 +734,16 @@ class TestCreateCustomDecompExpandFn:
         # not be further decomposed even though the custom decomposition is specified.
         custom_decomps = {"BasicEntanglerLayers": custom_basic_entangler_layers, "RX": custom_rx}
 
-        decomp_dev_2 = qml.device(
-            device_name, wires=2, custom_decomps=custom_decomps, decomp_depth=2
-        )
+        with pytest.warns(
+            qml.PennyLaneDeprecationWarning, match="The decomp_depth argument is deprecated"
+        ):
+            decomp_dev_2 = qml.device(
+                device_name, wires=2, custom_decomps=custom_decomps, decomp_depth=2
+            )
 
-        decomp_dev_3 = qml.device(
-            device_name, wires=2, custom_decomps=custom_decomps, decomp_depth=3
-        )
+            decomp_dev_3 = qml.device(
+                device_name, wires=2, custom_decomps=custom_decomps, decomp_depth=3
+            )
 
         def circuit():
             qml.BasicEntanglerLayers([[0.1, 0.2]], wires=[0, 1])
@@ -910,7 +926,7 @@ class TestCreateCustomDecompExpandFn:
             custom_decomps = {"MultiRZ": qml.MultiRZ.compute_decomposition}
             dev = qml.device("lightning.qubit", wires=2, custom_decomps=custom_decomps)
 
-            @qml.qnode(dev, diff_method="adjoint")
+            @qml.qnode(dev, diff_method="adjoint", expansion_strategy="device")
             def cost(theta):
                 qml.Hadamard(wires=0)
                 qml.Hadamard(wires=1)