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}