paulistrings/pauli_sum/
accumulator.rs

1//! [`BuildAccumulator<W>`], the hashmap ingestion path from unsorted terms to a [`crate::PauliSum`].
2
3use crate::pauli_string::PauliString;
4use crate::pauli_sum::hash::Gf2Hash;
5use crate::pauli_sum::storage::DEFAULT_TARGET_BUCKET_LEN;
6use crate::pauli_sum::storage::{desired_bits, DEFAULT_HASH_SEED, DEFAULT_MIN_BUCKETS};
7use crate::pauli_sum::PauliSum;
8use hashbrown::HashMap;
9use num_complex::Complex64;
10use rustc_hash::FxBuildHasher;
11
12/// Incremental builder for a [`crate::PauliSum`] from unsorted terms; repeated keys sum.
13pub struct BuildAccumulator<const W: usize> {
14    map: HashMap<PauliString<W>, Complex64, FxBuildHasher>,
15    num_qubits: usize,
16}
17
18impl<const W: usize> BuildAccumulator<W> {
19    /// New empty accumulator targeting `num_qubits` qubits.
20    pub fn new(num_qubits: usize) -> Self {
21        Self { map: HashMap::with_hasher(FxBuildHasher), num_qubits }
22    }
23
24    /// Allocate up-front for at least `capacity` distinct Pauli keys.
25    pub fn with_capacity(num_qubits: usize, capacity: usize) -> Self {
26        Self { map: HashMap::with_capacity_and_hasher(capacity, FxBuildHasher), num_qubits }
27    }
28
29    /// Add `c ยท p`.
30    pub fn add_term(&mut self, p: PauliString<W>, c: Complex64) {
31        self.map.entry(p).and_modify(|e| *e += c).or_insert(c);
32    }
33
34    /// Emit the [`crate::PauliSum`], dropping keys whose coefficients summed to exactly zero.
35    pub fn finalize(self) -> PauliSum<W> {
36        let zero = Complex64::new(0.0, 0.0);
37        let mut entries: Vec<(PauliString<W>, Complex64)> =
38            self.map.into_iter().filter(|(_, c)| *c != zero).collect();
39        entries.sort_unstable_by(|a, b| a.0.cmp(&b.0));
40        let n = entries.len();
41        let mut x = Vec::with_capacity(n);
42        let mut z = Vec::with_capacity(n);
43        let mut coeff = Vec::with_capacity(n);
44        for (p, c) in entries {
45            x.push(p.x);
46            z.push(p.z);
47            coeff.push(c);
48        }
49        let bits = desired_bits(n, DEFAULT_TARGET_BUCKET_LEN, DEFAULT_MIN_BUCKETS);
50        let hash = Gf2Hash::new(self.num_qubits, bits, DEFAULT_HASH_SEED);
51        PauliSum::from_key_sorted(&x, &z, &coeff, hash, self.num_qubits)
52    }
53}
54
55#[cfg(test)]
56mod tests;