Skip to content

Commit

Permalink
Deprecate map_batch_transform (#5676)
Browse files Browse the repository at this point in the history
**Context:** ***Part of  deprecations and removals for pennylane-0.37***

**Description of the Change:** Deprecating
``qml.transforms.map_batch_transform``, since a transform can be applied
directly to a batch of tapes. See #5163 and #5212.

[sc-58987]
  • Loading branch information
PietropaoloFrisoni authored and Shiro-Raven committed May 15, 2024
1 parent d4617a2 commit bbe0b70
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 14 deletions.
5 changes: 5 additions & 0 deletions doc/development/deprecations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ deprecations are listed below.
Pending deprecations
--------------------

* ``qml.transforms.map_batch_transform`` is deprecated, since transforms can be applied directly to a batch of tapes.
See :func:`~.pennylane.transform` for more information.

- Deprecated in v0.37

New operator arithmetic deprecations
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
3 changes: 3 additions & 0 deletions doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@

<h3>Deprecations 👋</h3>

* ``qml.transforms.map_batch_transform`` is deprecated, since a transform can be applied directly to a batch of tapes.
[(#5676)](https://github.com/PennyLaneAI/pennylane/pull/5676)

<h3>Documentation 📝</h3>

* A small typo was fixed in the docstring for `qml.sample`.
Expand Down
13 changes: 13 additions & 0 deletions pennylane/transforms/batch_transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"""Contains tools and decorators for registering batch transforms."""
# pylint: disable=too-few-public-methods

import warnings
from typing import Callable, Tuple

import pennylane as qml
Expand Down Expand Up @@ -64,7 +65,19 @@ def map_batch_transform(
>>> dev = qml.device("default.qubit", wires=2)
>>> fn(qml.execute(tapes, dev, qml.gradients.param_shift))
[array(0.99500417), array(0.8150893)]
.. warning::
qml.transforms.map_batch_transform is deprecated and will be removed in a future release.
Instead, a transform can be applied directly to a batch of tapes. See :func:`~.pennylane.transform` for more details.
"""

warnings.warn(
"qml.transforms.map_batch_transform is deprecated."
"Instead, a transform can be applied directly to a batch of tapes."
"See :func:`~.pennylane.transform` for more details.",
qml.PennyLaneDeprecationWarning,
)

execution_tapes = []
batch_fns = []
tape_counts = []
Expand Down
47 changes: 47 additions & 0 deletions pennylane/transforms/core/transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,48 @@ def qnode_circuit(a):
and creates a sequence of tapes to be executed. The execution results are then post-processed in the
reverse order of the transform program to obtain the final results.
.. details::
:title: Dispatch a transform onto a batch of tapes
We can compose multiple transforms when working in the tape paradigm and apply them to more than one tape.
The following example demonstrates how to apply a transform to a batch of tapes.
**Example**
In this example, we apply sequentially a transform to a tape and another one to a batch of tapes.
We then execute the transformed tapes on a device and post-process the results.
.. code-block:: python
import pennylane as qml
H = qml.PauliY(2) @ qml.PauliZ(1) + 0.5 * qml.PauliZ(2) + qml.PauliZ(1)
measurement = [qml.expval(H)]
operations = [qml.Hadamard(0), qml.RX(0.2, 0), qml.RX(0.6, 0), qml.CNOT((0, 1))]
tape = qml.tape.QuantumTape(operations, measurement)
batch1, function1 = qml.transforms.hamiltonian_expand(tape)
batch2, function2 = qml.transforms.merge_rotations(batch1)
dev = qml.device("default.qubit", wires=3)
result = dev.execute(batch2)
The first ``hamiltonian_expand`` transform splits the original tape, returning a batch of tapes ``batch1`` and a processing function ``function1``.
The second ``merge_rotations`` transform is applied to the batch of tapes returned by the first transform.
It returns a new batch of tapes ``batch2``, each of which has been transformed by the second transform, and a processing function ``function2``.
>>> batch2
(<QuantumTape: wires=[0, 1, 2], params=2>,
<QuantumTape: wires=[0, 1, 2], params=1>)
>>> type(function2)
function
We can combine the processing functions to post-process the results of the execution.
>>> function1(function2(result))
[array(0.5)]
.. details::
:title: Signature of a transform
Expand All @@ -134,6 +176,7 @@ def qnode_circuit(a):
- :class:`pennylane.QNode`
- a quantum function (callable)
- :class:`pennylane.tape.QuantumTape`
- a batch of :class:`pennylane.tape.QuantumTape`
- :class:`pennylane.devices.Device`.
For each object, the transform will be applied in a different way, but it always preserves the underlying
Expand All @@ -157,6 +200,10 @@ def qnode_circuit(a):
:class:`~.QuantumTape`. It returns a sequence of :class:`~.QuantumTape` and a processing
function to be applied after execution.
- For a batch of :class:`pennylane.tape.QuantumTape`, the quantum transform is mapped across all the tapes.
It returns a sequence of :class:`~.QuantumTape` and a processing function to be applied after execution.
Each tape in the sequence is transformed by the transform.
- For a :class:`~.devices.Device`, the transform is added to the device's transform program
and a transformed :class:`pennylane.devices.Device` is returned. The transform is added
to the end of the device program and will be last in the overall transform program.
Expand Down
12 changes: 6 additions & 6 deletions pennylane/transforms/core/transform_dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,13 +324,13 @@ def original_device(self):
return TransformedDevice(original_device, self._transform)

def _batch_transform(self, original_batch, targs, tkwargs):
"""Apply the transform on a batch of tapes"""
"""Apply the transform on a batch of tapes."""
execution_tapes = []
batch_fns = []
tape_counts = []

for t in original_batch:
# Preprocess the tapes by applying batch transforms
# Preprocess the tapes by applying transforms
# to each tape, and storing corresponding tapes
# for execution, processing functions, and list of tape lengths.
new_tapes, fn = self(t, *targs, **tkwargs)
Expand All @@ -342,14 +342,14 @@ def processing_fn(res: ResultBatch) -> ResultBatch:
"""Applies a batch of post-processing functions to results.
Args:
res (ResultBatch): the results of executing a batch of circuits
res (ResultBatch): the results of executing a batch of circuits.
Returns:
ResultBatch : results that have undergone classical post processing
ResultBatch: results that have undergone classical post processing.
Closure variables:
tape_counts: the number of tapes outputted from each application of the transform
batch_fns: the post processing functions to apply to each sub-batch
tape_counts: the number of tapes outputted from each application of the transform.
batch_fns: the post processing functions to apply to each sub-batch.
"""
count = 0
Expand Down
4 changes: 2 additions & 2 deletions tests/param_shift_dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def compute_derivatives(self, circuits, execution_config=None):
self.tracker.update(derivative_batches=1, derivatives=len(circuits))
self.tracker.record()

diff_batch, fn = qml.transforms.map_batch_transform(qml.gradients.param_shift, circuits)
diff_batch, fn = qml.gradients.param_shift(circuits)
diff_results = self.execute(diff_batch)

jacs = fn(diff_results)
Expand All @@ -73,7 +73,7 @@ def execute_and_compute_derivatives(self, circuits, execution_config=None):
)
self.tracker.record()

diff_batch, fn = qml.transforms.map_batch_transform(qml.gradients.param_shift, circuits)
diff_batch, fn = qml.gradients.param_shift(circuits)
combined_batch = tuple(circuits) + tuple(diff_batch)
all_results = self.execute(combined_batch)
results = all_results[: len(circuits)]
Expand Down
34 changes: 28 additions & 6 deletions tests/transforms/test_batch_transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,27 @@
"""
# pylint: disable=too-few-public-methods,not-callable

import pytest

import pennylane as qml
from pennylane import numpy as np


class TestMapBatchTransform:
"""Tests for the map_batch_transform function"""

def test_map_batch_transform_is_deprecated(self):
"""Test that map_batch_transform is deprecated."""
ops = [qml.RX(1.2345, wires=0)]
measurements = [qml.expval(qml.Z(0))]
tape = qml.tape.QuantumTape(ops, measurements)
transform = qml.transforms.compile
with pytest.warns(
qml.PennyLaneDeprecationWarning,
match="deprecated",
):
_ = qml.transforms.map_batch_transform(transform, [tape])

def test_result(self, mocker):
"""Test that it correctly applies the transform to be mapped"""
dev = qml.device("default.qubit.legacy", wires=2)
Expand All @@ -45,9 +59,13 @@ def test_result(self, mocker):

tape2 = qml.tape.QuantumScript.from_queue(q2)
spy = mocker.spy(qml.transforms, "hamiltonian_expand")
tapes, fn = qml.transforms.map_batch_transform(
qml.transforms.hamiltonian_expand, [tape1, tape2]
)
with pytest.warns(
qml.PennyLaneDeprecationWarning,
match="deprecated",
):
tapes, fn = qml.transforms.map_batch_transform(
qml.transforms.hamiltonian_expand, [tape1, tape2]
)

spy.assert_called()
assert len(tapes) == 5
Expand Down Expand Up @@ -79,9 +97,13 @@ def cost(weights):
qml.expval(H + qml.Hamiltonian([0.5], [qml.PauliY(0)]))

tape2 = qml.tape.QuantumScript.from_queue(q2)
tapes, fn = qml.transforms.map_batch_transform(
qml.transforms.hamiltonian_expand, [tape1, tape2]
)
with pytest.warns(
qml.PennyLaneDeprecationWarning,
match="deprecated",
):
tapes, fn = qml.transforms.map_batch_transform(
qml.transforms.hamiltonian_expand, [tape1, tape2]
)
res = qml.execute(tapes, dev, qml.gradients.param_shift, device_batch_transform=False)
return np.sum(fn(res))

Expand Down

0 comments on commit bbe0b70

Please sign in to comment.