From 50e009a51abc71ba674167166212b96d965a1164 Mon Sep 17 00:00:00 2001 From: Astral Cai Date: Thu, 20 Jun 2024 17:40:56 -0400 Subject: [PATCH] Add deprecation warning to from_qasm (#5882) **Context:** Previously, the QASM circuit converted to PL had no measurements (even if the circuit ended with measurements, the assumption was we remove those and add PL measurements once the circuit is converted). In the UnitaryHack, a contributor added the option to specify measurements when converting a circuit `from_qasm`, like the `from_qiskit` function. In doing so, the `from_qasm` circuit took on the current `from_qiskit` convention. Calling `from_qasm(qasm_str)` previously returned only the operators, and now returns operators + measurements. This is a breaking change with no deprecation cycle. https://github.com/PennyLaneAI/pennylane-qiskit/pull/469 **Description of the Change:** Adds a deprecation warning to `from_qasm` [sc-66318] --------- Co-authored-by: lillian542 <38584660+lillian542@users.noreply.github.com> Co-authored-by: Christina Lee Co-authored-by: David Wierichs --- doc/development/deprecations.rst | 8 ++++++++ doc/releases/changelog-dev.md | 3 +++ pennylane/io.py | 30 ++++++++++++++++++++++++++---- tests/test_io.py | 28 +++++++++++++++++++++++++++- 4 files changed, 64 insertions(+), 5 deletions(-) diff --git a/doc/development/deprecations.rst b/doc/development/deprecations.rst index 34c6e03b9b1..23f2c0749e9 100644 --- a/doc/development/deprecations.rst +++ b/doc/development/deprecations.rst @@ -21,6 +21,14 @@ Pending deprecations - Deprecated in v0.37 - Will be removed in v0.38 +* ``qml.from_qasm`` will no longer remove measurements from the QASM code. Calling ``qml.from_qasm`` + without specifying ``measurements`` will raise a deprecation warning in v0.37, and in v0.38, + the default behaviour will be changed to keeping measurements. Use ``measurements=[]`` to remove + measurements from the original circuit. + + - Deprecated in v0.37 + - Default behaviour will be changed in v0.38 + New operator arithmetic deprecations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 80597b8fcb0..a6b39d4c984 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -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) +

Documentation 📝

* Move information about mid-circuit measurements from the measurements quickstart page to its own diff --git a/pennylane/io.py b/pennylane/io.py index 3fcb4c36dce..44465521250 100644 --- a/pennylane/io.py +++ b/pennylane/io.py @@ -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. " @@ -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. @@ -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. + By default, ``from_qasm`` will remove any measurements that are present in the QASM code. + If the QASM code contains measurements, set ``measurements=None`` to keep them in the + output of ``from_qasm``. + + .. warning:: + + The current default behaviour of removing 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. Mid-circuit measurements inside the QASM code can also be interpreted. @@ -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 = [] return plugin_converter(quantum_circuit, measurements=measurements) diff --git a/tests/test_io.py b/tests/test_io.py index 489a5509fa9..d59b22ba892 100644 --- a/tests/test_io.py +++ b/tests/test_io.py @@ -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"), @@ -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(