Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an equiv method to the StabilizerState class #9543

Merged
merged 9 commits into from
Feb 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion qiskit/quantum_info/states/stabilizerstate.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
ShellyGarion marked this conversation as resolved.
Show resolved Hide resolved

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.

Expand Down
Original file line number Diff line number Diff line change
@@ -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]`.
44 changes: 44 additions & 0 deletions test/python/quantum_info/states/test_stabilizerstate.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()