diff --git a/crates/quantum_info/src/pauli_lindblad_map/qubit_sparse_pauli.rs b/crates/quantum_info/src/pauli_lindblad_map/qubit_sparse_pauli.rs index 26f4194378f6..08f75205496a 100644 --- a/crates/quantum_info/src/pauli_lindblad_map/qubit_sparse_pauli.rs +++ b/crates/quantum_info/src/pauli_lindblad_map/qubit_sparse_pauli.rs @@ -526,6 +526,26 @@ impl QubitSparsePauliList { } } } + + // Check if the elements of `self` commute with the elements of `other`. + pub fn commutes(&self, other: &QubitSparsePauliList) -> Result, ArithmeticError> { + if self.num_qubits != other.num_qubits { + return Err(ArithmeticError::MismatchedQubits { + left: self.num_qubits, + right: other.num_qubits, + }); + } + + Ok(Array2::from_shape_fn( + (self.num_terms(), other.num_terms()), + |(i, j)| { + self.term(i) + .to_term() + .commutes(&other.term(j).to_term()) + .unwrap() + }, + )) + } } type RawParts = (Vec, Vec, Vec); @@ -836,7 +856,7 @@ impl QubitSparsePauli { } } - // Check if `self` commutes with `other` + // Check if `self` commutes with `other`. pub fn commutes(&self, other: &QubitSparsePauli) -> Result { if self.num_qubits != other.num_qubits { return Err(ArithmeticError::MismatchedQubits { @@ -1465,7 +1485,7 @@ impl PyQubitSparsePauli { self.compose(other) } - /// Check if `self`` commutes with another qubit sparse pauli. + /// Check if `self`` commutes with another qubit sparse Pauli. /// /// Args: /// other (QubitSparsePauli): the qubit sparse Pauli to check for commutation with. @@ -2104,6 +2124,17 @@ impl PyQubitSparsePauliList { Ok(out.into_pyarray(py).unbind()) } + /// Check if the elements of `self`` commute with another qubit sparse Pauli list. + /// + /// Args: + /// other (QubitSparsePauliList): the qubit sparse Pauli list to check for commutation with. + #[pyo3(signature = (other))] + fn commutes(&self, py: Python, other: &PyQubitSparsePauliList) -> PyResult>> { + let slf_inner = self.inner.read().map_err(|_| InnerReadError)?; + let other_inner = other.inner.read().map_err(|_| InnerReadError)?; + Ok(slf_inner.commutes(&other_inner)?.into_pyarray(py).unbind()) + } + /// Return a :class:`~.quantum_info.PauliList` representing the same phaseless list of Paulis. fn to_pauli_list<'py>(&self, py: Python<'py>) -> PyResult> { let inner = self.inner.read().map_err(|_| InnerReadError)?; diff --git a/releasenotes/notes/qubit-sparse-pauli-list-commutes-46bf339bc87bd33c.yaml b/releasenotes/notes/qubit-sparse-pauli-list-commutes-46bf339bc87bd33c.yaml new file mode 100644 index 000000000000..d7e8b395bc14 --- /dev/null +++ b/releasenotes/notes/qubit-sparse-pauli-list-commutes-46bf339bc87bd33c.yaml @@ -0,0 +1,5 @@ +--- +features_quantum_info: + - | + Added the method :meth:`.QubitSparsePauliList.commutes`. This method returns the commutation + relations of this :class:`.QubitSparsePauliList` with another as an array of bools. \ No newline at end of file diff --git a/test/python/quantum_info/test_qubit_sparse_pauli.py b/test/python/quantum_info/test_qubit_sparse_pauli.py index f7cc3abd01ab..744ee5a7d293 100644 --- a/test/python/quantum_info/test_qubit_sparse_pauli.py +++ b/test/python/quantum_info/test_qubit_sparse_pauli.py @@ -1227,6 +1227,27 @@ def test_to_dense_array(self): expected = np.array([[0, 2, 0], [0, 0, 3], [1, 0, 0]], dtype=np.uint8) np.testing.assert_array_equal(pauli_list.to_dense_array(), expected, strict=True) + def test_commutes(self): + p0 = QubitSparsePauliList.from_list(["XIY", "IXI", "IZI"]) + p1 = QubitSparsePauliList.from_list(["IZI", "ZII"]) + expected = np.array([[True, False], [False, True], [True, True]], dtype=np.bool_) + np.testing.assert_array_equal(p0.commutes(p1), expected, strict=True) + np.testing.assert_array_equal(p1.commutes(p0), expected.T, strict=True) + + p0 = QubitSparsePauliList.from_sparse_list([], num_qubits=3) + p1 = QubitSparsePauliList.from_list(["ZZZ"]) + expected = np.empty((0, 1), dtype=np.bool_) + np.testing.assert_array_equal(p0.commutes(p1), expected, strict=True) + np.testing.assert_array_equal(p1.commutes(p0), expected.T, strict=True) + + def test_commutes_errors(self): + p0 = QubitSparsePauliList.from_label("XZYI") + p1 = QubitSparsePauliList.from_label("ZIY") + with self.assertRaisesRegex(ValueError, "mismatched numbers of qubits: 4, 3"): + p0.commutes(p1) + with self.assertRaisesRegex(ValueError, "mismatched numbers of qubits: 3, 4"): + p1.commutes(p0) + def canonicalize_term(pauli, indices): # canonicalize a sparse list term by sorting by indices (which is unique as