From 6d5a230449766b3b8eb7f5509518ee2c4af4db5d Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 18:30:32 +0000 Subject: [PATCH] Fix process-based non-determinism in `SparsePauliOp.to_matrix` (#13439) (#13440) * Fix process-based non-determinism in `SparsePauliOp.to_matrix` The simplification step of the summed Pauli terms in `SparsePauliOp.to_matrix` had introduced a non-determinism via hash-map iteration. In practice, the base hash seed was set per initialisation of the extension module, then stayed the same. This is hard to detect from within tests, but caused unexpected numerical behaviour on different invocations of the same script. * Use ahash::RandomState --------- Co-authored-by: Kevin Hartman (cherry picked from commit 74b32c9d2fd7fa0d30f3d05215367bf00eff6387) Co-authored-by: Jake Lishman --- crates/accelerate/src/sparse_pauli_op.rs | 8 +++++++- .../notes/spo-to-matrix-determinism-554389d6fc98627c.yaml | 6 ++++++ 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/spo-to-matrix-determinism-554389d6fc98627c.yaml diff --git a/crates/accelerate/src/sparse_pauli_op.rs b/crates/accelerate/src/sparse_pauli_op.rs index 1a85daf036d..89f03333895 100644 --- a/crates/accelerate/src/sparse_pauli_op.rs +++ b/crates/accelerate/src/sparse_pauli_op.rs @@ -10,6 +10,7 @@ // copyright notice, and modified files need to carry a notice indicating // that they have been altered from the originals. +use ahash::RandomState; use pyo3::exceptions::{PyRuntimeError, PyValueError}; use pyo3::prelude::*; use pyo3::types::PyTuple; @@ -20,6 +21,7 @@ use numpy::prelude::*; use numpy::{PyArray1, PyArray2, PyReadonlyArray1, PyReadonlyArray2, PyUntypedArrayMethods}; use hashbrown::HashMap; +use indexmap::IndexMap; use ndarray::{s, ArrayView1, ArrayView2, Axis}; use num_complex::Complex64; use num_traits::Zero; @@ -298,7 +300,11 @@ impl MatrixCompressedPaulis { /// explicitly stored operations, if there are duplicates. After the summation, any terms that /// have become zero are dropped. pub fn combine(&mut self) { - let mut hash_table = HashMap::<(u64, u64), Complex64>::with_capacity(self.coeffs.len()); + let mut hash_table = + IndexMap::<(u64, u64), Complex64, RandomState>::with_capacity_and_hasher( + self.coeffs.len(), + RandomState::new(), + ); for (key, coeff) in self .x_like .drain(..) diff --git a/releasenotes/notes/spo-to-matrix-determinism-554389d6fc98627c.yaml b/releasenotes/notes/spo-to-matrix-determinism-554389d6fc98627c.yaml new file mode 100644 index 00000000000..61961d492e7 --- /dev/null +++ b/releasenotes/notes/spo-to-matrix-determinism-554389d6fc98627c.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Fixed a per-process based non-determinism in `SparsePauliOp.to_matrix`. The exact order of the + floating-point operations in the summation would previously vary per process, but will now be + identical between different invocations of the same script. See `#13413 `__.