From ac099e58729c2f3f57f0d27b5999771dc6fcd0b6 Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Fri, 22 Sep 2023 18:31:41 +0100 Subject: [PATCH] Fix casting rules changing in Numpy 2 This fixes several places in `quantum_info` that will be affected by the casting-rule changes coming in Numpy 2.0[^1]. There are several other places where the casting rules will change the output types of certain arrays due to the removal of Python-scalar value-dependent casting, but those do not appear to have an observable effect on our behaviour, so I have left them. It is also becoming invalid to set a scalar entry in a Numpy array with an array-like with a non-zero number of dimensions, even if the sequence is of length 1. [^1]: https://numpy.org/neps/nep-0050-scalar-promotion.html --- .../operators/symplectic/base_pauli.py | 2 +- .../operators/symplectic/pauli_list.py | 23 ++++++++++++------- .../operators/symplectic/pauli_table.py | 11 +++++---- .../operators/symplectic/sparse_pauli_op.py | 5 ++-- 4 files changed, 25 insertions(+), 16 deletions(-) diff --git a/qiskit/quantum_info/operators/symplectic/base_pauli.py b/qiskit/quantum_info/operators/symplectic/base_pauli.py index f4267a70e1fd..28bb647d0364 100644 --- a/qiskit/quantum_info/operators/symplectic/base_pauli.py +++ b/qiskit/quantum_info/operators/symplectic/base_pauli.py @@ -438,7 +438,7 @@ def _to_matrix(z, x, phase=0, group_phase=False, sparse=False): phase %= 4 dim = 2**num_qubits - twos_array = 1 << np.arange(num_qubits) + twos_array = 1 << np.arange(num_qubits, dtype=np.uint) x_indices = np.asarray(x).dot(twos_array) z_indices = np.asarray(z).dot(twos_array) diff --git a/qiskit/quantum_info/operators/symplectic/pauli_list.py b/qiskit/quantum_info/operators/symplectic/pauli_list.py index 1af3e0d0d7ec..bc21af5c3770 100644 --- a/qiskit/quantum_info/operators/symplectic/pauli_list.py +++ b/qiskit/quantum_info/operators/symplectic/pauli_list.py @@ -338,22 +338,29 @@ def __setitem__(self, index, value): """Update PauliList.""" if isinstance(index, tuple): if len(index) == 1: - index = index[0] + row, qubit = index[0], None elif len(index) > 2: raise IndexError(f"Invalid PauliList index {index}") + else: + row, qubit = index + else: + row, qubit = index, None # Modify specified rows of the PauliList if not isinstance(value, PauliList): value = PauliList(value) - self._z[index] = value._z - self._x[index] = value._x - if not isinstance(index, tuple): - # Row-only indexing - self._phase[index] = value._phase + # It's not valid to set a single item with a sequence, even if the sequence is length 1. + phase = value._phase.item() if isinstance(row, (int, np.integer)) else value._phase + + if qubit is None: + self._z[row] = value._z + self._x[row] = value._x + self._phase[row] = phase else: - # Row and Qubit indexing - self._phase[index[0]] += value._phase + self._z[row, qubit] = value._z + self._x[row, qubit] = value._x + self._phase[row] += phase self._phase %= 4 def delete(self, ind: int | list, qubit: bool = False) -> PauliList: diff --git a/qiskit/quantum_info/operators/symplectic/pauli_table.py b/qiskit/quantum_info/operators/symplectic/pauli_table.py index 6bd3eda9cb95..3e99d11d6a33 100644 --- a/qiskit/quantum_info/operators/symplectic/pauli_table.py +++ b/qiskit/quantum_info/operators/symplectic/pauli_table.py @@ -1060,18 +1060,19 @@ def count1(i): z = symp[num_qubits : 2 * num_qubits] dim = 2**num_qubits - twos_array = 1 << np.arange(num_qubits) + twos_array = 1 << np.arange(num_qubits, dtype=np.uint) x_indices = np.array(x).dot(twos_array) z_indices = np.array(z).dot(twos_array) indptr = np.arange(dim + 1, dtype=np.uint) indices = indptr ^ x_indices - data = (-1) ** np.mod(count1(z_indices & indptr), 2) + parity = np.mod(count1(z_indices & indptr), 2) if real_valued: - dtype = float + dtype = np.float64 + data = np.where(parity, -1.0, 1.0) else: - dtype = complex - data = (-1j) ** np.sum(x & z) * data + dtype = np.complex64 + data = (-1j) ** np.sum(x & z) * np.where(parity, (-1 + 0j), (1 + 0j)) if sparse: # Return sparse matrix diff --git a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py index f7031276e6b5..b6f79af94720 100644 --- a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +++ b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py @@ -467,8 +467,9 @@ def to_complex(coeff): paulis_z = self.paulis.z[non_zero] nz_coeffs = self.coeffs[non_zero] - # Pack bool vectors into np.uint8 vectors by np.packbits - array = np.packbits(paulis_x, axis=1) * 256 + np.packbits(paulis_z, axis=1) + array = np.packbits(paulis_x, axis=1).astype(np.uint16) * 256 + np.packbits( + paulis_z, axis=1 + ) indexes, inverses = unordered_unique(array) if np.all(non_zero) and indexes.shape[0] == array.shape[0]: