Skip to content

Commit 64d248e

Browse files
AlexSchuckertclaude
andcommitted
fix: 32-bit-safe Pauli-word storage (wasm32 build)
bitvec implements BitStore for u64 only on 64-bit targets, so the [u64; 2] word storage broke the wasm32 workspace build. The chunk type is now target-gated: two u64 chunks on 64-bit targets (bit-identical to before), four u32 chunks elsewhere — 128 qubits either way. The chunk loops (anti_commutes / pauli_mul) were already width-agnostic. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 806a9b1 commit 64d248e

1 file changed

Lines changed: 25 additions & 13 deletions

File tree

  • crates/ppvm-lindblad/src

‎crates/ppvm-lindblad/src/lib.rs‎

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@
2828
//! FP noise).
2929
//!
3030
//! Pauli strings are stored as [`ppvm_pauli_word::word::PauliWord`] backed by
31-
//! `[u64; 2]` (≤128 qubits) with cached hashes for fast HashMap lookup. The
32-
//! hot-path commutator/product loops bypass the higher-level word API and
33-
//! operate directly on raw `u64` chunks for speed.
31+
//! two 64-bit chunks (≤128 qubits; four 32-bit chunks on 32-bit targets)
32+
//! with cached hashes for fast HashMap lookup. The hot-path commutator/
33+
//! product loops bypass the higher-level word API and operate directly on
34+
//! the raw chunks for speed.
3435
3536
use fxhash::{FxBuildHasher, FxHashMap, FxHashSet};
3637
use num::Complex;
@@ -46,18 +47,29 @@ pub(crate) mod expm;
4647
/// Matrix-free / quspin-expm-backed `exp(dt·L*)·b` engine. See module docs.
4748
pub(crate) mod mf_expm;
4849

49-
/// Words pack up to 128 qubits.
50-
const W_U64: usize = 2;
50+
/// Pauli-word storage chunk: `u64` on 64-bit targets, `u32` elsewhere
51+
/// (`bitvec` implements `BitStore` for `u64` only on 64-bit targets, so
52+
/// e.g. wasm32 builds use four 32-bit chunks instead of two 64-bit ones).
53+
#[cfg(target_pointer_width = "64")]
54+
type Chunk = u64;
55+
#[cfg(not(target_pointer_width = "64"))]
56+
type Chunk = u32;
5157

52-
/// Maximum number of qubits supported by [`Word`] (= `8 · W_U64 · sizeof(u64)`).
53-
pub const MAX_QUBITS: usize = 64 * W_U64;
58+
/// Chunks per word; words pack up to 128 qubits on every target.
59+
#[cfg(target_pointer_width = "64")]
60+
const W_CHUNKS: usize = 2;
61+
#[cfg(not(target_pointer_width = "64"))]
62+
const W_CHUNKS: usize = 4;
63+
64+
/// Maximum number of qubits supported by [`Word`].
65+
pub const MAX_QUBITS: usize = 128;
5466

5567
/// The Pauli-word storage type used throughout this crate.
5668
///
57-
/// `[u64; 2]` covers up to 128 qubits; the `FxBuildHasher` matches the
58-
/// hash used by the `FxHashMap` keys we wrap with; `REHASH=true` means
59-
/// `set()` keeps the cached hash in sync.
60-
pub type Word = PauliWord<[u64; W_U64], FxBuildHasher, true>;
69+
/// `[Chunk; W_CHUNKS]` covers up to 128 qubits; the `FxBuildHasher`
70+
/// matches the hash used by the `FxHashMap` keys we wrap with;
71+
/// `REHASH=true` means `set()` keeps the cached hash in sync.
72+
pub type Word = PauliWord<[Chunk; W_CHUNKS], FxBuildHasher, true>;
6173

6274
pub use config::PcStepConfig;
6375
pub use error::Error;
@@ -192,7 +204,7 @@ fn phase_factor(phase: u8) -> Complex<f64> {
192204
#[inline(always)]
193205
fn anti_commutes(a: &Word, b: &Word) -> bool {
194206
let mut bits: u32 = 0;
195-
for i in 0..W_U64 {
207+
for i in 0..W_CHUNKS {
196208
bits += (a.xbits.data[i] & b.zbits.data[i]).count_ones();
197209
bits += (a.zbits.data[i] & b.xbits.data[i]).count_ones();
198210
}
@@ -222,7 +234,7 @@ fn pauli_mul(p: &Word, q: &Word) -> (Word, u8) {
222234
let mut out = Word::new(p.n_qubits());
223235
let mut sign_count: u32 = 0;
224236
let mut imag_count: u32 = 0;
225-
for i in 0..W_U64 {
237+
for i in 0..W_CHUNKS {
226238
let a = p.xbits.data[i];
227239
let b = p.zbits.data[i];
228240
let c = q.xbits.data[i];

0 commit comments

Comments
 (0)