Repository: PennyLaneAI/pennylane
Pull request #5989 (2024-07-11 19:17:06)

Deprecate `expansion_strategy`
- `expansion_strategy` attribute in `QNode` class deprecated and default value changed to `None` to avoid raising the deprecation warning by default. Value is reverted back to the old default value "gradient" once the check is carried out.
- `expansion_strategy` argument has been deprecated in all of `qml.draw`, `qml.draw_mpl` and `qml.specs`.
- All instances of using `expansion_strategy` in `qml.draw` in the doc examples has been updated to use `level` instead. 

[[sc-67221](https://app.shortcut.com/xanaduai/story/67221)] [[sc-67223](https://app.shortcut.com/xanaduai/story/67223)]

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/5989?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.67%. Comparing base [(`dbb26f2`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/dbb26f2c7ca54798d70756b685ce566da776bfad?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) to head [(`d2f037f`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/d2f037f504c9655c862241e9c5cd0ba84fcde90b?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).
> Report is 3 commits behind head on master.

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


```diff
@@            Coverage Diff             @@
##           master    #5989      +/-   ##
==========================================
- Coverage   99.67%   99.67%   -0.01%     
==========================================
  Files         425      426       +1     
  Lines       40926    40665     -261     
==========================================
- Hits        40793    40531     -262     
- Misses        133      134       +1     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5989?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 b710d8852..b45525a08 100644
--- a/doc/development/deprecations.rst
+++ b/doc/development/deprecations.rst
@@ -15,6 +15,18 @@ Pending deprecations
   - Deprecated in v0.38
   - Will be removed in v0.39
 
+* The ``expansion_strategy`` attribute of ``qml.QNode`` is deprecated. 
+  Users should make use of ``qml.workflow.construct_batch``, should they require fine control over the output tape(s).
+
+  - Deprecated in v0.38
+  - Will be removed in v0.39
+
+* The ``expansion_strategy`` argument in ``qml.specs``, ``qml.draw``, and ``qml.draw_mpl`` is deprecated. 
+  Instead, use the ``level`` argument which provides a superset of options.
+
+  - Deprecated in v0.38
+  - Will be removed in v0.39
+
 * The ``expand_fn`` argument in ``qml.execute`` is deprecated. 
   Instead, please create a ``qml.transforms.core.TransformProgram`` with the desired preprocessing and pass it to the ``transform_program`` argument of ``qml.execute``.
 
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index d906618d7..6cd04adf4 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -78,6 +78,13 @@
 
 <h3>Deprecations 👋</h3>
 
+* The `expansion_strategy` attribute in the `QNode` class is deprecated.
+  [(#5989)](https://github.com/PennyLaneAI/pennylane/pull/5989)
+
+* The `expansion_strategy` argument has been deprecated in all of `qml.draw`, `qml.draw_mpl`, and `qml.specs`.
+  The `level` argument should be used instead.
+  [(#5989)](https://github.com/PennyLaneAI/pennylane/pull/5989)
+
 * `Operator.expand` has been deprecated. Users should simply use `qml.tape.QuantumScript(op.decomposition())`
   for equivalent behaviour.
   [(#5994)](https://github.com/PennyLaneAI/pennylane/pull/5994)
diff --git a/pennylane/drawer/draw.py b/pennylane/drawer/draw.py
index 50e692bc6..83874185c 100644
--- a/pennylane/drawer/draw.py
+++ b/pennylane/drawer/draw.py
@@ -20,25 +20,30 @@ import warnings
 from functools import wraps
 
 import pennylane as qml
+from pennylane.data.base.typing_util import UNSET
 
 from .tape_mpl import tape_mpl
 from .tape_text import tape_text
 
-_level_sentinel = object()
-
 
 def _determine_draw_level(kwargs, qnode=None):
-    sentinel = _level_sentinel
-
-    level = kwargs.get("level", sentinel)
-    expansion_strategy = kwargs.get("expansion_strategy", sentinel)
+    level = kwargs.get("level", UNSET)
+    expansion_strategy = kwargs.get("expansion_strategy", UNSET)
 
-    if all(val != sentinel for val in (level, expansion_strategy)):
+    if all(val != UNSET for val in (level, expansion_strategy)):
         raise ValueError("Either 'level' or 'expansion_strategy' need to be set, but not both.")
 
-    if level == sentinel:
-        if expansion_strategy == sentinel:
-            return qnode.expansion_strategy if qnode else sentinel
+    if expansion_strategy != UNSET:
+        warnings.warn(
+            "The 'expansion_strategy' argument is deprecated and will be removed in "
+            "version 0.39. Instead, use the 'level' argument which offers more flexibility "
+            "and options.",
+            qml.PennyLaneDeprecationWarning,
+        )
+
+    if level == UNSET:
+        if expansion_strategy == UNSET:
+            return qnode.expansion_strategy if qnode else UNSET
         return expansion_strategy
     return level
 
@@ -95,6 +100,9 @@ def draw(
         as it allows for the same values as ``expansion_strategy`` and offers more flexibility in choosing
         the desired transforms/expansions.
 
+    .. warning::
+        The ``expansion_strategy`` argument is deprecated and will be removed in version 0.39. Use the ``level``
+        argument instead to specify the resulting tape you want.
 
     **Example**
 
@@ -173,7 +181,7 @@ def draw(
                 qml.StronglyEntanglingLayers(params, wires=range(3))
                 return [qml.expval(qml.Z(i)) for i in range(3)]
 
-        >>> print(qml.draw(longer_circuit, max_length=60, expansion_strategy="device")(params))
+        >>> print(qml.draw(longer_circuit, max_length=60, level="device")(params))
         0: ──Rot(0.77,0.44,0.86)─╭●────╭X──Rot(0.45,0.37,0.93)─╭●─╭X
         1: ──Rot(0.70,0.09,0.98)─╰X─╭●─│───Rot(0.64,0.82,0.44)─│──╰●
         2: ──Rot(0.76,0.79,0.13)────╰X─╰●──Rot(0.23,0.55,0.06)─╰X───
@@ -293,7 +301,7 @@ def draw(
             level=_determine_draw_level(kwargs, qnode),
         )
 
-    if _determine_draw_level(kwargs) != _level_sentinel:
+    if _determine_draw_level(kwargs) != UNSET:
         warnings.warn(
             "When the input to qml.draw is not a QNode, the expansion_strategy and level arguments are ignored.",
             UserWarning,
@@ -443,6 +451,10 @@ def draw_mpl(
         as it allows for the same values as ``expansion_strategy`` and offers more flexibility in choosing
         the desired transforms/expansions.
 
+    .. warning::
+        The ``expansion_strategy`` argument is deprecated and will be removed in version 0.39. Use the ``level``
+        argument instead to specify the resulting tape you want.
+
     .. warning::
 
         Unlike :func:`~.draw`, this function can not draw the full result of a tape-splitting transform. In such cases,
@@ -701,7 +713,7 @@ def draw_mpl(
             **kwargs,
         )
 
-    if _determine_draw_level(kwargs) != _level_sentinel:
+    if _determine_draw_level(kwargs) != UNSET:
         warnings.warn(
             "When the input to qml.draw is not a QNode, the expansion_strategy and level arguments are ignored.",
             UserWarning,
diff --git a/pennylane/ops/op_math/adjoint.py b/pennylane/ops/op_math/adjoint.py
index 0ccd25113..d7f4d220e 100644
--- a/pennylane/ops/op_math/adjoint.py
+++ b/pennylane/ops/op_math/adjoint.py
@@ -85,7 +85,7 @@ def adjoint(fn, lazy=True):
     ...     return qml.expval(qml.Z(0))
     >>> print(qml.draw(circuit2)("y"))
     0: ──RY(y)†─┤  <Z>
-    >>> print(qml.draw(circuit2, expansion_strategy="device")(0.1))
+    >>> print(qml.draw(circuit2, level="device")(0.1))
     0: ──RY(-0.10)─┤  <Z>
 
     The adjoint transforms can also be used to apply the adjoint of
diff --git a/pennylane/qaoa/layers.py b/pennylane/qaoa/layers.py
index 0c1f7ab9c..dde6a39a9 100644
--- a/pennylane/qaoa/layers.py
+++ b/pennylane/qaoa/layers.py
@@ -91,7 +91,7 @@ def cost_layer(gamma, hamiltonian):
         >>> print(qml.draw(circuit)(0.5))
         0: ──H─╭ApproxTimeEvolution(1.00,1.00,0.50)─┤  <Z>
         1: ──H─╰ApproxTimeEvolution(1.00,1.00,0.50)─┤  <Z>
-        >>> print(qml.draw(circuit, expansion_strategy="device")(0.5))
+        >>> print(qml.draw(circuit, level="device")(0.5))
         0: ──H──RZ(1.00)─╭RZZ(1.00)─┤  <Z>
         1: ──H───────────╰RZZ(1.00)─┤  <Z>
 
@@ -149,7 +149,7 @@ def mixer_layer(alpha, hamiltonian):
         >>> print(qml.draw(circuit)(0.5))
         0: ──H─╭ApproxTimeEvolution(1.00,1.00,0.50)─┤  <Z>
         1: ──H─╰ApproxTimeEvolution(1.00,1.00,0.50)─┤  <Z>
-        >>> print(qml.draw(circuit, expansion_strategy="device")(0.5))
+        >>> print(qml.draw(circuit, level="device")(0.5))
         0: ──H──RX(1.00)─╭RXX(1.00)─┤  <Z>
         1: ──H───────────╰RXX(1.00)─┤  <Z>
 
diff --git a/pennylane/resource/specs.py b/pennylane/resource/specs.py
index a4d7aabde..95cc3221a 100644
--- a/pennylane/resource/specs.py
+++ b/pennylane/resource/specs.py
@@ -37,6 +37,14 @@ def _determine_spec_level(kwargs, qnode):
     if all(val != sentinel for val in (level, expansion_strategy)):
         raise ValueError("Either 'level' or 'expansion_strategy' need to be set, but not both.")
 
+    if expansion_strategy != sentinel:
+        warnings.warn(
+            "The 'expansion_strategy' argument is deprecated and will be removed in "
+            "version 0.39. Instead, use the 'level' argument which offers more flexibility "
+            "and options.",
+            qml.PennyLaneDeprecationWarning,
+        )
+
     if level == sentinel:
         if expansion_strategy == sentinel:
             return qnode.expansion_strategy
@@ -87,6 +95,9 @@ def specs(qnode, **kwargs):
         ``max_expansion`` and ``qnode.max_expansion`` have no effect on the return of this function and will
         be ignored.
 
+    .. warning::
+        The ``expansion_strategy`` argument is deprecated and will be removed in version 0.39. Use the ``level``
+        argument instead to specify the resulting tape you want.
 
     **Example**
 
diff --git a/pennylane/templates/embeddings/angle.py b/pennylane/templates/embeddings/angle.py
index d01ef8f31..6cb7eafd6 100644
--- a/pennylane/templates/embeddings/angle.py
+++ b/pennylane/templates/embeddings/angle.py
@@ -65,7 +65,7 @@ class AngleEmbedding(Operation):
         Here, we have also used rotation angles :class:`RZ`. If not specified, :class:`RX` is used as default.
         The resulting circuit is:
 
-        >>> print(qml.draw(circuit, expansion_strategy="device")(X))
+        >>> print(qml.draw(circuit, level="device")(X))
         0: ──RZ(1.00)──H─┤ ╭Probs
         1: ──RZ(2.00)────┤ ├Probs
         2: ──RZ(3.00)────┤ ╰Probs
diff --git a/pennylane/templates/embeddings/basis.py b/pennylane/templates/embeddings/basis.py
index a812c644c..18175a8fd 100644
--- a/pennylane/templates/embeddings/basis.py
+++ b/pennylane/templates/embeddings/basis.py
@@ -55,7 +55,7 @@ class BasisEmbedding(Operation):
 
         The resulting circuit is:
 
-        >>> print(qml.draw(circuit, expansion_strategy="device")(X))
+        >>> print(qml.draw(circuit, level="device")(X))
         0: ──X─┤  State
         1: ──X─┤  State
         2: ──X─┤  State
diff --git a/pennylane/templates/layers/random.py b/pennylane/templates/layers/random.py
index e00bb0886..c36e649ae 100644
--- a/pennylane/templates/layers/random.py
+++ b/pennylane/templates/layers/random.py
@@ -91,11 +91,11 @@ class RandomLayers(Operation):
 
         You can verify this by drawing the circuits.
 
-        >>> print(qml.draw(circuit1, expansion_strategy="device")(weights))
+        >>> print(qml.draw(circuit1, level="device")(weights))
         0: ──────────────────────╭X─╭X──RZ(1.40)─┤  <Z>
         1: ──RX(0.10)──RX(-2.10)─╰●─╰●───────────┤
 
-        >>> print(qml.draw(circuit2, expansion_strategy="device")(weights))
+        >>> print(qml.draw(circuit2, level="device")(weights))
         0: ──────────────────────╭X─╭X──RZ(1.40)─┤  <Z>
         1: ──RX(0.10)──RX(-2.10)─╰●─╰●───────────┤
 
@@ -111,10 +111,10 @@ class RandomLayers(Operation):
         ...     return qml.expval(qml.Z(0))
         >>> np.allclose(circuit(weights, seed=9), circuit(weights, seed=12))
         False
-        >>>  print(qml.draw(circuit, expansion_strategy="device")(weights, seed=9))
+        >>>  print(qml.draw(circuit, level="device")(weights, seed=9))
         0: ─╭X──RX(0.10)────────────┤  <Z>
         1: ─╰●──RY(-2.10)──RX(1.40)─┤
-        >>> print(qml.draw(circuit, expansion_strategy="device")(weights, seed=12))
+        >>> print(qml.draw(circuit, level="device")(weights, seed=12))
         0: ─╭X──RZ(0.10)──╭●─╭X───────────┤  <Z>
         1: ─╰●──RX(-2.10)─╰X─╰●──RZ(1.40)─┤
 
diff --git a/pennylane/templates/layers/strongly_entangling.py b/pennylane/templates/layers/strongly_entangling.py
index 554f100e8..7ffa6ae8a 100644
--- a/pennylane/templates/layers/strongly_entangling.py
+++ b/pennylane/templates/layers/strongly_entangling.py
@@ -72,7 +72,7 @@ class StronglyEntanglingLayers(Operation):
 
         The resulting circuit is:
 
-        >>> print(qml.draw(circuit, expansion_strategy="device")(weights))
+        >>> print(qml.draw(circuit, level="device")(weights))
         0: ──Rot(0.68,0.98,0.48)─╭●───────╭X──Rot(0.94,0.22,0.70)─╭●────╭X────┤  <Z>
         1: ──Rot(0.91,0.19,0.15)─╰X─╭●────│───Rot(0.50,0.20,0.63)─│──╭●─│──╭X─┤
         2: ──Rot(0.91,0.68,0.96)────╰X─╭●─│───Rot(0.14,0.05,0.16)─╰X─│──╰●─│──┤
@@ -108,7 +108,7 @@ class StronglyEntanglingLayers(Operation):
 
         The resulting circuit is:
 
-        >>> print(qml.draw(circuit, expansion_strategy="device")(weights))
+        >>> print(qml.draw(circuit, level="device")(weights))
         0: ──Rot(0.99,0.17,0.12)─╭●────╭Z──Rot(0.02,0.94,0.57)──────────────────────╭●─╭Z───────┤  <Z>
         1: ──Rot(0.55,0.42,0.61)─│──╭●─│──╭Z────────────────────Rot(0.15,0.26,0.82)─│──╰●─╭Z────┤
         2: ──Rot(0.79,0.93,0.27)─╰Z─│──╰●─│─────────────────────Rot(0.73,0.01,0.44)─│─────╰●─╭Z─┤
diff --git a/pennylane/templates/state_preparations/mottonen.py b/pennylane/templates/state_preparations/mottonen.py
index aaaece400..b86092c93 100644
--- a/pennylane/templates/state_preparations/mottonen.py
+++ b/pennylane/templates/state_preparations/mottonen.py
@@ -267,7 +267,7 @@ class MottonenStatePreparation(Operation):
             state = np.array([1, 2j, 3, 4j, 5, 6j, 7, 8j])
             state = state / np.linalg.norm(state)
 
-            print(qml.draw(circuit, expansion_strategy="device", max_length=80)(state))
+            print(qml.draw(circuit, level="device", max_length=80)(state))
 
         .. code-block::
 
diff --git a/pennylane/templates/subroutines/aqft.py b/pennylane/templates/subroutines/aqft.py
index 190b0b30b..04144ca15 100644
--- a/pennylane/templates/subroutines/aqft.py
+++ b/pennylane/templates/subroutines/aqft.py
@@ -84,7 +84,7 @@ class AQFT(Operation):
 
             The resulting circuit is:
 
-            >>> print(qml.draw(circ, expansion_strategy='device')())
+            >>> print(qml.draw(circ, level='device')())
             UserWarning: order=0, applying Hadamard transform warnings.warn("order=0, applying Hadamard transform")
             0: ──H─╭SWAP─────────────┤ ╭Probs
             1: ──H─│─────╭SWAP───────┤ ├Probs
@@ -105,7 +105,7 @@ class AQFT(Operation):
 
             The resulting circuit is:
 
-            >>> print(qml.draw(circ, expansion_strategy='device')())
+            >>> print(qml.draw(circ, level='device')())
             0: ──H─╭Rϕ(1.57)─╭Rϕ(0.79)────────────────────────────────────────╭SWAP───────┤ ╭Probs
             1: ────╰●────────│──────────H─╭Rϕ(1.57)─╭Rϕ(0.79)─────────────────│─────╭SWAP─┤ ├Probs
             2: ──────────────╰●───────────╰●────────│──────────H─╭Rϕ(1.57)────│─────╰SWAP─┤ ├Probs
diff --git a/pennylane/templates/subroutines/interferometer.py b/pennylane/templates/subroutines/interferometer.py
index d009b7ccb..1b58774ec 100644
--- a/pennylane/templates/subroutines/interferometer.py
+++ b/pennylane/templates/subroutines/interferometer.py
@@ -124,7 +124,7 @@ class Interferometer(CVOperation):
 
         Using these random parameters, the resulting circuit is:
 
-        >>> print(qml.draw(circuit, expansion_strategy="device")(params))
+        >>> print(qml.draw(circuit, level="device")(params))
         0: ─╭BS(0.97,0.09)────────────────╭BS(0.89,0.33)──R(0.83)────────────────┤  <I>
         1: ─╰BS(0.97,0.09)─╭BS(0.94,0.05)─╰BS(0.89,0.33)─╭BS(0.92,0.27)──R(0.36)─┤
         2: ─╭BS(0.78,0.20)─╰BS(0.94,0.05)─╭BS(0.60,0.39)─╰BS(0.92,0.27)──R(0.28)─┤
@@ -144,7 +144,7 @@ class Interferometer(CVOperation):
             for shape in shapes:
                 params.append(np.random.random(shape))
 
-            print(qml.draw(circuit, expansion_strategy="device")(params))
+            print(qml.draw(circuit, level="device")(params))
 
         .. code-block::
 
diff --git a/pennylane/templates/subroutines/permute.py b/pennylane/templates/subroutines/permute.py
index 28a3fd43e..7e0327f3d 100644
--- a/pennylane/templates/subroutines/permute.py
+++ b/pennylane/templates/subroutines/permute.py
@@ -67,7 +67,7 @@ class Permute(Operation):
                 qml.Permute([3, 2, 0, 1], dev.wires)
                 return qml.expval(qml.Z(0))
 
-        >>> print(qml.draw(apply_perm, expansion_strategy="device")())
+        >>> print(qml.draw(apply_perm, level="device")())
         0: ─╭SWAP─────────────┤  <Z>
         1: ─│─────╭SWAP───────┤
         2: ─│─────╰SWAP─╭SWAP─┤
@@ -108,7 +108,7 @@ class Permute(Operation):
 
         The permuted circuit is:
 
-        >>> print(qml.draw(circuit, expansion_strategy="device")())
+        >>> print(qml.draw(circuit, level="device")())
         3: ─╭SWAP─────────────┤
         2: ─│─────╭SWAP───────┤
         0: ─│─────│─────╭SWAP─┤
@@ -131,7 +131,7 @@ class Permute(Operation):
 
         will permute only the second, third, and fifth wires as follows:
 
-        >>> print(qml.draw(circuit, expansion_strategy="device", show_all_wires=True)())
+        >>> print(qml.draw(circuit, level="device", show_all_wires=True)())
         3: ─────────────┤
         2: ─╭SWAP───────┤
         a: ─│───────────┤
diff --git a/pennylane/templates/subroutines/select.py b/pennylane/templates/subroutines/select.py
index 357cf0113..4bab8074b 100644
--- a/pennylane/templates/subroutines/select.py
+++ b/pennylane/templates/subroutines/select.py
@@ -56,7 +56,7 @@ class Select(Operation):
     >>>     qml.Select(ops, control=[0,1])
     >>>     return qml.state()
     ...
-    >>> print(qml.draw(circuit, expansion_strategy='device')())
+    >>> print(qml.draw(circuit, level='device')())
     0: ─╭○─╭○─╭●─╭●────┤  State
     1: ─├○─├●─├○─├●────┤  State
     2: ─╰X─│──╰Y─├SWAP─┤  State
diff --git a/pennylane/templates/swapnetworks/ccl2.py b/pennylane/templates/swapnetworks/ccl2.py
index 378ff9e38..9595e577e 100644
--- a/pennylane/templates/swapnetworks/ccl2.py
+++ b/pennylane/templates/swapnetworks/ccl2.py
@@ -54,7 +54,7 @@ class TwoLocalSwapNetwork(Operation):
         ... def swap_network_circuit():
         ...    qml.templates.TwoLocalSwapNetwork(dev.wires, acquaintances, fermionic=True, shift=False)
         ...    return qml.state()
-        >>> qml.draw(swap_network_circuit, expansion_strategy='device')()
+        >>> qml.draw(swap_network_circuit, level='device')()
         0: ─╭●─╭fSWAP(3.14)─────────────────╭●─╭fSWAP(3.14)─────────────────╭●─╭fSWAP(3.14)─┤  State
         1: ─╰X─╰fSWAP(3.14)─╭●─╭fSWAP(3.14)─╰X─╰fSWAP(3.14)─╭●─╭fSWAP(3.14)─╰X─╰fSWAP(3.14)─┤  State
         2: ─╭●─╭fSWAP(3.14)─╰X─╰fSWAP(3.14)─╭●─╭fSWAP(3.14)─╰X─╰fSWAP(3.14)─╭●─╭fSWAP(3.14)─┤  State
@@ -79,7 +79,7 @@ class TwoLocalSwapNetwork(Operation):
             ... def swap_network_circuit():
             ...    qml.templates.TwoLocalSwapNetwork(dev.wires, acquaintances, weights, fermionic=False)
             ...    return qml.state()
-            >>> qml.draw(swap_network_circuit, expansion_strategy='device')()
+            >>> qml.draw(swap_network_circuit, level='device')()
             0: ─╭●────────╭SWAP─────────────────╭●────────╭SWAP─────────────────╭●────────╭SWAP─┤  State
             1: ─╰RY(0.20)─╰SWAP─╭●────────╭SWAP─╰RY(0.09)─╰SWAP─╭●────────╭SWAP─╰RY(0.62)─╰SWAP─┤  State
             2: ─╭●────────╭SWAP─╰RY(0.68)─╰SWAP─╭●────────╭SWAP─╰RY(0.34)─╰SWAP─╭●────────╭SWAP─┤  State
diff --git a/pennylane/templates/tensornetworks/mera.py b/pennylane/templates/tensornetworks/mera.py
index 5a4b6a1ee..7406b383e 100644
--- a/pennylane/templates/tensornetworks/mera.py
+++ b/pennylane/templates/tensornetworks/mera.py
@@ -160,7 +160,7 @@ class MERA(Operation):
 
         It may be necessary to reorder the wires to see the MERA architecture clearly:
 
-        >>> print(qml.draw(circuit, expansion_strategy='device', wire_order=[2,0,1,3])(template_weights))
+        >>> print(qml.draw(circuit, level='device', wire_order=[2,0,1,3])(template_weights))
         2: ───────────────╭●──RY(0.10)──╭X──RY(-0.30)───────────────┤
         0: ─╭X──RY(-0.30)─│─────────────╰●──RY(0.10)──╭●──RY(0.10)──┤
         1: ─╰●──RY(0.10)──│─────────────╭X──RY(-0.30)─╰X──RY(-0.30)─┤  <Z>
diff --git a/pennylane/templates/tensornetworks/mps.py b/pennylane/templates/tensornetworks/mps.py
index 922c5d531..4f7922e68 100644
--- a/pennylane/templates/tensornetworks/mps.py
+++ b/pennylane/templates/tensornetworks/mps.py
@@ -122,7 +122,7 @@ class MPS(Operation):
                 qml.MPS(range(n_wires),n_block_wires,block, n_params_block, template_weights)
                 return qml.expval(qml.Z(n_wires-1))
 
-        >>> print(qml.draw(circuit, expansion_strategy='device')(template_weights))
+        >>> print(qml.draw(circuit, level='device')(template_weights))
         0: ─╭●──RY(0.10)──────────────────────────────┤
         1: ─╰X──RY(-0.30)─╭●──RY(0.10)────────────────┤
         2: ───────────────╰X──RY(-0.30)─╭●──RY(0.10)──┤
@@ -148,7 +148,7 @@ class MPS(Operation):
                 qml.MPS(range(n_wires),n_block_wires, block, n_params_block, offset = 1)
                 return qml.state()
 
-        >>> print(qml.draw(circuit, expansion_strategy='device')())
+        >>> print(qml.draw(circuit, level='device')())
         0: ─╭●─────────────┤  State
         1: ─├●─╭●──────────┤  State
         2: ─├●─├●─╭●───────┤  State
diff --git a/pennylane/templates/tensornetworks/ttn.py b/pennylane/templates/tensornetworks/ttn.py
index 7bd88ce2e..40029f428 100644
--- a/pennylane/templates/tensornetworks/ttn.py
+++ b/pennylane/templates/tensornetworks/ttn.py
@@ -131,7 +131,7 @@ class TTN(Operation):
                 qml.TTN(range(n_wires),n_block_wires,block, n_params_block, template_weights)
                 return qml.expval(qml.Z(n_wires-1))
 
-        >>> print(qml.draw(circuit, expansion_strategy='device')(template_weights))
+        >>> print(qml.draw(circuit, level='device')(template_weights))
         0: ─╭●──RY(0.10)────────────────┤
         1: ─╰X──RY(-0.30)─╭●──RY(0.10)──┤
         2: ─╭●──RY(0.10)──│─────────────┤
diff --git a/pennylane/transforms/qmc.py b/pennylane/transforms/qmc.py
index 0f2dd76b3..3ef6d3384 100644
--- a/pennylane/transforms/qmc.py
+++ b/pennylane/transforms/qmc.py
@@ -336,7 +336,7 @@ def quantum_monte_carlo(
         It is also possible to explore the resources required to perform the quantum Monte Carlo
         algorithm
 
-        >>> qml.specs(qmc, expansion_strategy="device")()
+        >>> qml.specs(qmc, level="device")()
         {'resources': Resources(
             num_wires=12,
             num_gates=31882,
diff --git a/pennylane/workflow/qnode.py b/pennylane/workflow/qnode.py
index 728996315..9a4a920cf 100644
--- a/pennylane/workflow/qnode.py
+++ b/pennylane/workflow/qnode.py
@@ -106,7 +106,7 @@ def _to_qfunc_output_type(
 
 
 class QNode:
-    """Represents a quantum node in the hybrid computational graph.
+    r"""Represents a quantum node in the hybrid computational graph.
 
     A *quantum node* contains a :ref:`quantum function <intro_vcirc_qfunc>` (corresponding to
     a `variational circuit <https://pennylane.ai/qml/glossary/variational_circuit>`)
@@ -238,6 +238,9 @@ class QNode:
             method. Please refer to the :mod:`qml.gradients <.gradients>` module for details
             on supported options for your chosen gradient transform.
 
+    .. warning::
+        The ``expansion_strategy`` attribute is deprecated and will be removed in version 0.39.
+
     **Example**
 
     QNodes can be created by decorating a quantum function:
@@ -448,7 +451,7 @@ class QNode:
         device: Union[Device, "qml.devices.Device"],
         interface="auto",
         diff_method="best",
-        expansion_strategy="gradient",
+        expansion_strategy=None,
         max_expansion=10,
         grad_on_execution="best",
         cache="auto",
@@ -479,6 +482,17 @@ class QNode:
                 gradient_kwargs,
             )
 
+        if expansion_strategy is not None:
+            warnings.warn(
+                "The 'expansion_strategy' attribute is deprecated and will be removed  "
+                "in version 0.39. For full control over the stage to which the tape is "
+                "constructed, use the 'pennylane.workflow.construct_batch' function.",
+                qml.PennyLaneDeprecationWarning,
+            )
+
+        # Default to "gradient" to maintain default behaviour of "draw" and "specs"
+        expansion_strategy = expansion_strategy or "gradient"
+
         if interface not in SUPPORTED_INTERFACES:
             raise qml.QuantumFunctionError(
                 f"Unknown interface {interface}. Interface must be "


Test patch:
diff --git a/tests/drawer/test_draw.py b/tests/drawer/test_draw.py
index 83e153b44..c1ae86acf 100644
--- a/tests/drawer/test_draw.py
+++ b/tests/drawer/test_draw.py
@@ -953,8 +953,8 @@ class TestLevelExpansionStrategy:
         )
         assert out == expected
 
-    def test_draw_with_qfunc_warns_with_expansion_strategy(self):
-        """Test that draw warns the user about expansion_strategy being ignored."""
+    def test_draw_with_qfunc_warns_with_expansion_strategy_or_level(self):
+        """Test that draw warns the user about expansion_strategy and level being ignored."""
 
         def qfunc():
             qml.PauliZ(0)
@@ -962,17 +962,26 @@ class TestLevelExpansionStrategy:
         with pytest.warns(
             UserWarning, match="the expansion_strategy and level arguments are ignored"
         ):
-            _ = qml.draw(qfunc, expansion_strategy="gradient")
+            with pytest.warns(
+                qml.PennyLaneDeprecationWarning, match="'expansion_strategy' argument is deprecated"
+            ):
+                qml.draw(qfunc, expansion_strategy="gradient")
 
         with pytest.warns(
             UserWarning, match="the expansion_strategy and level arguments are ignored"
         ):
-            _ = qml.draw(qfunc, level="gradient")
+            qml.draw(qfunc, level="gradient")
 
     def test_providing_both_level_and_expansion_raises_error(self, transforms_circuit):
         with pytest.raises(ValueError, match="Either 'level' or 'expansion_strategy'"):
             qml.draw(transforms_circuit, level=0, expansion_strategy="device")
 
+    def test_deprecation_warning_when_expansion_strategy_provided(self, transforms_circuit):
+        with pytest.warns(
+            qml.PennyLaneDeprecationWarning, match="'expansion_strategy' argument is deprecated"
+        ):
+            qml.draw(transforms_circuit, expansion_strategy="device")
+
 
 def test_draw_batch_transform():
     """Test that drawing a batch transform works correctly."""
diff --git a/tests/drawer/test_draw_mpl.py b/tests/drawer/test_draw_mpl.py
index 2a8d2c833..91afca5f2 100644
--- a/tests/drawer/test_draw_mpl.py
+++ b/tests/drawer/test_draw_mpl.py
@@ -109,7 +109,7 @@ class TestLevelExpansionStrategy:
         ],
     )
     def test_equivalent_levels(self, transforms_circuit, levels, expected_metadata):
-        """Test that the expansion strategy keyword controls what operations are drawn."""
+        """Test that the level keyword controls what operations are drawn."""
         var1, var2 = levels
         expected_lines, expected_patches, expected_texts = expected_metadata
 
@@ -136,12 +136,20 @@ class TestLevelExpansionStrategy:
     def test_expansion_strategy(self, device, strategy, initial_strategy, n_lines):
         """Test that the expansion strategy keyword controls what operations are drawn."""
 
-        @qml.qnode(device, expansion_strategy=initial_strategy)
-        def circuit():
-            qml.Permute([2, 0, 1], wires=(0, 1, 2))
-            return qml.expval(qml.PauliZ(0))
+        with pytest.warns(
+            qml.PennyLaneDeprecationWarning,
+            match="'expansion_strategy' attribute is deprecated",
+        ):
+
+            @qml.qnode(device, expansion_strategy=initial_strategy)
+            def circuit():
+                qml.Permute([2, 0, 1], wires=(0, 1, 2))
+                return qml.expval(qml.PauliZ(0))
 
-        _, ax = qml.draw_mpl(circuit, expansion_strategy=strategy)()
+        with pytest.warns(
+            qml.PennyLaneDeprecationWarning, match="'expansion_strategy' argument is deprecated"
+        ):
+            _, ax = qml.draw_mpl(circuit, expansion_strategy=strategy)()
 
         assert len(ax.lines) == n_lines
         assert circuit.expansion_strategy == initial_strategy
@@ -172,7 +180,10 @@ class TestLevelExpansionStrategy:
         with pytest.warns(
             UserWarning, match="the expansion_strategy and level arguments are ignored"
         ):
-            qml.draw_mpl(qfunc, expansion_strategy="gradient")
+            with pytest.warns(
+                qml.PennyLaneDeprecationWarning, match="'expansion_strategy' argument is deprecated"
+            ):
+                qml.draw_mpl(qfunc, expansion_strategy="gradient")
 
         with pytest.warns(
             UserWarning, match="the expansion_strategy and level arguments are ignored"
@@ -446,7 +457,13 @@ def test_draw_mpl_with_qfunc_warns_with_expansion_strategy():
         qml.PauliZ(0)
 
     with pytest.warns(UserWarning, match="the expansion_strategy and level arguments are ignored"):
-        _ = qml.draw_mpl(qfunc, expansion_strategy="gradient")
+        with pytest.warns(
+            qml.PennyLaneDeprecationWarning, match="'expansion_strategy' argument is deprecated"
+        ):
+            qml.draw_mpl(qfunc, expansion_strategy="gradient")
+
+    with pytest.warns(UserWarning, match="the expansion_strategy and level arguments are ignored"):
+        qml.draw_mpl(qfunc, level="gradient")
 
 
 def test_qnode_mid_circuit_measurement_not_deferred_device_api(mocker):
diff --git a/tests/gradients/core/test_gradient_transform.py b/tests/gradients/core/test_gradient_transform.py
index 44b2ade9e..0a3ccb92a 100644
--- a/tests/gradients/core/test_gradient_transform.py
+++ b/tests/gradients/core/test_gradient_transform.py
@@ -619,10 +619,14 @@ class TestGradientTransformIntegration:
         correctly when the QNode contains a template"""
         dev = qml.device("default.qubit", wires=3)
 
-        @qml.qnode(dev, expansion_strategy=strategy)
-        def circuit(weights):
-            qml.templates.StronglyEntanglingLayers(weights, wires=[0, 1, 2])
-            return qml.probs(wires=[0, 1])
+        with pytest.warns(
+            qml.PennyLaneDeprecationWarning, match="'expansion_strategy' attribute is deprecated"
+        ):
+
+            @qml.qnode(dev, expansion_strategy=strategy)
+            def circuit(weights):
+                qml.templates.StronglyEntanglingLayers(weights, wires=[0, 1, 2])
+                return qml.probs(wires=[0, 1])
 
         weights = np.ones([2, 3, 3], dtype=np.float64, requires_grad=True)
         res = qml.gradients.param_shift(circuit)(weights)
diff --git a/tests/logging/test_logging_autograd.py b/tests/logging/test_logging_autograd.py
index 5b57bca85..b90c59f50 100644
--- a/tests/logging/test_logging_autograd.py
+++ b/tests/logging/test_logging_autograd.py
@@ -123,7 +123,7 @@ class TestLogging:
                 [
                     "Creating QNode(func=<function TestLogging.test_dq_qnode_execution_grad",
                     "device=<default.qubit device (wires=2)",
-                    f"interface=auto, diff_method={diff_method}, expansion_strategy=gradient, max_expansion=10, grad_on_execution=best,",
+                    f"interface=auto, diff_method={diff_method}, expansion_strategy=None, max_expansion=10, grad_on_execution=best,",
                 ],
             ),
             (
diff --git a/tests/resource/test_specs.py b/tests/resource/test_specs.py
index 5fdae495d..e049cc65d 100644
--- a/tests/resource/test_specs.py
+++ b/tests/resource/test_specs.py
@@ -257,7 +257,10 @@ class TestSpecsTransform:
         circuit, params = self.make_qnode_and_params("device")
 
         assert circuit.max_expansion == 10
-        info = qml.specs(circuit, max_expansion=0)(params)
+
+        with pytest.warns(UserWarning, match="'max_expansion' has no effect"):
+            info = qml.specs(circuit, max_expansion=0)(params)
+
         assert circuit.max_expansion == 10
 
         assert len(info) == 11
@@ -276,10 +279,18 @@ class TestSpecsTransform:
 
     def test_expansion_strategy(self):
         """Test that a user can calculate specs for different expansion strategies."""
-        circuit, params = self.make_qnode_and_params("gradient")
+        with pytest.warns(
+            qml.PennyLaneDeprecationWarning, match="'expansion_strategy' attribute is deprecated"
+        ):
+            circuit, params = self.make_qnode_and_params("gradient")
 
         assert circuit.expansion_strategy == "gradient"
-        info = qml.specs(circuit, expansion_strategy="device")(params)
+
+        with pytest.warns(
+            qml.PennyLaneDeprecationWarning, match="'expansion_strategy' argument is deprecated"
+        ):
+            info = qml.specs(circuit, expansion_strategy="device")(params)
+
         assert circuit.expansion_strategy == "gradient"
 
         assert len(info) == 13
diff --git a/tests/test_qnode.py b/tests/test_qnode.py
index cd51d7ceb..71e99e705 100644
--- a/tests/test_qnode.py
+++ b/tests/test_qnode.py
@@ -1984,10 +1984,15 @@ class TestTapeExpansion:
         dev = qml.device("default.qubit", wires=2)
         x = pnp.array(0.5, requires_grad=True)
 
-        @qnode(dev, diff_method="parameter-shift", expansion_strategy="device")
-        def circuit(x):
-            qml.SingleExcitation(x, wires=[0, 1])
-            return qml.expval(qml.PauliX(0))
+        with pytest.warns(
+            qml.PennyLaneDeprecationWarning,
+            match="'expansion_strategy' attribute is deprecated",
+        ):
+
+            @qnode(dev, diff_method="parameter-shift", expansion_strategy="device")
+            def circuit(x):
+                qml.SingleExcitation(x, wires=[0, 1])
+                return qml.expval(qml.PauliX(0))
 
         assert circuit.expansion_strategy == "device"
         assert circuit.execute_kwargs["expand_fn"] is None
@@ -2019,10 +2024,15 @@ class TestTapeExpansion:
         dev = qml.device("default.qubit", wires=2)
         monkeypatch.setattr(dev, "preprocess", preprocess_with_batchtransform)
 
-        @qnode(dev, diff_method="parameter-shift", expansion_strategy="device")
-        def circuit(x):
-            qml.SingleExcitation(x, wires=[0, 1])
-            return qml.expval(qml.PauliX(0))
+        with pytest.warns(
+            qml.PennyLaneDeprecationWarning,
+            match="'expansion_strategy' attribute is deprecated",
+        ):
+
+            @qnode(dev, diff_method="parameter-shift", expansion_strategy="device")
+            def circuit(x):
+                qml.SingleExcitation(x, wires=[0, 1])
+                return qml.expval(qml.PauliX(0))
 
         with pytest.raises(
             ValueError,
diff --git a/tests/test_qnode_legacy.py b/tests/test_qnode_legacy.py
index c98fc3f05..91fa971a4 100644
--- a/tests/test_qnode_legacy.py
+++ b/tests/test_qnode_legacy.py
@@ -1888,10 +1888,15 @@ class TestTapeExpansion:
         dev = qml.device("default.qubit.legacy", wires=2)
         x = pnp.array(0.5, requires_grad=True)
 
-        @qnode(dev, diff_method="parameter-shift", expansion_strategy="device")
-        def circuit(x):
-            qml.SingleExcitation(x, wires=[0, 1])
-            return qml.expval(qml.PauliX(0))
+        with pytest.warns(
+            qml.PennyLaneDeprecationWarning,
+            match="'expansion_strategy' attribute is deprecated",
+        ):
+
+            @qnode(dev, diff_method="parameter-shift", expansion_strategy="device")
+            def circuit(x):
+                qml.SingleExcitation(x, wires=[0, 1])
+                return qml.expval(qml.PauliX(0))
 
         assert circuit.expansion_strategy == "device"
         assert circuit.execute_kwargs["expand_fn"] is None
diff --git a/tests/transforms/test_tape_expand.py b/tests/transforms/test_tape_expand.py
index 003938898..56fee6948 100644
--- a/tests/transforms/test_tape_expand.py
+++ b/tests/transforms/test_tape_expand.py
@@ -24,6 +24,15 @@ 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."""