Repository: PennyLaneAI/pennylane
Pull request #5869 (2024-06-17 20:16:41)

Bugfix for split_non_commuting when tape measurements is empty
**Context:**
Currently on master, if we apply `split_non_commuting` to a tape with empty measurements, we delete the tape:

```
>>> from pennylane.transforms import split_non_commuting
>>> tape = qml.tape.QuantumScript([qml.X(0)], measurements=[])
>>> tapes, _ = split_non_commuting(tape)
>>> tapes
[]
```

**Description of the Change:**

We add a check for `len(measurements) == 0` and just return the tape if there are no measurements to split, so we get:

```
>>> from pennylane.transforms import split_non_commuting
>>> tape = qml.tape.QuantumScript([qml.X(0)], measurements=[])
>>> tapes, _ = split_non_commuting(tape)
>>> tapes
[<QuantumScript: wires=[0], params=0>]
```

**Benefits:**
We don't delete the tape if it has no measurements (came up in catalyst tests)

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5869?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.67%. Comparing base [(`94adc17`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/94adc177f4b507afd113f256df7413ac17b436ee?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) to head [(`0da7371`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/0da7371c594a6bc7f5a015e5a28937ffd071f07f?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).


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


```diff
@@            Coverage Diff             @@
##           master    #5869      +/-   ##
==========================================
- Coverage   99.68%   99.67%   -0.01%     
==========================================
  Files         421      421              
  Lines       40435    40141     -294     
==========================================
- Hits        40306    40011     -295     
- Misses        129      130       +1     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5869?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).

lillian542: [sc-60186]

Patch:
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index 1a240756f..320196f0a 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -115,6 +115,7 @@
 * `qml.transforms.split_non_commuting` can now handle circuits containing measurements of multi-term observables.
   [(#5729)](https://github.com/PennyLaneAI/pennylane/pull/5729)
   [(#5853)](https://github.com/PennyLaneAI/pennylane/pull/5838)
+  [(#5869)](https://github.com/PennyLaneAI/pennylane/pull/5869)
 
 * The qchem module has dedicated functions for calling `pyscf` and `openfermion` backends.
   [(#5553)](https://github.com/PennyLaneAI/pennylane/pull/5553)
diff --git a/pennylane/transforms/split_non_commuting.py b/pennylane/transforms/split_non_commuting.py
index 8c23061e8..71d7798f3 100644
--- a/pennylane/transforms/split_non_commuting.py
+++ b/pennylane/transforms/split_non_commuting.py
@@ -28,6 +28,13 @@ from pennylane.transforms import transform
 from pennylane.typing import Result, ResultBatch
 
 
+def null_postprocessing(results):
+    """A postprocessing function returned by a transform that only converts the batch of results
+    into a result for a single ``QuantumTape``.
+    """
+    return results[0]
+
+
 @transform
 def split_non_commuting(
     tape: qml.tape.QuantumScript,
@@ -243,6 +250,8 @@ def split_non_commuting(
         [[expval(X(0)), probs(wires=[1])], [probs(wires=[0, 1])]]
 
     """
+    if len(tape.measurements) == 0:
+        return [tape], null_postprocessing
 
     # Special case for a single measurement of a Sum or Hamiltonian, in which case
     # the grouping information can be computed and cached in the observable.


Test patch:
diff --git a/tests/transforms/test_split_non_commuting.py b/tests/transforms/test_split_non_commuting.py
index 709a0ed8e..5af275bc1 100644
--- a/tests/transforms/test_split_non_commuting.py
+++ b/tests/transforms/test_split_non_commuting.py
@@ -568,6 +568,17 @@ class TestUnits:
             fn([[0.1, 0.2], [0.3, 0.6], 0.4, 0.5, 0.7]), [0.01, 0.06, 0.06, 0.16, 0.25, 0.36, 0.49]
         )
 
+    @pytest.mark.parametrize("grouping_strategy", [None, "default", "qwc", "wires"])
+    def test_no_measurements(self, grouping_strategy):
+        """Test that if the tape contains no measurements, the transform doesn't
+        modify it"""
+
+        tape = qml.tape.QuantumScript([qml.X(0)])
+        tapes, post_processing_fn = split_non_commuting(tape, grouping_strategy=grouping_strategy)
+        assert len(tapes) == 1
+        assert tapes[0] == tape
+        assert post_processing_fn(tapes) == tape
+
 
 class TestIntegration:
     """Tests the ``split_non_commuting`` transform performed on a QNode"""