Repository: PennyLaneAI/pennylane
Pull request #5882 (2024-06-20 13:38:21)

Add deprecation warning to from_qasm
**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]

github-actions[bot]: Hello. You may have forgotten to update the changelog!
Please edit [`doc/releases/changelog-dev.md`](/PennyLaneAI/pennylane/blob/master/doc/releases/changelog-dev.md) with:
* A one-to-two sentence description of the change. You may include a small working example for new features.
* A link back to this PR.
* Your name (or GitHub username) in the contributors section.
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5882?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 99.66%. Comparing base [(`d330e9d`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/d330e9dbfc98fabb82e499b62507ce9fcdf2aa72?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) to head [(`95adb1e`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/95adb1e36173b7aa557a10fd0422197d1e80e0b0?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).
> Report is 1 commits behind head on master.


<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #5882      +/-   ##
==========================================
- Coverage   99.67%   99.66%   -0.01%     
==========================================
  Files         422      422              
  Lines       40658    40367     -291     
==========================================
- Hits        40525    40233     -292     
- Misses        133      134       +1     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5882?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).

timmysilv: don't forget to add an entry in [deprecations.rst](https://github.com/PennyLaneAI/pennylane/blob/master/doc/development/deprecations.rst)!
astralcai: > don't forget to add an entry in [deprecations.rst](https://github.com/PennyLaneAI/pennylane/blob/master/doc/development/deprecations.rst)!

ooops, will do.

Patch:
diff --git a/doc/development/deprecations.rst b/doc/development/deprecations.rst
index 34c6e03b9..23f2c0749 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 80597b8fc..a6b39d4c9 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)
+
 <h3>Documentation 📝</h3>
 
 * 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 3fcb4c36d..444655212 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 from_qasm(quantum_circuit: str, measurements=None):
         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)
 
 


Test patch:
diff --git a/tests/test_io.py b/tests/test_io.py
index 489a5509f..d59b22ba8 100644
--- a/tests/test_io.py
+++ b/tests/test_io.py
@@ -114,7 +114,6 @@ class TestLoad:
         [
             (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 @@ class TestLoad:
             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(