Repository: PennyLaneAI/pennylane
Pull request #5790 (2024-06-03 18:13:06)

Define BasisStateProjector.compute_sparse_matrix
Towards https://github.com/PennyLaneAI/pennylane/issues/5721.

AnuravModak: Hi, Let me know if i need to do any further changes on this, here i could see some conflicting files but the resolve conflicts option is disabled!! Let me know what else could be done from my side.
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5790?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.65%. Comparing base [(`46fa7ff`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/46fa7ff070937cfff3921b0d50dc91aaa9cc0a3a?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) to head [(`fe6e327`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/fe6e3275c42c2ffdfee0a658eb1b0a452d33f7e3?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    #5790      +/-   ##
==========================================
- Coverage   99.66%   99.65%   -0.01%     
==========================================
  Files         427      427              
  Lines       41081    40793     -288     
==========================================
- Hits        40942    40653     -289     
- Misses        139      140       +1     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5790?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 f67840f54..edd68908c 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -33,6 +33,9 @@
 
 <h3>Improvements 🛠</h3>
 
+* Added the `compute_sparse_matrix` method for `qml.ops.qubit.BasisStateProjector`.
+  [(#5790)](https://github.com/PennyLaneAI/pennylane/pull/5790)
+
 * `StateMP.process_state` defines rules in `cast_to_complex` for complex casting, avoiding a superfluous state vector copy in Lightning simulations
   [(#5995)](https://github.com/PennyLaneAI/pennylane/pull/5995)
 
@@ -221,9 +224,9 @@ Josh Izaac,
 Soran Jahangiri,
 Christina Lee,
 Austin Huang,
-Christina Lee,
 William Maxwell,
 Vincent Michaud-Rioux,
+Anurav Modak,
 Mudit Pandey,
 Erik Schultheis,
 nate stemen.
diff --git a/pennylane/ops/qubit/observables.py b/pennylane/ops/qubit/observables.py
index b20e96dd4..9fd4ee43e 100644
--- a/pennylane/ops/qubit/observables.py
+++ b/pennylane/ops/qubit/observables.py
@@ -584,6 +584,24 @@ class BasisStateProjector(Projector, Operation):
         """
         return []
 
+    @staticmethod
+    def compute_sparse_matrix(basis_state):  # pylint: disable=arguments-differ,unused-argument
+        """
+        Computes the sparse CSR matrix representation of the projector onto the basis state.
+
+        Args:
+            basis_state (Iterable): The basis state as an iterable of integers (0 or 1).
+
+        Returns:
+            scipy.sparse.csr_matrix: The sparse CSR matrix representation of the projector.
+        """
+
+        num_qubits = len(basis_state)
+        data = [1]
+        rows = [int("".join(str(bit) for bit in basis_state), 2)]
+        cols = rows
+        return csr_matrix((data, (rows, cols)), shape=(2**num_qubits, 2**num_qubits))
+
 
 class StateVectorProjector(Projector):
     r"""Observable corresponding to the state projector :math:`P=\ket{\phi}\bra{\phi}`, where


Test patch:
diff --git a/tests/ops/qubit/test_observables.py b/tests/ops/qubit/test_observables.py
index 50aa733fa..28a79d273 100644
--- a/tests/ops/qubit/test_observables.py
+++ b/tests/ops/qubit/test_observables.py
@@ -19,6 +19,7 @@ import pickle
 import numpy as np
 import pytest
 from gate_data import H, I, X, Y, Z
+from scipy.sparse import csr_matrix
 
 import pennylane as qml
 from pennylane.ops.qubit.observables import BasisStateProjector, StateVectorProjector
@@ -540,6 +541,75 @@ class TestProjector:
         qml.assert_equal(new_proj, proj)
         assert new_proj.id == proj.id  # Ensure they are identical
 
+    def test_single_qubit_basis_state_0(self):
+        """Tests the function with a single-qubit basis state |0>."""
+        basis_state = [0]
+        data = [1]
+        row_indices = [0]
+        col_indices = [0]
+        expected_matrix = csr_matrix((data, (row_indices, col_indices)), shape=(2, 2))
+
+        actual_matrix = BasisStateProjector.compute_sparse_matrix(basis_state)
+        actual_matrix = BasisStateProjector.compute_sparse_matrix(basis_state)
+
+        assert np.array_equal(expected_matrix.toarray(), actual_matrix.toarray())
+
+    def test_single_qubit_basis_state_1(self):
+        """Tests the function with a single-qubit basis state |1>."""
+        basis_state = [1]
+        data = [1]
+        row_indices = [1]
+        col_indices = [1]
+        expected_matrix = csr_matrix((data, (row_indices, col_indices)), shape=(2, 2))
+        actual_matrix = BasisStateProjector.compute_sparse_matrix(basis_state)
+        assert np.array_equal(expected_matrix.toarray(), actual_matrix.toarray())
+
+    def test_two_qubit_basis_state_10(self):
+        """Tests the function with a two-qubits basis state |10>."""
+        basis_state = [1, 0]
+        data = [1]
+        row_indices = [2]
+        col_indices = [2]
+        expected_matrix = csr_matrix((data, (row_indices, col_indices)), shape=(4, 4))
+        actual_matrix = BasisStateProjector.compute_sparse_matrix(basis_state)
+        assert np.array_equal(expected_matrix.toarray(), actual_matrix.toarray())
+
+    def test_two_qubit_basis_state_01(self):
+        """Tests the function with a two-qubits basis state |01>."""
+        basis_state = [0, 1]
+        data = [1]
+        row_indices = [1]
+        col_indices = [1]
+        expected_matrix = csr_matrix((data, (row_indices, col_indices)), shape=(4, 4))
+        actual_matrix = BasisStateProjector.compute_sparse_matrix(basis_state)
+        assert np.array_equal(expected_matrix.toarray(), actual_matrix.toarray())
+
+    def test_two_qubit_basis_state_11(self):
+        """Tests the function with a two-qubits basis state |11>."""
+        basis_state = [1, 1]
+        data = [1]
+        row_indices = [3]
+        col_indices = [3]
+        expected_matrix = csr_matrix((data, (row_indices, col_indices)), shape=(4, 4))
+        actual_matrix = BasisStateProjector.compute_sparse_matrix(basis_state)
+        assert np.array_equal(expected_matrix.toarray(), actual_matrix.toarray())
+
+    def test_three_qubit_basis_state_101(self):
+        """Tests the function with a three-qubits basis state |101>."""
+        basis_state = [1, 0, 1]
+        data = [1]
+        row_indices = [5]
+        col_indices = [5]
+        expected_matrix = csr_matrix((data, (row_indices, col_indices)), shape=(8, 8))
+        actual_matrix = BasisStateProjector.compute_sparse_matrix(basis_state)
+        assert np.array_equal(expected_matrix.toarray(), actual_matrix.toarray())
+
+    def test_invalid_basis_state(self):
+        """Tests the function with an invalid state."""
+        basis_state = [0, 2]  # Invalid basis state
+        with pytest.raises(ValueError):
+            BasisStateProjector.compute_sparse_matrix(basis_state)
+
     @pytest.mark.jax
     def test_jit_measurement(self):
         """Test that the measurement of a projector can be jitted."""