From d64133a36246cc7ed5aa20c629f15cfd786bd5e0 Mon Sep 17 00:00:00 2001 From: Shelly Garion <46566946+ShellyGarion@users.noreply.github.com> Date: Wed, 8 Feb 2023 20:26:42 +0200 Subject: [PATCH] Add an equiv method to the StabilizerState class (#9543) * add equiv method to StabilizerState class * add a test for equiv method * add release notes * add explanantion to the tests * update comment in tests * updates following review * update exp_val calculation * updates following review --------- Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- qiskit/quantum_info/states/stabilizerstate.py | 43 +++++++++++++++++- ...quiv-stabilizerstate-6ef8790c765690c1.yaml | 8 ++++ .../states/test_stabilizerstate.py | 44 +++++++++++++++++++ 3 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/add-equiv-stabilizerstate-6ef8790c765690c1.yaml diff --git a/qiskit/quantum_info/states/stabilizerstate.py b/qiskit/quantum_info/states/stabilizerstate.py index 0fc62d6b3565..18aa1d541ae1 100644 --- a/qiskit/quantum_info/states/stabilizerstate.py +++ b/qiskit/quantum_info/states/stabilizerstate.py @@ -18,7 +18,7 @@ from qiskit.exceptions import QiskitError from qiskit.quantum_info.operators.op_shape import OpShape -from qiskit.quantum_info.operators.symplectic import Clifford, Pauli +from qiskit.quantum_info.operators.symplectic import Clifford, Pauli, PauliList from qiskit.quantum_info.operators.symplectic.clifford_circuits import _append_x from qiskit.quantum_info.states.quantum_state import QuantumState @@ -266,6 +266,47 @@ def expectation_value(self, oper, qargs=None): return pauli_phase + def equiv(self, other): + """Return True if the two generating sets generate the same stabilizer group. + + Args: + other (StabilizerState): another StabilizerState. + + Returns: + bool: True if other has a generating set that generates the same StabilizerState. + """ + if not isinstance(other, StabilizerState): + try: + other = StabilizerState(other) + except QiskitError: + return False + + num_qubits = self.num_qubits + if other.num_qubits != num_qubits: + return False + + pauli_orig = PauliList.from_symplectic( + self._data.stab_z, self._data.stab_x, 2 * self._data.stab_phase + ) + pauli_other = PauliList.from_symplectic( + other._data.stab_z, other._data.stab_x, 2 * other._data.stab_phase + ) + + # Check that each stabilizer from the original set commutes with each stabilizer + # from the other set + if not np.all([pauli.commutes(pauli_other) for pauli in pauli_orig]): + return False + + # Compute the expected value of each stabilizer from the original set on the stabilizer state + # determined by the other set. The two stabilizer states coincide if and only if the + # expected value is +1 for each stabilizer + for i in range(num_qubits): + exp_val = self.expectation_value(pauli_other[i]) + if exp_val != 1: + return False + + return True + def probabilities(self, qargs=None, decimals=None): """Return the subsystem measurement probability vector. diff --git a/releasenotes/notes/add-equiv-stabilizerstate-6ef8790c765690c1.yaml b/releasenotes/notes/add-equiv-stabilizerstate-6ef8790c765690c1.yaml new file mode 100644 index 000000000000..93f5bdce235d --- /dev/null +++ b/releasenotes/notes/add-equiv-stabilizerstate-6ef8790c765690c1.yaml @@ -0,0 +1,8 @@ +--- +features: + - | + Added the method :class:`.StabilizerState.equiv`, + that checks if the generating sets of two stabilizer states generate the same stabilizer group. + For example, the stabilizer group of the two-qubit Bell state contains the four elements + :math:`\{II, XX, -YY, ZZ\}` and hence can be generated by either :math:`[XX, ZZ]`, + :math:`[XX, -YY]` or :math:`[-YY, ZZ]`. diff --git a/test/python/quantum_info/states/test_stabilizerstate.py b/test/python/quantum_info/states/test_stabilizerstate.py index 80ab7f2c5ffb..c6a9e6bb7462 100644 --- a/test/python/quantum_info/states/test_stabilizerstate.py +++ b/test/python/quantum_info/states/test_stabilizerstate.py @@ -936,6 +936,50 @@ def test_expval_random_subsystem(self, num_qubits): target = Statevector(qc).expectation_value(op, qargs) self.assertAlmostEqual(exp_val, target) + def test_stabilizer_bell_equiv(self): + """Test that two circuits produce the same stabilizer group.""" + + qc1 = QuantumCircuit(2) + qc1.h(0) + qc1.x(1) + qc1.cx(0, 1) + + qc2 = QuantumCircuit(2) + qc2.h(0) + qc2.cx(0, 1) + qc2.sdg(0) + qc2.sdg(1) + qc2.h(0) + qc2.h(1) + + qc3 = QuantumCircuit(2) + qc3.h(0) + qc3.cx(0, 1) + + qc4 = QuantumCircuit(2) + qc4.h(0) + qc4.cx(0, 1) + qc4.s(0) + qc4.sdg(1) + qc4.h(0) + qc4.h(1) + + cliff1 = StabilizerState(qc1) # ['+XX', '-ZZ'] + cliff2 = StabilizerState(qc2) # ['+YY', '+XX'] + cliff3 = StabilizerState(qc3) # ['+XX', '+ZZ'] + cliff4 = StabilizerState(qc4) # ['-YY', '+XX'] + + # [XX, -ZZ] and [XX, YY] both generate the stabilizer group {II, XX, YY, -ZZ} + self.assertTrue(cliff1.equiv(cliff2)) + self.assertEqual(cliff1.probabilities_dict(), cliff2.probabilities_dict()) + + # [XX, ZZ] and [XX, -YY] both generate the stabilizer group {II, XX, -YY, ZZ} + self.assertTrue(cliff3.equiv(cliff4)) + self.assertEqual(cliff3.probabilities_dict(), cliff4.probabilities_dict()) + + self.assertFalse(cliff1.equiv(cliff3)) + self.assertFalse(cliff2.equiv(cliff4)) + if __name__ == "__main__": unittest.main()