Repository: PennyLaneAI/pennylane
Pull request #5875 (2024-06-18 20:24:01)

Fix wrong PaulitRot decomposition with identity as pauli word
**Context:**

Currently the decomposition of a Pauli Rotation with an identity as Pauli word returns an empty list. I.e,

```python
qml.PauliRot(theta, Pauli_word="I").decomposition() == []
```

**Description of the Change:**

Implement the correct decomposition for this case using `qml.GlobalPhase`

**Benefits:**

The `TrotterProduct` for Hamiltonians with an identity using `default.qubit` and `default.lightning` now return the same correct result.

```python
import pennylane as qml
import numpy as np

symbols = ["H", "H"]
coordinates = np.array([[0.0, 0.0, -0.6614], [0.0, 0.0, 0.6614]])
H, num_qubits = qml.qchem.molecular_hamiltonian(symbols, coordinates, basis="sto-3g")

results = []
for dev_name in ["default.qubit", "lightning.qubit"]:
    dev = qml.device(dev_name, wires=num_qubits)
    @qml.qnode(dev)
    def circuit():
        qml.TrotterProduct(H, 1.0)
        return qml.state()
    results.append(circuit())
qml.math.allclose(*results)

>> True
```

**Related GitHub Issues:**
[#5859]

[sc-66115]


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/5875?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 [(`eb39402`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/eb39402bf52c6f45338465820c7acd557af0d7b7?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) to head [(`a6d7b72`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/a6d7b729e3dabc652bdc0469dc07dfb554e8fc5d?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    #5875      +/-   ##
==========================================
- Coverage   99.68%   99.67%   -0.01%     
==========================================
  Files         421      421              
  Lines       40499    40203     -296     
==========================================
- Hits        40370    40073     -297     
- Misses        129      130       +1     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5875?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-dev.md b/doc/releases/changelog-dev.md
index d93fa1685..2ca6093ac 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -562,6 +562,9 @@
 * Fix bug where `CompositeOp.overlapping_ops` sometimes puts overlapping ops in different groups, leading to incorrect results returned by `LinearCombination.eigvals()`
   [(#5847)](https://github.com/PennyLaneAI/pennylane/pull/5847)
 
+* Implement the correct decomposition for a `qml.PauliRot` with an identity as `pauli_word`, i.e. returns a `qml.GlobalPhase` with half the angle.
+  [(#5875)](https://github.com/PennyLaneAI/pennylane/pull/5875)
+
 * `qml.pauli_decompose` now works in a jit-ted context, such as `jax.jit` and `catalyst.qjit`.
   [(#5878)](https://github.com/PennyLaneAI/pennylane/pull/5878)
 
diff --git a/pennylane/ops/qubit/parametric_ops_multi_qubit.py b/pennylane/ops/qubit/parametric_ops_multi_qubit.py
index 2f6a36ab4..e29b1f029 100644
--- a/pennylane/ops/qubit/parametric_ops_multi_qubit.py
+++ b/pennylane/ops/qubit/parametric_ops_multi_qubit.py
@@ -489,7 +489,7 @@ class PauliRot(Operation):
 
         # Check for identity and do nothing
         if set(pauli_word) == {"I"}:
-            return []
+            return [qml.GlobalPhase(phi=theta / 2)]
 
         active_wires, active_gates = zip(
             *[(wire, gate) for wire, gate in zip(wires, pauli_word) if gate != "I"]


Test patch:
diff --git a/tests/ops/qubit/test_parametric_ops.py b/tests/ops/qubit/test_parametric_ops.py
index 566942be8..6780cfe21 100644
--- a/tests/ops/qubit/test_parametric_ops.py
+++ b/tests/ops/qubit/test_parametric_ops.py
@@ -2768,10 +2768,13 @@ class TestPauliRot:
         op = qml.PauliRot(theta, "II", wires=[0, 1])
         decomp_ops = op.decomposition()
 
-        assert np.allclose(op.eigvals(), np.exp(-1j * theta / 2) * np.ones(4))
-        assert np.allclose(op.matrix() / op.matrix()[0, 0], np.eye(4))
+        assert len(decomp_ops) == 1
+
+        decomp_op = decomp_ops[0]
+
+        qml.assert_equal(decomp_op, qml.GlobalPhase(theta / 2))
 
-        assert len(decomp_ops) == 0
+        assert qml.math.allclose(op.matrix(), decomp_op.matrix() * np.eye(4))
 
     def test_PauliRot_all_Identity_broadcasted(self):
         """Test handling of the broadcasted all-identity Pauli."""
@@ -2780,13 +2783,16 @@ class TestPauliRot:
         op = qml.PauliRot(theta, "II", wires=[0, 1])
         decomp_ops = op.decomposition()
 
-        phases = np.exp(-1j * theta / 2)
-        assert np.allclose(op.eigvals(), np.outer(phases, np.ones(4)))
-        mat = op.matrix()
-        for phase, sub_mat in zip(phases, mat):
-            assert np.allclose(sub_mat, phase * np.eye(4))
+        assert len(decomp_ops) == 1
+
+        decomp_op = decomp_ops[0]
+        qml.assert_equal(decomp_op, qml.GlobalPhase(theta / 2))
 
-        assert len(decomp_ops) == 0
+        op_matrices = op.matrix()
+        decomp_op_matrices = decomp_op.matrix().T
+        assert len(op_matrices) == len(decomp_op_matrices)
+        for op_matrix, decomp_phase in zip(op_matrices, decomp_op_matrices):
+            assert qml.math.allclose(op_matrix, decomp_phase * np.eye(4))
 
     @pytest.mark.parametrize("theta", [0.4, np.array([np.pi / 3, 0.1, -0.9])])
     def test_PauliRot_decomposition_ZZ(self, theta):