1fn 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#[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 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
74pub 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]; let mut weights = vec![0.0f64];
83 let k = 3 + (splitmix64(&mut st) % 3) as usize; for _ in 0..k {
85 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 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
106pub fn bell_max_error(ch: &SparseChannel, m: u64, seed: u64) -> f64 {
111 if m == 0 {
112 return 1.0;
113 }
114 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
142pub 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#[derive(Clone, Copy, Debug)]
178pub struct SepPoint {
179 pub copies: u64,
180 pub bell_err: f64,
181 pub conventional_err: f64,
182}
183
184pub 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
198pub 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 let ch = SparseChannel { patterns: vec![0, 0b101], weights: vec![0.7, 0.3], n: 3 };
238 assert!((ch.fidelity(0b000) - 1.0).abs() < 1e-12); assert!((ch.fidelity(0b100) - 0.4).abs() < 1e-12); assert!((ch.fidelity(0b010) - 1.0).abs() < 1e-12); }
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 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 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 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 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}