Skip to main content

wai_quantum/
quantum_qdata.rs

1//! Learning from quantum **data** — the surviving quantum advantage
2//! (`wai.quantum.qdata`).
3//!
4//! Most "quantum ML" evaporates under dequantization: on *classical* input the
5//! quantum kernel is a phasor layer ([[crate::quantum_kernel]]) and the generative
6//! model is a phasor sum ([[crate::quantum_phasor]]). What does **not** evaporate is
7//! an advantage in *access to quantum data* — and the sharpest provable instance is
8//! **Pauli-channel learning** (Chen–Cotler–Huang–Li 2022; Huang et al., *Science*
9//! 2022, 40-qubit demo):
10//!
11//! - To estimate an unknown `n`-qubit Pauli channel's `2^n` Pauli fidelities `{λ_x}`
12//!   to accuracy `ε`, **any entanglement-free strategy** — single copies, even
13//!   adaptive — needs **Ω(2^n / ε²)** channel uses. It must spread its shots across
14//!   `2^n` probes, so each fidelity is seen `M / 2^n` times.
15//! - **With a two-copy quantum memory** you Bell-measure the channel's Choi state.
16//!   Each measurement returns an error pattern `y ~ p(y)` *directly*, and `M` such
17//!   samples estimate **every** fidelity `λ_x = Σ_y p(y)·(−1)^{x·y}` at once, each to
18//!   error `~1/√M` — **independent of `n`**. So `ε` costs only **O(1/ε²)** copies.
19//!
20//! This is an unconditional, exponential separation in *sample* complexity, and it
21//! is what makes [[crate::quantum_noise]]'s Bell / Cycle-Benchmarking sampling
22//! (`wai.quantum.cal.noise_learn`) exponentially data-efficient — the real moat,
23//! surfaced here as a first-class quantum-ML capability. Both estimators are
24//! simulated exactly; deterministic given `seed` (reproducible f64).
25
26fn splitmix64(s: &mut u64) -> u64 {
27    *s = s.wrapping_add(0x9E37_79B9_7F4A_7C15);
28    let mut z = *s;
29    z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
30    z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
31    z ^ (z >> 31)
32}
33#[inline]
34fn u01(s: &mut u64) -> f64 {
35    (splitmix64(s) >> 11) as f64 / (1u64 << 53) as f64
36}
37#[inline]
38fn sign_bit(x: u32, y: u32) -> f64 {
39    if (x & y).count_ones() & 1 == 0 { 1.0 } else { -1.0 }
40}
41
42/// A sparse Pauli channel given by its error distribution `p(y)` over `n`-bit
43/// error patterns — the structure `noise_learn` assumes. `weights` sum to 1 and
44/// include the identity pattern (`0`). The channel's Pauli fidelities are
45/// `λ_x = Σ_y p(y)·(−1)^{x·y}` — its Walsh–Hadamard spectrum.
46#[derive(Clone, Debug)]
47pub struct SparseChannel {
48    pub patterns: Vec<u32>,
49    pub weights: Vec<f64>,
50    pub n: u32,
51}
52
53impl SparseChannel {
54    /// The Pauli fidelity `λ_x` (exact, from the model).
55    pub fn fidelity(&self, x: u32) -> f64 {
56        self.patterns
57            .iter()
58            .zip(&self.weights)
59            .map(|(&y, &w)| w * sign_bit(x, y))
60            .sum()
61    }
62    fn sample(&self, st: &mut u64) -> u32 {
63        let u = u01(st);
64        let mut c = 0.0;
65        for (&y, &w) in self.patterns.iter().zip(&self.weights) {
66            c += w;
67            if u < c {
68                return y;
69            }
70        }
71        *self.patterns.last().unwrap_or(&0)
72    }
73}
74
75/// A deterministic demonstration channel on `n` qubits: identity with probability
76/// `~0.5`, plus a few random weight-≤3 error patterns — sparse and non-trivially
77/// entangling in its fidelity spectrum. Weights normalized to sum to 1.
78pub fn demo_channel(n: u32, seed: u64) -> SparseChannel {
79    let n = n.clamp(1, 16);
80    let mask = ((1u64 << n) - 1) as u32;
81    let mut st = seed.wrapping_mul(0x2545_F491).wrapping_add(1);
82    let mut patterns = vec![0u32]; // identity
83    let mut weights = vec![0.0f64];
84    let k = 3 + (splitmix64(&mut st) % 3) as usize; // 3..=5 error patterns
85    for _ in 0..k {
86        // a low-weight error pattern: pick 1..=3 random bit positions
87        let bits = 1 + (splitmix64(&mut st) % 3);
88        let mut y = 0u32;
89        for _ in 0..bits {
90            let pos = (splitmix64(&mut st) % n as u64) as u32;
91            y |= 1 << pos;
92        }
93        y &= mask;
94        patterns.push(y);
95        weights.push(0.02 + 0.10 * u01(&mut st));
96    }
97    // identity takes the remaining mass (kept ≥ 0.4 so the channel is benign)
98    let err_mass: f64 = weights.iter().skip(1).sum();
99    weights[0] = (1.0 - err_mass).max(0.4);
100    let z: f64 = weights.iter().sum();
101    for w in &mut weights {
102        *w /= z;
103    }
104    SparseChannel { patterns, weights, n }
105}
106
107/// The **quantum-memory (Bell)** estimator: draw `m` error patterns `y ~ p` (each a
108/// two-copy Bell measurement of the Choi state), then estimate *every* fidelity
109/// `λ̂_x = (1/m) Σ_i (−1)^{x·y_i}` and return the worst-case error
110/// `max_x |λ̂_x − λ_x|`. The error scales `~1/√m`, **independent of `n`**.
111pub fn bell_max_error(ch: &SparseChannel, m: u64, seed: u64) -> f64 {
112    if m == 0 {
113        return 1.0;
114    }
115    // tally the empirical error distribution (support is small)
116    let mut st = seed.wrapping_mul(0x9E37_79B9).wrapping_add(7);
117    let mut counts = vec![0u64; ch.patterns.len()];
118    let idx: std::collections::HashMap<u32, usize> =
119        ch.patterns.iter().enumerate().map(|(i, &y)| (y, i)).collect();
120    for _ in 0..m {
121        let y = ch.sample(&mut st);
122        if let Some(&i) = idx.get(&y) {
123            counts[i] += 1;
124        }
125    }
126    let dim = 1u32 << ch.n;
127    let mut worst = 0.0f64;
128    for x in 0..dim {
129        let est: f64 = ch
130            .patterns
131            .iter()
132            .zip(&counts)
133            .map(|(&y, &c)| (c as f64 / m as f64) * sign_bit(x, y))
134            .sum();
135        let e = (est - ch.fidelity(x)).abs();
136        if e > worst {
137            worst = e;
138        }
139    }
140    worst
141}
142
143/// The **entanglement-free (conventional)** estimator: the `m` channel uses must be
144/// split across all `2^n` fidelities, so each is measured `m / 2^n` times as `±1`
145/// shots of bias `(1+λ_x)/2`. Returns `max_x |λ̂_x − λ_x|`, which scales
146/// `~√(2^n / m)` — exponentially worse in `n`. If `m < 2^n` some fidelity is never
147/// sampled and the error is maximal (`1.0`), the honest face of the Ω(2^n) bound.
148pub fn conventional_max_error(ch: &SparseChannel, m: u64, seed: u64) -> f64 {
149    let dim = 1u64 << ch.n;
150    let per = m / dim;
151    if per == 0 {
152        return 1.0;
153    }
154    let mut worst = 0.0f64;
155    for x in 0..dim as u32 {
156        let lam = ch.fidelity(x);
157        let p_plus = 0.5 * (1.0 + lam);
158        let mut st = seed
159            .wrapping_mul(0x100_0001)
160            .wrapping_add(x as u64 + 1);
161        let mut hits = 0u64;
162        for _ in 0..per {
163            if u01(&mut st) < p_plus {
164                hits += 1;
165            }
166        }
167        let est = 2.0 * (hits as f64 / per as f64) - 1.0;
168        let e = (est - lam).abs();
169        if e > worst {
170            worst = e;
171        }
172    }
173    worst
174}
175
176/// One point of the sample-complexity curve: at budget `m` channel uses, the
177/// worst-case fidelity error of each strategy.
178#[derive(Clone, Copy, Debug)]
179pub struct SepPoint {
180    pub copies: u64,
181    pub bell_err: f64,
182    pub conventional_err: f64,
183}
184
185/// The separation curve over a list of copy budgets `ms` for an `n`-qubit demo
186/// channel — the Bell error (n-independent, `~1/√m`) against the entanglement-free
187/// error (`~√(2^n/m)`), the exponential gap made visible.
188pub fn separation_curve(n: u32, seed: u64, ms: &[u64]) -> Vec<SepPoint> {
189    let ch = demo_channel(n, seed);
190    ms.iter()
191        .map(|&m| SepPoint {
192            copies: m,
193            bell_err: bell_max_error(&ch, m, seed ^ (m.wrapping_mul(0x51))),
194            conventional_err: conventional_max_error(&ch, m, seed ^ (m.wrapping_mul(0x71))),
195        })
196        .collect()
197}
198
199/// Empirically the smallest budget `m` on a geometric grid at which each strategy
200/// reaches worst-case error `≤ eps`. Returns `(bell_copies, conventional_copies)`;
201/// `conventional / bell ≈ 2^n` — the exponential price of no quantum memory. `0`
202/// means the grid's ceiling did not suffice.
203pub fn copies_to_epsilon(n: u32, seed: u64, eps: f64, ceil: u64) -> (u64, u64) {
204    let ch = demo_channel(n, seed);
205    let mut bell = 0u64;
206    let mut conv = 0u64;
207    let mut m = 16u64;
208    while m <= ceil {
209        if bell == 0 && bell_max_error(&ch, m, seed ^ (m.wrapping_mul(0x51))) <= eps {
210            bell = m;
211        }
212        if conv == 0 && conventional_max_error(&ch, m, seed ^ (m.wrapping_mul(0x71))) <= eps {
213            conv = m;
214        }
215        if bell != 0 && conv != 0 {
216            break;
217        }
218        m = (m as f64 * 1.5).ceil() as u64;
219    }
220    (bell, conv)
221}
222
223#[cfg(test)]
224mod tests {
225    use super::*;
226
227    #[test]
228    fn identity_channel_has_unit_fidelities() {
229        let ch = SparseChannel { patterns: vec![0], weights: vec![1.0], n: 5 };
230        for x in 0..(1u32 << 5) {
231            assert!((ch.fidelity(x) - 1.0).abs() < 1e-12);
232        }
233    }
234
235    #[test]
236    fn fidelities_are_a_walsh_spectrum() {
237        // a single error pattern y flips exactly the fidelities x that anticommute.
238        let ch = SparseChannel { patterns: vec![0, 0b101], weights: vec![0.7, 0.3], n: 3 };
239        // λ_x = 0.7 + 0.3·(−1)^{x·101}
240        assert!((ch.fidelity(0b000) - 1.0).abs() < 1e-12); // 0.7+0.3
241        assert!((ch.fidelity(0b100) - 0.4).abs() < 1e-12); // 0.7−0.3
242        assert!((ch.fidelity(0b010) - 1.0).abs() < 1e-12); // even overlap
243    }
244
245    #[test]
246    fn bell_error_shrinks_with_copies() {
247        let ch = demo_channel(6, 42);
248        let coarse = bell_max_error(&ch, 200, 1);
249        let fine = bell_max_error(&ch, 20_000, 1);
250        assert!(fine < coarse, "more Bell copies ⇒ smaller error ({fine} !< {coarse})");
251        assert!(fine < 0.05, "20k Bell copies resolve all fidelities: {fine}");
252    }
253
254    #[test]
255    fn the_exponential_separation() {
256        // Bell error is ~n-independent; conventional error explodes with n at a
257        // fixed copy budget — the whole point.
258        let m = 4096u64;
259        let bell4 = bell_max_error(&demo_channel(4, 7), m, 3);
260        let bell8 = bell_max_error(&demo_channel(8, 7), m, 3);
261        let conv4 = conventional_max_error(&demo_channel(4, 7), m, 3);
262        let conv8 = conventional_max_error(&demo_channel(8, 7), m, 3);
263        // Bell barely moves from 4→8 qubits…
264        assert!(bell8 < 3.0 * bell4 + 0.02, "Bell ~n-independent: {bell4} → {bell8}");
265        assert!(bell8 < 0.15, "Bell still resolves 8-qubit fidelities at {m} copies: {bell8}");
266        // …while conventional collapses (m=4096 < 2^12 spread ⇒ many probes starved).
267        assert!(conv8 > conv4, "conventional worsens with n: {conv4} → {conv8}");
268        assert!(conv8 > 0.3, "8-qubit conventional is starved at {m} copies: {conv8}");
269        assert!(conv8 > 5.0 * bell8, "Bell beats conventional at 8 qubits: {conv8} vs {bell8}");
270    }
271
272    #[test]
273    fn copies_ratio_is_exponential() {
274        let (bell, conv) = copies_to_epsilon(6, 11, 0.08, 5_000_000);
275        assert!(bell > 0, "Bell reaches ε");
276        assert!(conv > 0, "conventional reaches ε within the ceiling");
277        // conventional needs ~2^6 = 64× more copies (loose bounds for sampling noise)
278        assert!(conv > 8 * bell, "conventional needs exponentially more: {conv} vs {bell}");
279    }
280
281    #[test]
282    fn deterministic() {
283        let a = separation_curve(6, 99, &[128, 1024, 8192]);
284        let b = separation_curve(6, 99, &[128, 1024, 8192]);
285        for (p, q) in a.iter().zip(&b) {
286            assert_eq!(p.bell_err.to_bits(), q.bell_err.to_bits());
287            assert_eq!(p.conventional_err.to_bits(), q.conventional_err.to_bits());
288        }
289    }
290}