1fn 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#[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 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
75pub 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]; let mut weights = vec![0.0f64];
84 let k = 3 + (splitmix64(&mut st) % 3) as usize; for _ in 0..k {
86 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 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
107pub fn bell_max_error(ch: &SparseChannel, m: u64, seed: u64) -> f64 {
112 if m == 0 {
113 return 1.0;
114 }
115 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
143pub 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#[derive(Clone, Copy, Debug)]
179pub struct SepPoint {
180 pub copies: u64,
181 pub bell_err: f64,
182 pub conventional_err: f64,
183}
184
185pub 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
199pub 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 let ch = SparseChannel { patterns: vec![0, 0b101], weights: vec![0.7, 0.3], n: 3 };
239 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); }
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 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 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 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 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}