rustiq_core/structures/
pauli_set.rs

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/// A set of Pauli operators (module global phase)
19/// Conjugation by Clifford gates are vectorized
20#[derive(Clone, Debug, PartialEq)]
21pub struct PauliSet {
22    pub n: usize,
23    nstrides: usize,
24    noperators: usize,
25    start_offset: usize,
26    /// The X and Z parts of the Pauli operators (in row major)
27    /// The X part spans the first `n` rows and the Z part spans the last `n` rows
28    data_array: Vec<Vec<u64>>,
29    phases: Vec<u64>,
30}
31
32impl PauliSet {
33    /// Allocate an empty set of n-qubit Pauli operators
34    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    /// Allocate a set of m n-qubit Pauli operators set to the identity
45    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    // Construction from a list of operators
57    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    /// Returns the number of operators stored in the set
69    pub fn len(&self) -> usize {
70        self.noperators
71    }
72
73    pub fn is_empty(&self) -> bool {
74        self.noperators == 0
75    }
76
77    /// Inserts a new Pauli operator in the set and returns its index
78    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        // Setting the phase
87        if phase {
88            self.phases[stride] |= 1 << offset;
89        }
90        // Setting the operator
91        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    /// Inserts a new Pauli operator described as a vector of bool in the set and returns its index
107    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    /// Clears the data of the Pauli set
156    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    /// Pops the first rotation in the set
167    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    /// Pops the last rotation in the set
178    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    /// Set some operator to identity (because popping in the middle is expensive :O)
188    pub fn set_to_identity(&mut self, operator_index: usize) {
189        // set_entry(&mut self, operator_index: usize, qbit: usize, x_part: bool, z_part: bool)
190        for i in 0..self.n {
191            self.set_entry(operator_index, i, false, false);
192        }
193    }
194    /// Get the operator at index `operator_index` as a pair (phase, string)
195    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    /// Get the operator at index `operator_index` as a pair `(bool, Vec<bool>)`
223    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    /// Get the operator at index `operator_index` as a `Pauli` object
235    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    /// Get a single entry of the PauliSet
240    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    /// Get the inverse Z output of the tableau (assuming the PauliSet is a Tableau, i.e. has exactly 2n operators storing X1...Xn Z1...Zn images)
276    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    /// Get the inverse X output of the tableau (assuming the PauliSet is a Tableau, i.e. has exactly 2n operators storing X1...Xn Z1...Zn images)
299    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    /// Returns the sum mod 2 of the logical AND of a row with an external vector of booleans
325    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    /// Check equality between two operators
334    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    /// Checks if two operators in the set commute
341    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    // Returns the support of the operator (i.e. the list of qbit indices on which the operator acts non trivially)
354    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    /// Returns the support size of the operator (i.e. the number of non-I Pauli term in the operator)
370    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    /*
386           Internal methods
387    */
388
389    /// XORs row `i` into row `j`
390    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    /// Offset the phases by the logical bitwise and of two target rows
409    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    /// Same thing as `update_phase_and` but computes the and of 4 rows instead
420    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    /*
433       Gate conjugation
434    */
435
436    /*
437       Metrics for synthesis algorithms
438    */
439    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    /// Returns `true` if pauli `col` for qubit `q` is `I`
461    #[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    /// Returns (the index of) the Pauli pair over the qubits `i` and `j`
475    /// for the Pauli operator in column `col`.
476    #[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    /// Computes the score of conjugating the Pauli pair over the qubits `i` and `j`
488    /// by chunk `c`. This is equivalent to the scoring function described in the
489    /// paper but uses the precomputed table lookup instead of performing conjugation.
490    #[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    /// Sorts the set by support size
506    pub fn support_size_sort(&mut self) {
507        // We first build the "transpose" of data_array (cheaper this way)
508        let mut transposed: Vec<(bool, Vec<bool>)> = (0..self.noperators)
509            .map(|i| self.get_as_vec_bool(i))
510            .collect();
511        // We sort the operators by support size
512        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        // We clear the set
518        self.clear();
519        // And insert everybody back in place
520        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    /// Conjugate the set of rotations via a H gate
538    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    /// Conjugate the set of rotations via a S gate
543    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    /// Conjugate the set of rotations via a S dagger gate
548    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    /// Conjugate the set of rotations via a SQRT_X gate
553    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    /// Conjugate the set of rotations via a SQRT_X dagger gate
558    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    /// Conjugate the set of rotations via a CNOT gate
563    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    // TODO:write this test :'(
658    #[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}