Repository: PennyLaneAI/pennylane
Pull request #3797 (2023-02-21 20:12:26)

Undeprecate `ApproxTimeEvolution`
**Description of the Change:**

`qml.evolve` is not at feature parity with `ApproxTimeEvolution` and should not have been replaced (Two demos are failing)

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.
timmysilv: there's a bit more that needs changing. Can we just revert #3755 instead? If only specific files, you can do `git checkout 841932c56a1b2c10ae5798ba6963665893950794 list_of_files` (it's the parent commit from before that PR was merged)
mudit2812: Albert pushed a fix in #3796 that I think we should try before reverting the deprecation. 
rmoyard: Deprecation kept but remove the replacement of ApproxTimeEvolution
codecov[bot]: # [Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3797?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
> Merging [#3797](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3797?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (5840002) into [v0.29.0-rc0](https://codecov.io/gh/PennyLaneAI/pennylane/commit/3f997aed891f1cd2d2bd21f72adee421a6273a39?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (3f997ae) will **not change** coverage.
> The diff coverage is `100.00%`.

```diff
@@             Coverage Diff              @@
##           v0.29.0-rc0    #3797   +/-   ##
============================================
  Coverage        99.72%   99.72%           
============================================
  Files              336      336           
  Lines            29455    29455           
============================================
  Hits             29375    29375           
  Misses              80       80           
```


| [Impacted Files](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3797?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) | Coverage Δ | |
|---|---|---|
| [pennylane/optimize/lie\_algebra.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3797?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL29wdGltaXplL2xpZV9hbGdlYnJhLnB5) | `100.00% <100.00%> (ø)` | |
| [pennylane/qaoa/layers.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3797?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL3Fhb2EvbGF5ZXJzLnB5) | `100.00% <100.00%> (ø)` | |
| [...ylane/templates/subroutines/commuting\_evolution.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3797?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL3RlbXBsYXRlcy9zdWJyb3V0aW5lcy9jb21tdXRpbmdfZXZvbHV0aW9uLnB5) | `100.00% <100.00%> (ø)` | |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)

timmysilv: I'm not sure how I feel about keeping it in some files while also deprecating it. It seems essentially like deprecating some functionality from all of those things (QAOA layers, commuting evolution, etc.) which I don't think we should do
josh146: If we keep the deprecation but retain its use, would users get a deprecation warning from using e.g., `qml.qaoa.mixer_layer`, that they wouldn't be able to act on?

In general, deprecation warnings should be a 'call to action' to users to update their code. But if they _can't_ update their code to remove use of this function, it does not feel like a deprecation.
rmoyard: I totally re added `ApporxTimeEvolution`.

Patch:
diff --git a/doc/development/deprecations.rst b/doc/development/deprecations.rst
index e27ddcc20..1d6dbb83e 100644
--- a/doc/development/deprecations.rst
+++ b/doc/development/deprecations.rst
@@ -77,11 +77,6 @@ Pending deprecations
   - Deprecated in v0.29
   - Removed in v0.31
 
-* ``ApproxTimeEvolution`` has been deprecated.
-
-  - Deprecated in v0.29
-  - Will be removed in v0.31
-
 Completed deprecation cycles
 ----------------------------
 
diff --git a/doc/releases/changelog-0.29.0.md b/doc/releases/changelog-0.29.0.md
index 6a17b1f1c..2353354ae 100644
--- a/doc/releases/changelog-0.29.0.md
+++ b/doc/releases/changelog-0.29.0.md
@@ -762,9 +762,6 @@
   This new function changes the sign of the given parameter.
   [(#3706)](https://github.com/PennyLaneAI/pennylane/pull/3706)
 
-* `ApproxTimeEvolution` has been deprecated. Please use `qml.evolve` instead.
-  [(#3755)](https://github.com/PennyLaneAI/pennylane/pull/3755)
-
 <h3>Documentation</h3>
 
 * Organizes the module for documentation for ``operation``.
diff --git a/pennylane/ops/op_math/evolution.py b/pennylane/ops/op_math/evolution.py
index a78ce2880..b3d1c312b 100644
--- a/pennylane/ops/op_math/evolution.py
+++ b/pennylane/ops/op_math/evolution.py
@@ -93,13 +93,6 @@ class Evolution(Exp):
         """A real coefficient with ``1j`` factored out."""
         return self.data[0]
 
-    def __repr__(self):
-        return (
-            f"Evolution({self.coeff} {self.base})"
-            if self.base.arithmetic_depth > 0
-            else f"Evolution({self.coeff} {self.base.name})"
-        )
-
     @property
     def coeff(self):
         return 1j * self.data[0]
@@ -112,10 +105,6 @@ class Evolution(Exp):
         )
         return base_label or f"Exp({param}j {self.base.label(decimals=decimals, cache=cache)})"
 
-    @property
-    def grad_method(self):
-        return self.base.grad_method if hasattr(self.base, "grad_method") else super().grad_method
-
     def simplify(self):
         new_base = self.base.simplify()
         if isinstance(new_base, qml.ops.op_math.SProd):  # pylint: disable=no-member
diff --git a/pennylane/optimize/lie_algebra.py b/pennylane/optimize/lie_algebra.py
index e9e990040..9329ccbc2 100644
--- a/pennylane/optimize/lie_algebra.py
+++ b/pennylane/optimize/lie_algebra.py
@@ -16,7 +16,6 @@ import warnings
 
 import numpy as np
 from scipy.sparse.linalg import expm
-
 import pennylane as qml
 
 
@@ -64,7 +63,7 @@ def append_time_evolution(tape, riemannian_gradient, t, n, exact=False):
             wires=range(len(riemannian_gradient.wires)),
         )
     else:
-        qml.evolve(riemannian_gradient, coeff=t, num_steps=n)
+        qml.templates.ApproxTimeEvolution(riemannian_gradient, t, n)
 
     for obj in tape.measurements:
         qml.apply(obj)
diff --git a/pennylane/qaoa/layers.py b/pennylane/qaoa/layers.py
index 56193c847..e2625e617 100644
--- a/pennylane/qaoa/layers.py
+++ b/pennylane/qaoa/layers.py
@@ -72,7 +72,7 @@ def cost_layer(gamma, hamiltonian):
 
         .. code-block:: python
 
-            dev = qml.device('default.mixed', wires=2)
+            dev = qml.device('default.qubit', wires=2)
 
             @qml.qnode(dev)
             def circuit(gamma):
@@ -87,11 +87,11 @@ def cost_layer(gamma, hamiltonian):
         which gives us a circuit of the form:
 
         >>> print(qml.draw(circuit)(0.5))
-        0: ──H─╭Exp(-0.50j 𝓗(1.00,1.00))─┤  <Z>
-        1: ──H─╰Exp(-0.50j 𝓗(1.00,1.00))─┤  <Z>
+        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))
-        0: ──H──RZ(1.00+0.00j)─╭●─────────────────╭●─┤  <Z>
-        1: ──H─────────────────╰X──RZ(1.00+0.00j)─╰X─┤  <Z>
+        0: ──H──MultiRZ(1.00)─╭MultiRZ(1.00)─┤  <Z>
+        1: ──H────────────────╰MultiRZ(1.00)─┤  <Z>
 
     """
     if not isinstance(hamiltonian, qml.Hamiltonian):
@@ -102,7 +102,7 @@ def cost_layer(gamma, hamiltonian):
     if not _diagonal_terms(hamiltonian):
         raise ValueError("hamiltonian must be written only in terms of PauliZ and Identity gates")
 
-    qml.evolve(hamiltonian, coeff=gamma, num_steps=1)
+    qml.templates.ApproxTimeEvolution(hamiltonian, gamma, 1)
 
 
 def mixer_layer(alpha, hamiltonian):
@@ -134,7 +134,7 @@ def mixer_layer(alpha, hamiltonian):
 
         .. code-block:: python
 
-            dev = qml.device('default.mixed', wires=2)
+            dev = qml.device('default.qubit', wires=2)
 
             @qml.qnode(dev)
             def circuit(alpha):
@@ -149,11 +149,11 @@ def mixer_layer(alpha, hamiltonian):
         which gives us a circuit of the form:
 
         >>> print(qml.draw(circuit)(0.5))
-        0: ──H─╭Exp(-0.50j 𝓗(1.00,1.00))─┤  <Z>
-        1: ──H─╰Exp(-0.50j 𝓗(1.00,1.00))─┤  <Z>
+        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))
-        0: ──H──RX(1.00+0.00j)─╭●──RX(1.00+0.00j)─╭●─┤  <Z>
-        1: ──H─────────────────╰X─────────────────╰X─┤  <Z>
+        0: ──H──H──MultiRZ(1.00)──H──H─╭MultiRZ(1.00)──H─┤  <Z>
+        1: ──H──H──────────────────────╰MultiRZ(1.00)──H─┤  <Z>
 
 
     """
@@ -162,4 +162,4 @@ def mixer_layer(alpha, hamiltonian):
             f"hamiltonian must be of type pennylane.Hamiltonian, got {type(hamiltonian).__name__}"
         )
 
-    qml.evolve(hamiltonian, coeff=alpha, num_steps=1)
+    qml.templates.ApproxTimeEvolution(hamiltonian, alpha, 1)
diff --git a/pennylane/templates/subroutines/approx_time_evolution.py b/pennylane/templates/subroutines/approx_time_evolution.py
index c300d8cad..c1eca87d7 100644
--- a/pennylane/templates/subroutines/approx_time_evolution.py
+++ b/pennylane/templates/subroutines/approx_time_evolution.py
@@ -14,11 +14,9 @@
 r"""
 Contains the ApproxTimeEvolution template.
 """
-import warnings
-
 # pylint: disable-msg=too-many-branches,too-many-arguments,protected-access
 import pennylane as qml
-from pennylane.operation import AnyWires, Operation
+from pennylane.operation import Operation, AnyWires
 from pennylane.ops import PauliRot
 
 
@@ -104,10 +102,6 @@ class ApproxTimeEvolution(Operation):
     grad_method = None
 
     def __init__(self, hamiltonian, time, n, do_queue=True, id=None):
-        warnings.warn(
-            "The ApproxTimeEvolution class is deprecated. Please use `qml.evolve` instead.",
-            UserWarning,
-        )
         if not isinstance(hamiltonian, qml.Hamiltonian):
             raise ValueError(
                 f"hamiltonian must be of type pennylane.Hamiltonian, got {type(hamiltonian).__name__}"
diff --git a/pennylane/templates/subroutines/commuting_evolution.py b/pennylane/templates/subroutines/commuting_evolution.py
index 24078f445..6d3233b58 100644
--- a/pennylane/templates/subroutines/commuting_evolution.py
+++ b/pennylane/templates/subroutines/commuting_evolution.py
@@ -16,7 +16,7 @@ Contains the CommutingEvolution template.
 """
 # pylint: disable-msg=too-many-arguments,import-outside-toplevel
 import pennylane as qml
-from pennylane.operation import AnyWires, Operation
+from pennylane.operation import Operation, AnyWires
 
 
 class CommutingEvolution(Operation):
@@ -48,7 +48,7 @@ class CommutingEvolution(Operation):
 
     .. warning::
 
-       This template uses the :func:`pennylane.evolve` function with ``num_steps=1`` in order to
+       This template uses the :class:`~.ApproxTimeEvolution` operation with ``n=1`` in order to
        implement the time evolution, as a single-step Trotterization is exact for a commuting
        Hamiltonian.
 
@@ -108,7 +108,9 @@ class CommutingEvolution(Operation):
 
     def __init__(self, hamiltonian, time, frequencies=None, shifts=None, do_queue=True, id=None):
         # pylint: disable=import-outside-toplevel
-        from pennylane.gradients.general_shift_rules import generate_shift_rule
+        from pennylane.gradients.general_shift_rules import (
+            generate_shift_rule,
+        )
 
         if not isinstance(hamiltonian, qml.Hamiltonian):
             type_name = type(hamiltonian).__name__
@@ -154,9 +156,9 @@ class CommutingEvolution(Operation):
         Returns:
             list[.Operator]: decomposition of the operator
         """
-        # uses standard PauliRot decomposition through `qml.evolve`.
+        # uses standard PauliRot decomposition through ApproxTimeEvolution.
         hamiltonian = qml.Hamiltonian(coeffs, hamiltonian.ops)
-        return qml.evolve(hamiltonian, coeff=time, num_steps=1)
+        return qml.ApproxTimeEvolution(hamiltonian, time, 1)
 
     def adjoint(self):
         hamiltonian = qml.Hamiltonian(self.parameters[1:], self.hyperparameters["hamiltonian"].ops)


Test patch:
diff --git a/tests/ops/op_math/test_evolution.py b/tests/ops/op_math/test_evolution.py
index 50a5549ad..f850d6bdd 100644
--- a/tests/ops/op_math/test_evolution.py
+++ b/tests/ops/op_math/test_evolution.py
@@ -70,33 +70,6 @@ class TestEvolution:
         assert op.coeff == 1j * op.data[0]
         assert op.param == op.data[0]
 
-    @pytest.mark.parametrize(
-        "base", [qml.Hamiltonian([1], [qml.PauliX(0)]), qml.pow(qml.PauliX(0))]
-    )
-    def test_grad_method(self, base):
-        """Test that the grad_method of the Evolution operator corresponds to the grad_method of the base operator."""
-        ev = Evolution(base)
-        assert ev.grad_method == base.grad_method
-
-    def test_repr_paulix(self):
-        """Test the __repr__ method when the base is a simple observable."""
-        op = Evolution(qml.PauliX(0), 3)
-        assert repr(op) == "Evolution(3j PauliX)"
-
-    def test_repr_tensor(self):
-        """Test the __repr__ method when the base is a tensor."""
-        t = qml.PauliX(0) @ qml.PauliX(1)
-        isingxx = Evolution(t, 0.25)
-
-        assert repr(isingxx) == "Evolution(0.25j PauliX(wires=[0]) @ PauliX(wires=[1]))"
-
-    def test_repr_deep_operator(self):
-        """Test the __repr__ method when the base is any operator with arithmetic depth > 0."""
-        base = qml.S(0) @ qml.PauliX(0)
-        op = Evolution(base, 3)
-
-        assert repr(op) == "Evolution(3j S(wires=[0]) @ PauliX(wires=[0]))"
-
     @pytest.mark.parametrize(
         "op,decimals,expected",
         [
diff --git a/tests/templates/test_subroutines/test_commuting_evolution.py b/tests/templates/test_subroutines/test_commuting_evolution.py
index 7dbdf3a39..d115ac7da 100644
--- a/tests/templates/test_subroutines/test_commuting_evolution.py
+++ b/tests/templates/test_subroutines/test_commuting_evolution.py
@@ -15,10 +15,10 @@
 Tests for the CommutingEvolution template.
 """
 import pytest
-from scipy.linalg import expm
-
-import pennylane as qml
 from pennylane import numpy as np
+import pennylane as qml
+
+from scipy.linalg import expm
 
 
 def test_adjoint():
@@ -63,13 +63,13 @@ def test_decomposition_expand():
 
     decomp = op.decomposition()
 
-    assert isinstance(decomp, qml.ops.Exp)
-    assert decomp.base.coeffs == hamiltonian.coeffs
-    assert decomp.num_steps == 1
+    assert isinstance(decomp, qml.ApproxTimeEvolution)
+    assert all(decomp.hyperparameters["hamiltonian"].coeffs == hamiltonian.coeffs)
+    assert decomp.hyperparameters["n"] == 1
 
     tape = op.expand()
     assert len(tape) == 1
-    assert isinstance(tape[0], qml.ops.Exp)
+    assert isinstance(tape[0], qml.ApproxTimeEvolution)
 
 
 def test_matrix():
diff --git a/tests/test_qaoa.py b/tests/test_qaoa.py
index 6bd2e40fb..fce83c9fd 100644
--- a/tests/test_qaoa.py
+++ b/tests/test_qaoa.py
@@ -14,30 +14,32 @@
 """
 Unit tests for the :mod:`pennylane.qaoa` submodule.
 """
+import pytest
 import itertools
+import numpy as np
 
 import networkx as nx
-import numpy as np
-import pytest
-import retworkx as rx
 from networkx import Graph
-from scipy.linalg import expm
-from scipy.sparse import csc_matrix, kron
+import retworkx as rx
 
 import pennylane as qml
 from pennylane import qaoa
+
 from pennylane.qaoa.cycle import (
+    edges_to_wires,
+    wires_to_edges,
     _inner_net_flow_constraint_hamiltonian,
-    _inner_out_flow_constraint_hamiltonian,
-    _partial_cycle_mixer,
+    net_flow_constraint,
+    loss_hamiltonian,
     _square_hamiltonian_terms,
     cycle_mixer,
-    edges_to_wires,
-    loss_hamiltonian,
-    net_flow_constraint,
+    _partial_cycle_mixer,
     out_flow_constraint,
-    wires_to_edges,
+    _inner_out_flow_constraint_hamiltonian,
 )
+from scipy.linalg import expm
+from scipy.sparse import csc_matrix, kron
+
 
 #####################################################
 
@@ -1125,17 +1127,17 @@ class TestLayers:
         [
             [
                 qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.PauliX(1)]),
-                [qml.RX(2, wires=[0]), qml.RX(2, wires=[1])],
+                [qml.PauliRot(2, "X", wires=[0]), qml.PauliRot(2, "X", wires=[1])],
             ],
             [
                 qaoa.xy_mixer(Graph([(0, 1), (1, 2), (2, 0)])),
                 [
-                    qml.IsingXX(1, wires=[0, 1]),
-                    qml.IsingYY(1, wires=[0, 1]),
-                    qml.IsingXX(1, wires=[0, 2]),
-                    qml.IsingYY(1, wires=[0, 2]),
-                    qml.IsingXX(1, wires=[1, 2]),
-                    qml.IsingYY(1, wires=[1, 2]),
+                    qml.PauliRot(1, "XX", wires=[0, 1]),
+                    qml.PauliRot(1, "YY", wires=[0, 1]),
+                    qml.PauliRot(1, "XX", wires=[0, 2]),
+                    qml.PauliRot(1, "YY", wires=[0, 2]),
+                    qml.PauliRot(1, "XX", wires=[1, 2]),
+                    qml.PauliRot(1, "YY", wires=[1, 2]),
                 ],
             ],
         ],
@@ -1161,14 +1163,14 @@ class TestLayers:
         [
             [
                 qml.Hamiltonian([1, 1], [qml.PauliZ(0), qml.PauliZ(1)]),
-                [qml.RZ(2, wires=[0]), qml.RZ(2, wires=[1])],
+                [qml.PauliRot(2, "Z", wires=[0]), qml.PauliRot(2, "Z", wires=[1])],
             ],
             [
                 qaoa.maxcut(Graph([(0, 1), (1, 2), (2, 0)]))[0],
                 [
-                    qml.IsingZZ(1, wires=[0, 1]),
-                    qml.IsingZZ(1, wires=[0, 2]),
-                    qml.IsingZZ(1, wires=[1, 2]),
+                    qml.PauliRot(1, "ZZ", wires=[0, 1]),
+                    qml.PauliRot(1, "ZZ", wires=[0, 2]),
+                    qml.PauliRot(1, "ZZ", wires=[1, 2]),
                 ],
             ],
         ],