diff --git a/doc/releases/changelog-0.32.0.md b/doc/releases/changelog-0.32.0.md
index b359a784a3b..72f3f9bc225 100644
--- a/doc/releases/changelog-0.32.0.md
+++ b/doc/releases/changelog-0.32.0.md
@@ -240,11 +240,17 @@ array([False, False])
[(#4465)](https://github.com/PennyLaneAI/pennylane/pull/4465/)
[(#4478)](https://github.com/PennyLaneAI/pennylane/pull/4478/)
+* The label for `ParametrizedEvolution` can display parameters with the requested format as set by the
+ kwarg `decimals`. Array-like parameters are displayed in the same format as matrices and stored in the
+ cache.
+ [(#4151)](https://github.com/PennyLaneAI/pennylane/pull/4151)
+
* CI now runs tests with Tensorflow 2.13.0
[(#4472)](https://github.com/PennyLaneAI/pennylane/pull/4472)
* `draw_mpl` accepts `style='pennylane'` to draw PennyLane-style circuit diagrams, and `style.use` in `matplotlib.pyplot` accepts `pennylane.drawer.plot` to create PennyLane-style plots. If the font Quicksand Bold isn't available, an available default font is used instead. [(#3950)](https://github.com/PennyLaneAI/pennylane/pull/3950)
+
Breaking changes 💔
* Applying gradient transforms to broadcasted/batched tapes was deactivated until it is consistently
diff --git a/pennylane/pulse/parametrized_evolution.py b/pennylane/pulse/parametrized_evolution.py
index 838a878dbe6..f1a4cea2177 100644
--- a/pennylane/pulse/parametrized_evolution.py
+++ b/pennylane/pulse/parametrized_evolution.py
@@ -505,6 +505,67 @@ def fun(y, t):
mat = mat[-1]
return qml.math.expand_matrix(mat, wires=self.wires, wire_order=wire_order)
+ def label(self, decimals=None, base_label=None, cache=None):
+ r"""A customizable string representation of the operator.
+
+ Args:
+ decimals=None (int): If ``None``, no parameters are included. Else,
+ specifies how to round the parameters.
+ base_label=None (str): overwrite the non-parameter component of the label
+ cache=None (dict): dictionary that carries information between label calls
+ in the same drawing
+
+ Returns:
+ str: label to use in drawings
+
+ **Example:**
+
+ >>> H = qml.PauliX(1) + qml.pulse.constant * qml.PauliY(0) + jnp.polyval * qml.PauliY(1)
+ >>> params = [0.2, [1, 2, 3]]
+ >>> op = qml.evolve(H)(params, t=2)
+ >>> cache = {'matrices': []}
+
+ >>> op.label()
+ "Parametrized\nEvolution"
+ >>> op.label(decimals=2, cache=cache)
+ "Parametrized\nEvolution\n(p=[0.20,M0], t=[0. 2.])"
+ >>> op.label(base_label="my_label")
+ "my_label"
+ >>> op.label(decimals=2, base_label="my_label", cache=cache)
+ "my_label\n(p=[0.20,M0], t=[0. 2.])"
+
+ Array-like parameters are stored in ``cache['matrices']``.
+ """
+ op_label = base_label or "Parametrized\nEvolution"
+
+ if self.num_params == 0:
+ return op_label
+
+ if decimals is None:
+ return op_label
+
+ params = self.parameters
+ has_cache = cache and isinstance(cache.get("matrices", None), list)
+
+ if any(qml.math.ndim(p) for p in params) and not has_cache:
+ return op_label
+
+ def _format_number(x):
+ return format(qml.math.toarray(x), f".{decimals}f")
+
+ def _format_arraylike(x):
+ for i, mat in enumerate(cache["matrices"]):
+ if qml.math.shape(x) == qml.math.shape(mat) and qml.math.allclose(x, mat):
+ return f"M{i}"
+ mat_num = len(cache["matrices"])
+ cache["matrices"].append(x)
+ return f"M{mat_num}"
+
+ param_strings = [_format_arraylike(p) if p.shape else _format_number(p) for p in params]
+
+ p = ",".join(s for s in param_strings)
+ return f"{op_label}\n(p=[{p}], t={self.t})"
+
@functions.bind_new_parameters.register
def _bind_new_parameters_parametrized_evol(op: ParametrizedEvolution, params: Sequence[TensorLike]):
diff --git a/tests/pulse/test_parametrized_evolution.py b/tests/pulse/test_parametrized_evolution.py
index 7ebcfc57bda..29d153a9189 100644
--- a/tests/pulse/test_parametrized_evolution.py
+++ b/tests/pulse/test_parametrized_evolution.py
@@ -202,6 +202,7 @@ def test_update_attributes(self):
H = ParametrizedHamiltonian(coeffs, ops)
ev = ParametrizedEvolution(H=H, mxstep=10)
+ # pylint:disable = use-implicit-booleaness-not-comparison
assert ev.parameters == []
assert ev.num_params == 0
assert ev.t is None
@@ -304,6 +305,87 @@ def test_hash_with_data(self):
assert compare_to.hash != diff_ret_intmdt.hash
assert compare_to.hash != diff_complementary.hash
+ @pytest.mark.parametrize(
+ "params",
+ [
+ [0.2, [1, 2, 3], [4, 5, 6, 7]],
+ [0.2, np.array([1, 2, 3]), np.array([4, 5, 6, 7])],
+ [0.2, (1, 2, 3), (4, 5, 6, 7)],
+ ],
+ )
+ def test_label(self, params):
+ """Test that the label displays correctly with and without decimal and base_label"""
+ H = (
+ qml.PauliX(1)
+ + qml.pulse.constant * qml.PauliY(0)
+ + np.polyval * qml.PauliY(1)
+ + np.polyval * qml.PauliY(1)
+ )
+ op = qml.evolve(H)(params, 2)
+ cache = {"matrices": []}
+
+ assert op.label() == "Parametrized\nEvolution"
+ assert op.label(decimals=2) == "Parametrized\nEvolution"
+ assert (
+ op.label(decimals=2, cache=cache)
+ == "Parametrized\nEvolution\n(p=[0.20,M0,M1], t=[0. 2.])"
+ )
+ assert op.label(base_label="my_label") == "my_label"
+ assert (
+ op.label(base_label="my_label", decimals=2, cache=cache)
+ == "my_label\n(p=[0.20,M0,M1], t=[0. 2.])"
+ )
+
+ def test_label_no_params(self):
+ """Test that the label displays correctly with and without decimal and base_label"""
+ H = (
+ qml.PauliX(1)
+ + qml.pulse.constant * qml.PauliY(0)
+ + np.polyval * qml.PauliY(1)
+ + np.polyval * qml.PauliY(1)
+ )
+ op = qml.evolve(H)
+ cache = {"matrices": []}
+
+ assert op.label() == "Parametrized\nEvolution"
+ assert op.label(decimals=2) == "Parametrized\nEvolution"
+ assert op.label(decimals=2, cache=cache) == "Parametrized\nEvolution"
+ assert op.label(base_label="my_label") == "my_label"
+ assert op.label(base_label="my_label", decimals=2, cache=cache)
+
+ def test_label_reuses_cached_matrices(self):
+ """Test that the matrix is reused if it already exists in the cache, instead
+ of being added to the cache a second time"""
+
+ H = (
+ qml.PauliX(1)
+ + qml.pulse.constant * qml.PauliY(0)
+ + np.polyval * qml.PauliY(1)
+ + np.polyval * qml.PauliY(2)
+ )
+ cache = {"matrices": []}
+
+ params1 = [3, np.array([0.23, 0.47, 5]), np.array([3.4, 6.8])]
+ params2 = [5.67, np.array([0.23, 0.47, 5]), np.array([[3.7, 6.2], [1.2, 4.6]])]
+ op1 = qml.evolve(H)(params1, 2)
+ op2 = qml.evolve(H)(params2, 2)
+
+ assert (
+ op1.label(decimals=2, cache=cache)
+ == "Parametrized\nEvolution\n(p=[3.00,M0,M1], t=[0. 2.])"
+ )
+ assert len(cache["matrices"]) == 2
+ assert np.all(cache["matrices"][0] == params1[1])
+ assert np.all(cache["matrices"][1] == params1[2])
+
+ assert (
+ op2.label(decimals=2, cache=cache)
+ == "Parametrized\nEvolution\n(p=[5.67,M0,M2], t=[0. 2.])"
+ )
+ assert len(cache["matrices"]) == 3
+ assert np.all(cache["matrices"][0] == params2[1])
+ assert np.all(cache["matrices"][2] == params2[2])
+
def test_raises_wrong_number_of_params(self):
"""Test that an error is raised when instantiating (or calling) a
ParametrizedEvolution with the wrong number of parameters."""