Skip to main content

qec_code/
pauli.rs

1use crate::error::{QecError, Result};
2use crate::symplectic;
3
4#[derive(Debug, Clone, PartialEq, Eq)]
5pub struct Pauli {
6    x: Vec<u8>,
7    z: Vec<u8>,
8}
9
10impl Pauli {
11    pub fn from_xz_bits(x: Vec<u8>, z: Vec<u8>) -> Result<Self> {
12        if x.len() != z.len() {
13            return Err(QecError::InvalidPauliWidth {
14                x_width: x.len(),
15                z_width: z.len(),
16            });
17        }
18        validate_pauli_bits("X", &x)?;
19        validate_pauli_bits("Z", &z)?;
20        Ok(Self { x, z })
21    }
22
23    pub fn from_symplectic_row(row: Vec<u8>) -> Result<Self> {
24        if row.len() % 2 != 0 {
25            return Err(QecError::InvalidSymplecticRowWidth { width: row.len() });
26        }
27
28        let qubits = row.len() / 2;
29        let (x, z) = row.split_at(qubits);
30        Self::from_xz_bits(x.to_vec(), z.to_vec())
31    }
32
33    pub fn n(&self) -> usize {
34        self.x.len()
35    }
36
37    pub fn x_bits(&self) -> &[u8] {
38        &self.x
39    }
40
41    pub fn z_bits(&self) -> &[u8] {
42        &self.z
43    }
44
45    pub fn try_symplectic_product(&self, other: &Self) -> Result<u8> {
46        if self.n() != other.n() {
47            return Err(QecError::InvalidPauliWidth {
48                x_width: self.n(),
49                z_width: other.n(),
50            });
51        }
52
53        symplectic::symplectic_product(&self.to_symplectic_row(), &other.to_symplectic_row())
54    }
55
56    pub fn symplectic_product(&self, other: &Self) -> u8 {
57        self.try_symplectic_product(other)
58            .expect("Pauli widths must match")
59    }
60
61    pub fn weight(&self) -> usize {
62        self.x
63            .iter()
64            .zip(&self.z)
65            .filter(|(x, z)| (**x | **z) == 1)
66            .count()
67    }
68
69    pub fn try_commutes_with(&self, other: &Self) -> Result<bool> {
70        if self.n() != other.n() {
71            return Err(QecError::InvalidPauliWidth {
72                x_width: self.n(),
73                z_width: other.n(),
74            });
75        }
76
77        symplectic::commutes(&self.to_symplectic_row(), &other.to_symplectic_row())
78    }
79
80    pub fn commutes_with(&self, other: &Self) -> bool {
81        self.try_commutes_with(other)
82            .expect("Pauli widths must match")
83    }
84
85    pub fn try_anticommutes_with(&self, other: &Self) -> Result<bool> {
86        Ok(!self.try_commutes_with(other)?)
87    }
88
89    pub fn anticommutes_with(&self, other: &Self) -> bool {
90        self.try_anticommutes_with(other)
91            .expect("Pauli widths must match")
92    }
93
94    pub fn to_symplectic_row(&self) -> Vec<u8> {
95        let mut row = Vec::with_capacity(self.n() * 2);
96        row.extend_from_slice(&self.x);
97        row.extend_from_slice(&self.z);
98        row
99    }
100}
101
102fn validate_pauli_bits(which: &'static str, bits: &[u8]) -> Result<()> {
103    for (index, bit) in bits.iter().enumerate() {
104        if *bit > 1 {
105            return Err(QecError::InvalidPauliBit {
106                which,
107                index,
108                value: *bit,
109            });
110        }
111    }
112    Ok(())
113}