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