1use super::pauli::Pauli;
2use super::pauli_like::PauliLike;
3use crate::synthesis::pauli_network::chunks::CHUNK_CONJUGATION_SCORE;
4use itertools::izip;
5use std::cmp::max;
6use std::fmt;
7
8const WIDTH: usize = 64;
9
10fn get_stride(index: usize) -> usize {
11 index / WIDTH
12}
13
14fn get_offset(index: usize) -> usize {
15 index % WIDTH
16}
17
18#[derive(Clone, Debug, PartialEq)]
21pub struct PauliSet {
22 pub n: usize,
23 nstrides: usize,
24 noperators: usize,
25 start_offset: usize,
26 data_array: Vec<Vec<u64>>,
29 phases: Vec<u64>,
30}
31
32impl PauliSet {
33 pub fn new(n: usize) -> Self {
35 Self {
36 n,
37 nstrides: 0,
38 noperators: 0,
39 start_offset: 0,
40 data_array: vec![Vec::new(); 2 * n],
41 phases: Vec::new(),
42 }
43 }
44 pub fn new_empty(n: usize, m: usize) -> Self {
46 let nstrides = get_stride(m) + 1;
47 Self {
48 n,
49 nstrides,
50 noperators: m,
51 start_offset: 0,
52 data_array: vec![vec![0; nstrides]; 2 * n],
53 phases: vec![0; nstrides],
54 }
55 }
56 pub fn from_slice(data: &[String]) -> Self {
58 if data.is_empty() {
59 return Self::new(0);
60 }
61 let n = data.first().unwrap().len();
62 let mut pset = Self::new(n);
63 for piece in data {
64 pset.insert(piece, false);
65 }
66 pset
67 }
68 pub fn len(&self) -> usize {
70 self.noperators
71 }
72
73 pub fn is_empty(&self) -> bool {
74 self.noperators == 0
75 }
76
77 pub fn insert(&mut self, axis: &str, phase: bool) -> usize {
79 let stride = get_stride(self.noperators + self.start_offset);
80 let offset = get_offset(self.noperators + self.start_offset);
81 if stride == self.nstrides {
82 self.nstrides += 1;
83 self.data_array.iter_mut().for_each(|row| row.push(0));
84 self.phases.push(0);
85 }
86 if phase {
88 self.phases[stride] |= 1 << offset;
89 }
90 for (index, pauli) in axis.chars().enumerate() {
92 match pauli {
93 'Z' => self.data_array[index + self.n][stride] |= 1 << offset,
94 'X' => self.data_array[index][stride] |= 1 << offset,
95 'Y' => {
96 self.data_array[index][stride] |= 1 << offset;
97 self.data_array[index + self.n][stride] |= 1 << offset
98 }
99 _ => {}
100 }
101 }
102 self.noperators += 1;
103 self.noperators - 1
104 }
105
106 pub fn insert_vec_bool(&mut self, axis: &[bool], phase: bool) -> usize {
108 let stride = get_stride(self.noperators + self.start_offset);
109 let offset = get_offset(self.noperators + self.start_offset);
110 if stride == self.nstrides {
111 self.nstrides += 1;
112 self.data_array.iter_mut().for_each(|row| row.push(0));
113 self.phases.push(0);
114 }
115 if phase {
116 self.phases[stride] |= 1 << offset;
117 }
118 for (index, value) in axis.iter().enumerate() {
119 if *value {
120 self.data_array[index][stride] |= 1 << offset;
121 }
122 }
123 self.noperators += 1;
124 self.noperators - 1
125 }
126 pub fn insert_pauli(&mut self, pauli: &Pauli) -> usize {
127 self.insert_vec_bool(&pauli.data, pauli.phase == 2)
128 }
129 pub fn set_phase(&mut self, col: usize, phase: bool) {
130 let stride = get_stride(col);
131 let offset = get_offset(col);
132 if phase != ((self.phases[stride] >> offset & 1) != 0) {
133 self.phases[stride] ^= 1 << offset;
134 }
135 }
136
137 pub fn set_entry(&mut self, operator_index: usize, qbit: usize, x_part: bool, z_part: bool) {
138 let stride = get_stride(operator_index + self.start_offset);
139 let offset = get_offset(operator_index + self.start_offset);
140 if x_part != (1 == (self.data_array[qbit][stride] >> offset) & 1) {
141 self.data_array[qbit][stride] ^= 1 << offset;
142 }
143 if z_part != (1 == (self.data_array[qbit + self.n][stride] >> offset) & 1) {
144 self.data_array[qbit + self.n][stride] ^= 1 << offset;
145 }
146 }
147 pub fn set_raw_entry(&mut self, row: usize, col: usize, value: bool) {
148 let stride = get_stride(col);
149 let offset = get_offset(col);
150 if value != (1 == (self.data_array[row][stride] >> offset) & 1) {
151 self.data_array[row][stride] ^= 1 << offset;
152 }
153 }
154
155 pub fn clear(&mut self) {
157 for j in 0..self.nstrides {
158 for i in 0..2 * self.n {
159 self.data_array[i][j] = 0;
160 }
161 self.phases[j] = 0;
162 }
163 self.noperators = 0;
164 self.start_offset = 0;
165 }
166 pub fn pop(&mut self) {
168 let stride = get_stride(self.start_offset);
169 let offset = get_offset(self.start_offset);
170 for i in 0..2 * self.n {
171 self.data_array[i][stride] &= !(1 << offset);
172 }
173 self.phases[stride] &= !(1 << offset);
174 self.start_offset += 1;
175 self.noperators -= 1;
176 }
177 pub fn pop_last(&mut self) {
179 let stride = get_stride(self.start_offset + self.noperators - 1);
180 let offset = get_offset(self.start_offset + self.noperators - 1);
181 for i in 0..2 * self.n {
182 self.data_array[i][stride] &= !(1 << offset);
183 }
184 self.phases[stride] &= !(1 << offset);
185 self.noperators -= 1;
186 }
187 pub fn set_to_identity(&mut self, operator_index: usize) {
189 for i in 0..self.n {
191 self.set_entry(operator_index, i, false, false);
192 }
193 }
194 pub fn get(&self, operator_index: usize) -> (bool, String) {
196 let operator_index = operator_index + self.start_offset;
197 let mut output = String::new();
198 let stride = get_stride(operator_index);
199 let offset = get_offset(operator_index);
200 for i in 0..self.n {
201 match (
202 (self.data_array[i][stride] >> offset) & 1,
203 (self.data_array[i + self.n][stride] >> offset) & 1,
204 ) {
205 (1, 0) => {
206 output += "X";
207 }
208 (0, 1) => {
209 output += "Z";
210 }
211 (1, 1) => {
212 output += "Y";
213 }
214 _ => {
215 output += "I";
216 }
217 }
218 }
219 (((self.phases[stride] >> offset) & 1 != 0), output)
220 }
221
222 pub fn get_as_vec_bool(&self, operator_index: usize) -> (bool, Vec<bool>) {
224 let operator_index = operator_index + self.start_offset;
225 let mut output = Vec::new();
226 let stride = get_stride(operator_index);
227 let offset = get_offset(operator_index);
228 for i in 0..2 * self.n {
229 output.push(((self.data_array[i][stride] >> offset) & 1) != 0);
230 }
231 (((self.phases[stride] >> offset) & 1 != 0), output)
232 }
233
234 pub fn get_as_pauli(&self, operator_index: usize) -> Pauli {
236 let (phase, data) = self.get_as_vec_bool(operator_index);
237 Pauli::from_vec_bool(data, if phase { 2 } else { 0 })
238 }
239 pub fn get_entry(&self, row: usize, col: usize) -> bool {
241 let col = col + self.start_offset;
242 let stride = get_stride(col);
243 let offset = get_offset(col);
244 ((self.data_array[row][stride] >> offset) & 1) != 0
245 }
246 pub fn get_phase(&self, col: usize) -> bool {
247 let col = col + self.start_offset;
248 let stride = get_stride(col);
249 let offset = get_offset(col);
250 ((self.phases[stride] >> offset) & 1) != 0
251 }
252
253 pub fn get_i_factors(&self) -> Vec<u8> {
254 let mut output = Vec::new();
255 for i in 0..self.len() {
256 let mut ifact: u8 = 0;
257 for j in 0..self.n {
258 if self.get_entry(j, i) & self.get_entry(j + self.n, i) {
259 ifact += 1;
260 }
261 }
262 output.push(ifact % 4);
263 }
264 output
265 }
266 pub fn get_i_factors_single_col(&self, col: usize) -> u8 {
267 let mut ifact: u8 = 0;
268 for j in 0..self.n {
269 if self.get_entry(j, col) & self.get_entry(j + self.n, col) {
270 ifact += 1;
271 }
272 }
273 ifact % 4
274 }
275 pub fn get_inverse_z(&self, qbit: usize) -> (bool, String) {
277 let mut pstring = String::new();
278 for i in 0..self.n {
279 let x_bit = self.get_entry(qbit, i + self.n);
280 let z_bit = self.get_entry(qbit, i);
281 match (x_bit, z_bit) {
282 (false, false) => {
283 pstring.push('I');
284 }
285 (true, false) => {
286 pstring.push('X');
287 }
288 (false, true) => {
289 pstring.push('Z');
290 }
291 (true, true) => {
292 pstring.push('Y');
293 }
294 }
295 }
296 (self.get_phase(qbit + self.n), pstring)
297 }
298 pub fn get_inverse_x(&self, qbit: usize) -> (bool, String) {
300 let mut pstring = String::new();
301 let mut cy = 0;
302 for i in 0..self.n {
303 let x_bit = self.get_entry(qbit + self.n, i + self.n);
304 let z_bit = self.get_entry(qbit + self.n, i);
305 match (x_bit, z_bit) {
306 (false, false) => {
307 pstring.push('I');
308 }
309 (true, false) => {
310 pstring.push('X');
311 }
312 (false, true) => {
313 pstring.push('Z');
314 }
315 (true, true) => {
316 pstring.push('Y');
317 cy += 1;
318 }
319 }
320 }
321 ((cy % 2 != 0), pstring)
322 }
323
324 pub fn and_row_acc(&self, row: usize, vec: &[bool]) -> bool {
326 let mut output = false;
327 for (i, item) in vec.iter().enumerate().take(2 * self.n) {
328 output ^= self.get_entry(row, i) & item;
329 }
330 output
331 }
332
333 pub fn equals(&self, i: usize, j: usize) -> bool {
335 let (_, vec1) = self.get_as_vec_bool(i);
336 let (_, vec2) = self.get_as_vec_bool(j);
337 vec1 == vec2
338 }
339
340 pub fn commute(&self, i: usize, j: usize) -> bool {
342 let mut count_diff = 0;
343 for k in 0..self.n {
344 if (self.get_entry(k, i) & self.get_entry(k + self.n, j))
345 ^ (self.get_entry(k + self.n, i) & self.get_entry(k, j))
346 {
347 count_diff += 1;
348 }
349 }
350 (count_diff % 2) == 0
351 }
352
353 pub fn get_support(&self, index: usize) -> Vec<usize> {
355 let mut support = Vec::new();
356 let index = index + self.start_offset;
357 let stride = get_stride(index);
358 let offset = get_offset(index);
359 for i in 0..self.n {
360 if (((self.data_array[i][stride] | self.data_array[i + self.n][stride]) >> offset) & 1)
361 != 0
362 {
363 support.push(i);
364 }
365 }
366 support
367 }
368
369 pub fn support_size(&self, index: usize) -> usize {
371 let index = index + self.start_offset;
372 let mut count = 0;
373 let stride = get_stride(index);
374 let offset = get_offset(index);
375 for i in 0..self.n {
376 if (((self.data_array[i][stride] | self.data_array[i + self.n][stride]) >> offset) & 1)
377 != 0
378 {
379 count += 1;
380 }
381 }
382 count
383 }
384
385 fn row_op(&mut self, i: usize, j: usize) {
391 let (left, right) = self.data_array.split_at_mut(max(i, j));
392 let (target_row, source_row) = if i < j {
393 (right.get_mut(0).unwrap(), left.get(i).unwrap())
394 } else {
395 (left.get_mut(j).unwrap(), right.first().unwrap())
396 };
397
398 for (v1, v2) in source_row.iter().zip(target_row.iter_mut()) {
399 *v2 ^= *v1;
400 }
401 }
402
403 pub fn swap_qbits(&mut self, i: usize, j: usize) {
404 self.data_array.swap(i, j);
405 self.data_array.swap(self.n + i, self.n + j);
406 }
407
408 fn update_phase_and(&mut self, i: usize, j: usize) {
410 for (v1, v2, phase) in izip!(
411 self.data_array[i].iter(),
412 self.data_array[j].iter(),
413 self.phases.iter_mut()
414 ) {
415 *phase ^= *v1 & *v2;
416 }
417 }
418
419 fn update_phase_and_many(&mut self, i: usize, j: usize, k: usize, l: usize) {
421 for (v1, v2, v3, v4, phase) in izip!(
422 self.data_array[i].iter(),
423 self.data_array[j].iter(),
424 self.data_array[k].iter(),
425 self.data_array[l].iter(),
426 self.phases.iter_mut()
427 ) {
428 *phase ^= *v1 & *v2 & *v3 & *v4;
429 }
430 }
431
432 pub fn count_id(&self, qbit: usize) -> usize {
440 let mut count: usize = 0;
441 let nstart_stride = get_stride(self.start_offset);
442 for ns in nstart_stride..self.nstrides {
443 let r_x = self.data_array[qbit][ns];
444 let r_z = self.data_array[qbit + self.n][ns];
445 let value = r_x | r_z;
446 count += value.trailing_zeros() as usize;
447 if ns == nstart_stride {
448 count -= self.start_offset;
449 }
450 if ns == self.nstrides - 1 && value == 0 {
451 count -= 64 - get_offset(self.start_offset + self.noperators);
452 }
453 if value != 0 {
454 return count;
455 }
456 }
457 count
458 }
459
460 #[inline]
462 pub fn is_i(&self, qbit: usize, col: usize) -> bool {
463 !self.get_entry(qbit, col) && !self.get_entry(qbit + self.n, col)
464 }
465
466 #[inline]
467 pub fn count_leading_i(&self, qbit: usize, order: &[usize]) -> usize {
468 order
469 .iter()
470 .take_while(|&&col| self.is_i(qbit, col))
471 .count()
472 }
473
474 #[inline]
477 pub fn pauli_pair_index(&self, i: usize, j: usize, col: usize) -> usize {
478 let n = self.n;
479 let s0 = self.get_entry(i, col);
480 let d0 = self.get_entry(i + n, col);
481 let s1 = self.get_entry(j, col);
482 let d1 = self.get_entry(j + n, col);
483
484 ((s0 as usize) << 3) | ((d0 as usize) << 2) | ((s1 as usize) << 1) | (d1 as usize)
485 }
486
487 #[inline]
491 pub fn count_leading_i_conjugation(
492 &self,
493 i: usize,
494 j: usize,
495 q: usize,
496 c: usize,
497 order: &[usize],
498 ) -> usize {
499 order
500 .iter()
501 .take_while(|&&col| CHUNK_CONJUGATION_SCORE[c][q][self.pauli_pair_index(i, j, col)] > 0)
502 .count()
503 }
504
505 pub fn support_size_sort(&mut self) {
507 let mut transposed: Vec<(bool, Vec<bool>)> = (0..self.noperators)
509 .map(|i| self.get_as_vec_bool(i))
510 .collect();
511 transposed.sort_by_key(|(_, vec)| {
513 (0..self.n)
514 .map(|i| if vec[i] | vec[i + self.n] { 1 } else { 0 })
515 .sum::<i32>()
516 });
517 self.clear();
519 for (phase, axis) in transposed {
521 self.insert_vec_bool(&axis, phase);
522 }
523 }
524}
525
526impl fmt::Display for PauliSet {
527 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
528 for i in 0..self.len() {
529 let (phase, string) = self.get(i);
530 writeln!(f, "{}{}", if phase { "-" } else { "+" }, string)?;
531 }
532 writeln!(f)
533 }
534}
535
536impl PauliLike for PauliSet {
537 fn h(&mut self, i: usize) {
539 self.data_array.swap(i, i + self.n);
540 self.update_phase_and(i, i + self.n);
541 }
542 fn s(&mut self, i: usize) {
544 self.update_phase_and(i, i + self.n);
545 self.row_op(i, i + self.n);
546 }
547 fn sd(&mut self, i: usize) {
549 self.row_op(i, i + self.n);
550 self.update_phase_and(i, i + self.n);
551 }
552 fn sqrt_x(&mut self, i: usize) {
554 self.row_op(i + self.n, i);
555 self.update_phase_and(i, i + self.n);
556 }
557 fn sqrt_xd(&mut self, i: usize) {
559 self.update_phase_and(i, i + self.n);
560 self.row_op(i + self.n, i);
561 }
562 fn cnot(&mut self, i: usize, j: usize) {
564 self.update_phase_and_many(i, j, i + self.n, j + self.n);
565 self.row_op(j + self.n, i + self.n);
566 self.row_op(i, j);
567 self.update_phase_and_many(i, j, i + self.n, j + self.n);
568 }
569}
570
571#[cfg(test)]
572mod pauli_set_tests {
573 use super::*;
574
575 #[test]
576 fn construction() {
577 let pset = PauliSet::new(10);
578 assert_eq!(pset.data_array.len(), 20);
579 assert_eq!(pset.n, 10);
580 assert_eq!(pset.nstrides, 0);
581 assert_eq!(pset.noperators, 0);
582 }
583
584 #[test]
585 fn insertion() {
586 let mut pset = PauliSet::new(4);
587 pset.insert("XYZI", false);
588 assert_eq!(pset.data_array.len(), 8);
589 assert_eq!(pset.n, 4);
590 assert_eq!(pset.nstrides, 1);
591 assert_eq!(pset.noperators, 1);
592 assert_eq!(pset.data_array[0][0], 1);
593 assert_eq!(pset.data_array[1][0], 1);
594 assert_eq!(pset.data_array[2][0], 0);
595 assert_eq!(pset.data_array[3][0], 0);
596
597 assert_eq!(pset.data_array[4][0], 0);
598 assert_eq!(pset.data_array[5][0], 1);
599 assert_eq!(pset.data_array[6][0], 1);
600 assert_eq!(pset.data_array[7][0], 0);
601 }
602
603 #[test]
604 fn get() {
605 let mut pset = PauliSet::new(4);
606 pset.insert("XYZI", false);
607 assert_eq!(pset.get(0), (false, "XYZI".to_owned()));
608 }
609
610 #[test]
611 fn h_test() {
612 let mut pset = PauliSet::new(1);
613 pset.insert("X", false);
614 pset.insert("Z", false);
615 pset.insert("Y", false);
616 pset.insert("I", false);
617
618 pset.h(0);
619
620 assert_eq!(pset.get(0), (false, "Z".to_owned()));
621 assert_eq!(pset.get(1), (false, "X".to_owned()));
622 assert_eq!(pset.get(2), (true, "Y".to_owned()));
623 assert_eq!(pset.get(3), (false, "I".to_owned()));
624 }
625
626 #[test]
627 fn s_test() {
628 let mut pset = PauliSet::new(1);
629 pset.insert("X", false);
630 pset.insert("Z", false);
631 pset.insert("Y", false);
632 pset.insert("I", false);
633
634 pset.s(0);
635
636 assert_eq!(pset.get(0), (false, "Y".to_owned()));
637 assert_eq!(pset.get(1), (false, "Z".to_owned()));
638 assert_eq!(pset.get(2), (true, "X".to_owned()));
639 assert_eq!(pset.get(3), (false, "I".to_owned()));
640 }
641
642 #[test]
643 fn sqrt_x_test() {
644 let mut pset = PauliSet::new(1);
645 pset.insert("X", false);
646 pset.insert("Z", false);
647 pset.insert("Y", false);
648 pset.insert("I", false);
649
650 pset.sqrt_x(0);
651
652 assert_eq!(pset.get(0), (false, "X".to_owned()));
653 assert_eq!(pset.get(1), (true, "Y".to_owned()));
654 assert_eq!(pset.get(2), (false, "Z".to_owned()));
655 assert_eq!(pset.get(3), (false, "I".to_owned()));
656 }
657 #[test]
659 fn cnot_test() {
660 const INPUTS: [&str; 16] = [
661 "II", "IX", "IY", "IZ", "XI", "XX", "XY", "XZ", "YI", "YX", "YY", "YZ", "ZI", "ZX",
662 "ZY", "ZZ",
663 ];
664 const OUTPUTS: [(bool, &str); 16] = [
665 (false, "II"),
666 (false, "IX"),
667 (false, "ZY"),
668 (false, "ZZ"),
669 (false, "XX"),
670 (false, "XI"),
671 (false, "YZ"),
672 (true, "YY"),
673 (false, "YX"),
674 (false, "YI"),
675 (true, "XZ"),
676 (false, "XY"),
677 (false, "ZI"),
678 (false, "ZX"),
679 (false, "IY"),
680 (false, "IZ"),
681 ];
682 for (ins, (phase, outs)) in INPUTS.iter().zip(OUTPUTS.iter()) {
683 let mut pset = PauliSet::new(2);
684 pset.insert(ins, false);
685 pset.cnot(0, 1);
686 assert_eq!(pset.get(0), (*phase, (*outs).to_owned()));
687 }
688 }
689
690 #[test]
691 fn support_size_test() {
692 let mut pset = PauliSet::new(4);
693 pset.insert("XYIZ", false);
694 pset.insert("XYII", false);
695 pset.insert("IYIZ", false);
696 pset.insert("IIII", false);
697 assert_eq!(pset.support_size(0), 3);
698 assert_eq!(pset.support_size(1), 2);
699 assert_eq!(pset.support_size(2), 2);
700 assert_eq!(pset.support_size(3), 0);
701 }
702 #[test]
703 fn count_id() {
704 let mut pset = PauliSet::new(5);
705 pset.insert("IIIII", false);
706 pset.insert("XIIII", false);
707 pset.insert("XXIII", false);
708 pset.insert("XXXII", false);
709 pset.insert("XXXXI", false);
710 for i in 0..5 {
711 assert_eq!(pset.count_id(i), i + 1);
712 }
713 }
714 #[test]
715 fn sort_test() {
716 let mut pset = PauliSet::new(4);
717 pset.insert("IIII", false);
718 pset.insert("XXII", false);
719 pset.insert("XXXX", false);
720 pset.insert("XIII", false);
721 pset.insert("XXXI", false);
722 pset.support_size_sort();
723 assert_eq!(pset.get(0), (false, "IIII".to_owned()));
724 assert_eq!(pset.get(1), (false, "XIII".to_owned()));
725 assert_eq!(pset.get(2), (false, "XXII".to_owned()));
726 assert_eq!(pset.get(3), (false, "XXXI".to_owned()));
727 assert_eq!(pset.get(4), (false, "XXXX".to_owned()));
728 }
729 #[test]
730 fn pop_test() {
731 let mut pset = PauliSet::new(1);
732 pset.insert("I", false);
733 pset.insert("X", false);
734 assert_eq!(pset.noperators, 2);
735 pset.pop();
736 assert_eq!(pset.noperators, 1);
737 assert_eq!(pset.start_offset, 1);
738 assert_eq!(pset.get(0), (false, "X".to_owned()));
739 }
740 #[test]
741 fn commute_test() {
742 let mut pset = PauliSet::new(2);
743 pset.insert("ZI", false);
744 pset.insert("XI", false);
745 pset.insert("ZZ", false);
746 pset.insert("XX", false);
747 pset.insert("YY", false);
748 assert!(pset.commute(0, 2));
749 assert!(!pset.commute(0, 1));
750 assert!(pset.commute(2, 3));
751 assert!(pset.commute(2, 4));
752 assert!(pset.commute(3, 4));
753 assert!(pset.commute(1, 3));
754 }
755}