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

Fix setter so that SparsePauliOp.paulis.phase stays zero #12884

Merged
merged 14 commits into from
Aug 15, 2024
2 changes: 2 additions & 0 deletions qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,8 @@ def paulis(self, value):
raise ValueError(
f"incorrect number of operators: expected {len(self.paulis)}, got {len(value)}"
)
self.coeffs *= (-1j) ** value.phase
value.phase = 0
self._pauli_list = value

@property
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
fixes:
- |
Fixed a bug when :attr:`.SparsePauliOp.paulis` is set to be a :class:`.PauliList` with nonzero
phase, where subsequent calls to several :class:`.SparsePauliOp` methods would produce
incorrect results. Now when :attr:`.SparsePauliOp.paulis` is set to a :class:`.PauliList` with
nonzero phase, the phase is absorbed into :attr:`.SparsePauliOp.coeffs`, and the phase of the
input :class:`.PauliList` is set to zero.
Original file line number Diff line number Diff line change
Expand Up @@ -1097,6 +1097,25 @@ def test_paulis_setter_rejects_bad_inputs(self):
with self.assertRaisesRegex(ValueError, "incorrect number of operators"):
op.paulis = PauliList([Pauli("XY"), Pauli("ZX"), Pauli("YZ")])

def test_paulis_setter_absorbs_phase(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you also add a test to check the simplify() behavior you described in your original issue?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added.

"""Test that the setter for `paulis` absorbs `paulis.phase` to `self.coeffs`."""
coeffs_init = np.array([1, 1j])
op = SparsePauliOp(["XY", "ZX"], coeffs=coeffs_init)
paulis_new = PauliList(["-1jXY", "1jZX"])
op.paulis = paulis_new
# Paulis attribute should have no phase:
self.assertEqual(op.paulis, PauliList(["XY", "ZX"]))
# Coeffs attribute should now include that phase:
self.assertTrue(np.allclose(op.coeffs, coeffs_init * np.array([-1j, 1j])))
# The phase of the input array is now zero:
self.assertTrue(np.allclose(paulis_new.phase, np.array([0, 0])))

def test_paulis_setter_absorbs_phase_2(self):
"""Test that `paulis` setter followed by `simplify()` handle phase OK."""
spo = SparsePauliOp(["X", "X"])
spo.paulis = ["X", "-X"]
self.assertEqual(spo.simplify(), SparsePauliOp(["I"], coeffs=[0.0 + 0.0j]))

def test_apply_layout_with_transpile(self):
"""Test the apply_layout method with a transpiler layout."""
psi = EfficientSU2(4, reps=4, entanglement="circular")
Expand Down
Loading