Repository: PennyLaneAI/pennylane
Pull request #6940 (2025-02-07 21:14:58)

Fixing dismatch between dense `matrix()` and sparse repr `sparse_matrix()` of GlobalPhase
**Context:**
`qml.GlobalPhase` does not set its `hyperparameters` during initialization. This caused certain bugs, e.g.
```pycon
>>> np.allclose(qml.GlobalPhase(0.123).matrix(), qml.GlobalPhase(0.123).sparse_matrix().toarray())
False
>>> qml.GlobalPhase(0.123).matrix().shape
(1, 1)
>>> qml.GlobalPhase(0.123).sparse_matrix().shape
(2, 2)
>>> qml.GlobalPhase(0.123).matrix()
array([[0.99244503-0.12269009j]])
>>> qml.GlobalPhase(0.123).sparse_matrix().toarray()
array([[0.99244503-0.12269009j, 0.        +0.j        ],
       [0.        +0.j        , 0.99244503-0.12269009j]])
```
where `GlobalPhase.sparse_matrix()` did the calculation wrongly because it never received correct `n_wires` from `hyperparameters`, which is required by the base class `Operator`

**Description of the Change:**
Mimic a similar wrapper `sparse_matrix()` to overload the method inherited from parents. Meanwhile, the expected shape in `pennylane/math/matrix_manipulation.py`

**Benefits:**
Consistent behavior of sparse representation with dense.

**Possible Drawbacks:**
The essential reason is that the `hyperparameters` of this class were never successfully set up. However, our first try to fix by directly implementing them failed completely due to complications invovled with vast, different modules. Therefore, this current fix is a roundabout.

**Related GitHub Issues:**
[sc-84333]

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/6940?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
Attention: Patch coverage is `87.50000%` with `1 line` in your changes missing coverage. Please review.
> Project coverage is 99.59%. Comparing base [(`a7514b7`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/a7514b7261a9c9f42fb6c9bec4f5a98214f5b24c?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) to head [(`003ec3f`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/003ec3fd47228213fcead9bd012f385a447c084a?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).

| [Files with missing lines](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/6940?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) | Patch % | Lines |
|---|---|---|
| [pennylane/math/matrix\_manipulation.py](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/6940?src=pr&el=tree&filepath=pennylane%2Fmath%2Fmatrix_manipulation.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL21hdGgvbWF0cml4X21hbmlwdWxhdGlvbi5weQ==) | 80.00% | [1 Missing :warning: ](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/6940?src=pr&el=tree&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    #6940      +/-   ##
==========================================
- Coverage   99.59%   99.59%   -0.01%     
==========================================
  Files         480      480              
  Lines       45510    45516       +6     
==========================================
+ Hits        45325    45330       +5     
- Misses        185      186       +1     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/6940?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 28ea7ce14..585aab458 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -286,6 +286,9 @@
 
 <h3>Bug fixes 🐛</h3>
 
+* `qml.GlobalPhase.sparse_matrix` now correctly returns a sparse matrix of the same shape as `matrix`.
+  [(#6940)](https://github.com/PennyLaneAI/pennylane/pull/6940)
+
 * `qml.expval` no longer silently casts to a real number when observable coefficients are imaginary.
   [(#6939)](https://github.com/PennyLaneAI/pennylane/pull/6939)
 
diff --git a/pennylane/math/matrix_manipulation.py b/pennylane/math/matrix_manipulation.py
index 0e4149c21..c7108d0ed 100644
--- a/pennylane/math/matrix_manipulation.py
+++ b/pennylane/math/matrix_manipulation.py
@@ -113,9 +113,8 @@ def expand_matrix(mat, wires, wire_order=None, sparse_format="csr"):
     if (wire_order is None) or (wire_order == wires):
         return mat
 
-    if not wires and qml.math.shape(mat) == (qudit_dim, qudit_dim):
-        # global phase
-        wires = wire_order[0:1]
+    if not wires and qml.math.shape(mat) == (1, 1):
+        return complex(mat[0, 0])
 
     wires = list(wires)
     wire_order = list(wire_order)
diff --git a/pennylane/ops/identity.py b/pennylane/ops/identity.py
index 5667e849a..c36dd8787 100644
--- a/pennylane/ops/identity.py
+++ b/pennylane/ops/identity.py
@@ -16,6 +16,7 @@ This module contains the Identity operation that is common to both
 cv and qubit computing paradigms in PennyLane.
 """
 from functools import lru_cache
+from typing import Sequence
 
 from scipy import sparse
 
@@ -451,10 +452,14 @@ class GlobalPhase(Operation):
     def eigvals(self):
         return self.compute_eigvals(self.data[0], n_wires=len(self.wires))
 
-    def matrix(self, wire_order=None):
-        n_wires = len(wire_order) if wire_order else len(self.wires)
+    def matrix(self, wire_order: Sequence = None):
+        n_wires = len(self.wires) if wire_order is None else len(wire_order)
         return self.compute_matrix(self.data[0], n_wires=n_wires)
 
+    def sparse_matrix(self, wire_order: Sequence = None):
+        n_wires = len(self.wires) if wire_order is None else len(wire_order)
+        return self.compute_sparse_matrix(self.data[0], n_wires=n_wires)
+
     def adjoint(self):
         return GlobalPhase(-1 * self.data[0], self.wires)
 


Test patch:
diff --git a/tests/ops/qubit/test_parametric_ops.py b/tests/ops/qubit/test_parametric_ops.py
index 413eea086..b06feb7c7 100644
--- a/tests/ops/qubit/test_parametric_ops.py
+++ b/tests/ops/qubit/test_parametric_ops.py
@@ -129,6 +129,7 @@ NON_PARAMETRIZED_OPERATIONS = [
 ]
 
 ALL_OPERATIONS = NON_PARAMETRIZED_OPERATIONS + PARAMETRIZED_OPERATIONS
+SPARSE_OPERATIONS = [op for op in ALL_OPERATIONS if op.has_sparse_matrix]
 
 
 def dot_broadcasted(a, b):
@@ -139,6 +140,15 @@ def multi_dot_broadcasted(matrices):
     return reduce(dot_broadcasted, matrices)
 
 
+class TestSparseOperators:
+    @pytest.mark.parametrize("op", SPARSE_OPERATIONS)
+    def test_validity(self, op):
+        """Test that the operations are valid."""
+        assert qml.math.allclose(
+            op.sparse_matrix().toarray(), qml.math.asarray(op.matrix(), like="numpy")
+        )
+
+
 class TestOperations:
     @pytest.mark.parametrize("op", ALL_OPERATIONS + BROADCASTED_OPERATIONS)
     def test_parametrized_op_copy(self, op, tol):