From d76bba40f9bfb565d9616eb5e180c5c0892ee2a6 Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Thu, 14 Nov 2024 16:41:05 +0000 Subject: [PATCH] Fix process-based non-determinism in `SparsePauliOp.to_matrix` (#13439) * 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) --- 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 `__.