Skip to content

Commit

Permalink
Fix phase management in StabilizerState.expectation_value (#7460) (#…
Browse files Browse the repository at this point in the history
…7675)

* fix a bug in expectation value. Handle Pauli phases.

* extend the test to include Pauli expectation vals with phases.

* add release notes

* add more 1-qubit and 2-qubit tests for exp_val=-1,i,-i

* more ddt

* Fix typo in error message

* Add comment on phase

Co-authored-by: Ikko Hamamura <ikkoham@users.noreply.github.com>
Co-authored-by: Luciano Bello <bel@zurich.ibm.com>
Co-authored-by: Jake Lishman <jake@binhbar.com>
Co-authored-by: Jake Lishman <jake.lishman@ibm.com>
(cherry picked from commit 6da136c)

Co-authored-by: Shelly Garion <46566946+ShellyGarion@users.noreply.github.com>
  • Loading branch information
mergify[bot] and ShellyGarion authored Feb 17, 2022
1 parent 6069e5c commit 4c9df00
Show file tree
Hide file tree
Showing 3 changed files with 296 additions and 234 deletions.
32 changes: 16 additions & 16 deletions qiskit/quantum_info/states/stabilizerstate.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,15 +196,21 @@ def evolve(self, other, qargs=None):
return ret

def expectation_value(self, oper, qargs=None):
"""Compute the expectation value of an operator.
"""Compute the expectation value of a Pauli operator.
Args:
oper (BaseOperator): an operator to evaluate expval.
oper (Pauli): a Pauli operator to evaluate expval.
qargs (None or list): subsystems to apply the operator on.
Returns:
complex: the expectation value (only 0 or 1 or -1).
complex: the expectation value (only 0 or 1 or -1 or i or -i).
Raises:
QiskitError: if oper is not a Pauli operator.
"""
if not isinstance(oper, Pauli):
raise QiskitError("Operator for expectation value is not a Pauli operator.")

num_qubits = self.clifford.num_qubits
if qargs is None:
qubits = range(num_qubits)
Expand All @@ -214,19 +220,12 @@ def expectation_value(self, oper, qargs=None):
# Construct Pauli on num_qubits
pauli = Pauli(num_qubits * "I")
phase = 0
pauli_phase = (-1j) ** oper.phase if oper.phase else 1

for pos, qubit in enumerate(qubits):
pauli_pos = (oper.to_label())[len(oper) - 1 - pos]
if pauli_pos == "X":
pauli.x[qubit] = 1
elif pauli_pos == "Y":
pauli.x[qubit] = 1
pauli.z[qubit] = 1
phase += 1
elif pauli_pos == "Z":
pauli.z[qubit] = 1
else:
pass
pauli.x[qubit] = oper.x[pos]
pauli.z[qubit] = oper.z[pos]
phase += pauli.x[qubit] & pauli.z[qubit]

# Check if there is a stabilizer that anti-commutes with an odd number of qubits
# If so the expectation value is 0
Expand Down Expand Up @@ -258,10 +257,11 @@ def expectation_value(self, oper, qargs=None):
phase += 2 * np.count_nonzero(pauli_z & stab.X[p])
pauli_z = pauli_z ^ stab.Z[p]

# For valid stabilizers, `phase` can only be 0 (= 1) or 2 (= -1) at this point.
if phase % 4 != 0:
return -1
return -pauli_phase

return 1
return pauli_phase

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,5 @@
---
fixes:
- |
Fix a bug in :class:`~qiskit.quantum_info.StabilizerState` expectation value calculation,
that occured when the Pauli operator has a non-trivial phase.
Loading

0 comments on commit 4c9df00

Please sign in to comment.