quantrs2_core/error_correction/
pauli.rs1use crate::error::{QuantRS2Error, QuantRS2Result};
4use scirs2_core::ndarray::Array2;
5use scirs2_core::Complex64;
6use std::fmt;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10pub enum Pauli {
11 I,
12 X,
13 Y,
14 Z,
15}
16
17impl Pauli {
18 pub fn matrix(&self) -> Array2<Complex64> {
20 match self {
21 Self::I => Array2::from_shape_vec(
22 (2, 2),
23 vec![
24 Complex64::new(1.0, 0.0),
25 Complex64::new(0.0, 0.0),
26 Complex64::new(0.0, 0.0),
27 Complex64::new(1.0, 0.0),
28 ],
29 )
30 .expect("Pauli I matrix: 2x2 shape with 4 elements is always valid"),
31 Self::X => Array2::from_shape_vec(
32 (2, 2),
33 vec![
34 Complex64::new(0.0, 0.0),
35 Complex64::new(1.0, 0.0),
36 Complex64::new(1.0, 0.0),
37 Complex64::new(0.0, 0.0),
38 ],
39 )
40 .expect("Pauli X matrix: 2x2 shape with 4 elements is always valid"),
41 Self::Y => Array2::from_shape_vec(
42 (2, 2),
43 vec![
44 Complex64::new(0.0, 0.0),
45 Complex64::new(0.0, -1.0),
46 Complex64::new(0.0, 1.0),
47 Complex64::new(0.0, 0.0),
48 ],
49 )
50 .expect("Pauli Y matrix: 2x2 shape with 4 elements is always valid"),
51 Self::Z => Array2::from_shape_vec(
52 (2, 2),
53 vec![
54 Complex64::new(1.0, 0.0),
55 Complex64::new(0.0, 0.0),
56 Complex64::new(0.0, 0.0),
57 Complex64::new(-1.0, 0.0),
58 ],
59 )
60 .expect("Pauli Z matrix: 2x2 shape with 4 elements is always valid"),
61 }
62 }
63
64 pub const fn multiply(&self, other: &Self) -> (Complex64, Self) {
66 use Pauli::{I, X, Y, Z};
67 match (self, other) {
68 (I, p) | (p, I) => (Complex64::new(1.0, 0.0), *p),
69 (X, X) | (Y, Y) | (Z, Z) => (Complex64::new(1.0, 0.0), I),
70 (X, Y) => (Complex64::new(0.0, 1.0), Z),
71 (Y, X) => (Complex64::new(0.0, -1.0), Z),
72 (Y, Z) => (Complex64::new(0.0, 1.0), X),
73 (Z, Y) => (Complex64::new(0.0, -1.0), X),
74 (Z, X) => (Complex64::new(0.0, 1.0), Y),
75 (X, Z) => (Complex64::new(0.0, -1.0), Y),
76 }
77 }
78}
79
80#[derive(Debug, Clone, PartialEq)]
82pub struct PauliString {
83 pub phase: Complex64,
85 pub paulis: Vec<Pauli>,
87}
88
89impl PauliString {
90 pub const fn new(paulis: Vec<Pauli>) -> Self {
92 Self {
93 phase: Complex64::new(1.0, 0.0),
94 paulis,
95 }
96 }
97
98 pub fn identity(n: usize) -> Self {
100 Self::new(vec![Pauli::I; n])
101 }
102
103 pub fn weight(&self) -> usize {
105 self.paulis.iter().filter(|&&p| p != Pauli::I).count()
106 }
107
108 pub fn multiply(&self, other: &Self) -> QuantRS2Result<Self> {
110 if self.paulis.len() != other.paulis.len() {
111 return Err(QuantRS2Error::InvalidInput(
112 "Pauli strings must have same length".to_string(),
113 ));
114 }
115
116 let mut phase = self.phase * other.phase;
117 let mut paulis = Vec::with_capacity(self.paulis.len());
118
119 for (p1, p2) in self.paulis.iter().zip(&other.paulis) {
120 let (factor, result) = p1.multiply(p2);
121 phase *= factor;
122 paulis.push(result);
123 }
124
125 Ok(Self { phase, paulis })
126 }
127
128 pub fn commutes_with(&self, other: &Self) -> QuantRS2Result<bool> {
130 if self.paulis.len() != other.paulis.len() {
131 return Err(QuantRS2Error::InvalidInput(
132 "Pauli strings must have same length".to_string(),
133 ));
134 }
135
136 let mut commutation_count = 0;
137 for (p1, p2) in self.paulis.iter().zip(&other.paulis) {
138 if *p1 != Pauli::I && *p2 != Pauli::I && p1 != p2 {
139 commutation_count += 1;
140 }
141 }
142
143 Ok(commutation_count % 2 == 0)
144 }
145}
146
147impl fmt::Display for PauliString {
148 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149 let phase_str = if self.phase == Complex64::new(1.0, 0.0) {
150 "+".to_string()
151 } else if self.phase == Complex64::new(-1.0, 0.0) {
152 "-".to_string()
153 } else if self.phase == Complex64::new(0.0, 1.0) {
154 "+i".to_string()
155 } else {
156 "-i".to_string()
157 };
158
159 write!(f, "{phase_str}")?;
160 for p in &self.paulis {
161 write!(f, "{p:?}")?;
162 }
163 Ok(())
164 }
165}