use std::collections::HashMap;
use std::hash::{BuildHasher, Hasher};
pub type FoldBuildHasher = foldhash::fast::FixedState;
pub type FoldHashMap<K, V> = HashMap<K, V, FoldBuildHasher>;
pub trait HashFinalize {
#[inline(always)]
fn word_hash(parts: &[&[u8]], storage_bytes: usize) -> u64
where
Self: BuildHasher + Default,
{
let mut hasher = Self::default().build_hasher();
for part in parts {
hasher.write(part);
}
Self::finalize_hash(hasher.finish(), storage_bytes)
}
#[inline(always)]
fn finalize_hash(raw: u64, _storage_bytes: usize) -> u64 {
raw
}
}
impl HashFinalize for fxhash::FxBuildHasher {
#[inline(always)]
fn word_hash(parts: &[&[u8]], _storage_bytes: usize) -> u64 {
let mut hasher = FoldBuildHasher::default().build_hasher();
for part in parts {
hasher.write(part);
}
hasher.finish()
}
}
#[cfg(all(
feature = "gxhash",
target_feature = "aes",
any(target_arch = "x86_64", target_arch = "aarch64")
))]
impl HashFinalize for gxhash::GxBuildHasher {}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashSet;
const KEYS: u64 = 1 << 14;
fn buckets(hashes: impl Iterator<Item = u64>) -> usize {
hashes.map(|h| h & 0xFFFF).collect::<HashSet<_>>().len()
}
#[test]
fn fold_build_hasher_spreads_high_bit_keys() {
let fold = FoldBuildHasher::default();
let usize_keys = (0..KEYS).map(|k| fold.hash_one((k as usize) << (usize::BITS - 14)));
let u128_keys = (0..KEYS).map(|k| fold.hash_one(u128::from(k) << 114));
assert!(buckets(usize_keys) > KEYS as usize / 2);
assert!(buckets(u128_keys) > KEYS as usize / 2);
}
#[test]
fn fxhash_word_hash_spreads_top_qubit_words() {
let fx = fxhash::FxBuildHasher::default();
for bytes in [8, 16, 64] {
let n_qubits = 8 * bytes;
let hashes = (0..KEYS).map(|code| {
let (mut x, mut z) = (vec![0u8; bytes], vec![0u8; bytes]);
for j in 0..7 {
let (q, pauli) = (n_qubits - 7 + j, (code >> (2 * j)) & 3);
x[q / 8] |= ((pauli & 1) as u8) << (q % 8);
z[q / 8] |= ((pauli >> 1) as u8) << (q % 8);
}
let cached = <fxhash::FxBuildHasher as HashFinalize>::word_hash(&[&x, &z], bytes);
fx.hash_one(cached)
});
assert!(buckets(hashes) > KEYS as usize / 2, "{n_qubits} qubits");
}
}
}