Skip to content

Commit

Permalink
Fix setter so that SparsePauliOp.paulis.phase stays zero (#12884)
Browse files Browse the repository at this point in the history
* absorb `phase` into `coeffs` in `paulis()` setter

* do not mutate input array

* add test that paulis setter absorbs phase

* lint

* If input paulis have phase, mutate and warn

* add release note

* release-note formatting

* add test with `simplify()`

* remove phase-warning from paulis setter

* lint

* remove unused import

* update reno
  • Loading branch information
aeddins-ibm authored Aug 15, 2024
1 parent 48cca36 commit 8c74a49
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 0 deletions.
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):
"""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

0 comments on commit 8c74a49

Please sign in to comment.