Engine

Rust Engine

The quantum simulator core is written in Rust and compiled to WebAssembly. 442 lines. 12 physics unit tests. All 15 JS-engine bugs corrected.

Why Rust?

Zero GC

No garbage collector — no pauses during tight numerical loops. Critical for VQE which calls the gate loop millions of times.

LLVM SIMD

LLVM auto-vectorises the complex multiply loop. AVX2 processes 4 pairs per cycle.

Safe Memory

Ownership system prevents use-after-free and data races at compile time. No segfaults.

WASM + Native

Same lib.rs compiles to WASM (browser) and native (cargo test). Zero code duplication.

Build Instructions

Prerequisites

# 1. Install Rust toolchain
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"

# 2. Add WebAssembly target
rustup target add wasm32-unknown-unknown

# 3. Install wasm-pack
cargo install wasm-pack

# Verify
rustc --version    # rustc 1.78.0+
wasm-pack --version  # wasm-pack 0.13.0+

WASM Build (for browser/Node.js)

# From the repository root — one command does everything
bash build.sh

# OR manually:
cd quantum_engine
wasm-pack build   --target bundler   --out-dir ../wasm_pkg   --release   -- --features wasm

# Output files in wasm_pkg/:
# quantum_engine.js      — JavaScript glue code
# quantum_engine_bg.wasm — compiled Rust (~178 KB)
# quantum_engine.d.ts    — TypeScript declarations

Run Unit Tests (native, fast)

cd quantum_engine
cargo test

# Expected output:
# running 12 tests
# test tests::s_on_one ... ok
# test tests::bell ... ok
# test tests::cross_shard_cnot ... ok
# test tests::grover_4q ... ok
# test tests::qft_roundtrip ... ok
# ... (12 total)
# test result: ok. 12 passed; 0 failed

Architecture

Complex64 — Zero-overhead arithmetic

#[derive(Clone, Copy)]
pub struct C64 { pub re: f64, pub im: f64 }

impl C64 {
    // e^(iθ) = cos θ + i sin θ
    pub fn phase(t: f64) -> Self {
        Self::new(t.cos(), t.sin())
    }
    pub fn norm_sq(self) -> f64 {
        self.re * self.re + self.im * self.im
    }
}

// Rust operator overloading — zero cost, LLVM-optimised
impl std::ops::Mul for C64 {
    type Output = Self;
    fn mul(self, b: Self) -> Self {
        Self::new(
            self.re*b.re - self.im*b.im,
            self.re*b.im + self.im*b.re
        )
    }
}

Amps — Sparse/Dense Adaptive Storage

// Automatically switches between sparse and dense
// Sparse: HashMap — O(nonzero) gates
// Dense: Vec (interleaved re/im) — cache-friendly
// Threshold: switch to dense at 12% fill

enum Storage {
    Sparse(HashMap<u64, C64>),
    Dense(Vec<f64>),   // [re0,im0, re1,im1, ...]
}

// Hot path — only non-zero amplitudes visited
pub fn each<F: FnMut(u64, C64)>(&self, mut f: F) {
    match &self.s {
        Stor::Sp(map) => map.iter().for_each(|(&i,&a)| f(i,a)),
        Stor::De(arr) => { /* iterate non-zero entries */ }
    }
}

Shard — 10-Qubit Subsystem

Large registers are split into 10-qubit shards. Gates within a shard are local. Cross-shard gates trigger a merge operation.

// BUG FIX #1: HashSet anchors cover both |0⟩ and |1⟩ cases
pub fn apply_1q(&mut self, lq: usize, gate: G2) {
    let stride = 1u64 << lq;
    let mut anchors: HashSet<u64> = HashSet::new();
    self.a.each(|i, _| { anchors.insert(i & !stride); });
    // Now covers both i0 (bit=0) and i1 (bit=1) states
    for &i0 in &anchors {
        let i1 = i0 | stride;
        let (a0, a1) = (self.a.get(i0), self.a.get(i1));
        self.a.set(i0, gate[0][0]*a0 + gate[0][1]*a1);
        self.a.set(i1, gate[1][0]*a0 + gate[1][1]*a1);
    }
}

All 15 Bug Fixes

#BugSymptomFix
1apply_1q missed |1⟩Gates silent on pure |1⟩ statesHashSet of idx & !stride — covers both bit values
2CNOT missed ctrl=1,tgt=1Swap skipped when both bits setAnchor from all ctrl=1 indices
3SWAP only one orientationSWAP|11⟩ wrongCanonical anchor from both orientations
4Toffoli anchor incompleteToffoli wrong for nQ>3Set-based anchors for all ctrl1=1,ctrl2=1
5mergeShards bit orderCross-shard gates applied to wrong qubitsia|(ib<<na) — shard A in LOW bits
6statevector returned √probPhase information lostTensor-product actual {re,im} from Amps
7Bit string MSB-firstQubit 0 mapped to wrong bitBuild string[i] from bit i of idx
8iSWAP decompositionSWAP+S+S gave −|11⟩H,CNOT(a→b),CNOT(b→a),H,S,S
9state_fidelity no conjugateF(ψ,ψ) ≠ 1 for complex statesConjugate bra: re+=a1.re*b.re+a1.im*b.im
10QFT phase off by 2QFT+IQFT ≠ identity2π/2^(k-j+1) not π/2^(k-j)
11Grover oracle 2 controls onlyGrover fails for nQ>3n_controlled_z_all() merges all shards
12Grover diffusion CZ(0,1) onlyDiffusion wrong for nQ>2Same n-controlled-Z fix
13RXX/RYY/MS missingNameError in stdlibAdded via decomposition
14pauli_expectation was nullVQE energy calculations brokenFull implementation via clone+rotate
15QFT arg orderInverse QFT received nQ instead of flagFixed function signature

Unit Tests (12)

#[test] fn s_on_one() {
    // S|1⟩ = i|1⟩ — was broken in JS engine
    let mut q = QReg::new("q",1); q.X(0); q.S(0);
    let sv = q.statevector();
    assert!((sv[0].re).abs() < 1e-10);         // re=0
    assert!((sv[0].im - 1.0).abs() < 1e-10); // im=1
}
#[test] fn bell() { /* P(01)=0, P(10)=0 */ }
#[test] fn cross_shard_cnot() { /* q[0]=q[10] always */ }
#[test] fn grover_4q() { /* target found >90% */ }
#[test] fn qft_roundtrip() { /* QFT+IQFT = identity */ }
#[test] fn fidelity_tests() { /* F(ψ,ψ)=1, F(+i,-i)=0 */ }
// ... 6 more tests