Repository: PennyLaneAI/pennylane
Pull request #5939 (2024-07-03 15:41:24)

Fix `split_non_commuting` for `Sum` containing non-pauli-word terms
**Description of the Change:**
Fall back to wire-based grouping for single `Sum` with non-pauli-word observables

Fixes https://github.com/PennyLaneAI/catalyst/actions/runs/9769748288/job/26969777919

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/5939?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:
> Please [upload](https://docs.codecov.com/docs/codecov-uploader) report for BASE (`v0.37.0-rc0@5d1747e`). [Learn more](https://docs.codecov.io/docs/error-reference?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#section-missing-base-commit) about missing BASE report.

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


```diff
@@              Coverage Diff               @@
##             v0.37.0-rc0    #5939   +/-   ##
==============================================
  Coverage               ?   99.66%           
==============================================
  Files                  ?      425           
  Lines                  ?    40460           
  Branches               ?        0           
==============================================
  Hits                   ?    40326           
  Misses                 ?      134           
  Partials               ?        0           
```

</details>

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


Patch:
diff --git a/doc/releases/changelog-0.37.0.md b/doc/releases/changelog-0.37.0.md
index 7bd0969e7..a9e4d08f3 100644
--- a/doc/releases/changelog-0.37.0.md
+++ b/doc/releases/changelog-0.37.0.md
@@ -350,6 +350,7 @@
   [(#5838)](https://github.com/PennyLaneAI/pennylane/pull/5838)
   [(#5828)](https://github.com/PennyLaneAI/pennylane/pull/5828)
   [(#5869)](https://github.com/PennyLaneAI/pennylane/pull/5869)
+  [(#5939)](https://github.com/PennyLaneAI/pennylane/pull/5939)
 
 * `qml.devices.LegacyDevice` is now an alias for `qml.Device`, so it is easier to distinguish it from
   `qml.devices.Device`, which follows the new device API.
diff --git a/pennylane/transforms/split_non_commuting.py b/pennylane/transforms/split_non_commuting.py
index 71d7798f3..42d517665 100644
--- a/pennylane/transforms/split_non_commuting.py
+++ b/pennylane/transforms/split_non_commuting.py
@@ -16,7 +16,7 @@
 Contains the tape transform that splits a tape into tapes measuring commuting observables.
 """
 
-# pylint: disable=too-many-arguments
+# pylint: disable=too-many-arguments,too-many-boolean-expressions
 
 from functools import partial
 from typing import Callable, Dict, List, Optional, Sequence, Tuple
@@ -260,7 +260,10 @@ def split_non_commuting(
         and isinstance(tape.measurements[0], ExpectationMP)
         and isinstance(tape.measurements[0].obs, (Hamiltonian, Sum))
         and (
-            grouping_strategy in ("default", "qwc")
+            (
+                grouping_strategy in ("default", "qwc")
+                and all(qml.pauli.is_pauli_word(o) for o in tape.measurements[0].obs.terms()[1])
+            )
             or tape.measurements[0].obs.grouping_indices is not None
         )
     ):


Test patch:
diff --git a/tests/transforms/test_split_non_commuting.py b/tests/transforms/test_split_non_commuting.py
index 5113514f8..fccb53949 100644
--- a/tests/transforms/test_split_non_commuting.py
+++ b/tests/transforms/test_split_non_commuting.py
@@ -426,6 +426,40 @@ class TestUnits:
         expected = 0.52 if not qml.operation.active_new_opmath() else 1.12
         assert qml.math.allclose(fn([[0.1, 0.2], [0.3, 0.4, 0.5]]), expected)
 
+    @pytest.mark.usefixtures("new_opmath_only")
+    @pytest.mark.parametrize(
+        "H",
+        [
+            qml.sum(qml.X(0), qml.Hadamard(1) @ qml.Z(0), qml.Y(1)),
+            qml.Hamiltonian([1, 2, 3], [qml.X(0), qml.Hadamard(1) @ qml.Z(0), qml.Y(1)]),
+        ],
+    )
+    def test_single_hamiltonian_non_pauli_words(self, H):
+        """Tests that a single Hamiltonian with non-pauli words is split correctly"""
+
+        tape = qml.tape.QuantumScript([], [qml.expval(H)], shots=100)
+        tapes, _ = split_non_commuting(tape)
+        expected_tapes = [
+            qml.tape.QuantumScript([], [qml.expval(qml.X(0)), qml.expval(qml.Y(1))], shots=100),
+            qml.tape.QuantumScript([], [qml.expval(qml.Hadamard(1) @ qml.Z(0))], shots=100),
+        ]
+        for actual_tape, expected_tape in zip(tapes, expected_tapes):
+            qml.assert_equal(actual_tape, expected_tape)
+
+    @pytest.mark.usefixtures("legacy_opmath_only")
+    def test_single_hamiltonian_non_pauli_words_legacy(self):
+        """Tests that a single Hamiltonian with non-pauli words is split correctly"""
+
+        H = qml.Hamiltonian([1, 2, 3], [qml.X(0), qml.Hadamard(1) @ qml.Z(0), qml.Y(1)])
+        tape = qml.tape.QuantumScript([], [qml.expval(H)], shots=100)
+        tapes, _ = split_non_commuting(tape)
+        expected_tapes = [
+            qml.tape.QuantumScript([], [qml.expval(qml.X(0)), qml.expval(qml.Y(1))], shots=100),
+            qml.tape.QuantumScript([], [qml.expval(qml.Hadamard(1) @ qml.Z(0))], shots=100),
+        ]
+        for actual_tape, expected_tape in zip(tapes, expected_tapes):
+            qml.assert_equal(actual_tape, expected_tape)
+
     @pytest.mark.parametrize(
         "grouping_strategy, expected_tapes, processing_fn, mock_results",
         [