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(