pecos_qsim/
sparse_stab.rs

1// Copyright 2024 The PECOS Developers
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
4// in compliance with the License.You may obtain a copy of the License at
5//
6//     https://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software distributed under the License
9// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
10// or implied. See the License for the specific language governing permissions and limitations under
11// the License.
12
13use crate::{CliffordSimulator, Gens, QuantumSimulator};
14use core::fmt::Debug;
15use core::mem;
16use pecos_core::SimRng;
17use pecos_core::{IndexableElement, Set};
18use rand_chacha::ChaCha8Rng;
19// TODO: Look into seeing if a dense bool for signs_minus and signs_i is more efficient
20
21#[derive(Clone, Debug)]
22pub struct SparseStab<T, E, R = ChaCha8Rng>
23where
24    T: for<'a> Set<'a, Element = E>,
25    E: IndexableElement,
26    R: SimRng,
27{
28    num_qubits: usize,
29    stabs: Gens<T, E>,
30    destabs: Gens<T, E>,
31    rng: R,
32}
33impl<T, E, R> SparseStab<T, E, R>
34where
35    E: IndexableElement,
36    R: SimRng,
37    T: for<'a> Set<'a, Element = E>,
38{
39    #[inline]
40    #[must_use]
41    fn new(num_qubits: usize) -> Self {
42        let rng = SimRng::from_entropy();
43        Self::with_rng(num_qubits, rng)
44    }
45
46    #[inline]
47    pub fn with_rng(num_qubits: usize, rng: R) -> Self {
48        let mut stab = Self {
49            num_qubits,
50            stabs: Gens::<T, E>::new(num_qubits),
51            destabs: Gens::<T, E>::new(num_qubits),
52            rng,
53        };
54        stab.reset();
55        stab
56    }
57
58    #[expect(clippy::single_call_fn)]
59    #[inline]
60    fn reset(&mut self) -> &mut Self {
61        self.stabs.init_all_z();
62        self.destabs.init_all_x();
63        self
64    }
65
66    #[inline]
67    pub fn verify_matrix(&self) {
68        Self::check_row_eq_col(&self.stabs);
69        Self::check_row_eq_col(&self.destabs);
70
71        // TODO: Check that stabilizers commute.
72        // TODO: Check destabilizers commute.
73        // TODO: Check that only stab[i] anti-commutes with destab[j] only iff i == j;
74        todo!()
75    }
76
77    #[inline]
78    fn check_row_eq_col(gens: &Gens<T, E>) {
79        // TODO: Verify that this is doing what is intended...
80        for (i, row) in gens.row_x.iter().enumerate() {
81            for j in row.iter() {
82                assert!(
83                    gens.col_x[j.to_usize()].contains(&E::from_usize(i)),
84                    "Column-wise sparse matrix doesn't match row-wise spare matrix"
85                );
86            }
87        }
88    }
89
90    /// Utility that creates a string for the Pauli generates of a `Gens`.
91    fn tableau_string(num_qubits: usize, gens: &Gens<T, E>) -> String {
92        // TODO: calculate signs so we are really doing Y and not W
93        let mut result =
94            String::with_capacity(num_qubits * gens.row_x.len() + gens.row_x.len() + 2);
95        for i in 0..gens.row_x.len() {
96            if gens.signs_minus.contains(&(E::from_usize(i))) {
97                result.push('-');
98            } else {
99                result.push('+');
100            }
101            if gens.signs_i.contains(&(E::from_usize(i))) {
102                result.push('i');
103            }
104
105            for qubit in 0..num_qubits {
106                let qubit_u = E::from_usize(qubit);
107                let in_row_x = gens.row_x[i].contains(&qubit_u);
108                let in_row_z = gens.row_z[i].contains(&qubit_u);
109
110                let char = match (in_row_x, in_row_z) {
111                    (false, false) => 'I',
112                    (true, false) => 'X',
113                    (false, true) => 'Z',
114                    (true, true) => 'Y',
115                };
116                result.push(char);
117            }
118            result.push('\n');
119        }
120
121        result
122    }
123
124    /// Produces a textual representation of the stabilizer in tableau form.
125    #[inline]
126    pub fn stab_tableau(&self) -> String {
127        Self::tableau_string(self.num_qubits, &self.stabs)
128    }
129
130    /// Produces a textual representation of the destabilizer in tableau form.
131    #[inline]
132    pub fn destab_tableau(&self) -> String {
133        Self::tableau_string(self.num_qubits, &self.destabs)
134    }
135
136    /// Negate the sign of a stabilizer generator.
137    #[inline]
138    pub fn neg(&mut self, s: E) {
139        self.stabs.signs_minus ^= &s;
140    }
141
142    #[inline]
143    pub fn signs_minus(&self) -> &T {
144        &self.stabs.signs_minus
145    }
146
147    /// # Panics
148    /// Will panic if qubit ids don't convert to usize.
149    #[inline]
150    fn deterministic_meas(&mut self, q: E) -> bool {
151        let qu = q.to_usize();
152
153        let mut num_minuses = self.destabs.col_x[qu]
154            .intersection(&self.stabs.signs_minus)
155            .count();
156
157        let num_is = &self.destabs.col_x[qu]
158            .intersection(&self.stabs.signs_i)
159            .count();
160
161        let mut cumulative_x = T::new();
162        for row in self.destabs.col_x[qu].iter() {
163            let rowu = row.to_usize();
164            num_minuses += &self.stabs.row_z[rowu].intersection(&cumulative_x).count();
165            cumulative_x ^= &self.stabs.row_x[rowu];
166        }
167        if num_is & 3 != 0 {
168            // num_is % 4 != 0
169            num_minuses += 1;
170        }
171        num_minuses & 1 != 0 // num_minuses % 2 != 0 (is odd)
172    }
173
174    /// # Panics
175    /// Will panic if qubit ids don't convert to usize.
176    #[inline]
177    fn nondeterministic_meas(&mut self, q: E) -> E {
178        let qu = q.to_usize();
179
180        let mut anticom_stabs_col = self.stabs.col_x[qu].clone();
181        let mut anticom_destabs_col = self.destabs.col_x[qu].clone();
182
183        let mut smallest_wt = 2 * self.num_qubits + 2;
184        let mut removed_id: Option<E> = None;
185
186        for stab_id in anticom_stabs_col.iter() {
187            let stab_usize = stab_id.to_usize();
188            let weight = self.stabs.row_x[stab_usize].len() + self.stabs.row_z[stab_usize].len();
189
190            if weight < smallest_wt {
191                smallest_wt = weight;
192                removed_id = Some(*stab_id);
193                // break // TODO: Should it exit early? // If we do... it avoids smallest weight
194                // TODO: Does the smallest weight matter? Maybe at least break if smallest weight == 1
195                // TODO: Does it always exist? If so, can we avoid Some()?
196            }
197        }
198
199        let id = removed_id.expect("Critical error: removed_id was None");
200
201        anticom_stabs_col.remove(&id);
202        let id_usize = id.to_usize(); // Convert `id` to `usize`
203        let removed_row_x = self.stabs.row_x[id_usize].clone();
204        let removed_row_z = self.stabs.row_z[id_usize].clone();
205
206        if self.stabs.signs_minus.contains(&id) {
207            self.stabs.signs_minus ^= &anticom_stabs_col;
208        }
209
210        if self.stabs.signs_i.contains(&id) {
211            self.stabs.signs_i.remove(&id);
212
213            let gens_common = self
214                .stabs
215                .signs_i
216                .intersection(&anticom_stabs_col)
217                .copied()
218                .collect::<Vec<_>>();
219            let gens_only_stabs = anticom_stabs_col
220                .difference(&self.stabs.signs_i)
221                .copied()
222                .collect::<Vec<_>>();
223
224            for i in gens_common {
225                self.stabs.signs_minus ^= &i;
226                self.stabs.signs_i.remove(&i);
227            }
228
229            for i in gens_only_stabs {
230                self.stabs.signs_i.insert(i);
231            }
232        }
233
234        for gen in anticom_stabs_col.iter() {
235            let gen_usize = gen.to_usize(); // Convert `gen` to `usize`
236            let num_minuses = removed_row_z
237                .intersection(&self.stabs.row_x[gen_usize])
238                .count();
239
240            if num_minuses & 1 != 0 {
241                // num_minuses % 2 != 0 (is odd)
242                self.stabs.signs_minus ^= gen;
243            }
244
245            self.stabs.row_x[gen_usize] ^= &removed_row_x;
246            self.stabs.row_z[gen_usize] ^= &removed_row_z;
247            // Use `num_minuses` as needed
248        }
249
250        for i in removed_row_x.iter() {
251            let iu = i.to_usize();
252            self.stabs.col_x[iu] ^= &anticom_stabs_col;
253        }
254
255        for i in removed_row_z.iter() {
256            let iu = i.to_usize();
257            self.stabs.col_z[iu] ^= &anticom_stabs_col;
258        }
259
260        for i in self.stabs.row_x[id_usize].iter() {
261            let iu = i.to_usize();
262            self.stabs.col_x[iu].remove(&id);
263        }
264
265        for i in self.stabs.row_z[id_usize].iter() {
266            let iu = i.to_usize();
267            self.stabs.col_z[iu].remove(&id);
268        }
269
270        // Remove replaced stabilizer with the measured stabilizer
271        self.stabs.col_z[qu].insert(id);
272
273        // Row update
274        self.stabs.row_x[id_usize].clear();
275        self.stabs.row_z[id_usize].clear();
276        self.stabs.row_z[id_usize].insert(q);
277
278        for i in self.destabs.row_x[id_usize].iter() {
279            let iu = i.to_usize();
280            self.destabs.col_x[iu].remove(&id);
281        }
282
283        for i in self.destabs.row_z[id_usize].iter() {
284            let iu = i.to_usize();
285            self.destabs.col_z[iu].remove(&id);
286        }
287
288        anticom_destabs_col.remove(&id);
289
290        for i in removed_row_x.iter() {
291            let iu = i.to_usize();
292            self.destabs.col_x[iu].insert(id);
293            self.destabs.col_x[iu] ^= &anticom_destabs_col;
294        }
295
296        for i in removed_row_z.iter() {
297            let iu = i.to_usize();
298            self.destabs.col_z[iu].insert(id);
299            self.destabs.col_z[iu] ^= &anticom_destabs_col;
300        }
301
302        for row in anticom_destabs_col.iter() {
303            let ru = row.to_usize();
304            self.destabs.row_x[ru] ^= &removed_row_x;
305            self.destabs.row_z[ru] ^= &removed_row_z;
306        }
307
308        self.destabs.row_x[id_usize] = removed_row_x;
309        self.destabs.row_z[id_usize] = removed_row_z;
310
311        id
312    }
313
314    /// Measurement of the +`Z_q` operator where random outcomes are forced to a particular value.
315    /// # Panics
316    /// Will panic if qubit ids don't convert to usize.
317    #[inline]
318    pub fn mz_forced(&mut self, q: E, forced_outcome: bool) -> (bool, bool) {
319        let qu = q.to_usize();
320
321        let deterministic = self.stabs.col_x[qu].is_empty();
322
323        // There are no stabilizers that anti-commute with Z_q
324        let meas_out = if deterministic {
325            self.deterministic_meas(q)
326        } else {
327            let id = self.nondeterministic_meas(q);
328
329            self.apply_outcome(id, forced_outcome)
330        };
331        (meas_out, deterministic)
332    }
333
334    /// Preparation of the +`Z_q` operator where random outcomes are forced to a particular value.
335    /// # Panics
336    /// Will panic if qubit ids don't convert to usize.
337    #[inline]
338    pub fn pz_forced(&mut self, q: E, forced_outcome: bool) -> (bool, bool) {
339        let (meas, deter) = self.mz_forced(q, forced_outcome);
340        if meas {
341            self.x(q);
342        }
343        (meas, deter)
344    }
345
346    /// Apply measurement outcome
347    #[inline]
348    fn apply_outcome(&mut self, id: E, meas_outcome: bool) -> bool {
349        if meas_outcome {
350            self.stabs.signs_minus.insert(id);
351        } else {
352            self.stabs.signs_minus.remove(&id);
353        }
354        meas_outcome
355    }
356}
357
358impl<T, E, R> QuantumSimulator for SparseStab<T, E, R>
359where
360    E: IndexableElement,
361    R: SimRng,
362    T: for<'a> Set<'a, Element = E>,
363{
364    #[inline]
365    #[must_use]
366    fn new(num_qubits: usize) -> Self {
367        Self::new(num_qubits)
368    }
369
370    #[inline]
371    fn num_qubits(&self) -> usize {
372        self.num_qubits
373    }
374
375    #[inline]
376    fn reset(&mut self) -> &mut Self {
377        Self::reset(self)
378    }
379}
380
381impl<T, E, R> CliffordSimulator<E> for SparseStab<T, E, R>
382where
383    T: for<'a> Set<'a, Element = E>,
384    E: IndexableElement,
385    R: SimRng,
386{
387    // TODO: pub fun p(&mut self, pauli: &pauli, q: U) { todo!() }
388    // TODO: pub fun m(&mut self, pauli: &pauli, q: U) -> bool { todo!() }
389
390    /// Measurement of the +`Z_q` operator.
391    /// # Panics
392    /// Will panic if qubit ids don't convert to usize.
393    #[inline]
394    fn mz(&mut self, q: E) -> (bool, bool) {
395        let qu = q.to_usize();
396
397        let deterministic = self.stabs.col_x[qu].is_empty();
398
399        // There are no stabilizers that anti-commute with Z_q
400        let meas_out = if deterministic {
401            self.deterministic_meas(q)
402        } else {
403            let id = self.nondeterministic_meas(q);
404
405            let meas_outcome = self.rng.gen_bool(0.5);
406
407            self.apply_outcome(id, meas_outcome)
408        };
409        (meas_out, deterministic)
410    }
411
412    /// Pauli X gate. X -> X, Z -> -Z
413    /// # Panics
414    /// Will panic if qubit ids don't convert to usize.
415    #[inline]
416    fn x(&mut self, q: E) {
417        let qu = q.to_usize();
418        self.stabs.signs_minus ^= &self.stabs.col_z[qu];
419    }
420
421    /// Pauli Y gate. X -> -X, Z -> -Z
422    /// # Panics
423    /// Will panic if qubit ids don't convert to usize.
424    #[inline]
425    fn y(&mut self, q: E) {
426        // TODO: Add test
427        let qu = q.to_usize();
428        // stabs.signs_minus ^= stabs.col_x[qubit] ^ stabs.col_z[qubit]
429        for i in self.stabs.col_x[qu].symmetric_difference(&self.stabs.col_z[qu]) {
430            self.stabs.signs_minus ^= i;
431        }
432    }
433
434    /// Pauli Z gate. X -> -X, Z -> Z
435    /// # Panics
436    /// Will panic if qubit ids don't convert to usize.
437    #[inline]
438    fn z(&mut self, q: E) {
439        // TODO: Add test
440        self.stabs.signs_minus ^= &self.stabs.col_x[q.to_usize()];
441    }
442
443    /// Sqrt of Z gate.
444    ///     X -> iW = Y
445    ///     Z -> Z
446    ///     W -> iX
447    ///     Y -> -X
448    /// # Panics
449    /// Will panic if qubit ids don't convert to usize.
450    #[inline]
451    fn sz(&mut self, q: E) {
452        let qu = q.to_usize();
453
454        // X -> i
455        // ---------------------
456        // i * i = -1
457        // stabs.signs_minus ^= stabs.signs_i & stabs.col_x[qubit]
458        // For each X add an i unless there is already an i there then delete it.
459        // stabs.signs_i ^= stabs.col_x[qubit]
460        for i in self.stabs.signs_i.intersection(&self.stabs.col_x[qu]) {
461            self.stabs.signs_minus ^= i;
462        }
463        self.stabs.signs_i ^= &self.stabs.col_x[qu];
464
465        for g in [&mut self.stabs, &mut self.destabs] {
466            g.col_z[qu] ^= &g.col_x[qu];
467
468            for &i in g.col_x[qu].iter() {
469                let iu = i.to_usize();
470                g.row_z[iu] ^= &q;
471            }
472        }
473    }
474
475    /// Hadamard gate. X -> Z, Z -> X
476    /// # Panics
477    /// Will panic if qubit ids don't convert to usize.
478    #[inline]
479    fn h(&mut self, q: E) {
480        let qu = q.to_usize();
481
482        // self.stabs.signs_minus.symmetric_difference_update(self.stabs.col_x[qu].intersection())
483        // self.stabs.signs_minus ^= &self.stabs.col_x[qu] & &self.stabs.col_z[qu];
484        for i in self.stabs.col_x[qu].intersection(&self.stabs.col_z[qu]) {
485            self.stabs.signs_minus ^= i;
486        }
487
488        for g in [&mut self.stabs, &mut self.destabs] {
489            for i in g.col_x[qu].difference(&g.col_z[qu]) {
490                let iu = i.to_usize();
491                g.row_x[iu].remove(&q);
492                g.row_z[iu].insert(q);
493            }
494
495            for i in g.col_z[qu].difference(&g.col_x[qu]) {
496                let iu = i.to_usize();
497                g.row_z[iu].remove(&q);
498                g.row_x[iu].insert(q);
499            }
500
501            mem::swap(&mut g.col_x[qu], &mut g.col_z[qu]);
502        }
503    }
504
505    /// CX: +IX -> +IX; +IZ -> +ZZ; +XI -> +XX; +ZI -> +ZI
506    /// # Panics
507    /// Will panic if qubit ids don't convert to usize.
508    #[inline]
509    fn cx(&mut self, q1: E, q2: E) {
510        let qu1 = q1.to_usize();
511        let qu2 = q2.to_usize();
512
513        for g in &mut [&mut self.stabs, &mut self.destabs] {
514            let (qu_min, qu_max) = if qu1 < qu2 { (qu1, qu2) } else { (qu2, qu1) };
515
516            // Handle col_x
517            {
518                let (_left, right) = g.col_x.split_at_mut(qu_min);
519                let (mid, right) = right.split_at_mut(qu_max - qu_min);
520                let col_x_min = &mut mid[0];
521                let col_x_max = &mut right[0];
522
523                let (col_x_qu1, col_x_qu2) = if qu1 < qu2 {
524                    (col_x_min, col_x_max)
525                } else {
526                    (col_x_max, col_x_min)
527                };
528
529                let mut q2_set = T::new();
530                q2_set.insert(q2);
531
532                for i in col_x_qu1.iter() {
533                    let iu = i.to_usize();
534                    g.row_x[iu].symmetric_difference_update(&q2_set);
535                }
536                col_x_qu2.symmetric_difference_update(col_x_qu1);
537            }
538
539            // Handle col_z
540            {
541                let (_left, right) = g.col_z.split_at_mut(qu_min);
542                let (mid, right) = right.split_at_mut(qu_max - qu_min);
543                let col_z_min = &mut mid[0];
544                let col_z_max = &mut right[0];
545
546                let (col_z_qu1, col_z_qu2) = if qu1 < qu2 {
547                    (col_z_min, col_z_max)
548                } else {
549                    (col_z_max, col_z_min)
550                };
551
552                let mut q1_set = T::new();
553                q1_set.insert(q1);
554
555                for i in col_z_qu2.iter() {
556                    let iu = i.to_usize();
557                    g.row_z[iu].symmetric_difference_update(&q1_set);
558                }
559                col_z_qu1.symmetric_difference_update(col_z_qu2);
560            }
561        }
562    }
563}
564
565#[cfg(test)]
566mod tests {
567    use super::*;
568    use pecos_core::VecSet;
569
570    #[allow(clippy::cast_possible_truncation)]
571    fn check_matrix(m: &[&str], gens: &Gens<VecSet<u32>, u32>) {
572        for (r, v) in m.iter().enumerate() {
573            let ru32 = &(r as u32);
574
575            let (_, phase, v) = split_pauli(v);
576
577            // TODO: Allow +Y in place of +iW
578            // TODO: Return bools instead of doing the asserts here...
579
580            match phase {
581                "+" => {
582                    assert!(!gens.signs_minus.contains(ru32));
583                    assert!(!gens.signs_i.contains(ru32));
584                }
585                "-" => {
586                    assert!(gens.signs_minus.contains(ru32));
587                    assert!(!gens.signs_i.contains(ru32));
588                }
589                "+i" => {
590                    assert!(!gens.signs_minus.contains(ru32));
591                    assert!(gens.signs_i.contains(ru32));
592                }
593                "-i" => {
594                    assert!(gens.signs_minus.contains(ru32));
595                    assert!(gens.signs_i.contains(ru32));
596                }
597                _ => unreachable!(),
598            }
599
600            for (c, val) in v.chars().enumerate() {
601                let cu32 = &(c as u32);
602                match val {
603                    'I' => {
604                        assert!(!gens.col_x[c].contains(ru32));
605                        assert!(!gens.col_z[c].contains(ru32));
606                        assert!(!gens.row_x[r].contains(cu32));
607                        assert!(!gens.row_z[r].contains(cu32));
608                    }
609                    'X' => {
610                        assert!(gens.col_x[c].contains(ru32));
611                        assert!(!gens.col_z[c].contains(ru32));
612                        assert!(gens.row_x[r].contains(cu32));
613                        assert!(!gens.row_z[r].contains(cu32));
614                    }
615                    'Z' => {
616                        assert!(!gens.col_x[c].contains(ru32));
617                        assert!(gens.col_z[c].contains(ru32));
618                        assert!(!gens.row_x[r].contains(cu32));
619                        assert!(gens.row_z[r].contains(cu32));
620                    }
621                    'W' => {
622                        assert!(gens.col_x[c].contains(ru32));
623                        assert!(gens.col_z[c].contains(ru32));
624                        assert!(gens.row_x[r].contains(cu32));
625                        assert!(gens.row_z[r].contains(cu32));
626                    }
627                    _ => unreachable!(),
628                }
629            }
630        }
631    }
632
633    fn check_state(state: &SparseStab<VecSet<u32>, u32>, stabs: &[&str], destabs: &[&str]) {
634        check_matrix(stabs, &state.stabs);
635        check_matrix(destabs, &state.destabs);
636        // SparseStab::verify_matrix(&state);
637        // TODO: Add matrix verification func
638    }
639
640    fn split_pauli(pauli_str: &str) -> (usize, &str, &str) {
641        let (phase, pauli_str) = if pauli_str.contains("+i") || pauli_str.contains("-i") {
642            pauli_str.split_at(2)
643        } else if pauli_str.contains('+') || pauli_str.contains('-') || pauli_str.contains('i') {
644            pauli_str.split_at(1)
645        } else {
646            ("+", pauli_str)
647        };
648        let n = pauli_str.chars().count();
649
650        let phase = if phase == "i" { "+i" } else { phase };
651
652        (n, phase, pauli_str)
653    }
654
655    #[allow(clippy::cast_possible_truncation)]
656    fn prep_pauli_gens(pauli_vec: &[&str], gens: &mut Gens<VecSet<u32>, u32>) {
657        // TODO: Think about how to automatically determine the destabilizers you need so you can optionally only provide stabilizers...
658
659        gens.signs_i.clear();
660        gens.signs_minus.clear();
661
662        let (n, _, _) = split_pauli(pauli_vec[0]);
663
664        for u in 0..n {
665            gens.col_x[u].clear();
666            gens.col_z[u].clear();
667            gens.row_x[u].clear();
668            gens.row_z[u].clear();
669        }
670
671        for (ru, pauli_str) in pauli_vec.iter().enumerate() {
672            let (n_, phase, pauli_str) = split_pauli(pauli_str);
673
674            assert_eq!(
675                n, n_,
676                "The number of qubits differs between the first generator and another!"
677            );
678
679            match phase {
680                "+" => {}
681                "-" => {
682                    gens.signs_minus.insert(ru as u32);
683                }
684                "+i" => {
685                    gens.signs_i.insert(ru as u32);
686                }
687                "-i" => {
688                    gens.signs_minus.insert(ru as u32);
689                    gens.signs_i.insert(ru as u32);
690                }
691                _ => unreachable!(),
692            }
693
694            for (cu, p) in pauli_str.chars().enumerate() {
695                match p {
696                    'I' => {}
697                    'X' => {
698                        gens.col_x[cu].insert(ru as u32);
699                        gens.row_x[ru].insert(cu as u32);
700                    }
701                    'W' => {
702                        gens.col_x[cu].insert(ru as u32);
703                        gens.col_z[cu].insert(ru as u32);
704                        gens.row_x[ru].insert(cu as u32);
705                        gens.row_z[ru].insert(cu as u32);
706                    }
707                    'Z' => {
708                        gens.col_z[cu].insert(ru as u32);
709                        gens.row_z[ru].insert(cu as u32);
710                    }
711                    _ => unreachable!(),
712                }
713            }
714        }
715    }
716
717    fn prep_state(stabs: &[&str], destabs: &[&str]) -> SparseStab<VecSet<u32>, u32> {
718        let mut state = SparseStab::<VecSet<u32>, u32>::new(3);
719        prep_pauli_gens(stabs, &mut state.stabs);
720        prep_pauli_gens(destabs, &mut state.destabs);
721
722        state
723    }
724
725    #[test]
726    fn test_setting_up_stab_state() {
727        let tab_stab = vec!["XII", "iIWI", "IIZ"];
728        let tab_destab = vec!["ZII", "IXI", "IIX"];
729
730        let state = prep_state(&tab_stab, &tab_destab);
731        check_state(&state, &tab_stab, &tab_destab);
732    }
733
734    #[test]
735    fn test_setting_up_neg_stab_state() {
736        let tab_stab = vec!["-XII", "-iIWI", "-IIZ"];
737        let tab_destab = vec!["ZII", "IXI", "IIX"];
738
739        let state = prep_state(&tab_stab, &tab_destab);
740        check_state(&state, &tab_stab, &tab_destab);
741    }
742
743    #[test]
744    fn test_nondeterministic_px() {
745        for _ in 1_u32..=100 {
746            let mut state = prep_state(&["Z"], &["X"]);
747            let (_m0, d0) = state.px(0);
748            let (m1, d1) = state.mx(0);
749            let m1_int = u8::from(m1);
750
751            assert_eq!(m1_int, 0); // |+X>
752            assert!(!d0); // Not deterministic
753            assert!(d1); // Deterministic
754        }
755    }
756
757    #[test]
758    fn test_deterministic_px() {
759        let mut state = prep_state(&["X"], &["Z"]);
760        let (m0, d0) = state.px(0);
761        let m0_int = u8::from(m0);
762
763        assert!(d0); // Deterministic
764        assert_eq!(m0_int, 0); // |+X>
765    }
766
767    #[test]
768    fn test_nondeterministic_pnx() {
769        for _ in 1_u32..=100 {
770            let mut state = prep_state(&["Z"], &["X"]);
771            let (_m0, d0) = state.pnx(0);
772            let (m1, d1) = state.mx(0);
773            let m1_int = u8::from(m1);
774
775            assert_eq!(m1_int, 1); // |-X>
776            assert!(!d0); // Not deterministic
777            assert!(d1); // Deterministic
778        }
779    }
780
781    #[test]
782    fn test_deterministic_pnx() {
783        let mut state = prep_state(&["-X"], &["Z"]);
784        let (m0, d0) = state.pnx(0);
785        let m0_int = u8::from(m0);
786
787        assert!(d0); // Deterministic
788        assert_eq!(m0_int, 0); // |-X>
789    }
790
791    #[test]
792    fn test_nondeterministic_py() {
793        for _ in 1_u32..=100 {
794            let mut state = prep_state(&["Z"], &["X"]);
795            let (_m0, d0) = state.py(0);
796            let (m1, d1) = state.my(0);
797            let m1_int = u8::from(m1);
798
799            assert_eq!(m1_int, 0); // |+Y>
800            assert!(!d0); // Not deterministic
801            assert!(d1); // Deterministic
802        }
803    }
804
805    #[test]
806    fn test_deterministic_py() {
807        let mut state = prep_state(&["iW"], &["Z"]);
808        let (m0, d0) = state.py(0);
809        let m0_int = u8::from(m0);
810
811        assert!(d0); // Deterministic
812        assert_eq!(m0_int, 0); // |+Y>
813    }
814
815    #[test]
816    fn test_nondeterministic_pny() {
817        for _ in 1_u32..=100 {
818            let mut state = prep_state(&["Z"], &["X"]);
819            let (_m0, d0) = state.pny(0);
820            let (m1, d1) = state.my(0);
821            let m1_int = u8::from(m1);
822
823            assert_eq!(m1_int, 1); // |-Y>
824            assert!(!d0); // Not deterministic
825            assert!(d1); // Deterministic
826        }
827    }
828
829    #[test]
830    fn test_deterministic_pny() {
831        let mut state = prep_state(&["-iW"], &["Z"]);
832        let (m0, d0) = state.pny(0);
833        let m0_int = u8::from(m0);
834
835        assert!(d0); // Deterministic
836        assert_eq!(m0_int, 0); // |-Y>
837    }
838
839    #[test]
840    fn test_nondeterministic_pz() {
841        for _ in 1_u32..=100 {
842            let mut state = prep_state(&["X"], &["Z"]);
843            let (_m0, d0) = state.pz(0);
844            let (m1, d1) = state.mz(0);
845            let m1_int = u8::from(m1);
846
847            assert_eq!(m1_int, 0); // |0>
848            assert!(!d0); // Not deterministic
849            assert!(d1); // Deterministic
850        }
851    }
852
853    #[test]
854    fn test_deterministic_pz() {
855        let mut state = prep_state(&["Z"], &["X"]);
856        let (m0, d0) = state.pz(0);
857        let m0_int = u8::from(m0);
858
859        assert!(d0); // Deterministic
860        assert_eq!(m0_int, 0); // |+Z>
861    }
862
863    #[test]
864    fn test_nondeterministic_pnz() {
865        for _ in 1_u32..=100 {
866            let mut state = prep_state(&["X"], &["Z"]);
867            let (_m0, d0) = state.pnz(0);
868            let (m1, d1) = state.mz(0);
869            let m1_int = u8::from(m1);
870
871            assert_eq!(m1_int, 1); // |1>
872            assert!(!d0); // Not deterministic
873            assert!(d1); // Deterministic
874        }
875    }
876
877    #[test]
878    fn test_deterministic_pnz() {
879        let mut state = prep_state(&["-Z"], &["X"]);
880        let (m0, d0) = state.pnz(0);
881        let m0_int = u8::from(m0);
882
883        assert!(d0); // Deterministic
884        assert_eq!(m0_int, 0); // |-Z>
885    }
886
887    #[test]
888    fn test_nondeterministic_mx() {
889        let mut state = prep_state(&["Z"], &["X"]);
890        let (_meas, determined) = state.mx(0);
891        assert!(!determined);
892    }
893
894    #[test]
895    fn test_deterministic_mx() {
896        let mut state0 = prep_state(&["X"], &["Z"]);
897        let (meas0, determined0) = state0.mx(0);
898        assert!(determined0);
899        assert!(!meas0);
900
901        let mut state1 = prep_state(&["-X"], &["Z"]);
902        let (meas1, determined1) = state1.mx(0);
903        assert!(determined1);
904        assert!(meas1);
905    }
906
907    #[test]
908    fn test_nondeterministic_mnx() {
909        let mut state = prep_state(&["Z"], &["X"]);
910        let (_meas, determined) = state.mnx(0);
911        assert!(!determined);
912    }
913
914    #[test]
915    fn test_deterministic_mnx() {
916        let mut state0 = prep_state(&["-X"], &["Z"]);
917        let (meas0, determined0) = state0.mnx(0);
918        assert!(determined0);
919        assert!(!meas0);
920
921        let mut state1 = prep_state(&["X"], &["Z"]);
922        let (meas1, determined1) = state1.mnx(0);
923        assert!(determined1);
924        assert!(meas1);
925    }
926
927    #[test]
928    fn test_nondeterministic_my() {
929        let mut state = prep_state(&["Z"], &["X"]);
930        let (_meas, determined) = state.my(0);
931        assert!(!determined);
932    }
933
934    #[test]
935    fn test_deterministic_my() {
936        let mut state0 = prep_state(&["iW"], &["Z"]);
937        let (meas0, determined0) = state0.my(0);
938        assert!(determined0);
939        assert!(!meas0);
940
941        let mut state1 = prep_state(&["-iW"], &["Z"]);
942        let (meas1, determined1) = state1.my(0);
943        assert!(determined1);
944        assert!(meas1);
945    }
946
947    #[test]
948    fn test_nondeterministic_mny() {
949        let mut state = prep_state(&["Z"], &["X"]);
950        let (_meas, determined) = state.mny(0);
951        assert!(!determined);
952    }
953
954    #[test]
955    fn test_deterministic_mny() {
956        let mut state0 = prep_state(&["-iW"], &["Z"]);
957        let (meas0, determined0) = state0.mny(0);
958        assert!(determined0);
959        assert!(!meas0);
960
961        let mut state1 = prep_state(&["iW"], &["Z"]);
962        let (meas1, determined1) = state1.mny(0);
963        assert!(determined1);
964        assert!(meas1);
965    }
966
967    #[test]
968    fn test_nondeterministic_mz() {
969        let mut state = prep_state(&["X"], &["Z"]);
970        let (_meas, determined) = state.mz(0);
971        assert!(!determined);
972    }
973
974    #[test]
975    fn test_deterministic_mz() {
976        let mut state0 = prep_state(&["Z"], &["X"]);
977        let (meas0, determined0) = state0.mz(0);
978        assert!(determined0);
979        assert!(!meas0);
980
981        let mut state1 = prep_state(&["-Z"], &["X"]);
982        let (meas1, determined1) = state1.mz(0);
983        assert!(determined1);
984        assert!(meas1);
985    }
986
987    #[test]
988    fn test_nondeterministic_mnz() {
989        let mut state = prep_state(&["X"], &["Z"]);
990        let (_meas, determined) = state.mnz(0);
991        assert!(!determined);
992    }
993
994    #[test]
995    fn test_deterministic_mnz() {
996        let mut state0 = prep_state(&["Z"], &["X"]);
997        let (meas0, determined0) = state0.mnz(0);
998        assert!(determined0);
999        assert!(meas0);
1000
1001        let mut state1 = prep_state(&["-Z"], &["X"]);
1002        let (meas1, determined1) = state1.mnz(0);
1003        assert!(determined1);
1004        assert!(!meas1);
1005    }
1006
1007    #[test]
1008    fn test_identity() {
1009        // I: +X -> +X; +Z -> +Z; +Y -> +Y;
1010
1011        // +X -> +X
1012        let mut state = prep_state(&["X"], &["Z"]);
1013        state.identity(0);
1014        check_state(&state, &["X"], &["Z"]);
1015
1016        // +Y -> -Y
1017        let mut state = prep_state(&["iW"], &["X"]);
1018        state.identity(0);
1019        check_state(&state, &["iW"], &["X"]);
1020
1021        // +Z -> -Z
1022        let mut state = prep_state(&["Z"], &["X"]);
1023        state.identity(0);
1024        check_state(&state, &["Z"], &["X"]);
1025
1026        // -IYI -> +IYI
1027        let mut state = prep_state(&["-iIWI"], &["IXI"]);
1028        state.identity(1);
1029        check_state(&state, &["-iIWI"], &["IXI"]);
1030    }
1031
1032    #[test]
1033    #[expect(clippy::shadow_unrelated)]
1034    fn test_x() {
1035        // X: +X -> +X; +Z -> -Z; +Y -> -Y;
1036
1037        // +X -> +X
1038        let mut state = prep_state(&["X"], &["Z"]);
1039        state.x(0);
1040        check_state(&state, &["X"], &["Z"]);
1041
1042        // +Y -> -Y
1043        let mut state = prep_state(&["iW"], &["X"]);
1044        state.x(0);
1045        check_state(&state, &["-iW"], &["X"]);
1046
1047        // +Z -> -Z
1048        let mut state = prep_state(&["Z"], &["X"]);
1049        state.x(0);
1050        check_state(&state, &["-Z"], &["X"]);
1051
1052        // -IYI -> +IYI
1053        let mut state = prep_state(&["-iIWI"], &["IXI"]);
1054        state.x(1);
1055        check_state(&state, &["iIWI"], &["IXI"]);
1056    }
1057
1058    #[test]
1059    #[expect(clippy::shadow_unrelated)]
1060    fn test_y() {
1061        // Y: +X -> -X; +Z -> -Z; +Y -> +Y;
1062
1063        // +X -> -X
1064        let mut state = prep_state(&["X"], &["Z"]);
1065        state.y(0);
1066        check_state(&state, &["-X"], &["Z"]);
1067
1068        // +Y -> +Y
1069        let mut state = prep_state(&["iW"], &["X"]);
1070        state.y(0);
1071        check_state(&state, &["iW"], &["X"]);
1072
1073        // +Z -> -Z
1074        let mut state = prep_state(&["Z"], &["X"]);
1075        state.y(0);
1076        check_state(&state, &["-Z"], &["X"]);
1077
1078        // -IXI -> +IXI
1079        let mut state = prep_state(&["-IXI"], &["IZI"]);
1080        state.y(1);
1081        check_state(&state, &["IXI"], &["IZI"]);
1082    }
1083
1084    #[test]
1085    #[expect(clippy::shadow_unrelated)]
1086    fn test_z() {
1087        // Z: +X -> -X; +Z -> +Z; +Y -> -Y;
1088
1089        // +X -> -X
1090        let mut state = prep_state(&["X"], &["Z"]);
1091        state.z(0);
1092        check_state(&state, &["-X"], &["Z"]);
1093
1094        // +Y -> -Y
1095        let mut state = prep_state(&["iW"], &["X"]);
1096        state.z(0);
1097        check_state(&state, &["-iW"], &["X"]);
1098
1099        // +Z -> +Z
1100        let mut state = prep_state(&["Z"], &["X"]);
1101        state.z(0);
1102        check_state(&state, &["Z"], &["X"]);
1103
1104        // -IXI -> +IXI
1105        let mut state = prep_state(&["-IXI"], &["IZI"]);
1106        state.z(1);
1107        check_state(&state, &["IXI"], &["IZI"]);
1108    }
1109
1110    #[test]
1111    #[expect(clippy::shadow_unrelated)]
1112    fn test_sx() {
1113        // SX: +X -> +X; +Z -> -Y; +Y -> +Z;
1114
1115        // +X -> +X
1116        let mut state = prep_state(&["X"], &["Z"]);
1117        state.sx(0);
1118        check_state(&state, &["X"], &["W"]);
1119
1120        // +Y -> +Z
1121        let mut state = prep_state(&["iW"], &["X"]);
1122        state.sx(0);
1123        check_state(&state, &["Z"], &["X"]);
1124
1125        // +Z -> -Y
1126        let mut state = prep_state(&["Z"], &["X"]);
1127        state.sx(0);
1128        check_state(&state, &["-iW"], &["X"]);
1129
1130        // -IYI -> -IZI
1131        let mut state = prep_state(&["-iIWI"], &["IXI"]);
1132        state.sx(1);
1133        check_state(&state, &["-IZI"], &["IXI"]);
1134    }
1135
1136    #[test]
1137    #[expect(clippy::shadow_unrelated)]
1138    fn test_sxdg() {
1139        // SXdg: +X -> +X; +Z -> +Y; +Y -> -Z;
1140
1141        // +X -> +X
1142        let mut state = prep_state(&["X"], &["Z"]);
1143        state.sxdg(0);
1144        check_state(&state, &["X"], &["W"]);
1145
1146        // +Y -> -Z
1147        let mut state = prep_state(&["iW"], &["X"]);
1148        state.sxdg(0);
1149        check_state(&state, &["-Z"], &["X"]);
1150
1151        // +Z -> +Y
1152        let mut state = prep_state(&["Z"], &["X"]);
1153        state.sxdg(0);
1154        check_state(&state, &["iW"], &["X"]);
1155
1156        // -IYI -> +IZI
1157        let mut state = prep_state(&["-iIWI"], &["IXI"]);
1158        state.sxdg(1);
1159        check_state(&state, &["IZI"], &["IXI"]);
1160    }
1161
1162    #[test]
1163    #[expect(clippy::shadow_unrelated)]
1164    fn test_sy() {
1165        // SY: +X -> -Z; +Z -> +X; +Y -> +Y;
1166
1167        // +X -> -Z
1168        let mut state = prep_state(&["X"], &["Z"]);
1169        state.sy(0);
1170        check_state(&state, &["-Z"], &["X"]);
1171
1172        // +Y -> +Y
1173        let mut state = prep_state(&["iW"], &["X"]);
1174        state.sy(0);
1175        check_state(&state, &["iW"], &["Z"]);
1176
1177        // +Z -> +X
1178        let mut state = prep_state(&["Z"], &["X"]);
1179        state.sy(0);
1180        check_state(&state, &["X"], &["Z"]);
1181
1182        // -IYI -> -IYI
1183        let mut state = prep_state(&["-iIWI"], &["IXI"]);
1184        state.sy(1);
1185        check_state(&state, &["-iIWI"], &["IZI"]);
1186    }
1187
1188    #[test]
1189    #[expect(clippy::shadow_unrelated)]
1190    fn test_sydg() {
1191        // SYdg: +X -> +Z; +Z -> -X; +Y -> +Y;
1192
1193        // +X -> +Z
1194        let mut state = prep_state(&["X"], &["Z"]);
1195        state.sydg(0);
1196        check_state(&state, &["Z"], &["X"]);
1197
1198        // +Y -> +Y
1199        let mut state = prep_state(&["iW"], &["X"]);
1200        state.sydg(0);
1201        check_state(&state, &["iW"], &["Z"]);
1202
1203        // +Z -> -X
1204        let mut state = prep_state(&["Z"], &["X"]);
1205        state.sydg(0);
1206        check_state(&state, &["-X"], &["Z"]);
1207
1208        // -IYI -> -IYI
1209        let mut state = prep_state(&["-iIWI"], &["IXI"]);
1210        state.sydg(1);
1211        check_state(&state, &["-iIWI"], &["IZI"]);
1212    }
1213
1214    #[test]
1215    #[expect(clippy::shadow_unrelated)]
1216    fn test_sz() {
1217        // SZ: +X -> +Y; +Z -> +Z; +Y -> -X;
1218
1219        // +X -> +Y
1220        let mut state = prep_state(&["X"], &["Z"]);
1221        state.sz(0);
1222        check_state(&state, &["iW"], &["Z"]);
1223
1224        // +Y -> -X
1225        let mut state = prep_state(&["iW"], &["X"]);
1226        state.sz(0);
1227        check_state(&state, &["-X"], &["W"]);
1228
1229        // +Z -> +Z
1230        let mut state = prep_state(&["Z"], &["X"]);
1231        state.sz(0);
1232        check_state(&state, &["Z"], &["W"]);
1233
1234        // -IYI -> +IXI
1235        let mut state = prep_state(&["-iIWI"], &["IXI"]);
1236        state.sz(1);
1237        check_state(&state, &["IXI"], &["IWI"]);
1238    }
1239
1240    #[test]
1241    fn test_szdg() {
1242        // SZdg: +X -> -Y; +Z -> +Z; +Y -> +X;
1243
1244        // +X -> -Y
1245        let mut state = prep_state(&["X"], &["Z"]);
1246        state.szdg(0);
1247        check_state(&state, &["-iW"], &["Z"]);
1248
1249        // +Y -> +X
1250        let mut state = prep_state(&["iW"], &["X"]);
1251        state.szdg(0);
1252        check_state(&state, &["X"], &["W"]);
1253
1254        // +Z -> +Z
1255        let mut state = prep_state(&["Z"], &["X"]);
1256        state.szdg(0);
1257        check_state(&state, &["Z"], &["W"]);
1258
1259        // -IYI -> -IXI
1260        let mut state = prep_state(&["-iIWI"], &["IXI"]);
1261        state.szdg(1);
1262        check_state(&state, &["-IXI"], &["IWI"]);
1263    }
1264
1265    #[test]
1266    #[expect(clippy::shadow_unrelated)]
1267    fn test_h() {
1268        // H: X -> Z; Z -> X; Y -> -Y;
1269
1270        // +X -> +Z
1271        let mut state = prep_state(&["X"], &["Z"]);
1272        state.h(0);
1273        check_state(&state, &["Z"], &["X"]);
1274
1275        // +Y -> -Y
1276        let mut state = prep_state(&["iW"], &["X"]);
1277        state.h(0);
1278        check_state(&state, &["-iW"], &["Z"]);
1279
1280        // +Z -> +X
1281        let mut state = prep_state(&["Z"], &["X"]);
1282        state.h(0);
1283        check_state(&state, &["X"], &["Z"]);
1284
1285        // -IYI -> +IYI
1286        let mut state = prep_state(&["-iIWI"], &["IXI"]);
1287        state.h(1);
1288        check_state(&state, &["iIWI"], &["IZI"]);
1289    }
1290
1291    #[test]
1292    #[expect(clippy::shadow_unrelated)]
1293    fn test_h2() {
1294        // H2: X -> -Z, Z -> -X, Y -> -Y
1295
1296        // +X -> -Z
1297        let mut state = prep_state(&["X"], &["Z"]);
1298        state.h2(0);
1299        check_state(&state, &["-Z"], &["X"]);
1300
1301        // +Y -> -Y
1302        let mut state = prep_state(&["iW"], &["X"]);
1303        state.h2(0);
1304        check_state(&state, &["-iW"], &["Z"]);
1305
1306        // +Z -> -X
1307        let mut state = prep_state(&["Z"], &["X"]);
1308        state.h2(0);
1309        check_state(&state, &["-X"], &["Z"]);
1310
1311        // -IYI -> +IYI
1312        let mut state = prep_state(&["-iIWI"], &["IXI"]);
1313        state.h2(1);
1314        check_state(&state, &["iIWI"], &["IZI"]);
1315    }
1316
1317    #[test]
1318    #[expect(clippy::shadow_unrelated)]
1319    fn test_h3() {
1320        // H3: X -> Y, Z -> -Z, Y -> X
1321
1322        // +X -> Y
1323        let mut state = prep_state(&["X"], &["Z"]);
1324        state.h3(0);
1325        check_state(&state, &["iW"], &["Z"]);
1326
1327        // +Y -> +X
1328        let mut state = prep_state(&["iW"], &["X"]);
1329        state.h3(0);
1330        check_state(&state, &["X"], &["W"]);
1331
1332        // +Z -> -Z
1333        let mut state = prep_state(&["Z"], &["X"]);
1334        state.h3(0);
1335        check_state(&state, &["-Z"], &["W"]);
1336
1337        // -IYI -> -IXI
1338        let mut state = prep_state(&["-iIWI"], &["IXI"]);
1339        state.h3(1);
1340        check_state(&state, &["-IXI"], &["IWI"]);
1341    }
1342
1343    #[test]
1344    #[expect(clippy::shadow_unrelated)]
1345    fn test_h4() {
1346        // H4: X -> -Y, Z -> -Z, Y -> -X
1347
1348        // +X -> -Y
1349        let mut state = prep_state(&["X"], &["Z"]);
1350        state.h4(0);
1351        check_state(&state, &["-iW"], &["Z"]);
1352
1353        // +Y -> -X
1354        let mut state = prep_state(&["iW"], &["X"]);
1355        state.h4(0);
1356        check_state(&state, &["-X"], &["W"]);
1357
1358        // +Z -> -Z
1359        let mut state = prep_state(&["Z"], &["X"]);
1360        state.h4(0);
1361        check_state(&state, &["-Z"], &["W"]);
1362
1363        // -IYI -> IXI
1364        let mut state = prep_state(&["-iIWI"], &["IXI"]);
1365        state.h4(1);
1366        check_state(&state, &["IXI"], &["IWI"]);
1367    }
1368
1369    #[test]
1370    #[expect(clippy::shadow_unrelated)]
1371    fn test_h5() {
1372        // H5: X -> -X, Z -> Y, Y -> Z
1373
1374        // +X -> -X
1375        let mut state = prep_state(&["X"], &["Z"]);
1376        state.h5(0);
1377        check_state(&state, &["-X"], &["W"]);
1378
1379        // +Y -> +Z
1380        let mut state = prep_state(&["iW"], &["X"]);
1381        state.h5(0);
1382        check_state(&state, &["Z"], &["X"]);
1383
1384        // +Z -> +Y
1385        let mut state = prep_state(&["Z"], &["X"]);
1386        state.h5(0);
1387        check_state(&state, &["iW"], &["X"]);
1388
1389        // -IYI -> -IZI
1390        let mut state = prep_state(&["-iIWI"], &["IXI"]);
1391        state.h5(1);
1392        check_state(&state, &["-IZI"], &["IXI"]);
1393    }
1394
1395    #[test]
1396    #[expect(clippy::shadow_unrelated)]
1397    fn test_h6() {
1398        // H6: X -> -X, Z -> -Y, Y -> -Z
1399
1400        // +X -> -X
1401        let mut state = prep_state(&["X"], &["Z"]);
1402        state.h6(0);
1403        check_state(&state, &["-X"], &["W"]);
1404
1405        // +Y -> -Z
1406        let mut state = prep_state(&["iW"], &["X"]);
1407        state.h6(0);
1408        check_state(&state, &["-Z"], &["X"]);
1409
1410        // +Z -> -Y
1411        let mut state = prep_state(&["Z"], &["X"]);
1412        state.h6(0);
1413        check_state(&state, &["-iW"], &["X"]);
1414
1415        // -IYI -> IZI
1416        let mut state = prep_state(&["-iIWI"], &["IXI"]);
1417        state.h6(1);
1418        check_state(&state, &["IZI"], &["IXI"]);
1419    }
1420
1421    #[test]
1422    #[expect(clippy::shadow_unrelated)]
1423    fn test_f() {
1424        // F: X -> Y, Z -> X, Y -> Z
1425
1426        // +X -> +Y
1427        let mut state = prep_state(&["X"], &["Z"]);
1428        state.f(0);
1429        check_state(&state, &["iW"], &["X"]);
1430
1431        // +Y -> +Z
1432        let mut state = prep_state(&["iW"], &["X"]);
1433        state.f(0);
1434        check_state(&state, &["Z"], &["W"]);
1435
1436        // +Z -> +X
1437        let mut state = prep_state(&["Z"], &["X"]);
1438        state.f(0);
1439        check_state(&state, &["X"], &["W"]);
1440
1441        // -IYI -> -IZI
1442        let mut state = prep_state(&["-iIWI"], &["IXI"]);
1443        state.f(1);
1444        check_state(&state, &["-IZI"], &["IWI"]);
1445    }
1446
1447    #[test]
1448    #[expect(clippy::shadow_unrelated)]
1449    fn test_fdg() {
1450        // Fdg: X -> Z, Z -> Y, Y -> X
1451
1452        // +X -> +Z
1453        let mut state = prep_state(&["X"], &["Z"]);
1454        state.fdg(0);
1455        check_state(&state, &["Z"], &["W"]);
1456
1457        // +Y -> +X
1458        let mut state = prep_state(&["iW"], &["X"]);
1459        state.fdg(0);
1460        check_state(&state, &["X"], &["Z"]);
1461
1462        // +Z -> +Y
1463        let mut state = prep_state(&["Z"], &["X"]);
1464        state.fdg(0);
1465        check_state(&state, &["iW"], &["Z"]);
1466
1467        // -IYI -> -IXI
1468        let mut state = prep_state(&["-iIWI"], &["IXI"]);
1469        state.fdg(1);
1470        check_state(&state, &["-IXI"], &["IZI"]);
1471    }
1472
1473    #[test]
1474    #[expect(clippy::shadow_unrelated)]
1475    fn test_f2() {
1476        // F2: X -> -Z, Z -> Y, Y -> -X
1477
1478        // +X -> -Z
1479        let mut state = prep_state(&["X"], &["Z"]);
1480        state.f2(0);
1481        check_state(&state, &["-Z"], &["W"]);
1482
1483        // +Y -> -X
1484        let mut state = prep_state(&["iW"], &["X"]);
1485        state.f2(0);
1486        check_state(&state, &["-X"], &["Z"]);
1487
1488        // +Z -> +Y
1489        let mut state = prep_state(&["Z"], &["X"]);
1490        state.f2(0);
1491        check_state(&state, &["iW"], &["Z"]);
1492
1493        // -IYI -> IXI
1494        let mut state = prep_state(&["-iIWI"], &["IXI"]);
1495        state.f2(1);
1496        check_state(&state, &["IXI"], &["IZI"]);
1497    }
1498
1499    #[test]
1500    #[expect(clippy::shadow_unrelated)]
1501    fn test_f2dg() {
1502        // F2dg: X -> -Y, Z -> -X, Y -> Z
1503
1504        // +X -> -Y
1505        let mut state = prep_state(&["X"], &["Z"]);
1506        state.f2dg(0);
1507        check_state(&state, &["-iW"], &["X"]);
1508
1509        // +Y -> +Z
1510        let mut state = prep_state(&["iW"], &["X"]);
1511        state.f2dg(0);
1512        check_state(&state, &["Z"], &["W"]);
1513
1514        // +Z -> -X
1515        let mut state = prep_state(&["Z"], &["X"]);
1516        state.f2dg(0);
1517        check_state(&state, &["-X"], &["W"]);
1518
1519        // -IYI -> -IZI
1520        let mut state = prep_state(&["-iIWI"], &["IXI"]);
1521        state.f2dg(1);
1522        check_state(&state, &["-IZI"], &["IWI"]);
1523    }
1524
1525    #[test]
1526    #[expect(clippy::shadow_unrelated)]
1527    fn test_f3() {
1528        // F3: X -> Y, Z -> -X, Y -> -Z
1529
1530        // +X -> +Y
1531        let mut state = prep_state(&["X"], &["Z"]);
1532        state.f3(0);
1533        check_state(&state, &["iW"], &["X"]);
1534
1535        // +Y -> -Z
1536        let mut state = prep_state(&["iW"], &["X"]);
1537        state.f3(0);
1538        check_state(&state, &["-Z"], &["W"]);
1539
1540        // +Z -> -X
1541        let mut state = prep_state(&["Z"], &["X"]);
1542        state.f3(0);
1543        check_state(&state, &["-X"], &["W"]);
1544
1545        // -IYI -> IZI
1546        let mut state = prep_state(&["-iIWI"], &["IXI"]);
1547        state.f3(1);
1548        check_state(&state, &["IZI"], &["IWI"]);
1549    }
1550
1551    #[test]
1552    #[expect(clippy::shadow_unrelated)]
1553    fn test_f3dg() {
1554        // F3dg: X -> -Z, Z -> -Y, Y -> X
1555
1556        // +X -> -Z
1557        let mut state = prep_state(&["X"], &["Z"]);
1558        state.f3dg(0);
1559        check_state(&state, &["-Z"], &["W"]);
1560
1561        // +Y -> +X
1562        let mut state = prep_state(&["iW"], &["X"]);
1563        state.f3dg(0);
1564        check_state(&state, &["X"], &["Z"]);
1565
1566        // +Z -> -Y
1567        let mut state = prep_state(&["Z"], &["X"]);
1568        state.f3dg(0);
1569        check_state(&state, &["-iW"], &["Z"]);
1570
1571        // -IYI -> -IXI
1572        let mut state = prep_state(&["-iIWI"], &["IXI"]);
1573        state.f3dg(1);
1574        check_state(&state, &["-IXI"], &["IZI"]);
1575    }
1576
1577    #[test]
1578    #[expect(clippy::shadow_unrelated)]
1579    fn test_f4() {
1580        // F4: X -> Z, Z -> -Y, Y -> -X
1581
1582        // +X -> +Z
1583        let mut state = prep_state(&["X"], &["Z"]);
1584        state.f4(0);
1585        check_state(&state, &["Z"], &["W"]);
1586
1587        // +Y -> -X
1588        let mut state = prep_state(&["iW"], &["X"]);
1589        state.f4(0);
1590        check_state(&state, &["-X"], &["Z"]);
1591
1592        // +Z -> -Y
1593        let mut state = prep_state(&["Z"], &["X"]);
1594        state.f4(0);
1595        check_state(&state, &["-iW"], &["Z"]);
1596
1597        // -IYI -> IXI
1598        let mut state = prep_state(&["-iIWI"], &["IXI"]);
1599        state.f4(1);
1600        check_state(&state, &["IXI"], &["IZI"]);
1601    }
1602
1603    #[test]
1604    #[expect(clippy::shadow_unrelated)]
1605    fn test_f4dg() {
1606        // F4dg: X -> -Y, Z -> X, Y -> -Z
1607
1608        // +X -> -Y
1609        let mut state = prep_state(&["X"], &["Z"]);
1610        state.f4dg(0);
1611        check_state(&state, &["-iW"], &["X"]);
1612
1613        // +Y -> -Z
1614        let mut state = prep_state(&["iW"], &["X"]);
1615        state.f4dg(0);
1616        check_state(&state, &["-Z"], &["W"]);
1617
1618        // +Z -> +X
1619        let mut state = prep_state(&["Z"], &["X"]);
1620        state.f4dg(0);
1621        check_state(&state, &["X"], &["W"]);
1622
1623        // -IYI -> +IZI
1624        let mut state = prep_state(&["-iIWI"], &["IXI"]);
1625        state.f4dg(1);
1626        check_state(&state, &["IZI"], &["IWI"]);
1627    }
1628
1629    #[test]
1630    #[expect(clippy::shadow_unrelated)]
1631    fn test_cx() {
1632        // CX: +IX -> +IX; +IZ -> +ZZ; +XI -> +XX; +ZI -> +ZI;
1633
1634        // TODO: Expand the set of stabilizer transformations evaluated.
1635
1636        // +IX -> +IX
1637        let mut state = prep_state(&["IX"], &["IZ"]);
1638        state.cx(0, 1);
1639        check_state(&state, &["IX"], &["ZZ"]);
1640
1641        // +IZ -> +ZZ
1642        let mut state = prep_state(&["IZ"], &["IX"]);
1643        state.cx(0, 1);
1644        check_state(&state, &["ZZ"], &["IX"]);
1645
1646        // +XI -> +XX
1647        let mut state = prep_state(&["XI"], &["ZI"]);
1648        state.cx(0, 1);
1649        check_state(&state, &["XX"], &["ZI"]);
1650
1651        // +ZI -> +ZI
1652        let mut state = prep_state(&["ZI"], &["XI"]);
1653        state.cx(0, 1);
1654        check_state(&state, &["ZI"], &["XX"]);
1655    }
1656
1657    #[test]
1658    fn test_cy() {
1659        // CY: +IX -> +ZX; +IZ -> +ZZ; +XI -> -XY; +ZI -> +ZI;
1660
1661        // TODO: Expand the set of stabilizer transformations evaluated.
1662
1663        // +IX -> +ZX
1664        let mut state = prep_state(&["IX"], &["IZ"]);
1665        state.cy(0, 1);
1666        check_state(&state, &["ZX"], &["ZZ"]);
1667
1668        // +IZ -> +ZZ
1669        let mut state = prep_state(&["IZ"], &["IX"]);
1670        state.cy(0, 1);
1671        check_state(&state, &["ZZ"], &["ZX"]);
1672
1673        // +XI -> -XY
1674        let mut state = prep_state(&["XI"], &["ZI"]);
1675        state.cy(0, 1);
1676        check_state(&state, &["-iXW"], &["ZI"]);
1677
1678        // +ZI -> +ZI
1679        let mut state = prep_state(&["ZI"], &["XI"]);
1680        state.cy(0, 1);
1681        check_state(&state, &["ZI"], &["XW"]);
1682    }
1683
1684    #[test]
1685    #[expect(clippy::shadow_unrelated)]
1686    fn test_cz() {
1687        // CZ: +IX -> +ZX; +IZ -> +IZ; +XI -> +XZ; +ZI -> +ZI;
1688
1689        // TODO: Expand the set of stabilizer transformations evaluated.
1690
1691        // +IX -> +ZX
1692        let mut state = prep_state(&["IX"], &["IZ"]);
1693        state.cz(0, 1);
1694        check_state(&state, &["ZX"], &["IZ"]);
1695
1696        // +IZ -> +IZ
1697        let mut state = prep_state(&["IZ"], &["IX"]);
1698        state.cz(0, 1);
1699        check_state(&state, &["IZ"], &["ZX"]);
1700
1701        // +XI -> +XZ
1702        let mut state = prep_state(&["XI"], &["ZI"]);
1703        state.cz(0, 1);
1704        check_state(&state, &["XZ"], &["ZI"]);
1705
1706        // +ZI -> +ZI
1707        let mut state = prep_state(&["ZI"], &["XI"]);
1708        state.cz(0, 1);
1709        check_state(&state, &["ZI"], &["XZ"]);
1710    }
1711
1712    #[test]
1713    #[expect(clippy::shadow_unrelated)]
1714    fn test_sxx() {
1715        // SXX: XI -> XI
1716        //      IX -> IX
1717        //      ZI -> -YX
1718        //      IZ -> -XY
1719
1720        // TODO: Expand the set of stabilizer transformations evaluated.
1721
1722        // +IX -> +XI
1723        let mut state = prep_state(&["IX"], &["IZ"]);
1724        state.sxx(0, 1);
1725        check_state(&state, &["IX"], &["XW"]);
1726
1727        // +IZ -> -XY
1728        let mut state = prep_state(&["IZ"], &["IX"]);
1729        state.sxx(0, 1);
1730        check_state(&state, &["-iXW"], &["IX"]);
1731
1732        // +XI -> +XI
1733        let mut state = prep_state(&["XI"], &["ZI"]);
1734        state.sxx(0, 1);
1735        check_state(&state, &["XI"], &["WX"]);
1736
1737        // +ZI -> -YX
1738        let mut state = prep_state(&["ZI"], &["XI"]);
1739        state.sxx(0, 1);
1740        check_state(&state, &["-iWX"], &["XI"]);
1741    }
1742
1743    #[test]
1744    #[expect(clippy::shadow_unrelated)]
1745    fn test_sxxdg() {
1746        // SXXdg: XI -> XI
1747        //        IX -> IX
1748        //        ZI -> YX
1749        //        IZ -> XY
1750
1751        // TODO: Expand the set of stabilizer transformations evaluated.
1752
1753        // +IX -> +XI
1754        let mut state = prep_state(&["IX"], &["IZ"]);
1755        state.sxxdg(0, 1);
1756        check_state(&state, &["IX"], &["XW"]);
1757
1758        // +IZ -> +XY
1759        let mut state = prep_state(&["IZ"], &["IX"]);
1760        state.sxxdg(0, 1);
1761        check_state(&state, &["iXW"], &["IX"]);
1762
1763        // +XI -> +XI
1764        let mut state = prep_state(&["XI"], &["ZI"]);
1765        state.sxxdg(0, 1);
1766        check_state(&state, &["XI"], &["WX"]);
1767
1768        // +ZI -> +YX
1769        let mut state = prep_state(&["ZI"], &["XI"]);
1770        state.sxxdg(0, 1);
1771        check_state(&state, &["iWX"], &["XI"]);
1772    }
1773
1774    #[test]
1775    #[expect(clippy::shadow_unrelated)]
1776    fn test_syy() {
1777        // SYY: XI -> -ZY
1778        //      IX -> -YZ
1779        //      ZI -> XY
1780        //      IZ -> YX
1781
1782        // TODO: Expand the set of stabilizer transformations evaluated.
1783
1784        // +IX -> -YZ
1785        let mut state = prep_state(&["IX"], &["IZ"]);
1786        state.syy(0, 1);
1787        check_state(&state, &["-iWZ"], &["WX"]);
1788
1789        // +IZ -> +YX
1790        let mut state = prep_state(&["IZ"], &["IX"]);
1791        state.syy(0, 1);
1792        check_state(&state, &["iWX"], &["WZ"]);
1793
1794        // +XI -> -ZY
1795        let mut state = prep_state(&["XI"], &["ZI"]);
1796        state.syy(0, 1);
1797        check_state(&state, &["-iZW"], &["XW"]);
1798
1799        // +ZI -> +XY
1800        let mut state = prep_state(&["ZI"], &["XI"]);
1801        state.syy(0, 1);
1802        check_state(&state, &["iXW"], &["ZW"]);
1803    }
1804
1805    #[test]
1806    #[expect(clippy::shadow_unrelated)]
1807    fn test_syydg() {
1808        // SYYdg: XI -> ZY
1809        //        IX -> YZ
1810        //        ZI -> -XY
1811        //        IZ -> -YX
1812
1813        // TODO: Expand the set of stabilizer transformations evaluated.
1814
1815        // +IX -> YZ
1816        let mut state = prep_state(&["IX"], &["IZ"]);
1817        state.syydg(0, 1);
1818        check_state(&state, &["iWZ"], &["WX"]);
1819
1820        // +IZ -> -YX
1821        let mut state = prep_state(&["IZ"], &["IX"]);
1822        state.syydg(0, 1);
1823        check_state(&state, &["-iWX"], &["WZ"]);
1824
1825        // +XI -> ZY
1826        let mut state = prep_state(&["XI"], &["ZI"]);
1827        state.syydg(0, 1);
1828        check_state(&state, &["iZW"], &["XW"]);
1829
1830        // +ZI -> +XY
1831        let mut state = prep_state(&["ZI"], &["XI"]);
1832        state.syydg(0, 1);
1833        check_state(&state, &["-iXW"], &["ZW"]);
1834    }
1835
1836    #[test]
1837    #[expect(clippy::shadow_unrelated)]
1838    fn test_szz() {
1839        // SZZ: +IX -> +ZY;
1840        //      +IZ -> +IZ;
1841        //      +XI -> +ZY;
1842        //      +ZI -> +ZI;
1843
1844        // TODO: Expand the set of stabilizer transformations evaluated.
1845
1846        // +IX -> ZY
1847        let mut state = prep_state(&["IX"], &["IZ"]);
1848        state.szz(0, 1);
1849        check_state(&state, &["iZW"], &["IZ"]);
1850
1851        // +IZ -> IZ
1852        let mut state = prep_state(&["IZ"], &["IX"]);
1853        state.szz(0, 1);
1854        check_state(&state, &["IZ"], &["ZW"]);
1855
1856        // +XI -> YZ
1857        let mut state = prep_state(&["XI"], &["ZI"]);
1858        state.szz(0, 1);
1859        check_state(&state, &["iWZ"], &["ZI"]);
1860
1861        // +ZI -> ZI
1862        let mut state = prep_state(&["ZI"], &["XI"]);
1863        state.szz(0, 1);
1864        check_state(&state, &["ZI"], &["WZ"]);
1865    }
1866
1867    #[test]
1868    #[expect(clippy::shadow_unrelated)]
1869    fn test_szzdg() {
1870        // SZZ: +IX -> -ZY;
1871        //      +IZ -> +IZ;
1872        //      +XI -> -ZY;
1873        //      +ZI -> +ZI;
1874
1875        // TODO: Expand the set of stabilizer transformations evaluated.
1876
1877        // +IX -> -ZY
1878        let mut state = prep_state(&["IX"], &["IZ"]);
1879        state.szzdg(0, 1);
1880        check_state(&state, &["-iZW"], &["IZ"]);
1881
1882        // +IZ -> IZ
1883        let mut state = prep_state(&["IZ"], &["IX"]);
1884        state.szzdg(0, 1);
1885        check_state(&state, &["IZ"], &["ZW"]);
1886
1887        // +XI -> -YZ
1888        let mut state = prep_state(&["XI"], &["ZI"]);
1889        state.szzdg(0, 1);
1890        check_state(&state, &["-iWZ"], &["ZI"]);
1891
1892        // +ZI -> ZI
1893        let mut state = prep_state(&["ZI"], &["XI"]);
1894        state.szzdg(0, 1);
1895        check_state(&state, &["ZI"], &["WZ"]);
1896    }
1897
1898    #[test]
1899    #[expect(clippy::shadow_unrelated)]
1900    fn test_swap() {
1901        // SWAP: +IX -> +XI;
1902        //       +IZ -> +ZI;
1903        //       +XI -> +IX;
1904        //       +ZI -> +IZ;
1905
1906        // TODO: Expand the set of stabilizer transformations evaluated.
1907
1908        // +IX -> +XI
1909        let mut state = prep_state(&["IX"], &["IZ"]);
1910        state.swap(0, 1);
1911        check_state(&state, &["XI"], &["ZI"]);
1912
1913        // +IZ -> +ZI
1914        let mut state = prep_state(&["IZ"], &["IX"]);
1915        state.swap(0, 1);
1916        check_state(&state, &["ZI"], &["XI"]);
1917
1918        // +XI -> +IX
1919        let mut state = prep_state(&["XI"], &["ZI"]);
1920        state.swap(0, 1);
1921        check_state(&state, &["IX"], &["IZ"]);
1922
1923        // +ZI -> +IZ
1924        let mut state = prep_state(&["ZI"], &["XI"]);
1925        state.swap(0, 1);
1926        check_state(&state, &["IZ"], &["IX"]);
1927    }
1928
1929    #[test]
1930    #[expect(clippy::shadow_unrelated)]
1931    fn test_g2() {
1932        // G2: +XI -> +IX
1933        //     +IX -> +XI
1934        //     +ZI -> +XZ
1935        //     +IZ -> +ZX
1936
1937        // TODO: Expand the set of stabilizer transformations evaluated.
1938
1939        // +IX -> +XI
1940        let mut state = prep_state(&["IX"], &["IZ"]);
1941        state.g2(0, 1);
1942        check_state(&state, &["XI"], &["ZX"]);
1943
1944        // +IZ -> +ZX
1945        let mut state = prep_state(&["IZ"], &["IX"]);
1946        state.g2(0, 1);
1947        check_state(&state, &["ZX"], &["XI"]);
1948
1949        // +XI -> +IX
1950        let mut state = prep_state(&["XI"], &["ZI"]);
1951        state.g2(0, 1);
1952        check_state(&state, &["IX"], &["XZ"]);
1953
1954        // +ZI -> +XZ
1955        let mut state = prep_state(&["ZI"], &["XI"]);
1956        state.g2(0, 1);
1957        check_state(&state, &["XZ"], &["IX"]);
1958    }
1959
1960    fn one_bit_z_teleport(
1961        mut state: SparseStab<VecSet<u32>, u32>,
1962    ) -> (SparseStab<VecSet<u32>, u32>, bool) {
1963        state.cx(1, 0);
1964        state.h(1);
1965        let (m1, d1) = state.mz(1);
1966        if m1 {
1967            state.z(0);
1968        }
1969        (state, d1)
1970    }
1971
1972    /// Test one-bit Z teleportation of |+X>
1973    #[test]
1974    fn test_nondeterministic_mz_one_bit_z_teleportation_of_x() {
1975        // See: arXiv:quant-ph/0002039
1976
1977        for _ in 1_u32..=100 {
1978            let d1;
1979            let mut state: SparseStab<VecSet<u32>, u32> = SparseStab::new(2);
1980            state.h(1); // Set input to |+>
1981            (state, d1) = one_bit_z_teleport(state);
1982            // X basis meas
1983            state.h(0);
1984            let (m0, d0) = state.mz(0);
1985            let m0_int = u8::from(m0);
1986            assert_eq!(m0_int, 0); // |+> -> 0 == false
1987            assert!(!d1); // Not deterministic
1988            assert!(d0); // Deterministic
1989        }
1990    }
1991
1992    /// Test one-bit Z teleportation of |-X>
1993    #[test]
1994    fn test_nondeterministic_mz_one_bit_z_teleportation_of_nx() {
1995        // See: arXiv:quant-ph/0002039
1996
1997        for _ in 1_u32..=100 {
1998            let d1;
1999            let mut state: SparseStab<VecSet<u32>, u32> = SparseStab::new(2);
2000            state.x(1);
2001            state.h(1); // Set input to |->
2002            (state, d1) = one_bit_z_teleport(state);
2003            // X basis meas
2004            state.h(0);
2005            let (m0, d0) = state.mz(0);
2006            let m0_int = u8::from(m0);
2007            assert_eq!(m0_int, 1); // |-> -> 1 == true
2008            assert!(!d1); // Not deterministic
2009            assert!(d0); // Deterministic
2010        }
2011    }
2012
2013    /// Test one-bit Z teleportation of |+Y>
2014    #[test]
2015    fn test_nondeterministic_mz_one_bit_z_teleportation_of_y() {
2016        // See: arXiv:quant-ph/0002039
2017
2018        for _ in 1_u32..=100 {
2019            let d1;
2020            let mut state: SparseStab<VecSet<u32>, u32> = SparseStab::new(2);
2021            state.sxdg(1); // Set input to |+i>
2022            (state, d1) = one_bit_z_teleport(state);
2023            // Y basis meas
2024            state.sx(0); // Y -> Z
2025            let (m0, d0) = state.mz(0);
2026            let m0_int = u8::from(m0);
2027            assert_eq!(m0_int, 0); // |+X> -> 0 == false
2028            assert!(!d1); // Not deterministic
2029            assert!(d0); // Deterministic
2030        }
2031    }
2032
2033    /// Test one-bit Z teleportation of |-Y>
2034    #[test]
2035    fn test_nondeterministic_mz_one_bit_z_teleportation_of_ny() {
2036        // See: arXiv:quant-ph/0002039
2037
2038        for _ in 1_u32..=100 {
2039            let d1;
2040            let mut state: SparseStab<VecSet<u32>, u32> = SparseStab::new(2);
2041            state.x(1);
2042            state.sxdg(1); // Set input to |-i>
2043            (state, d1) = one_bit_z_teleport(state);
2044            // Y basis meas
2045            state.sx(0); // Y -> Z
2046            let (m0, d0) = state.mz(0);
2047            let m0_int = u8::from(m0);
2048            assert_eq!(m0_int, 1); // |-Y> -> 1 == true
2049            assert!(!d1); // Not deterministic
2050            assert!(d0); // Deterministic
2051        }
2052    }
2053
2054    /// Test one-bit Z teleportation of |+Z>
2055    #[test]
2056    fn test_nondeterministic_mz_one_bit_z_teleportation_of_z() {
2057        // See: arXiv:quant-ph/0002039
2058
2059        for _ in 1_u32..=100 {
2060            let d1;
2061            let mut state: SparseStab<VecSet<u32>, u32> = SparseStab::new(2);
2062            // Set input to |0>
2063            (state, d1) = one_bit_z_teleport(state);
2064            let (m0, d0) = state.mz(0);
2065            let m0_int = u8::from(m0);
2066            assert_eq!(m0_int, 0); // |0>
2067            assert!(!d1); // Not deterministic
2068            assert!(d0); // Deterministic
2069        }
2070    }
2071
2072    /// Test one-bit Z teleportation of |-Z>
2073    #[test]
2074    fn test_nondeterministic_mz_one_bit_z_teleportation_of_nz() {
2075        // See: arXiv:quant-ph/0002039
2076
2077        for _ in 1_u32..=100 {
2078            let d1;
2079            let mut state: SparseStab<VecSet<u32>, u32> = SparseStab::new(2);
2080            state.x(1); // Set input to |1>
2081            (state, d1) = one_bit_z_teleport(state);
2082            let (m0, d0) = state.mz(0);
2083            let m0_int = u8::from(m0);
2084            assert_eq!(m0_int, 1); // |1> -> 1 == true
2085            assert!(!d1); // Not deterministic
2086            assert!(d0); // Deterministic
2087        }
2088    }
2089
2090    fn teleport(
2091        mut state: SparseStab<VecSet<u32>, u32>,
2092    ) -> (SparseStab<VecSet<u32>, u32>, bool, bool) {
2093        // |psi> -----.-H-MZ=m0
2094        //            |
2095        // |0>   -H-.-X---MZ=m1
2096        //          |
2097        // |0>   ---X------------X^m1-Z^m0-MZ=m2
2098
2099        state.h(1);
2100        state.cx(1, 2);
2101        state.cx(0, 1);
2102        state.h(0);
2103        let (m0, d0) = state.mz(0);
2104        let (m1, d1) = state.mz(1);
2105        if m1 {
2106            state.x(2);
2107        }
2108        if m0 {
2109            state.z(2);
2110        }
2111        (state, d0, d1)
2112    }
2113
2114    #[test]
2115    fn test_nondeterministic_mz_via_teleportation_x() {
2116        for _ in 1_u32..=100 {
2117            let d0;
2118            let d1;
2119            let mut state: SparseStab<VecSet<u32>, u32> = SparseStab::new(3);
2120            state.h(0);
2121            (state, d0, d1) = teleport(state);
2122            state.h(2);
2123            let (m2, d2) = state.mz(2);
2124            let m2_int = u8::from(m2);
2125            assert_eq!(m2_int, 0);
2126            assert!(!d0);
2127            assert!(!d1);
2128            assert!(d2);
2129        }
2130    }
2131
2132    #[test]
2133    fn test_nondeterministic_mz_via_teleportation_nx() {
2134        for _ in 1_u32..=100 {
2135            let d0;
2136            let d1;
2137            let mut state: SparseStab<VecSet<u32>, u32> = SparseStab::new(3);
2138            state.x(0);
2139            state.h(0);
2140            (state, d0, d1) = teleport(state);
2141            state.h(2);
2142            let (m2, d2) = state.mz(2);
2143            let m2_int = u8::from(m2);
2144
2145            assert_eq!(m2_int, 1);
2146            assert!(!d0);
2147            assert!(!d1);
2148            assert!(d2);
2149        }
2150    }
2151
2152    #[test]
2153    fn test_nondeterministic_mz_via_teleportation_y() {
2154        for _ in 1_u32..=100 {
2155            let d0;
2156            let d1;
2157            let mut state: SparseStab<VecSet<u32>, u32> = SparseStab::new(3);
2158            state.sxdg(0);
2159            (state, d0, d1) = teleport(state);
2160            state.sx(2);
2161            let (m2, d2) = state.mz(2);
2162            let m2_int = u8::from(m2);
2163            assert_eq!(m2_int, 0);
2164            assert!(!d0);
2165            assert!(!d1);
2166            assert!(d2);
2167        }
2168    }
2169
2170    #[test]
2171    fn test_nondeterministic_mz_via_teleportation_ny() {
2172        for _ in 1_u32..=100 {
2173            let d0;
2174            let d1;
2175            let mut state: SparseStab<VecSet<u32>, u32> = SparseStab::new(3);
2176            state.x(0);
2177            state.sxdg(0);
2178            (state, d0, d1) = teleport(state);
2179            state.sx(2);
2180            let (m2, d2) = state.mz(2);
2181            let m2_int = u8::from(m2);
2182            assert_eq!(m2_int, 1);
2183            assert!(!d0);
2184            assert!(!d1);
2185            assert!(d2);
2186        }
2187    }
2188
2189    #[test]
2190    fn test_nondeterministic_mz_via_teleportation_z() {
2191        for _ in 1_u32..=100 {
2192            let d0;
2193            let d1;
2194            let mut state: SparseStab<VecSet<u32>, u32> = SparseStab::new(3);
2195            (state, d0, d1) = teleport(state);
2196            let (m2, d2) = state.mz(2);
2197            let m2_int = u8::from(m2);
2198
2199            assert_eq!(m2_int, 0);
2200            assert!(!d0);
2201            assert!(!d1);
2202            assert!(d2);
2203        }
2204    }
2205
2206    #[test]
2207    fn test_nondeterministic_mz_via_teleportation_nz() {
2208        for _ in 1_u32..=100 {
2209            let d0;
2210            let d1;
2211            let mut state: SparseStab<VecSet<u32>, u32> = SparseStab::new(3);
2212            state.x(0); // input state |-Z>
2213            (state, d0, d1) = teleport(state);
2214            let (m2, d2) = state.mz(2);
2215            let m2_int = u8::from(m2);
2216
2217            assert_eq!(m2_int, 1);
2218            assert!(!d0);
2219            assert!(!d1);
2220            assert!(d2);
2221        }
2222    }
2223
2224    // TODO: Consider "forcing" the random number for cleaner testing.
2225    // TODO: Consider a seed to still have random numbers but make them predictable
2226}