1#![allow(unused)]
4
5use super::{Channel, OutputBuffer};
6use num_complex::Complex64;
7
8pub struct Depolarizing {
23 pub support: [u32; 1],
25 pub p: f64,
27}
28
29impl<const W: usize> Channel<W> for Depolarizing {
30 #[inline]
31 fn max_fanout(&self) -> usize {
32 1
33 }
34
35 #[inline]
36 fn support(&self) -> &[u32] {
37 &self.support
38 }
39
40 #[inline]
41 fn apply(
42 &self,
43 input_x: &[u64; W],
44 input_z: &[u64; W],
45 coeff: Complex64,
46 out: &mut OutputBuffer<'_, W>,
47 ) {
48 let q = self.support[0] as usize;
49 debug_assert!(q < 64 * W);
50 let word = q / 64;
51 let bit = q % 64;
52 let x_bit = (input_x[word] >> bit) & 1;
53 let z_bit = (input_z[word] >> bit) & 1;
54 let scale = if (x_bit | z_bit) == 0 {
55 1.0
56 } else {
57 1.0 - 4.0 * self.p / 3.0
58 };
59 out.push(*input_x, *input_z, coeff * scale);
60 }
61}
62
63pub struct Dephasing {
69 pub support: [u32; 1],
71 pub p: f64,
73}
74
75impl<const W: usize> Channel<W> for Dephasing {
76 #[inline]
77 fn max_fanout(&self) -> usize {
78 1
79 }
80
81 #[inline]
82 fn support(&self) -> &[u32] {
83 &self.support
84 }
85
86 #[inline]
87 fn apply(
88 &self,
89 input_x: &[u64; W],
90 input_z: &[u64; W],
91 coeff: Complex64,
92 out: &mut OutputBuffer<'_, W>,
93 ) {
94 let q = self.support[0] as usize;
95 debug_assert!(q < 64 * W);
96 let word = q / 64;
97 let bit = q % 64;
98 let x_bit = (input_x[word] >> bit) & 1;
99 let scale = if x_bit == 1 { 1.0 - 2.0 * self.p } else { 1.0 };
100 out.push(*input_x, *input_z, coeff * scale);
101 }
102}
103
104pub struct AmplitudeDamping {
121 pub support: [u32; 1],
123 pub gamma: f64,
126}
127
128impl<const W: usize> Channel<W> for AmplitudeDamping {
129 #[inline]
130 fn max_fanout(&self) -> usize {
131 2
132 }
133
134 #[inline]
135 fn support(&self) -> &[u32] {
136 &self.support
137 }
138
139 fn apply(
140 &self,
141 input_x: &[u64; W],
142 input_z: &[u64; W],
143 coeff: Complex64,
144 out: &mut OutputBuffer<'_, W>,
145 ) {
146 let q = self.support[0] as usize;
147 debug_assert!(q < 64 * W);
148 let word = q / 64;
149 let bit = q % 64;
150 let mask = 1u64 << bit;
151 let x_bit = (input_x[word] >> bit) & 1;
152 let z_bit = (input_z[word] >> bit) & 1;
153 match (x_bit, z_bit) {
154 (0, 0) => {
155 out.push(*input_x, *input_z, coeff);
157 }
158 (1, _) => {
159 let scale = (1.0 - self.gamma).sqrt();
161 out.push(*input_x, *input_z, coeff * scale);
162 }
163 (0, 1) => {
164 out.push(*input_x, *input_z, coeff * (1.0 - self.gamma));
167 let mut nz = *input_z;
168 nz[word] &= !mask;
169 out.push(*input_x, nz, coeff * self.gamma);
170 }
171 _ => unreachable!(),
172 }
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179 use crate::pauli_string::PauliString;
180
181 const TOL: f64 = 1e-12;
182
183 #[allow(clippy::type_complexity)]
184 fn make_buf<const W: usize>(
185 cap: usize,
186 ) -> (Vec<[u64; W]>, Vec<[u64; W]>, Vec<Complex64>, usize) {
187 (
188 vec![[0u64; W]; cap],
189 vec![[0u64; W]; cap],
190 vec![Complex64::new(0.0, 0.0); cap],
191 0,
192 )
193 }
194
195 fn approx_eq(a: Complex64, b: Complex64, tol: f64) -> bool {
196 (a - b).norm() <= tol
197 }
198
199 #[test]
202 fn depolarizing_passes_identity_through() {
203 let ch = Depolarizing {
204 support: [0],
205 p: 0.1,
206 };
207 let p = PauliString::<1>::identity();
208 let (mut bx, mut bz, mut bc, mut len) = make_buf::<1>(1);
209 let mut buf = OutputBuffer::<1> {
210 x: &mut bx,
211 z: &mut bz,
212 coeff: &mut bc,
213 len: &mut len,
214 };
215 <Depolarizing as Channel<1>>::apply(&ch, &p.x, &p.z, Complex64::new(2.0, 0.0), &mut buf);
216 assert_eq!(len, 1);
217 assert_eq!(bx[0], p.x);
218 assert_eq!(bz[0], p.z);
219 assert!(approx_eq(bc[0], Complex64::new(2.0, 0.0), TOL));
220 }
221
222 #[test]
224 fn depolarizing_scales_xyz() {
225 let p = 0.15;
226 let ch = Depolarizing { support: [0], p };
227 let scale = 1.0 - 4.0 * p / 3.0;
228 for pauli in [
229 PauliString::<1>::x(0),
230 PauliString::<1>::y(0),
231 PauliString::<1>::z(0),
232 ] {
233 let (mut bx, mut bz, mut bc, mut len) = make_buf::<1>(1);
234 let mut buf = OutputBuffer::<1> {
235 x: &mut bx,
236 z: &mut bz,
237 coeff: &mut bc,
238 len: &mut len,
239 };
240 <Depolarizing as Channel<1>>::apply(
241 &ch,
242 &pauli.x,
243 &pauli.z,
244 Complex64::new(1.0, 0.0),
245 &mut buf,
246 );
247 assert_eq!(len, 1);
248 assert_eq!(bx[0], pauli.x);
249 assert_eq!(bz[0], pauli.z);
250 assert!(approx_eq(bc[0], Complex64::new(scale, 0.0), TOL));
251 }
252 }
253
254 #[test]
258 fn depolarizing_off_support_is_no_op() {
259 let ch = Depolarizing {
260 support: [0],
261 p: 0.2,
262 };
263 let p = PauliString::<1>::z(1);
264 let (mut bx, mut bz, mut bc, mut len) = make_buf::<1>(1);
265 let mut buf = OutputBuffer::<1> {
266 x: &mut bx,
267 z: &mut bz,
268 coeff: &mut bc,
269 len: &mut len,
270 };
271 <Depolarizing as Channel<1>>::apply(&ch, &p.x, &p.z, Complex64::new(3.0, 0.0), &mut buf);
272 assert_eq!(len, 1);
273 assert!(approx_eq(bc[0], Complex64::new(3.0, 0.0), TOL));
274 }
275
276 #[test]
279 fn depolarizing_w2_word_boundary() {
280 let p = 0.25;
281 let ch = Depolarizing { support: [64], p };
282 let scale = 1.0 - 4.0 * p / 3.0;
283 let pauli = PauliString::<2>::x(64);
285 let (mut bx, mut bz, mut bc, mut len) = make_buf::<2>(1);
286 let mut buf = OutputBuffer::<2> {
287 x: &mut bx,
288 z: &mut bz,
289 coeff: &mut bc,
290 len: &mut len,
291 };
292 <Depolarizing as Channel<2>>::apply(
293 &ch,
294 &pauli.x,
295 &pauli.z,
296 Complex64::new(1.0, 0.0),
297 &mut buf,
298 );
299 assert_eq!(len, 1);
300 assert_eq!(bx[0], pauli.x);
301 assert_eq!(bz[0], pauli.z);
302 assert!(approx_eq(bc[0], Complex64::new(scale, 0.0), TOL));
303 }
304
305 #[test]
307 fn dephasing_preserves_i_and_z() {
308 let ch = Dephasing {
309 support: [0],
310 p: 0.3,
311 };
312 for pauli in [PauliString::<1>::identity(), PauliString::<1>::z(0)] {
313 let (mut bx, mut bz, mut bc, mut len) = make_buf::<1>(1);
314 let mut buf = OutputBuffer::<1> {
315 x: &mut bx,
316 z: &mut bz,
317 coeff: &mut bc,
318 len: &mut len,
319 };
320 <Dephasing as Channel<1>>::apply(
321 &ch,
322 &pauli.x,
323 &pauli.z,
324 Complex64::new(2.5, 0.0),
325 &mut buf,
326 );
327 assert_eq!(len, 1);
328 assert_eq!(bx[0], pauli.x);
329 assert_eq!(bz[0], pauli.z);
330 assert!(approx_eq(bc[0], Complex64::new(2.5, 0.0), TOL));
331 }
332 }
333
334 #[test]
336 fn dephasing_scales_x_and_y() {
337 let p = 0.2;
338 let ch = Dephasing { support: [0], p };
339 let scale = 1.0 - 2.0 * p;
340 for pauli in [PauliString::<1>::x(0), PauliString::<1>::y(0)] {
341 let (mut bx, mut bz, mut bc, mut len) = make_buf::<1>(1);
342 let mut buf = OutputBuffer::<1> {
343 x: &mut bx,
344 z: &mut bz,
345 coeff: &mut bc,
346 len: &mut len,
347 };
348 <Dephasing as Channel<1>>::apply(
349 &ch,
350 &pauli.x,
351 &pauli.z,
352 Complex64::new(1.0, 0.0),
353 &mut buf,
354 );
355 assert_eq!(len, 1);
356 assert_eq!(bx[0], pauli.x);
357 assert_eq!(bz[0], pauli.z);
358 assert!(approx_eq(bc[0], Complex64::new(scale, 0.0), TOL));
359 }
360 }
361
362 #[test]
364 fn dephasing_w2_word_boundary() {
365 let p = 0.4;
366 let ch = Dephasing { support: [64], p };
367 let scale = 1.0 - 2.0 * p;
368 let pauli = PauliString::<2>::x(64);
369 let (mut bx, mut bz, mut bc, mut len) = make_buf::<2>(1);
370 let mut buf = OutputBuffer::<2> {
371 x: &mut bx,
372 z: &mut bz,
373 coeff: &mut bc,
374 len: &mut len,
375 };
376 <Dephasing as Channel<2>>::apply(
377 &ch,
378 &pauli.x,
379 &pauli.z,
380 Complex64::new(1.0, 0.0),
381 &mut buf,
382 );
383 assert_eq!(len, 1);
384 assert!(approx_eq(bc[0], Complex64::new(scale, 0.0), TOL));
385 }
386
387 #[test]
390 fn amplitude_damping_passes_identity_through() {
391 let ch = AmplitudeDamping {
392 support: [0],
393 gamma: 0.3,
394 };
395 let p = PauliString::<1>::identity();
396 let (mut bx, mut bz, mut bc, mut len) = make_buf::<1>(2);
397 let mut buf = OutputBuffer::<1> {
398 x: &mut bx,
399 z: &mut bz,
400 coeff: &mut bc,
401 len: &mut len,
402 };
403 <AmplitudeDamping as Channel<1>>::apply(
404 &ch,
405 &p.x,
406 &p.z,
407 Complex64::new(2.0, 0.0),
408 &mut buf,
409 );
410 assert_eq!(len, 1);
411 assert_eq!(bx[0], p.x);
412 assert_eq!(bz[0], p.z);
413 assert!(approx_eq(bc[0], Complex64::new(2.0, 0.0), TOL));
414 }
415
416 #[test]
418 fn amplitude_damping_scales_x_and_y_by_sqrt() {
419 let gamma = 0.2;
420 let ch = AmplitudeDamping {
421 support: [0],
422 gamma,
423 };
424 let scale = (1.0 - gamma).sqrt();
425 for pauli in [PauliString::<1>::x(0), PauliString::<1>::y(0)] {
426 let (mut bx, mut bz, mut bc, mut len) = make_buf::<1>(2);
427 let mut buf = OutputBuffer::<1> {
428 x: &mut bx,
429 z: &mut bz,
430 coeff: &mut bc,
431 len: &mut len,
432 };
433 <AmplitudeDamping as Channel<1>>::apply(
434 &ch,
435 &pauli.x,
436 &pauli.z,
437 Complex64::new(1.0, 0.0),
438 &mut buf,
439 );
440 assert_eq!(len, 1);
441 assert_eq!(bx[0], pauli.x);
442 assert_eq!(bz[0], pauli.z);
443 assert!(approx_eq(bc[0], Complex64::new(scale, 0.0), TOL));
444 }
445 }
446
447 #[test]
451 fn amplitude_damping_z_fans_out_to_z_plus_i() {
452 let gamma = 0.25;
453 let ch = AmplitudeDamping {
454 support: [0],
455 gamma,
456 };
457 let p = PauliString::<1>::z(0);
458 let (mut bx, mut bz, mut bc, mut len) = make_buf::<1>(2);
459 let mut buf = OutputBuffer::<1> {
460 x: &mut bx,
461 z: &mut bz,
462 coeff: &mut bc,
463 len: &mut len,
464 };
465 <AmplitudeDamping as Channel<1>>::apply(
466 &ch,
467 &p.x,
468 &p.z,
469 Complex64::new(1.0, 0.0),
470 &mut buf,
471 );
472 assert_eq!(len, 2);
473 assert_eq!(bx[0], p.x);
475 assert_eq!(bz[0], p.z);
476 assert!(approx_eq(bc[0], Complex64::new(1.0 - gamma, 0.0), TOL));
477 let id = PauliString::<1>::identity();
479 assert_eq!(bx[1], id.x);
480 assert_eq!(bz[1], id.z);
481 assert!(approx_eq(bc[1], Complex64::new(gamma, 0.0), TOL));
482 }
483
484 #[test]
487 fn amplitude_damping_w2_word_boundary() {
488 let gamma = 0.4;
489 let ch = AmplitudeDamping {
490 support: [64],
491 gamma,
492 };
493 let p = PauliString::<2>::z(64);
494 let (mut bx, mut bz, mut bc, mut len) = make_buf::<2>(2);
495 let mut buf = OutputBuffer::<2> {
496 x: &mut bx,
497 z: &mut bz,
498 coeff: &mut bc,
499 len: &mut len,
500 };
501 <AmplitudeDamping as Channel<2>>::apply(
502 &ch,
503 &p.x,
504 &p.z,
505 Complex64::new(1.0, 0.0),
506 &mut buf,
507 );
508 assert_eq!(len, 2);
509 assert_eq!(bx[0], p.x);
510 assert_eq!(bz[0], p.z);
511 assert!(approx_eq(bc[0], Complex64::new(1.0 - gamma, 0.0), TOL));
512 assert_eq!(bx[1], [0u64, 0u64]);
514 assert_eq!(bz[1], [0u64, 0u64]);
515 assert!(approx_eq(bc[1], Complex64::new(gamma, 0.0), TOL));
516 }
517}