Skip to content

Commit

Permalink
Fix phase of pauli_list.insert(..., qubit=True) for length-1 `pauli…
Browse files Browse the repository at this point in the history
…_list` (#13624)

* switch order of if-clauses

`len(value) == 1` is the simplest case, and is also the case where the problematic clause `len(value) == size` fails.

This commit switches the order, so we check for `len(value) == 1` first.

This ensures that when the `len(value) == size` clause runs, we know that `size != 1`, avoiding the bug in #13623.

* add test

* Simplify `value.phase` broadcasting

Here, `phase` should be a 1D array. `np.vstack()` docs say explicitly output will be at least 2D, so we should not use that to create `phase`.

The intent of using `np.vstack()` was essentially to broadcast `phase` to properly add to `self.phase`. But, this happens automatically and correctly if we just add as-is. So this commit simplifies the code accordingly.

* Verify that `phase` is 1D in `from_symplectic()`

Verifying the input arg `phase`, since otherwise `from_symplectic()` will silently create PauliLists with malformed `phase` attributes (i.e. not 1D).

Zero-dimensional is OK (e.g. `phase` defaults to `0`) since that can broadcast per the shape of the `z` and `x` arrays.

* remove vestigial line

* lint

* release note

* Update releasenotes/notes/fix-paulilist-length1-phase-688d0e3a64ec9a9f.yaml

Co-authored-by: Jake Lishman <[email protected]>

---------

Co-authored-by: Jake Lishman <[email protected]>
  • Loading branch information
aeddins-ibm and jakelishman authored Jan 8, 2025
1 parent 682cf3b commit 408741c
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 8 deletions.
16 changes: 8 additions & 8 deletions qiskit/quantum_info/operators/symplectic/pauli_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -451,16 +451,14 @@ def insert(self, ind: int, value: PauliList, qubit: bool = False) -> PauliList:
f"Index {ind} is greater than number of qubits"
f" in the PauliList ({self.num_qubits})"
)
if len(value) == 1:
# Pad blocks to correct size
value_x = np.vstack(size * [value.x])
value_z = np.vstack(size * [value.z])
value_phase = np.vstack(size * [value.phase])
elif len(value) == size:
if len(value) == size:
# Blocks are already correct size
value_x = value.x
value_z = value.z
value_phase = value.phase
elif len(value) == 1:
# Pad blocks to correct size
value_x = np.vstack(size * [value.x])
value_z = np.vstack(size * [value.z])
else:
# Blocks are incorrect size
raise QiskitError(
Expand All @@ -471,7 +469,7 @@ def insert(self, ind: int, value: PauliList, qubit: bool = False) -> PauliList:
# Build new array by blocks
z = np.hstack([self.z[:, :ind], value_z, self.z[:, ind:]])
x = np.hstack([self.x[:, :ind], value_x, self.x[:, ind:]])
phase = self.phase + value_phase
phase = self.phase + value.phase

return PauliList.from_symplectic(z, x, phase)

Expand Down Expand Up @@ -1131,6 +1129,8 @@ def from_symplectic(
Returns:
PauliList: the constructed PauliList.
"""
if isinstance(phase, np.ndarray) and np.ndim(phase) > 1:
raise ValueError(f"phase should be at most 1D but has {np.ndim(phase)} dimensions.")
base_z, base_x, base_phase = cls._from_array(z, x, phase)
return cls(BasePauli(base_z, base_x, base_phase))

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
fixes:
- |
Fixed a bug that caused :meth:`.PauliList.insert` with ``qubit=True`` to produce a `phase`
attribute with the wrong shape when the original object was length 1.
Fixed `#13623 <https://github.com/Qiskit/qiskit/issues/13623>`__.
Original file line number Diff line number Diff line change
Expand Up @@ -1560,6 +1560,15 @@ def test_insert(self):
value1 = pauli.insert(1, insert)
self.assertEqual(value1, target1)

# Insert single column to length-1 PauliList:
with self.subTest(msg="length-1, single-column, single-val"):
pauli = PauliList(["X"])
insert = PauliList(["Y"])
target0 = PauliList(["YX"])
value0 = pauli.insert(1, insert, qubit=True)
self.assertEqual(value0, target0)
self.assertEqual(value0.phase.shape, (1,))

# Insert single column
pauli = PauliList(["X", "Y", "Z", "-iI"])
for i in ["I", "X", "Y", "Z", "iY"]:
Expand Down

0 comments on commit 408741c

Please sign in to comment.