Skip to content

Commit 8c7e294

Browse files
Deprecate num_steps in qml.evolve (#7954)
**Context:** Currently, `qml.evolve` accepts a `num_steps` keyword argument, which is intended to control the Trotter decomposition of time evolution. However, on devices like default.qubit that support analytic evolution, this argument is silently ignored. This leads to ambiguous behaviour: users may assume that specifying `num_steps` explicitly forces Trotterization, but this is not the case. To resolve this ambiguity, we are remove support for the `num_steps` argument in `qml.evolve` and `Evolution` (making `qml.evolve` analytic-only). Note: this deprecation doesn't have a release scheduled for removal yet. Therefore, I just mention that this argument will be removed in a future version **Description of the Change:** As above. [sc-94755] --------- Co-authored-by: Isaac De Vlugt <[email protected]>
1 parent 490b913 commit 8c7e294

File tree

6 files changed

+187
-16
lines changed

6 files changed

+187
-16
lines changed

doc/development/deprecations.rst

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,42 @@ deprecations are listed below.
99
Pending deprecations
1010
--------------------
1111

12+
* Providing ``num_steps`` to ``qml.evolve`` and ``Evolution`` is deprecated and will be removed in a future version.
13+
Instead, use :class:`~.TrotterProduct` for approximate methods, providing the ``n`` parameter to perform the
14+
Suzuki-Trotter product approximation of a Hamiltonian with the specified number of Trotter steps.
15+
16+
As a concrete example, consider the following case:
17+
18+
```python
19+
coeffs = [0.5, -0.6]
20+
ops = [qml.X(0), qml.X(0) @ qml.Y(1)]
21+
H_flat = qml.dot(coeffs, ops)
22+
```
23+
24+
Instead of computing the Suzuki-Trotter product approximation as:
25+
26+
```pycon
27+
>>> qml.evolve(H_flat, num_steps=2).decomposition()
28+
[RX(0.5, wires=[0]),
29+
PauliRot(-0.6, XY, wires=[0, 1]),
30+
RX(0.5, wires=[0]),
31+
PauliRot(-0.6, XY, wires=[0, 1])]
32+
```
33+
34+
The same result can be obtained using :class:`~.TrotterProduct` as follows:
35+
36+
```pycon
37+
>>> decomp_ops = qml.adjoint(qml.TrotterProduct(H_flat, time=1.0, n=2)).decomposition()
38+
>>> [simp_op for op in decomp_ops for simp_op in map(qml.simplify, op.decomposition())]
39+
[RX(0.5, wires=[0]),
40+
PauliRot(-0.6, XY, wires=[0, 1]),
41+
RX(0.5, wires=[0]),
42+
PauliRot(-0.6, XY, wires=[0, 1])]
43+
```
44+
45+
- Deprecated in v0.43
46+
- Will be removed in a future version
47+
1248
* `MeasurementProcess.expand` is deprecated. The relevant method can be replaced with
1349
`qml.tape.QuantumScript(mp.obs.diagonalizing_gates(), [type(mp)(eigvals=mp.obs.eigvals(), wires=mp.obs.wires)])`
1450

doc/releases/changelog-dev.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,40 @@
194194

195195
<h3>Deprecations 👋</h3>
196196

197+
* Providing `num_steps` to `qml.evolve` and `Evolution` is deprecated and will be removed in a future version.
198+
Instead, use :class:`~.TrotterProduct` for approximate methods, providing the `n` parameter to perform the
199+
Suzuki-Trotter product approximation of a Hamiltonian with the specified number of Trotter steps.
200+
201+
As a concrete example, consider the following case:
202+
203+
```python
204+
coeffs = [0.5, -0.6]
205+
ops = [qml.X(0), qml.X(0) @ qml.Y(1)]
206+
H_flat = qml.dot(coeffs, ops)
207+
```
208+
209+
Instead of computing the Suzuki-Trotter product approximation as:
210+
211+
```pycon
212+
>>> qml.evolve(H_flat, num_steps=2).decomposition()
213+
[RX(0.5, wires=[0]),
214+
PauliRot(-0.6, XY, wires=[0, 1]),
215+
RX(0.5, wires=[0]),
216+
PauliRot(-0.6, XY, wires=[0, 1])]
217+
```
218+
219+
The same result can be obtained using :class:`~.TrotterProduct` as follows:
220+
221+
```pycon
222+
>>> decomp_ops = qml.adjoint(qml.TrotterProduct(H_flat, time=1.0, n=2)).decomposition()
223+
>>> [simp_op for op in decomp_ops for simp_op in map(qml.simplify, op.decomposition())]
224+
[RX(0.5, wires=[0]),
225+
PauliRot(-0.6, XY, wires=[0, 1]),
226+
RX(0.5, wires=[0]),
227+
PauliRot(-0.6, XY, wires=[0, 1])]
228+
```
229+
[(#7954)](https://github.com/PennyLaneAI/pennylane/pull/7954)
230+
197231
* `MeasurementProcess.expand` is deprecated. The relevant method can be replaced with
198232
`qml.tape.QuantumScript(mp.obs.diagonalizing_gates(), [type(mp)(eigvals=mp.obs.eigvals(), wires=mp.obs.wires)])`
199233
[(#7953)](https://github.com/PennyLaneAI/pennylane/pull/7953)

pennylane/ops/functions/evolve.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
"""
1717
from functools import singledispatch
1818
from typing import overload
19+
from warnings import warn
1920

21+
from pennylane.exceptions import PennyLaneDeprecationWarning
2022
from pennylane.operation import Operator
2123
from pennylane.ops import Evolution
2224
from pennylane.pulse import ParametrizedEvolution, ParametrizedHamiltonian
@@ -54,6 +56,35 @@ def evolve(*args, **kwargs): # pylint: disable=unused-argument
5456
Returns:
5557
.Evolution: evolution operator
5658
59+
.. warning::
60+
61+
Providing ``num_steps`` to ``qml.evolve`` and ``Evolution`` is deprecated and will be removed in a future version.
62+
Instead, use :class:`~.TrotterProduct` for approximate methods, providing the ``n`` parameter to perform the
63+
Suzuki-Trotter product approximation of a Hamiltonian with the specified number of Trotter steps.
64+
65+
As a concrete example, consider the following case:
66+
67+
>>> coeffs = [0.5, -0.6]
68+
>>> ops = [qml.X(0), qml.X(0) @ qml.Y(1)]
69+
>>> H_flat = qml.dot(coeffs, ops)
70+
71+
Instead of computing the Suzuki-Trotter product approximation as:
72+
73+
>>> qml.evolve(H_flat, num_steps=2).decomposition()
74+
[RX(0.5, wires=[0]),
75+
PauliRot(-0.6, XY, wires=[0, 1]),
76+
RX(0.5, wires=[0]),
77+
PauliRot(-0.6, XY, wires=[0, 1])]
78+
79+
The same result can be obtained using :class:`~.TrotterProduct` as follows:
80+
81+
>>> decomp_ops = qml.adjoint(qml.TrotterProduct(H_flat, time=1.0, n=2)).decomposition()
82+
>>> [simp_op for op in decomp_ops for simp_op in map(qml.simplify, op.decomposition())]
83+
[RX(0.5, wires=[0]),
84+
PauliRot(-0.6, XY, wires=[0, 1]),
85+
RX(0.5, wires=[0]),
86+
PauliRot(-0.6, XY, wires=[0, 1])]
87+
5788
**Examples**
5889
5990
We can use ``qml.evolve`` to compute the evolution of any PennyLane operator:
@@ -178,4 +209,11 @@ def parametrized_evolution(op: ParametrizedHamiltonian, **kwargs):
178209
# pylint: disable=missing-docstring
179210
@evolve.register
180211
def evolution(op: Operator, coeff: float = 1, num_steps: int = None):
212+
if num_steps is not None:
213+
warn(
214+
"Providing ``num_steps`` to ``qml.evolve`` and ``Evolution`` is deprecated and will be removed in a future version. "
215+
"Instead, you can use ``qml.TrotterProduct`` providing the ``n`` parameter to perform the "
216+
"Suzuki-Trotter product approximation of a Hamiltonian with the specified number of Trotter steps.",
217+
PennyLaneDeprecationWarning,
218+
)
181219
return Evolution(op, coeff, num_steps)

pennylane/ops/op_math/evolution.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
import pennylane as qml
2121
from pennylane import math
22-
from pennylane.exceptions import GeneratorUndefinedError
22+
from pennylane.exceptions import GeneratorUndefinedError, PennyLaneDeprecationWarning
2323

2424
from .exp import Exp
2525

@@ -40,6 +40,35 @@ class Evolution(Exp):
4040
:class:`Evolution`: A :class:`~.operation.Operator` representing an operator exponential of the form :math:`e^{-ix\hat{G}}`,
4141
where x is real.
4242
43+
.. warning::
44+
45+
Providing ``num_steps`` to ``qml.evolve`` and ``Evolution`` is deprecated and will be removed in a future version.
46+
Instead, use :class:`~.TrotterProduct` for approximate methods, providing the ``n`` parameter to perform the
47+
Suzuki-Trotter product approximation of a Hamiltonian with the specified number of Trotter steps.
48+
49+
As a concrete example, consider the following case:
50+
51+
>>> coeffs = [0.5, -0.6]
52+
>>> ops = [qml.X(0), qml.X(0) @ qml.Y(1)]
53+
>>> H_flat = qml.dot(coeffs, ops)
54+
55+
Instead of computing the Suzuki-Trotter product approximation as:
56+
57+
>>> qml.ops.op_math.Evolution(H_flat, num_steps=2).decomposition()
58+
[RX(0.5, wires=[0]),
59+
PauliRot(-0.6, XY, wires=[0, 1]),
60+
RX(0.5, wires=[0]),
61+
PauliRot(-0.6, XY, wires=[0, 1])]
62+
63+
The same result can be obtained using :class:`~.TrotterProduct` as follows:
64+
65+
>>> decomp_ops = qml.adjoint(qml.TrotterProduct(H_flat, time=1.0, n=2)).decomposition()
66+
>>> [simp_op for op in decomp_ops for simp_op in map(qml.simplify, op.decomposition())]
67+
[RX(0.5, wires=[0]),
68+
PauliRot(-0.6, XY, wires=[0, 1]),
69+
RX(0.5, wires=[0]),
70+
PauliRot(-0.6, XY, wires=[0, 1])]
71+
4372
**Usage Details**
4473
4574
In contrast to the general :class:`~.Exp` class, the ``Evolution`` operator :math:`e^{-ix\hat{G}}` is constrained to have a single trainable
@@ -78,6 +107,13 @@ class Evolution(Exp):
78107
num_params = 1
79108

80109
def __init__(self, generator, param=1, num_steps=None, id=None):
110+
if num_steps is not None:
111+
warn(
112+
"Providing ``num_steps`` to ``qml.evolve`` and ``Evolution`` is deprecated and will be removed in a future version. "
113+
"Instead, use ``qml.TrotterProduct`` for approximate methods, providing the ``n`` parameter to perform the "
114+
"Suzuki-Trotter product approximation of a Hamiltonian with the specified number of Trotter steps.",
115+
PennyLaneDeprecationWarning,
116+
)
81117
super().__init__(generator, coeff=-1j * param, num_steps=num_steps, id=id)
82118
self._data = (param,)
83119

tests/ops/functions/test_bind_new_parameters.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from gate_data import GELL_MANN, I, X, Y, Z
2121

2222
import pennylane as qml
23+
from pennylane.exceptions import PennyLaneDeprecationWarning
2324
from pennylane.ops.functions import bind_new_parameters
2425

2526

@@ -91,21 +92,20 @@ def test_composite_ops(op, new_params, expected_op):
9192
assert all(no is not o for no, o in zip(new_op.operands, op.operands))
9293

9394

95+
def test_num_steps_is_deprecated():
96+
"""Test that providing `num_steps` to `qml.evolve` raises a deprecation warning."""
97+
with pytest.warns(
98+
PennyLaneDeprecationWarning,
99+
match="Providing ``num_steps`` to ``qml.evolve`` and ``Evolution`` is deprecated",
100+
):
101+
qml.evolve(qml.PauliX(0), 0.5, num_steps=15)
102+
103+
94104
@pytest.mark.parametrize(
95105
"op, new_params, expected_op",
96106
[
97107
(qml.evolve(qml.PauliX(0), 0.5), [-0.5], qml.evolve(qml.PauliX(0), -0.5)),
98-
(
99-
qml.evolve(qml.PauliX(0), 0.5, num_steps=15),
100-
[-0.5],
101-
qml.evolve(qml.PauliX(0), -0.5, num_steps=15),
102-
),
103108
(qml.exp(qml.PauliX(0), 0.5), [-0.5], qml.exp(qml.PauliX(0), -0.5)),
104-
(
105-
qml.exp(qml.PauliX(0), 0.5, num_steps=15),
106-
[-0.5],
107-
qml.exp(qml.PauliX(0), -0.5, num_steps=15),
108-
),
109109
(qml.pow(qml.RX(0.123, 0), 2), [0.456], qml.pow(qml.RX(0.456, 0), 2)),
110110
(qml.s_prod(0.5, qml.RX(0.123, 0)), [-0.5, 0.456], qml.s_prod(-0.5, qml.RX(0.456, 0))),
111111
(

tests/ops/op_math/test_exp.py

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
DecompositionUndefinedError,
2424
GeneratorUndefinedError,
2525
ParameterFrequenciesUndefinedError,
26+
PennyLaneDeprecationWarning,
2627
)
2728
from pennylane.ops.op_math import Evolution, Exp
2829
from pennylane.ops.op_math.exp import trotter_decomp
@@ -502,7 +503,12 @@ def test_trotter_is_used_if_num_steps_is_defined(self):
502503
phi = 1.23
503504
num_steps = 3
504505
op = qml.IsingXY(phi, wires=[0, 1])
505-
exp = qml.evolve(op.generator(), coeff=-phi, num_steps=num_steps)
506+
with pytest.warns(
507+
PennyLaneDeprecationWarning,
508+
match="Providing ``num_steps`` to ``qml.evolve`` and ``Evolution`` is deprecated",
509+
):
510+
exp = qml.evolve(op.generator(), coeff=-phi, num_steps=num_steps)
511+
506512
dec = exp.decomposition()
507513
assert qml.math.allclose(
508514
qml.matrix(qml.tape.QuantumScript(dec), wire_order=[0, 1]),
@@ -680,11 +686,10 @@ def test_repr_paulix(self):
680686
assert repr(op) == "Exp(3 PauliX)"
681687

682688
# pylint: disable=protected-access
683-
@pytest.mark.parametrize("exp_type", (Exp, Evolution))
684-
def test_flatten_unflatten(self, exp_type):
685-
"""Tests the _unflatten and _flatten methods."""
689+
def test_flatten_unflatten_Exp(self):
690+
"""Tests the _unflatten and _flatten methods for the Exp operator."""
686691
base = qml.RX(1.2, wires=0)
687-
op = exp_type(base, 2.5, num_steps=5)
692+
op = Exp(base, 2.5, num_steps=5)
688693

689694
data, metadata = op._flatten()
690695
assert data[0] is base
@@ -697,6 +702,28 @@ def test_flatten_unflatten(self, exp_type):
697702
new_op = type(op)._unflatten(*op._flatten())
698703
qml.assert_equal(new_op, op)
699704

705+
# pylint: disable=protected-access
706+
def test_flatten_unflatten_Evolution(self):
707+
"""Tests the _unflatten and _flatten methods for the Evolution operator."""
708+
base = qml.RX(1.2, wires=0)
709+
op = Evolution(base, 2.5)
710+
711+
data, _ = op._flatten()
712+
assert data[0] is base
713+
assert data[1] == 2.5
714+
715+
new_op = type(op)._unflatten(*op._flatten())
716+
qml.assert_equal(new_op, op)
717+
718+
def test_num_steps_is_deprecated(self):
719+
"""Test that providing `num_steps` raises a deprecation warning."""
720+
with pytest.warns(
721+
PennyLaneDeprecationWarning,
722+
match="Providing ``num_steps`` to ``qml.evolve`` and ``Evolution`` is deprecated",
723+
):
724+
base = qml.RX(1.2, wires=0)
725+
Evolution(base, 2.5, num_steps=5)
726+
700727
def test_repr_tensor(self):
701728
"""Test the __repr__ method when the base is a tensor."""
702729
t = qml.PauliX(0) @ qml.PauliX(1)

0 commit comments

Comments
 (0)