1use crate::prelude::*;
5use bitvec::view::BitView;
6use bitvec::view::BitViewSized;
7use num::complex::Complex;
8use num::{One, PrimInt, Zero};
9use smallvec::{SmallVec, smallvec};
10
11type MaskBuf<T> = SmallVec<[<<T as Config>::Storage as BitView>::Store; 8]>;
14
15macro_rules! impl_generalized_tableau_clifford {
18 ($name:ident) => {
19 fn $name(&mut self, index: usize) {
20 if self.is_inactive(index) {
21 return;
22 }
23 self.tableau.$name(index);
24 }
25 };
26}
27
28macro_rules! impl_generalized_tableau_clifford_pair {
30 ($name:ident) => {
31 fn $name(&mut self, control: usize, target: usize) {
32 if self.is_inactive(control) || self.is_inactive(target) {
33 return;
34 }
35 self.tableau.$name(control, target);
36 }
37 };
38}
39
40impl<T: Config> Clifford for Tableau<T>
51where
52 <T::Storage as BitView>::Store: PrimInt,
53{
54 #[inline]
55 fn x(&mut self, index: usize) {
56 let bits = std::mem::size_of::<<T::Storage as BitView>::Store>() * 8;
57 let wi = index / bits;
58 let off = index % bits;
59 let one = <T::Storage as BitView>::Store::one();
60 let zero = <T::Storage as BitView>::Store::zero();
61 let mask = one << off;
62 self.data.iter_mut().for_each(|pw| {
63 let zp = pw.word.zbits.data.as_raw_slice();
64 let zw = zp[wi];
65 pw.phase ^= (((zw & mask) != zero) as u8) << 1;
66 });
67 }
68
69 #[inline]
70 fn y(&mut self, index: usize) {
71 let bits = std::mem::size_of::<<T::Storage as BitView>::Store>() * 8;
72 let wi = index / bits;
73 let off = index % bits;
74 let one = <T::Storage as BitView>::Store::one();
75 let zero = <T::Storage as BitView>::Store::zero();
76 let mask = one << off;
77 self.data.iter_mut().for_each(|pw| {
78 let xp = pw.word.xbits.data.as_raw_slice();
79 let zp = pw.word.zbits.data.as_raw_slice();
80 let xw = xp[wi];
81 let zw = zp[wi];
82 pw.phase ^= ((((xw ^ zw) & mask) != zero) as u8) << 1;
83 });
84 }
85
86 #[inline]
87 fn z(&mut self, index: usize) {
88 let bits = std::mem::size_of::<<T::Storage as BitView>::Store>() * 8;
89 let wi = index / bits;
90 let off = index % bits;
91 let one = <T::Storage as BitView>::Store::one();
92 let zero = <T::Storage as BitView>::Store::zero();
93 let mask = one << off;
94 self.data.iter_mut().for_each(|pw| {
95 let xp = pw.word.xbits.data.as_raw_slice();
96 let xw = xp[wi];
97 pw.phase ^= (((xw & mask) != zero) as u8) << 1;
98 });
99 }
100
101 #[inline]
102 fn h(&mut self, index: usize) {
103 let bits = std::mem::size_of::<<T::Storage as BitView>::Store>() * 8;
104 let wi = index / bits;
105 let off = index % bits;
106 let one = <T::Storage as BitView>::Store::one();
107 let zero = <T::Storage as BitView>::Store::zero();
108 let mask = one << off;
109 self.data.iter_mut().for_each(|pw| {
110 let xp = pw.word.xbits.data.as_raw_mut_slice();
111 let zp = pw.word.zbits.data.as_raw_mut_slice();
112 let xw = xp[wi];
113 let zw = zp[wi];
114 let xb = xw & mask;
115 let zb = zw & mask;
116 xp[wi] = (xw & !mask) | zb;
117 zp[wi] = (zw & !mask) | xb;
118 pw.phase ^= (((xb & zb) != zero) as u8) << 1;
119 });
120 }
121
122 #[inline]
123 fn s(&mut self, index: usize) {
124 let bits = std::mem::size_of::<<T::Storage as BitView>::Store>() * 8;
127 let wi = index / bits;
128 let off = index % bits;
129 let one = <T::Storage as BitView>::Store::one();
130 let zero = <T::Storage as BitView>::Store::zero();
131 let mask = one << off;
132 self.data.iter_mut().for_each(|pw| {
133 let xp = pw.word.xbits.data.as_raw_slice();
134 let zp = pw.word.zbits.data.as_raw_mut_slice();
135 let xw = xp[wi];
136 let zw = zp[wi];
137 pw.phase ^= ((((xw & zw) & mask) != zero) as u8) << 1;
138 zp[wi] = zw ^ (xw & mask);
139 });
140 }
141
142 #[inline]
143 fn cnot(&mut self, control: usize, target: usize) {
144 let bits = std::mem::size_of::<<T::Storage as BitView>::Store>() * 8;
145 let one = <T::Storage as BitView>::Store::one();
146 let zero = <T::Storage as BitView>::Store::zero();
147 let (wc, sc) = (control / bits, control % bits);
148 let (wt, st) = (target / bits, target % bits);
149 self.data.iter_mut().for_each(|pw| {
150 let xp = pw.word.xbits.data.as_raw_mut_slice();
151 let zp = pw.word.zbits.data.as_raw_mut_slice();
152 let xa = (xp[wc] >> sc) & one;
153 let za = (zp[wc] >> sc) & one;
154 let xb = (xp[wt] >> st) & one;
155 let zb = (zp[wt] >> st) & one;
156 let phase_flip = (xa & zb) & (xb ^ za ^ one);
157 pw.phase ^= (((phase_flip & one) != zero) as u8) << 1;
158 zp[wc] = zp[wc] ^ (zb << sc);
159 xp[wt] = xp[wt] ^ (xa << st);
160 });
161 }
162
163 #[inline]
164 fn cz(&mut self, control: usize, target: usize) {
165 let bits = std::mem::size_of::<<T::Storage as BitView>::Store>() * 8;
166 let one = <T::Storage as BitView>::Store::one();
167 let zero = <T::Storage as BitView>::Store::zero();
168 let (wc, sc) = (control / bits, control % bits);
169 let (wt, st) = (target / bits, target % bits);
170 self.data.iter_mut().for_each(|pw| {
171 let xp = pw.word.xbits.data.as_raw_slice();
172 let zp = pw.word.zbits.data.as_raw_mut_slice();
173 let xa = (xp[wc] >> sc) & one;
174 let za = (zp[wc] >> sc) & one;
175 let xb = (xp[wt] >> st) & one;
176 let zb = (zp[wt] >> st) & one;
177 let phase_flip = (xa & xb) & (za ^ zb);
178 pw.phase ^= (((phase_flip & one) != zero) as u8) << 1;
179 zp[wc] = zp[wc] ^ (xb << sc);
180 zp[wt] = zp[wt] ^ (xa << st);
181 });
182 }
183}
184
185impl<T: Config> CliffordExtensions for Tableau<T>
186where
187 <T::Storage as BitView>::Store: PrimInt,
188{
189 #[inline]
199 fn s_dag(&mut self, index: usize) {
200 let bits = std::mem::size_of::<<T::Storage as BitView>::Store>() * 8;
203 let wi = index / bits;
204 let off = index % bits;
205 let one = <T::Storage as BitView>::Store::one();
206 let zero = <T::Storage as BitView>::Store::zero();
207 let mask = one << off;
208 self.data.iter_mut().for_each(|pw| {
209 let xp = pw.word.xbits.data.as_raw_slice();
210 let zp = pw.word.zbits.data.as_raw_mut_slice();
211 let xw = xp[wi];
212 let zw = zp[wi];
213 pw.phase ^= ((((xw & !zw) & mask) != zero) as u8) << 1;
214 zp[wi] = zw ^ (xw & mask);
215 });
216 }
217
218 #[inline]
219 fn sqrt_x(&mut self, index: usize) {
220 let bits = std::mem::size_of::<<T::Storage as BitView>::Store>() * 8;
221 let wi = index / bits;
222 let off = index % bits;
223 let one = <T::Storage as BitView>::Store::one();
224 let zero = <T::Storage as BitView>::Store::zero();
225 let mask = one << off;
226 self.data.iter_mut().for_each(|pw| {
227 let xp = pw.word.xbits.data.as_raw_mut_slice();
228 let zp = pw.word.zbits.data.as_raw_slice();
229 let xw = xp[wi];
230 let zw = zp[wi];
231 pw.phase ^= ((((zw & !xw) & mask) != zero) as u8) << 1;
232 xp[wi] = xw ^ (zw & mask);
233 });
234 }
235
236 #[inline]
237 fn sqrt_x_dag(&mut self, index: usize) {
238 let bits = std::mem::size_of::<<T::Storage as BitView>::Store>() * 8;
239 let wi = index / bits;
240 let off = index % bits;
241 let one = <T::Storage as BitView>::Store::one();
242 let zero = <T::Storage as BitView>::Store::zero();
243 let mask = one << off;
244 self.data.iter_mut().for_each(|pw| {
245 let xp = pw.word.xbits.data.as_raw_mut_slice();
246 let zp = pw.word.zbits.data.as_raw_slice();
247 let xw = xp[wi];
248 let zw = zp[wi];
249 pw.phase ^= ((((xw & zw) & mask) != zero) as u8) << 1;
250 xp[wi] = xw ^ (zw & mask);
251 });
252 }
253
254 #[inline]
255 fn sqrt_y(&mut self, index: usize) {
256 let bits = std::mem::size_of::<<T::Storage as BitView>::Store>() * 8;
257 let wi = index / bits;
258 let off = index % bits;
259 let one = <T::Storage as BitView>::Store::one();
260 let zero = <T::Storage as BitView>::Store::zero();
261 let mask = one << off;
262 self.data.iter_mut().for_each(|pw| {
263 let xp = pw.word.xbits.data.as_raw_mut_slice();
264 let zp = pw.word.zbits.data.as_raw_mut_slice();
265 let xw = xp[wi];
266 let zw = zp[wi];
267 let xb = xw & mask;
268 let zb = zw & mask;
269 xp[wi] = (xw & !mask) | zb;
270 zp[wi] = (zw & !mask) | xb;
271 pw.phase ^= ((((xw & !zw) & mask) != zero) as u8) << 1;
272 });
273 }
274
275 #[inline]
276 fn sqrt_y_dag(&mut self, index: usize) {
277 let bits = std::mem::size_of::<<T::Storage as BitView>::Store>() * 8;
278 let wi = index / bits;
279 let off = index % bits;
280 let one = <T::Storage as BitView>::Store::one();
281 let zero = <T::Storage as BitView>::Store::zero();
282 let mask = one << off;
283 self.data.iter_mut().for_each(|pw| {
284 let xp = pw.word.xbits.data.as_raw_mut_slice();
285 let zp = pw.word.zbits.data.as_raw_mut_slice();
286 let xw = xp[wi];
287 let zw = zp[wi];
288 let xb = xw & mask;
289 let zb = zw & mask;
290 xp[wi] = (xw & !mask) | zb;
291 zp[wi] = (zw & !mask) | xb;
292 pw.phase ^= ((((zw & !xw) & mask) != zero) as u8) << 1;
293 });
294 }
295
296 #[inline]
307 fn cy(&mut self, control: usize, target: usize) {
308 let bits = std::mem::size_of::<<T::Storage as BitView>::Store>() * 8;
309 let one = <T::Storage as BitView>::Store::one();
310 let zero = <T::Storage as BitView>::Store::zero();
311 let (wc, sc) = (control / bits, control % bits);
312 let (wt, st) = (target / bits, target % bits);
313 self.data.iter_mut().for_each(|pw| {
314 let xp = pw.word.xbits.data.as_raw_mut_slice();
315 let zp = pw.word.zbits.data.as_raw_mut_slice();
316 let xc = (xp[wc] >> sc) & one;
317 let zc = (zp[wc] >> sc) & one;
318 let xt = (xp[wt] >> st) & one;
319 let zt = (zp[wt] >> st) & one;
320 let phase_flip = (xc & (xt ^ zt)) & (zc ^ zt ^ one);
321 pw.phase ^= (((phase_flip & one) != zero) as u8) << 1;
322 zp[wc] = zp[wc] ^ ((xt ^ zt) << sc);
323 xp[wt] = xp[wt] ^ (xc << st);
324 zp[wt] = zp[wt] ^ (xc << st);
325 });
326 }
327}
328
329impl<T: Config, I, C: SparseVector<Complex<T::Coeff>, I>> Clifford for GeneralizedTableau<T, I, C>
330where
331 Complex<<T as Config>::Coeff>: From<Complex<f64>>,
332 <T::Storage as BitView>::Store: PrimInt,
333{
334 impl_generalized_tableau_clifford!(x);
335 impl_generalized_tableau_clifford!(y);
336 impl_generalized_tableau_clifford!(z);
337 impl_generalized_tableau_clifford!(h);
338 impl_generalized_tableau_clifford!(s);
339 impl_generalized_tableau_clifford_pair!(cnot);
340 impl_generalized_tableau_clifford_pair!(cz);
341}
342
343impl<T: Config, I, C: SparseVector<Complex<T::Coeff>, I>> CliffordExtensions
344 for GeneralizedTableau<T, I, C>
345where
346 Complex<<T as Config>::Coeff>: From<Complex<f64>>,
347 <T::Storage as BitView>::Store: PrimInt,
348{
349 impl_generalized_tableau_clifford!(s_dag);
350 impl_generalized_tableau_clifford!(sqrt_x);
351 impl_generalized_tableau_clifford!(sqrt_x_dag);
352 impl_generalized_tableau_clifford!(sqrt_y);
353 impl_generalized_tableau_clifford!(sqrt_y_dag);
354 impl_generalized_tableau_clifford_pair!(cy);
355}
356
357#[inline]
360fn mask_bits<S: PrimInt>(masks: &[S]) -> u64 {
361 masks.iter().map(|m| u64::from(m.count_ones())).sum()
362}
363
364macro_rules! masks_or_scalar_fallback {
386 ($self:ident, $indices:ident, $fallback:ident) => {
387 match $self.build_masks($indices) {
388 Some((masks, n_words, false)) => (masks, n_words),
390 None => return,
392 Some((.., true)) => return $self.$fallback($indices),
394 }
395 };
396}
397
398macro_rules! repeated_index_loops {
405 ($($fallback:ident => $one:ident),+ $(,)?) => {
406 impl<T: Config> Tableau<T>
407 where
408 <T::Storage as BitView>::Store: PrimInt,
409 {
410 $(
411 #[cold]
412 #[inline(never)]
413 fn $fallback(&mut self, indices: &[usize]) {
414 for &q in indices {
415 self.$one(q);
416 }
417 }
418 )+
419 }
420 };
421}
422
423repeated_index_loops! {
424 x_each => x,
425 y_each => y,
426 z_each => z,
427 h_each => h,
428 s_each => s,
429 s_dag_each => s_dag,
430 sqrt_x_each => sqrt_x,
431 sqrt_x_dag_each => sqrt_x_dag,
432 sqrt_y_each => sqrt_y,
433 sqrt_y_dag_each => sqrt_y_dag,
434}
435
436impl<T: Config> Tableau<T>
437where
438 <T::Storage as BitView>::Store: PrimInt,
439{
440 #[inline]
454 fn build_masks(&self, indices: &[usize]) -> Option<(MaskBuf<T>, usize, bool)> {
455 if self.data.is_empty() || indices.is_empty() {
456 return None;
457 }
458 let n_words = self.data[0].word.xbits.data.as_raw_slice().len();
459 let bits_per_word = std::mem::size_of::<<T::Storage as BitView>::Store>() * 8;
460 let one = <T::Storage as BitView>::Store::one();
461 let zero = <T::Storage as BitView>::Store::zero();
462 let mut masks: MaskBuf<T> = smallvec![zero; n_words];
463 for &addr0 in indices {
464 masks[addr0 / bits_per_word] =
465 masks[addr0 / bits_per_word] | (one << (addr0 % bits_per_word));
466 }
467 let repeated = mask_bits(&masks) != indices.len() as u64;
468 Some((masks, n_words, repeated))
469 }
470}
471
472impl<T: Config> CliffordBatch for Tableau<T>
473where
474 <T::Storage as BitView>::Store: PrimInt,
475{
476 #[inline]
478 fn x_many(&mut self, indices: &[usize]) {
479 let (masks, n_words) = masks_or_scalar_fallback!(self, indices, x_each);
480 let zero = <T::Storage as BitView>::Store::zero();
481
482 self.data.iter_mut().for_each(|pw| {
483 let zp = pw.word.zbits.data.as_raw_slice();
484 let mut popcount = 0u32;
485 for wi in 0..n_words {
486 let mask = masks[wi];
487 if mask == zero {
488 continue;
489 }
490 popcount += (zp[wi] & mask).count_ones();
491 }
492 pw.phase ^= ((popcount & 1) as u8) << 1;
493 });
494 }
495
496 #[inline]
498 fn y_many(&mut self, indices: &[usize]) {
499 let (masks, n_words) = masks_or_scalar_fallback!(self, indices, y_each);
500 let zero = <T::Storage as BitView>::Store::zero();
501
502 self.data.iter_mut().for_each(|pw| {
503 let xp = pw.word.xbits.data.as_raw_slice();
504 let zp = pw.word.zbits.data.as_raw_slice();
505 let mut popcount = 0u32;
506 for wi in 0..n_words {
507 let mask = masks[wi];
508 if mask == zero {
509 continue;
510 }
511 popcount += ((xp[wi] ^ zp[wi]) & mask).count_ones();
512 }
513 pw.phase ^= ((popcount & 1) as u8) << 1;
514 });
515 }
516
517 #[inline]
519 fn z_many(&mut self, indices: &[usize]) {
520 let (masks, n_words) = masks_or_scalar_fallback!(self, indices, z_each);
521 let zero = <T::Storage as BitView>::Store::zero();
522
523 self.data.iter_mut().for_each(|pw| {
524 let xp = pw.word.xbits.data.as_raw_slice();
525 let mut popcount = 0u32;
526 for wi in 0..n_words {
527 let mask = masks[wi];
528 if mask == zero {
529 continue;
530 }
531 popcount += (xp[wi] & mask).count_ones();
532 }
533 pw.phase ^= ((popcount & 1) as u8) << 1;
534 });
535 }
536
537 #[inline]
539 fn s_many(&mut self, indices: &[usize]) {
540 let (masks, n_words) = masks_or_scalar_fallback!(self, indices, s_each);
541 let zero = <T::Storage as BitView>::Store::zero();
542
543 self.data.iter_mut().for_each(|pw| {
544 let xp = pw.word.xbits.data.as_raw_mut_slice();
545 let zp = pw.word.zbits.data.as_raw_mut_slice();
546 let mut popcount = 0u32;
547 for wi in 0..n_words {
548 let mask = masks[wi];
549 if mask == zero {
550 continue;
551 }
552 let xw = xp[wi];
553 let zw = zp[wi];
554 popcount += ((xw & zw) & mask).count_ones();
555 zp[wi] = zw ^ (xw & mask);
556 }
557 pw.phase ^= ((popcount & 1) as u8) << 1;
558 });
559 }
560
561 fn cnot_many(&mut self, pairs: &[(usize, usize)]) {
565 let bits = std::mem::size_of::<<T::Storage as BitView>::Store>() * 8;
566 let one = <T::Storage as BitView>::Store::one();
567 let zero = <T::Storage as BitView>::Store::zero();
568
569 self.data.iter_mut().for_each(|pw| {
570 let xp = pw.word.xbits.data.as_raw_mut_slice();
571 let zp = pw.word.zbits.data.as_raw_mut_slice();
572 let mut phase_flips = zero;
573 for &(control, target) in pairs {
574 let (wc, sc) = (control / bits, control % bits);
575 let (wt, st) = (target / bits, target % bits);
576 let xa = (xp[wc] >> sc) & one;
577 let za = (zp[wc] >> sc) & one;
578 let xb = (xp[wt] >> st) & one;
579 let zb = (zp[wt] >> st) & one;
580 phase_flips = phase_flips ^ ((xa & zb) & (xb ^ za ^ one));
582 zp[wc] = zp[wc] ^ (zb << sc);
584 xp[wt] = xp[wt] ^ (xa << st);
585 }
586 pw.phase ^= (((phase_flips & one) != zero) as u8) << 1;
587 });
588 }
589
590 #[inline]
594 fn h_many(&mut self, indices: &[usize]) {
595 let (masks, n_words) = masks_or_scalar_fallback!(self, indices, h_each);
596 let zero = <T::Storage as BitView>::Store::zero();
597
598 self.data.iter_mut().for_each(|pw| {
599 let xp = pw.word.xbits.data.as_raw_mut_slice();
600 let zp = pw.word.zbits.data.as_raw_mut_slice();
601 for wi in 0..n_words {
602 let mask = masks[wi];
603 if mask == zero {
604 continue;
605 }
606 let not_mask = !mask;
607 let xw = xp[wi];
608 let zw = zp[wi];
609 let x_bits = xw & mask;
610 let z_bits = zw & mask;
611 xp[wi] = (xw & not_mask) | z_bits;
612 zp[wi] = (zw & not_mask) | x_bits;
613 let phase_bits = x_bits & z_bits;
614 pw.phase ^= ((phase_bits.count_ones() & 1) as u8) << 1;
615 }
616 });
617 }
618
619 fn cz_many(&mut self, pairs: &[(usize, usize)]) {
623 let bits = std::mem::size_of::<<T::Storage as BitView>::Store>() * 8;
624 let one = <T::Storage as BitView>::Store::one();
625 let zero = <T::Storage as BitView>::Store::zero();
626
627 self.data.iter_mut().for_each(|pw| {
628 let xp = pw.word.xbits.data.as_raw_slice();
629 let zp = pw.word.zbits.data.as_raw_mut_slice();
630 let mut phase_flips = zero;
631 for &(control, target) in pairs {
632 let (wc, sc) = (control / bits, control % bits);
633 let (wt, st) = (target / bits, target % bits);
634 let xa = (xp[wc] >> sc) & one;
635 let za = (zp[wc] >> sc) & one;
636 let xb = (xp[wt] >> st) & one;
637 let zb = (zp[wt] >> st) & one;
638 phase_flips = phase_flips ^ ((xa & xb) & (za ^ zb));
640 zp[wc] = zp[wc] ^ (xb << sc);
642 zp[wt] = zp[wt] ^ (xa << st);
643 }
644 pw.phase ^= (((phase_flips & one) != zero) as u8) << 1;
645 });
646 }
647}
648
649impl<T: Config> CliffordExtensionsBatch for Tableau<T>
650where
651 <T::Storage as BitView>::Store: PrimInt,
652{
653 #[inline]
656 fn s_dag_many(&mut self, indices: &[usize]) {
657 let (masks, n_words) = masks_or_scalar_fallback!(self, indices, s_dag_each);
658 let zero = <T::Storage as BitView>::Store::zero();
659
660 self.data.iter_mut().for_each(|pw| {
661 let xp = pw.word.xbits.data.as_raw_mut_slice();
662 let zp = pw.word.zbits.data.as_raw_mut_slice();
663 let mut popcount = 0u32;
664 for wi in 0..n_words {
665 let mask = masks[wi];
666 if mask == zero {
667 continue;
668 }
669 let xw = xp[wi];
670 let zw = zp[wi];
671 popcount += ((xw & !zw) & mask).count_ones();
672 zp[wi] = zw ^ (xw & mask);
673 }
674 pw.phase ^= ((popcount & 1) as u8) << 1;
675 });
676 }
677
678 fn cy_many(&mut self, pairs: &[(usize, usize)]) {
681 let bits = std::mem::size_of::<<T::Storage as BitView>::Store>() * 8;
682 let one = <T::Storage as BitView>::Store::one();
683 let zero = <T::Storage as BitView>::Store::zero();
684
685 self.data.iter_mut().for_each(|pw| {
686 let xp = pw.word.xbits.data.as_raw_mut_slice();
687 let zp = pw.word.zbits.data.as_raw_mut_slice();
688 let mut phase_flips = zero;
689 for &(control, target) in pairs {
690 let (wc, sc) = (control / bits, control % bits);
691 let (wt, st) = (target / bits, target % bits);
692 let xc = (xp[wc] >> sc) & one;
693 let zc = (zp[wc] >> sc) & one;
694 let xt = (xp[wt] >> st) & one;
695 let zt = (zp[wt] >> st) & one;
696 phase_flips = phase_flips ^ ((xc & (xt ^ zt)) & (zc ^ zt ^ one));
698 zp[wc] = zp[wc] ^ ((xt ^ zt) << sc);
700 xp[wt] = xp[wt] ^ (xc << st);
701 zp[wt] = zp[wt] ^ (xc << st);
702 }
703 pw.phase ^= (((phase_flips & one) != zero) as u8) << 1;
704 });
705 }
706
707 #[inline]
711 fn sqrt_y_many(&mut self, indices: &[usize]) {
712 let (masks, n_words) = masks_or_scalar_fallback!(self, indices, sqrt_y_each);
713 let zero = <T::Storage as BitView>::Store::zero();
714
715 self.data.iter_mut().for_each(|pw| {
716 let xp = pw.word.xbits.data.as_raw_mut_slice();
717 let zp = pw.word.zbits.data.as_raw_mut_slice();
718 for wi in 0..n_words {
719 let mask = masks[wi];
720 if mask == zero {
721 continue;
722 }
723 let not_mask = !mask;
724 let xw = xp[wi];
725 let zw = zp[wi];
726 let x_bits = xw & mask;
727 let z_bits = zw & mask;
728 xp[wi] = (xw & not_mask) | z_bits;
729 zp[wi] = (zw & not_mask) | x_bits;
730 let phase_bits = x_bits & !z_bits;
731 pw.phase ^= ((phase_bits.count_ones() & 1) as u8) << 1;
732 }
733 });
734 }
735
736 #[inline]
738 fn sqrt_y_dag_many(&mut self, indices: &[usize]) {
739 let (masks, n_words) = masks_or_scalar_fallback!(self, indices, sqrt_y_dag_each);
740 let zero = <T::Storage as BitView>::Store::zero();
741
742 self.data.iter_mut().for_each(|pw| {
743 let xp = pw.word.xbits.data.as_raw_mut_slice();
744 let zp = pw.word.zbits.data.as_raw_mut_slice();
745 for wi in 0..n_words {
746 let mask = masks[wi];
747 if mask == zero {
748 continue;
749 }
750 let not_mask = !mask;
751 let xw = xp[wi];
752 let zw = zp[wi];
753 let x_bits = xw & mask;
754 let z_bits = zw & mask;
755 xp[wi] = (xw & not_mask) | z_bits;
756 zp[wi] = (zw & not_mask) | x_bits;
757 let phase_bits = z_bits & !x_bits;
758 pw.phase ^= ((phase_bits.count_ones() & 1) as u8) << 1;
759 }
760 });
761 }
762
763 #[inline]
765 fn sqrt_x_many(&mut self, indices: &[usize]) {
766 let (masks, n_words) = masks_or_scalar_fallback!(self, indices, sqrt_x_each);
767 let zero = <T::Storage as BitView>::Store::zero();
768
769 self.data.iter_mut().for_each(|pw| {
770 let xp = pw.word.xbits.data.as_raw_mut_slice();
771 let zp = pw.word.zbits.data.as_raw_mut_slice();
772 for wi in 0..n_words {
773 let mask = masks[wi];
774 if mask == zero {
775 continue;
776 }
777 let xw = xp[wi];
778 let zw = zp[wi];
779 let phase_bits = (zw & !xw) & mask;
780 pw.phase ^= ((phase_bits.count_ones() & 1) as u8) << 1;
781 xp[wi] = xw ^ (zw & mask);
782 }
783 });
784 }
785
786 #[inline]
788 fn sqrt_x_dag_many(&mut self, indices: &[usize]) {
789 let (masks, n_words) = masks_or_scalar_fallback!(self, indices, sqrt_x_dag_each);
790 let zero = <T::Storage as BitView>::Store::zero();
791
792 self.data.iter_mut().for_each(|pw| {
793 let xp = pw.word.xbits.data.as_raw_mut_slice();
794 let zp = pw.word.zbits.data.as_raw_mut_slice();
795 for wi in 0..n_words {
796 let mask = masks[wi];
797 if mask == zero {
798 continue;
799 }
800 let xw = xp[wi];
801 let zw = zp[wi];
802 let phase_bits = (xw & zw) & mask;
803 pw.phase ^= ((phase_bits.count_ones() & 1) as u8) << 1;
804 xp[wi] = xw ^ (zw & mask);
805 }
806 });
807 }
808}
809
810macro_rules! impl_gen_tableau_batch_single {
811 ($name:ident) => {
812 fn $name(&mut self, indices: &[usize]) {
813 if !self.any_inactive(indices) {
814 self.tableau.$name(indices);
815 return;
816 }
817 let filtered: Vec<usize> = indices
818 .iter()
819 .copied()
820 .filter(|&i| !self.is_inactive(i))
821 .collect();
822 self.tableau.$name(&filtered);
823 }
824 };
825}
826
827macro_rules! impl_gen_tableau_batch_pair {
828 ($name:ident) => {
829 fn $name(&mut self, pairs: &[(usize, usize)]) {
830 if !self.any_inactive_pair(pairs) {
831 self.tableau.$name(pairs);
832 return;
833 }
834 let filtered: Vec<(usize, usize)> = pairs
835 .iter()
836 .copied()
837 .filter(|&(c, t)| !self.is_inactive(c) && !self.is_inactive(t))
838 .collect();
839 self.tableau.$name(&filtered);
840 }
841 };
842}
843
844impl<T: Config, I, C: SparseVector<Complex<T::Coeff>, I>> CliffordBatch
845 for GeneralizedTableau<T, I, C>
846where
847 Complex<<T as Config>::Coeff>: From<Complex<f64>>,
848 <T::Storage as BitView>::Store: PrimInt,
849{
850 impl_gen_tableau_batch_single!(x_many);
851 impl_gen_tableau_batch_single!(y_many);
852 impl_gen_tableau_batch_single!(z_many);
853 impl_gen_tableau_batch_single!(h_many);
854 impl_gen_tableau_batch_single!(s_many);
855 impl_gen_tableau_batch_pair!(cnot_many);
856 impl_gen_tableau_batch_pair!(cz_many);
857}
858
859impl<T: Config, I, C: SparseVector<Complex<T::Coeff>, I>> CliffordExtensionsBatch
860 for GeneralizedTableau<T, I, C>
861where
862 Complex<<T as Config>::Coeff>: From<Complex<f64>>,
863 <T::Storage as BitView>::Store: PrimInt,
864{
865 impl_gen_tableau_batch_single!(s_dag_many);
866 impl_gen_tableau_batch_single!(sqrt_x_many);
867 impl_gen_tableau_batch_single!(sqrt_x_dag_many);
868 impl_gen_tableau_batch_single!(sqrt_y_many);
869 impl_gen_tableau_batch_single!(sqrt_y_dag_many);
870 impl_gen_tableau_batch_pair!(cy_many);
871}
872
873#[cfg(test)]
874mod tests {
875 use super::*;
876 use ppvm_pauli_sum::config::fxhash::ByteF64;
877
878 type TestConfig = ByteF64<1>;
879 type TestTableau = GeneralizedTableau<TestConfig>;
880
881 fn rows(tab: &TestTableau) -> [(bool, bool, u8); 2] {
883 [0, 1].map(|i| {
884 let pw = &tab.tableau.data[i];
885 (pw.word.xbits[0], pw.word.zbits[0], pw.phase)
886 })
887 }
888
889 #[test]
892 fn test_sqrt_x_stabilizer() {
893 let mut tab: TestTableau = GeneralizedTableau::new(1, 1e-12);
895 tab.sqrt_x(0);
896 let r = rows(&tab);
897 assert_eq!(r[0], (true, false, 0), "destabilizer X should stay X");
898 assert_eq!(r[1], (true, true, 2), "stabilizer Z should become -Y");
899 }
900
901 #[test]
902 fn test_sqrt_x_dag_stabilizer() {
903 let mut tab: TestTableau = GeneralizedTableau::new(1, 1e-12);
905 tab.sqrt_x_dag(0);
906 let r = rows(&tab);
907 assert_eq!(r[0], (true, false, 0), "destabilizer X should stay X");
908 assert_eq!(r[1], (true, true, 0), "stabilizer Z should become +Y");
909 }
910
911 #[test]
912 fn test_sqrt_y_stabilizer() {
913 let mut tab: TestTableau = GeneralizedTableau::new(1, 1e-12);
915 tab.sqrt_y(0);
916 let r = rows(&tab);
917 assert_eq!(r[0], (false, true, 2), "destabilizer X should become -Z");
918 assert_eq!(r[1], (true, false, 0), "stabilizer Z should become +X");
919 }
920
921 #[test]
922 fn test_sqrt_y_dag_stabilizer() {
923 let mut tab: TestTableau = GeneralizedTableau::new(1, 1e-12);
925 tab.sqrt_y_dag(0);
926 let r = rows(&tab);
927 assert_eq!(r[0], (false, true, 0), "destabilizer X should become +Z");
928 assert_eq!(r[1], (true, false, 2), "stabilizer Z should become -X");
929 }
930
931 #[test]
932 fn test_sqrt_x_round_trip() {
933 let initial = rows(&GeneralizedTableau::new(1, 1e-12));
934 let mut tab: TestTableau = GeneralizedTableau::new(1, 1e-12);
935 tab.sqrt_x(0);
936 tab.sqrt_x_dag(0);
937 assert_eq!(rows(&tab), initial);
938 }
939
940 #[test]
941 fn test_sqrt_y_round_trip() {
942 let initial = rows(&GeneralizedTableau::new(1, 1e-12));
943 let mut tab: TestTableau = GeneralizedTableau::new(1, 1e-12);
944 tab.sqrt_y(0);
945 tab.sqrt_y_dag(0);
946 assert_eq!(rows(&tab), initial);
947 }
948
949 #[test]
950 fn test_sqrt_x_fourth_power_is_identity() {
951 let initial = rows(&GeneralizedTableau::new(1, 1e-12));
952 let mut tab: TestTableau = GeneralizedTableau::new(1, 1e-12);
953 for _ in 0..4 {
954 tab.sqrt_x(0);
955 }
956 assert_eq!(rows(&tab), initial);
957 }
958
959 #[test]
960 fn test_sqrt_y_fourth_power_is_identity() {
961 let initial = rows(&GeneralizedTableau::new(1, 1e-12));
962 let mut tab: TestTableau = GeneralizedTableau::new(1, 1e-12);
963 for _ in 0..4 {
964 tab.sqrt_y(0);
965 }
966 assert_eq!(rows(&tab), initial);
967 }
968
969 #[test]
970 fn test_sqrt_x_on_lost_qubit_is_noop() {
971 let initial = rows(&GeneralizedTableau::new(1, 1e-12));
972 let mut tab: TestTableau = GeneralizedTableau::new(1, 1e-12);
973 tab.qubit_status[0] = QubitStatus::Lost;
974 tab.sqrt_x(0);
975 assert_eq!(rows(&tab), initial);
976 }
977
978 #[test]
979 fn test_sqrt_y_on_lost_qubit_is_noop() {
980 let initial = rows(&GeneralizedTableau::new(1, 1e-12));
981 let mut tab: TestTableau = GeneralizedTableau::new(1, 1e-12);
982 tab.qubit_status[0] = QubitStatus::Lost;
983 tab.sqrt_y(0);
984 assert_eq!(rows(&tab), initial);
985 }
986
987 fn rows2(tab: &GeneralizedTableau<TestConfig>) -> [(bool, bool, bool, bool, u8); 4] {
989 [0, 1, 2, 3].map(|i| {
990 let pw = &tab.tableau.data[i];
991 (
992 pw.word.xbits[0],
993 pw.word.zbits[0],
994 pw.word.xbits[1],
995 pw.word.zbits[1],
996 pw.phase,
997 )
998 })
999 }
1000
1001 #[test]
1002 fn test_cy_stabilizers() {
1003 let mut tab: GeneralizedTableau<TestConfig> = GeneralizedTableau::new(2, 1e-12);
1011 tab.cy(0, 1);
1012 let r = rows2(&tab);
1013 assert_eq!(r[0], (true, false, true, true, 0), "XI should become +XY");
1014 assert_eq!(r[1], (false, true, true, false, 0), "IX should become ZX");
1015 assert_eq!(r[2], (false, true, false, false, 0), "ZI should stay ZI");
1016 assert_eq!(r[3], (false, true, false, true, 0), "IZ should become ZZ");
1017 }
1018
1019 #[test]
1020 fn test_cy_round_trip() {
1021 let initial = rows2(&GeneralizedTableau::new(2, 1e-12));
1023 let mut tab: GeneralizedTableau<TestConfig> = GeneralizedTableau::new(2, 1e-12);
1024 tab.cy(0, 1);
1025 tab.cy(0, 1);
1026 assert_eq!(rows2(&tab), initial);
1027 }
1028
1029 mod batch_tests {
1032 use super::*;
1033 use ppvm_pauli_sum::config::fxhash::ByteF64;
1034
1035 type TC = ByteF64<2>; type TTab = Tableau<TC>;
1037
1038 fn snapshot(tab: &TTab) -> Vec<(Vec<u8>, Vec<u8>, u8)> {
1040 tab.data
1041 .iter()
1042 .map(|pw| {
1043 (
1044 pw.word.xbits.data.as_raw_slice().to_vec(),
1045 pw.word.zbits.data.as_raw_slice().to_vec(),
1046 pw.phase,
1047 )
1048 })
1049 .collect()
1050 }
1051
1052 fn apply_individual_sqrt_y(n: usize, indices: &[usize]) -> Vec<(Vec<u8>, Vec<u8>, u8)> {
1054 let mut tab = TTab::new(n);
1055 tab.h(0);
1057 tab.h(3);
1058 tab.s(1);
1059 for &i in indices {
1060 tab.sqrt_y(i);
1061 }
1062 snapshot(&tab)
1063 }
1064
1065 fn apply_batch_sqrt_y(n: usize, indices: &[usize]) -> Vec<(Vec<u8>, Vec<u8>, u8)> {
1066 let mut tab = TTab::new(n);
1067 tab.h(0);
1068 tab.h(3);
1069 tab.s(1);
1070 tab.sqrt_y_many(indices);
1071 snapshot(&tab)
1072 }
1073
1074 #[test]
1075 fn test_sqrt_y_many_matches_individual() {
1076 let n = 8;
1077 let indices = vec![0, 2, 5, 7];
1078 assert_eq!(
1079 apply_individual_sqrt_y(n, &indices),
1080 apply_batch_sqrt_y(n, &indices)
1081 );
1082 }
1083
1084 #[test]
1085 fn test_sqrt_y_dag_many_matches_individual() {
1086 let n = 8;
1087 let indices = vec![1, 3, 4, 6];
1088 let mut tab_ind = TTab::new(n);
1089 tab_ind.h(0);
1090 tab_ind.s(2);
1091 for &i in &indices {
1092 tab_ind.sqrt_y_dag(i);
1093 }
1094 let mut tab_batch = TTab::new(n);
1095 tab_batch.h(0);
1096 tab_batch.s(2);
1097 tab_batch.sqrt_y_dag_many(&indices);
1098 assert_eq!(snapshot(&tab_ind), snapshot(&tab_batch));
1099 }
1100
1101 #[test]
1102 fn test_sqrt_x_many_matches_individual() {
1103 let n = 8;
1104 let indices = vec![0, 1, 4, 7];
1105 let mut tab_ind = TTab::new(n);
1106 tab_ind.h(2);
1107 tab_ind.s(5);
1108 for &i in &indices {
1109 tab_ind.sqrt_x(i);
1110 }
1111 let mut tab_batch = TTab::new(n);
1112 tab_batch.h(2);
1113 tab_batch.s(5);
1114 tab_batch.sqrt_x_many(&indices);
1115 assert_eq!(snapshot(&tab_ind), snapshot(&tab_batch));
1116 }
1117
1118 #[test]
1119 fn test_sqrt_x_dag_many_matches_individual() {
1120 let n = 8;
1121 let indices = vec![2, 3, 5, 6];
1122 let mut tab_ind = TTab::new(n);
1123 tab_ind.h(1);
1124 tab_ind.s(4);
1125 for &i in &indices {
1126 tab_ind.sqrt_x_dag(i);
1127 }
1128 let mut tab_batch = TTab::new(n);
1129 tab_batch.h(1);
1130 tab_batch.s(4);
1131 tab_batch.sqrt_x_dag_many(&indices);
1132 assert_eq!(snapshot(&tab_ind), snapshot(&tab_batch));
1133 }
1134
1135 #[test]
1136 fn test_h_many_matches_individual() {
1137 let n = 8;
1138 let indices = vec![0, 3, 5, 7];
1139 let mut tab_ind = TTab::new(n);
1140 tab_ind.s(1);
1141 tab_ind.sqrt_y(2);
1142 for &i in &indices {
1143 tab_ind.h(i);
1144 }
1145 let mut tab_batch = TTab::new(n);
1146 tab_batch.s(1);
1147 tab_batch.sqrt_y(2);
1148 tab_batch.h_many(&indices);
1149 assert_eq!(snapshot(&tab_ind), snapshot(&tab_batch));
1150 }
1151
1152 #[test]
1153 fn test_cz_many_matches_individual() {
1154 let n = 8;
1155 let pairs = vec![(0, 1), (2, 3), (4, 5)];
1156 let mut tab_ind = TTab::new(n);
1157 tab_ind.h(0);
1158 tab_ind.h(2);
1159 tab_ind.h(4);
1160 for &(c, t) in &pairs {
1161 tab_ind.cz(c, t);
1162 }
1163 let mut tab_batch = TTab::new(n);
1164 tab_batch.h(0);
1165 tab_batch.h(2);
1166 tab_batch.h(4);
1167 tab_batch.cz_many(&pairs);
1168 assert_eq!(snapshot(&tab_ind), snapshot(&tab_batch));
1169 }
1170
1171 #[test]
1172 fn test_cz_many_cross_word() {
1173 let n = 16;
1175 let pairs = vec![(1, 9), (8, 2), (7, 15), (10, 0)];
1176 let setup = [0usize, 7, 8, 9, 15];
1177 let mut tab_ind = TTab::new(n);
1178 for &q in &setup {
1179 tab_ind.h(q);
1180 }
1181 tab_ind.s(2);
1182 for &(c, t) in &pairs {
1183 tab_ind.cz(c, t);
1184 }
1185 let mut tab_batch = TTab::new(n);
1186 for &q in &setup {
1187 tab_batch.h(q);
1188 }
1189 tab_batch.s(2);
1190 tab_batch.cz_many(&pairs);
1191 assert_eq!(snapshot(&tab_ind), snapshot(&tab_batch));
1192 }
1193
1194 #[test]
1195 fn test_batch_empty_indices() {
1196 let n = 4;
1197 let initial = {
1198 let tab = TTab::new(n);
1199 snapshot(&tab)
1200 };
1201 let mut tab = TTab::new(n);
1202 tab.sqrt_y_many(&[]);
1203 assert_eq!(snapshot(&tab), initial);
1204 tab.sqrt_x_many(&[]);
1205 assert_eq!(snapshot(&tab), initial);
1206 tab.h_many(&[]);
1207 assert_eq!(snapshot(&tab), initial);
1208 tab.x_many(&[]);
1209 assert_eq!(snapshot(&tab), initial);
1210 tab.y_many(&[]);
1211 assert_eq!(snapshot(&tab), initial);
1212 tab.z_many(&[]);
1213 assert_eq!(snapshot(&tab), initial);
1214 tab.s_many(&[]);
1215 assert_eq!(snapshot(&tab), initial);
1216 tab.s_dag_many(&[]);
1217 assert_eq!(snapshot(&tab), initial);
1218 }
1219
1220 #[test]
1221 fn test_batch_all_qubits() {
1222 let n = 8;
1223 let all: Vec<usize> = (0..n).collect();
1224 let mut tab_ind = TTab::new(n);
1225 for &i in &all {
1226 tab_ind.sqrt_y(i);
1227 }
1228 let mut tab_batch = TTab::new(n);
1229 tab_batch.sqrt_y_many(&all);
1230 assert_eq!(snapshot(&tab_ind), snapshot(&tab_batch));
1231 }
1232
1233 #[test]
1234 fn test_batch_round_trip() {
1235 let n = 8;
1236 let indices = vec![1, 3, 5, 7];
1237 let initial = {
1238 let tab = TTab::new(n);
1239 snapshot(&tab)
1240 };
1241 let mut tab = TTab::new(n);
1242 tab.sqrt_y_many(&indices);
1243 tab.sqrt_y_dag_many(&indices);
1244 assert_eq!(snapshot(&tab), initial);
1245 }
1246
1247 #[test]
1248 fn test_x_many_matches_individual() {
1249 let n = 8;
1250 let indices = vec![0, 2, 5, 7];
1251 let mut tab_ind = TTab::new(n);
1252 tab_ind.h(0);
1253 tab_ind.s(3);
1254 for &i in &indices {
1255 tab_ind.x(i);
1256 }
1257 let mut tab_batch = TTab::new(n);
1258 tab_batch.h(0);
1259 tab_batch.s(3);
1260 tab_batch.x_many(&indices);
1261 assert_eq!(snapshot(&tab_ind), snapshot(&tab_batch));
1262 }
1263
1264 #[test]
1265 fn test_y_many_matches_individual() {
1266 let n = 8;
1267 let indices = vec![1, 3, 4, 6];
1268 let mut tab_ind = TTab::new(n);
1269 tab_ind.h(0);
1270 tab_ind.s(2);
1271 for &i in &indices {
1272 tab_ind.y(i);
1273 }
1274 let mut tab_batch = TTab::new(n);
1275 tab_batch.h(0);
1276 tab_batch.s(2);
1277 tab_batch.y_many(&indices);
1278 assert_eq!(snapshot(&tab_ind), snapshot(&tab_batch));
1279 }
1280
1281 #[test]
1282 fn test_z_many_matches_individual() {
1283 let n = 8;
1284 let indices = vec![0, 1, 4, 7];
1285 let mut tab_ind = TTab::new(n);
1286 tab_ind.h(2);
1287 tab_ind.s(5);
1288 for &i in &indices {
1289 tab_ind.z(i);
1290 }
1291 let mut tab_batch = TTab::new(n);
1292 tab_batch.h(2);
1293 tab_batch.s(5);
1294 tab_batch.z_many(&indices);
1295 assert_eq!(snapshot(&tab_ind), snapshot(&tab_batch));
1296 }
1297
1298 #[test]
1299 fn test_s_many_matches_individual() {
1300 let n = 8;
1301 let indices = vec![0, 2, 5, 7];
1302 let mut tab_ind = TTab::new(n);
1303 tab_ind.h(1);
1304 tab_ind.h(4);
1305 for &i in &indices {
1306 tab_ind.s(i);
1307 }
1308 let mut tab_batch = TTab::new(n);
1309 tab_batch.h(1);
1310 tab_batch.h(4);
1311 tab_batch.s_many(&indices);
1312 assert_eq!(snapshot(&tab_ind), snapshot(&tab_batch));
1313 }
1314
1315 #[test]
1316 fn test_s_dag_many_matches_individual() {
1317 let n = 8;
1318 let indices = vec![1, 3, 4, 6];
1319 let mut tab_ind = TTab::new(n);
1320 tab_ind.h(2);
1321 tab_ind.h(5);
1322 for &i in &indices {
1323 tab_ind.s_dag(i);
1324 }
1325 let mut tab_batch = TTab::new(n);
1326 tab_batch.h(2);
1327 tab_batch.h(5);
1328 tab_batch.s_dag_many(&indices);
1329 assert_eq!(snapshot(&tab_ind), snapshot(&tab_batch));
1330 }
1331
1332 #[test]
1333 fn test_cnot_many_matches_individual() {
1334 let n = 8;
1335 let pairs = vec![(0, 1), (2, 3), (4, 5)];
1336 let mut tab_ind = TTab::new(n);
1337 tab_ind.h(0);
1338 tab_ind.h(2);
1339 tab_ind.h(4);
1340 for &(c, t) in &pairs {
1341 tab_ind.cnot(c, t);
1342 }
1343 let mut tab_batch = TTab::new(n);
1344 tab_batch.h(0);
1345 tab_batch.h(2);
1346 tab_batch.h(4);
1347 tab_batch.cnot_many(&pairs);
1348 assert_eq!(snapshot(&tab_ind), snapshot(&tab_batch));
1349 }
1350
1351 #[test]
1352 fn test_cnot_many_cross_word() {
1353 let n = 16;
1355 let pairs = vec![(1, 9), (8, 2), (7, 15), (10, 0)];
1356 let setup = [0usize, 7, 8, 9, 15];
1357 let mut tab_ind = TTab::new(n);
1358 for &q in &setup {
1359 tab_ind.h(q);
1360 }
1361 tab_ind.s(2);
1362 for &(c, t) in &pairs {
1363 tab_ind.cnot(c, t);
1364 }
1365 let mut tab_batch = TTab::new(n);
1366 for &q in &setup {
1367 tab_batch.h(q);
1368 }
1369 tab_batch.s(2);
1370 tab_batch.cnot_many(&pairs);
1371 assert_eq!(snapshot(&tab_ind), snapshot(&tab_batch));
1372 }
1373
1374 #[test]
1375 fn test_cy_many_matches_individual() {
1376 let n = 8;
1377 let pairs = vec![(0, 1), (2, 3), (4, 5)];
1378 let mut tab_ind = TTab::new(n);
1379 tab_ind.h(0);
1380 tab_ind.h(2);
1381 tab_ind.s(4);
1382 for &(c, t) in &pairs {
1383 tab_ind.cy(c, t);
1384 }
1385 let mut tab_batch = TTab::new(n);
1386 tab_batch.h(0);
1387 tab_batch.h(2);
1388 tab_batch.s(4);
1389 tab_batch.cy_many(&pairs);
1390 assert_eq!(snapshot(&tab_ind), snapshot(&tab_batch));
1391 }
1392
1393 #[test]
1394 fn test_cy_many_cross_word() {
1395 let n = 16;
1397 let pairs = vec![(1, 9), (8, 2), (7, 15), (10, 0)];
1398 let setup = [0usize, 7, 8, 9, 15];
1399 let mut tab_ind = TTab::new(n);
1400 for &q in &setup {
1401 tab_ind.h(q);
1402 }
1403 tab_ind.s(2);
1404 for &(c, t) in &pairs {
1405 tab_ind.cy(c, t);
1406 }
1407 let mut tab_batch = TTab::new(n);
1408 for &q in &setup {
1409 tab_batch.h(q);
1410 }
1411 tab_batch.s(2);
1412 tab_batch.cy_many(&pairs);
1413 assert_eq!(snapshot(&tab_ind), snapshot(&tab_batch));
1414 }
1415
1416 #[test]
1417 fn test_batch_more_than_8_storage_words() {
1418 type BigConfig = ByteF64<16>;
1421 type BigTab = Tableau<BigConfig>;
1422
1423 fn big_snapshot(tab: &BigTab) -> Vec<(Vec<u8>, Vec<u8>, u8)> {
1424 tab.data
1425 .iter()
1426 .map(|pw| {
1427 (
1428 pw.word.xbits.data.as_raw_slice().to_vec(),
1429 pw.word.zbits.data.as_raw_slice().to_vec(),
1430 pw.phase,
1431 )
1432 })
1433 .collect()
1434 }
1435
1436 let n = 128;
1437 let indices: Vec<usize> = vec![0, 7, 8, 63, 64, 71, 72, 120, 127];
1440
1441 let mut tab_ind = BigTab::new(n);
1442 tab_ind.h(3);
1443 tab_ind.s(60);
1444 for &i in &indices {
1445 tab_ind.h(i);
1446 }
1447
1448 let mut tab_batch = BigTab::new(n);
1449 tab_batch.h(3);
1450 tab_batch.s(60);
1451 tab_batch.h_many(&indices);
1452
1453 assert_eq!(big_snapshot(&tab_ind), big_snapshot(&tab_batch));
1454 }
1455
1456 #[test]
1457 fn test_batch_fourth_power_identity() {
1458 let n = 8;
1459 let indices = vec![0, 2, 4, 6];
1460 let initial = {
1461 let tab = TTab::new(n);
1462 snapshot(&tab)
1463 };
1464 let mut tab = TTab::new(n);
1465 for _ in 0..4 {
1466 tab.sqrt_x_many(&indices);
1467 }
1468 assert_eq!(snapshot(&tab), initial);
1469 }
1470 }
1471
1472 mod multiword_canonical_tests {
1486 use super::*;
1487 use ppvm_pauli_sum::config::fxhash::ByteF64;
1488
1489 type TC = ByteF64<2>;
1491 type TTab = Tableau<TC>;
1492
1493 fn snapshot(tab: &TTab) -> Vec<(Vec<u8>, Vec<u8>, u8)> {
1494 tab.data
1495 .iter()
1496 .map(|pw| {
1497 (
1498 pw.word.xbits.data.as_raw_slice().to_vec(),
1499 pw.word.zbits.data.as_raw_slice().to_vec(),
1500 pw.phase,
1501 )
1502 })
1503 .collect()
1504 }
1505
1506 fn seed(tab: &mut TTab) {
1509 for &q in &[0usize, 3, 7, 8, 11, 15] {
1510 tab.h(q);
1511 }
1512 tab.s(2);
1513 tab.s(10);
1514 tab.sqrt_y(5);
1515 tab.sqrt_x(13);
1516 tab.cnot(1, 9);
1517 tab.cz(6, 14);
1518 }
1519
1520 const SINGLE_INDICES: [usize; 6] = [0, 7, 8, 11, 15, 4];
1523
1524 macro_rules! single_gate_matches_batch {
1528 ($test:ident, $gate:ident, $gate_many:ident) => {
1529 #[test]
1530 fn $test() {
1531 let n = 16;
1532 for &i in &SINGLE_INDICES {
1533 let mut tab_canon = TTab::new(n);
1534 seed(&mut tab_canon);
1535 tab_canon.$gate(i);
1536
1537 let mut tab_batch = TTab::new(n);
1538 seed(&mut tab_batch);
1539 tab_batch.$gate_many(&[i]);
1540
1541 assert_eq!(
1542 snapshot(&tab_canon),
1543 snapshot(&tab_batch),
1544 "canonical {} disagrees with batch {} at qubit {i}",
1545 stringify!($gate),
1546 stringify!($gate_many),
1547 );
1548 }
1549 }
1550 };
1551 }
1552
1553 single_gate_matches_batch!(canonical_x_matches_batch_multiword, x, x_many);
1554 single_gate_matches_batch!(canonical_y_matches_batch_multiword, y, y_many);
1555 single_gate_matches_batch!(canonical_z_matches_batch_multiword, z, z_many);
1556 single_gate_matches_batch!(canonical_h_matches_batch_multiword, h, h_many);
1557 single_gate_matches_batch!(canonical_s_matches_batch_multiword, s, s_many);
1558 single_gate_matches_batch!(canonical_s_dag_matches_batch_multiword, s_dag, s_dag_many);
1559 single_gate_matches_batch!(
1560 canonical_sqrt_x_matches_batch_multiword,
1561 sqrt_x,
1562 sqrt_x_many
1563 );
1564 single_gate_matches_batch!(
1565 canonical_sqrt_x_dag_matches_batch_multiword,
1566 sqrt_x_dag,
1567 sqrt_x_dag_many
1568 );
1569 single_gate_matches_batch!(
1570 canonical_sqrt_y_matches_batch_multiword,
1571 sqrt_y,
1572 sqrt_y_many
1573 );
1574 single_gate_matches_batch!(
1575 canonical_sqrt_y_dag_matches_batch_multiword,
1576 sqrt_y_dag,
1577 sqrt_y_dag_many
1578 );
1579
1580 const PAIRS: [(usize, usize); 4] = [(0, 3), (9, 12), (2, 10), (13, 5)];
1583
1584 macro_rules! pair_gate_matches_batch {
1587 ($test:ident, $gate:ident, $gate_many:ident) => {
1588 #[test]
1589 fn $test() {
1590 let n = 16;
1591 for &(c, t) in &PAIRS {
1592 let mut tab_canon = TTab::new(n);
1593 seed(&mut tab_canon);
1594 tab_canon.$gate(c, t);
1595
1596 let mut tab_batch = TTab::new(n);
1597 seed(&mut tab_batch);
1598 tab_batch.$gate_many(&[(c, t)]);
1599
1600 assert_eq!(
1601 snapshot(&tab_canon),
1602 snapshot(&tab_batch),
1603 "canonical {} disagrees with batch {} on pair ({c}, {t})",
1604 stringify!($gate),
1605 stringify!($gate_many),
1606 );
1607 }
1608 }
1609 };
1610 }
1611
1612 pair_gate_matches_batch!(canonical_cnot_matches_batch_multiword, cnot, cnot_many);
1613 pair_gate_matches_batch!(canonical_cz_matches_batch_multiword, cz, cz_many);
1614 pair_gate_matches_batch!(canonical_cy_matches_batch_multiword, cy, cy_many);
1615
1616 #[test]
1624 fn sqrt_x_on_second_word_qubit_known_transform() {
1625 let n = 16;
1628 let q = 11; let bit = 1u8 << (q % 8);
1630 let wi = q / 8; let mut tab = TTab::new(n);
1633 tab.sqrt_x(q);
1634
1635 let dz = &tab.data[q];
1637 assert_eq!(dz.word.xbits.data.as_raw_slice()[wi] & bit, bit);
1638 assert_eq!(dz.word.zbits.data.as_raw_slice()[wi] & bit, 0);
1639 assert_eq!(dz.phase, 0);
1640
1641 let st = &tab.data[n + q];
1643 assert_eq!(st.word.xbits.data.as_raw_slice()[wi] & bit, bit);
1644 assert_eq!(st.word.zbits.data.as_raw_slice()[wi] & bit, bit);
1645 assert_eq!(st.phase, 2);
1646 }
1647
1648 #[test]
1656 fn cnot_cross_word_known_transform() {
1657 let n = 16;
1658 let (c, t) = (2usize, 10usize);
1659 let mut tab = TTab::new(n);
1660 tab.cnot(c, t);
1661
1662 let cbit = 1u8 << (c % 8);
1663 let tbit = 1u8 << (t % 8);
1664 let (cw, tw) = (c / 8, t / 8);
1665
1666 let read = |row: usize| {
1668 let pw = &tab.data[row];
1669 let x = pw.word.xbits.data.as_raw_slice();
1670 let z = pw.word.zbits.data.as_raw_slice();
1671 (
1672 x[cw] & cbit != 0,
1673 z[cw] & cbit != 0,
1674 x[tw] & tbit != 0,
1675 z[tw] & tbit != 0,
1676 pw.phase,
1677 )
1678 };
1679
1680 assert_eq!(read(c), (true, false, true, false, 0));
1682 assert_eq!(read(n + c), (false, true, false, false, 0));
1684 assert_eq!(read(t), (false, false, true, false, 0));
1686 assert_eq!(read(n + t), (false, true, false, true, 0));
1688 }
1689 }
1690
1691 mod duplicate_index_tests {
1699 use super::*;
1700 use ppvm_pauli_sum::config::fxhash::ByteF64;
1701
1702 type TC = ByteF64<2>;
1704 type TTab = Tableau<TC>;
1705 type GenTab = GeneralizedTableau<TC>;
1706
1707 fn snapshot(tab: &TTab) -> Vec<(Vec<u8>, Vec<u8>, u8)> {
1708 tab.data
1709 .iter()
1710 .map(|pw| {
1711 (
1712 pw.word.xbits.data.as_raw_slice().to_vec(),
1713 pw.word.zbits.data.as_raw_slice().to_vec(),
1714 pw.phase,
1715 )
1716 })
1717 .collect()
1718 }
1719
1720 fn prepared(n: usize) -> TTab {
1724 let mut tab = TTab::new(n);
1725 for q in 0..n {
1726 tab.h(q);
1727 if q % 2 == 0 {
1728 tab.s(q);
1729 }
1730 }
1731 for q in 1..n {
1732 tab.cnot(q - 1, q);
1733 }
1734 tab
1735 }
1736
1737 macro_rules! check {
1740 ($many:ident, $one:ident, $indices:expr) => {{
1741 let indices: &[usize] = $indices;
1742 let mut batched = prepared(16);
1743 let mut looped = prepared(16);
1744 batched.$many(indices);
1745 for &q in indices {
1746 looped.$one(q);
1747 }
1748 assert_eq!(
1749 snapshot(&batched),
1750 snapshot(&looped),
1751 "{} on repeated indices {:?} must equal the {} loop",
1752 stringify!($many),
1753 indices,
1754 stringify!($one),
1755 );
1756 }};
1757 }
1758
1759 macro_rules! check_all {
1760 ($indices:expr) => {{
1761 check!(x_many, x, $indices);
1762 check!(y_many, y, $indices);
1763 check!(z_many, z, $indices);
1764 check!(h_many, h, $indices);
1765 check!(s_many, s, $indices);
1766 check!(s_dag_many, s_dag, $indices);
1767 check!(sqrt_x_many, sqrt_x, $indices);
1768 check!(sqrt_x_dag_many, sqrt_x_dag, $indices);
1769 check!(sqrt_y_many, sqrt_y, $indices);
1770 check!(sqrt_y_dag_many, sqrt_y_dag, $indices);
1771 }};
1772 }
1773
1774 #[test]
1775 fn duplicate_indices_batch_equals_per_index_loop() {
1776 check_all!(&[0, 0]);
1777 check_all!(&[1, 1, 1]);
1778 check_all!(&[0, 2, 2, 3]);
1779 }
1780
1781 #[test]
1784 fn duplicate_indices_equal_conjugation_by_g_squared() {
1785 for (name, apply) in [
1787 ("x_many", TTab::x_many as fn(&mut TTab, &[usize])),
1788 ("y_many", TTab::y_many),
1789 ("z_many", TTab::z_many),
1790 ("h_many", TTab::h_many),
1791 ] {
1792 let mut batched = prepared(16);
1793 let untouched = prepared(16);
1794 apply(&mut batched, &[1, 1]);
1795 assert_eq!(
1796 snapshot(&batched),
1797 snapshot(&untouched),
1798 "{name}(&[1, 1]) must be the identity (G² = I)"
1799 );
1800 }
1801
1802 for (name, apply, square) in [
1804 (
1805 "s_many",
1806 TTab::s_many as fn(&mut TTab, &[usize]),
1807 TTab::z as fn(&mut TTab, usize),
1808 ),
1809 ("s_dag_many", TTab::s_dag_many, TTab::z),
1810 ("sqrt_x_many", TTab::sqrt_x_many, TTab::x),
1811 ("sqrt_x_dag_many", TTab::sqrt_x_dag_many, TTab::x),
1812 ("sqrt_y_many", TTab::sqrt_y_many, TTab::y),
1813 ("sqrt_y_dag_many", TTab::sqrt_y_dag_many, TTab::y),
1814 ] {
1815 let mut batched = prepared(16);
1816 let mut squared = prepared(16);
1817 apply(&mut batched, &[1, 1]);
1818 square(&mut squared, 1);
1819 assert_eq!(
1820 snapshot(&batched),
1821 snapshot(&squared),
1822 "{name}(&[1, 1]) must equal conjugation by G²"
1823 );
1824 }
1825 }
1826
1827 #[test]
1830 fn duplicate_indices_batch_equals_loop_multiword() {
1831 check!(h_many, h, &[3, 10, 10]);
1832 check!(s_many, s, &[3, 10, 10]);
1833 check!(sqrt_y_many, sqrt_y, &[3, 10, 10]);
1834 }
1835
1836 #[test]
1839 fn duplicate_indices_generalized_tableau_equals_loop() {
1840 let indices: &[usize] = &[0, 2, 2];
1841 for lost in [None, Some(0usize)] {
1842 let mut batched: GenTab = GeneralizedTableau::new(16, 1e-12);
1843 let mut looped: GenTab = GeneralizedTableau::new(16, 1e-12);
1844 for tab in [&mut batched, &mut looped] {
1845 tab.tableau = prepared(16);
1846 if let Some(q) = lost {
1847 tab.qubit_status[q] = QubitStatus::Lost;
1848 }
1849 }
1850 batched.sqrt_x_many(indices);
1851 for &q in indices {
1852 looped.sqrt_x(q);
1853 }
1854 assert_eq!(
1855 snapshot(&batched.tableau),
1856 snapshot(&looped.tableau),
1857 "sqrt_x_many(&[0, 2, 2]) with lost = {lost:?}"
1858 );
1859 }
1860 }
1861
1862 #[test]
1865 fn batched_x_twice_on_ground_state_measures_zero() {
1866 let mut tab: GenTab = GeneralizedTableau::new(1, 1e-12);
1867 tab.x_many(&[0, 0]);
1868 assert_eq!(tab.measure(0), Some(false));
1869 }
1870 }
1871}