Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add deprecation warning to from_qasm #5882

Merged
merged 9 commits into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
3 changes: 3 additions & 0 deletions doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,9 @@
* `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)

* The default behaviour of `qml.from_qasm()` to remove measurements in the QASM code is deprecated. Use `measurements=[]` to keep this behaviour or `measurements=None` to keep the measurements from the QASM code.
[(#5882)](https://github.com/PennyLaneAI/pennylane/pull/5882)

<h3>Documentation 📝</h3>

* The documentation for the `default.tensor` device has been added.
Expand Down
30 changes: 26 additions & 4 deletions pennylane/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@
This module contains functions to load circuits from other frameworks as
PennyLane templates.
"""
import warnings
from collections import defaultdict
from importlib import metadata
from sys import version_info

import pennylane as qml

# Error message to show when the PennyLane-Qiskit plugin is required but missing.
_MISSING_QISKIT_PLUGIN_MESSAGE = (
"Conversion from Qiskit requires the PennyLane-Qiskit plugin. "
Expand Down Expand Up @@ -407,7 +410,7 @@ def from_qiskit_op(qiskit_op, params=None, wires=None):
raise RuntimeError(_MISSING_QISKIT_PLUGIN_MESSAGE) from e


def from_qasm(quantum_circuit: str, measurements=None):
def from_qasm(quantum_circuit: str, measurements=False):
"""Loads quantum circuits from a QASM string using the converter in the
PennyLane-Qiskit plugin.

Expand Down Expand Up @@ -436,8 +439,16 @@ def from_qasm(quantum_circuit: str, measurements=None):
that will **override** any terminal measurements present in the QASM code,
so that they are not performed before the operations specified in ``measurements``.

If the existing QASM code already contains measurements, ``from_qasm``
will return those measurements, provided that they are not overriden as shown above.
If the existing QASM code already contains measurements, set `measurements=None` so that
astralcai marked this conversation as resolved.
Show resolved Hide resolved
`from_qasm` returns those measurements. By default, `from_qasm` will remove any measurements
astralcai marked this conversation as resolved.
Show resolved Hide resolved
that are present in the QASM code.

.. warning::

The current default behaviour of excluding measurements in the QASM code is deprecated
and will be changed in a future release. Starting in version ``0.38``, ``from_qasm``
will keep the measurements from the QASM code by default. To remove all measurements,
set ``measurements=[]`` which overrides the existing measurements with an empty list.
astralcai marked this conversation as resolved.
Show resolved Hide resolved

Mid-circuit measurements inside the QASM code can also be interpreted.

Expand Down Expand Up @@ -485,12 +496,23 @@ def circuit():
quantum_circuit (str): a QASM string containing a valid quantum circuit
measurements (None | MeasurementProcess | list[MeasurementProcess]): an optional PennyLane
measurement or list of PennyLane measurements that overrides any terminal measurements
that may be present in the input circuit
that may be present in the input circuit. If set to ``None``, existing measurements
in the input circuit will be used.

Returns:
function: the PennyLane template created based on the QASM string

"""
plugin_converter = plugin_converters["qasm"].load()
if measurements is False:
warnings.warn(
"The current default behaviour of removing measurements in the QASM code is "
"deprecated. Set measurements=None to keep the existing measurements in the QASM "
"code or set measurements=[] to remove them from the returned circuit. Starting "
"in version 0.38, measurements=None will be the new default.",
qml.PennyLaneDeprecationWarning,
)
measurements = []
Comment on lines +507 to +515
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I was expecting to only raise a warning if measurements is False and there are measurements present in the QASM circuit. Is that something we could determine?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll make another PR to do that.

return plugin_converter(quantum_circuit, measurements=measurements)


Expand Down
28 changes: 27 additions & 1 deletion tests/test_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,6 @@ def test_qiskit_converter_load_fails(self, monkeypatch, method, entry_point_name
[
(qml.from_qiskit, "qiskit"),
(qml.from_qiskit_op, "qiskit_op"),
(qml.from_qasm, "qasm"),
(qml.from_pyquil, "pyquil_program"),
(qml.from_quil, "quil"),
(qml.from_quil_file, "quil_file"),
Expand All @@ -135,11 +134,38 @@ def test_convenience_functions(self, method, entry_point_name, mock_plugin_conve
if mock_plugin_converters[plugin_converter].called:
raise RuntimeError(f"The other plugin converter {plugin_converter} was called.")

def test_from_qasm(self, mock_plugin_converters):
"""Tests that the correct entry point is called for from_qasm."""

qml.from_qasm("Test", measurements=None)
assert mock_plugin_converters["qasm"].called
assert mock_plugin_converters["qasm"].last_args == ("Test",)

for plugin_converter in mock_plugin_converters:
if mock_plugin_converters[plugin_converter].called and plugin_converter != "qasm":
raise RuntimeError(f"The other plugin converter {plugin_converter} was called.")

def test_from_qasm_deprecated(self, mock_plugin_converters):
"""Tests that the current default behaviour of from_qasm is deprecated."""

with pytest.warns(qml.PennyLaneDeprecationWarning, match="The current default behaviour"):
qml.from_qasm("Test")

called_args, called_kwargs = mock_plugin_converters["qasm"].call_args
assert called_args == ("Test",)
assert called_kwargs == {"measurements": []}

@pytest.mark.parametrize(
"method, entry_point_name, args, kwargs",
[
(qml.from_qiskit, "qiskit", ("Circuit",), {"measurements": []}),
(qml.from_qiskit_op, "qiskit_op", ("Op",), {"params": [1, 2], "wires": [3, 4]}),
(
qml.from_qasm,
"qasm",
("Circuit",),
{"measurements": []},
),
],
)
def test_convenience_function_arguments(
Expand Down
Loading