Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions doc/releases/changelog-0.32.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)


<h3>Breaking changes 💔</h3>

* Applying gradient transforms to broadcasted/batched tapes was deactivated until it is consistently
Expand Down
61 changes: 61 additions & 0 deletions pennylane/pulse/parametrized_evolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]):
Expand Down
82 changes: 82 additions & 0 deletions tests/pulse/test_parametrized_evolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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."""
Expand Down