1use std::fmt::Display;
5
6use crate::utils::{set_row_to_row_sum, set_vec_to_row_sum, swap_columns};
7use ndarray::{s, Array, Array1, Array2};
8use serde::{Deserialize, Serialize};
9
10#[cfg(feature = "python")]
11use pyo3::{prelude::*, PyObjectProtocol};
12
13#[cfg(feature = "python")]
14use std::io::{Error, ErrorKind};
15
16#[cfg_attr(feature = "python", pyclass(name = "Tableau"))]
23#[derive(Clone, Debug, Serialize, Deserialize)]
24pub struct Tableau {
25 n_qubits: usize,
29 table: Array2<bool>,
30}
31
32impl Display for Tableau {
33 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34 write!(
35 f,
36 "Stabilizer tableau on {} qubits:\n{:?}",
37 self.n_qubits, self.table
38 )
39 }
40}
41
42impl Tableau {
43 const fn idx_phase(&self) -> usize {
44 2 * self.n_qubits
45 }
46
47 pub fn new(n_qubits: usize) -> Self {
51 Tableau {
52 n_qubits,
53 table: Array::from_shape_fn((2 * n_qubits, 2 * n_qubits + 1), |(i, j)| i == j),
54 }
55 }
56
57 fn idx_x(&self, idx_col: usize) -> usize {
58 idx_col
59 }
60
61 fn idx_z(&self, idx_col: usize) -> usize {
62 idx_col + self.n_qubits
63 }
64
65 fn determinstic_result(&self, idx_target: usize) -> Option<bool> {
68 let determined = !self
69 .table
70 .slice(s![self.n_qubits.., idx_target])
71 .iter()
72 .any(|b| *b);
73 if determined {
74 let mut vector: Array1<bool> = Array::default(2 * self.n_qubits + 1);
76 for idx_destabilizer in 0..self.n_qubits {
77 if self.table[(idx_destabilizer, self.idx_x(idx_target))] {
78 set_vec_to_row_sum(&mut vector, &self.table, idx_destabilizer + self.n_qubits);
79 }
80 }
81 Some(vector[2 * self.n_qubits])
82 } else {
83 None
84 }
85 }
86}
87
88impl Tableau {
89 pub fn assert_meas(&self, idx_target: usize, expected: bool) -> Result<(), String> {
95 let actual = self.determinstic_result(idx_target).ok_or(format!(
96 "Expected {}, but measurement result would be random.",
97 expected
98 ))?;
99 if actual != expected {
100 Err(format!(
101 "Expected {}, but measurement result would actually be {}.",
102 expected, actual
103 ))
104 } else {
105 Ok(())
106 }
107 }
108}
109
110#[cfg_attr(feature = "python", pymethods)]
111impl Tableau {
112 #[cfg(feature = "python")]
114 pub fn as_json(&self) -> PyResult<String> {
115 serde_json::to_string(self)
116 .map_err(|e| PyErr::from(Error::new(ErrorKind::Other, e.to_string())))
117 }
118
119 pub fn apply_h_mut(&mut self, idx_target: usize) {
121 let idxs = (self.idx_x(idx_target), self.idx_z(idx_target));
122 swap_columns(&mut self.table, idxs);
123 let idx_phase = self.idx_phase();
124 for idx_row in 0..2 * self.n_qubits {
125 let a = self.table[(idx_row, self.idx_x(idx_target))];
126 let b = self.table[(idx_row, self.idx_z(idx_target))];
127 self.table[(idx_row, idx_phase)] ^= a && b;
128 }
129 }
130
131 pub fn apply_s_mut(&mut self, idx_target: usize) {
133 let idx_phase = self.idx_phase();
134 for idx_row in 0..2 * self.n_qubits {
135 self.table[(idx_row, idx_phase)] ^= self.table[(idx_row, self.idx_x(idx_target))]
136 && self.table[(idx_row, self.idx_z(idx_target))];
137 }
138
139 for idx_row in 0..2 * self.n_qubits {
140 let idx_x_target = self.idx_x(idx_target);
141 let idx_z_target = self.idx_z(idx_target);
142 self.table[(idx_row, idx_z_target)] ^= self.table[(idx_row, idx_x_target)];
143 }
144 }
145
146 pub fn apply_cnot_mut(&mut self, idx_control: usize, idx_target: usize) {
149 let idx_phase = self.idx_phase();
150 for idx_row in 0..2 * self.n_qubits {
151 self.table[(idx_row, idx_phase)] ^= self.table[(idx_row, self.idx_x(idx_control))]
152 && self.table[(idx_row, self.idx_z(idx_target))]
153 && (self.table[(idx_row, self.idx_x(idx_target))]
154 ^ self.table[(idx_row, self.idx_z(idx_control))]
155 ^ true);
156 }
157
158 for idx_row in 0..2 * self.n_qubits {
159 let idx_x_target = self.idx_x(idx_target);
160 let idx_x_control = self.idx_x(idx_control);
161 self.table[(idx_row, idx_x_target)] ^= self.table[(idx_row, idx_x_control)];
162 }
163
164 for idx_row in 0..2 * self.n_qubits {
165 let idx_z_target = self.idx_z(idx_target);
166 let idx_z_control = self.idx_z(idx_control);
167 self.table[(idx_row, idx_z_control)] ^= self.table[(idx_row, idx_z_target)];
168 }
169 }
170
171 pub fn apply_x_mut(&mut self, idx_target: usize) {
173 self.apply_h_mut(idx_target);
174 self.apply_z_mut(idx_target);
175 self.apply_h_mut(idx_target);
176 }
177
178 pub fn apply_s_adj_mut(&mut self, idx_target: usize) {
181 self.apply_s_mut(idx_target);
182 self.apply_s_mut(idx_target);
183 self.apply_s_mut(idx_target);
184 }
185
186 pub fn apply_y_mut(&mut self, idx_target: usize) {
188 self.apply_s_adj_mut(idx_target);
189 self.apply_x_mut(idx_target);
190 self.apply_s_mut(idx_target);
191 }
192
193 pub fn apply_z_mut(&mut self, idx_target: usize) {
195 self.apply_s_mut(idx_target);
196 self.apply_s_mut(idx_target);
197 }
198
199 pub fn apply_swap_mut(&mut self, idx_1: usize, idx_2: usize) {
201 self.apply_cnot_mut(idx_1, idx_2);
202 self.apply_cnot_mut(idx_2, idx_1);
203 self.apply_cnot_mut(idx_1, idx_2);
204 }
205
206 pub fn meas_mut(&mut self, idx_target: usize) -> bool {
209 if let Some(result) = self.determinstic_result(idx_target) {
210 return result;
211 }
212
213 let idx_phase = self.idx_phase();
217 let result = rand::random();
218 let collisions: Vec<usize> = self
219 .table
220 .slice(s![.., self.idx_x(idx_target)])
221 .indexed_iter()
222 .filter(|(_i, b)| **b)
223 .map(|(i, _b)| i)
224 .collect();
225 let idx_first: usize = self.n_qubits
228 + self
229 .table
230 .slice(s![self.n_qubits.., self.idx_x(idx_target)])
231 .indexed_iter()
232 .find(|(_i, b)| **b)
233 .unwrap()
234 .0;
235 let old_stab = self.table.slice(s![idx_first, ..]).to_owned();
238
239 for idx_collision in collisions.iter() {
242 if *idx_collision != idx_first {
243 set_row_to_row_sum(&mut self.table, *idx_collision, idx_first);
244 }
245 }
246
247 self.table
250 .slice_mut(s![idx_first - self.n_qubits, ..])
251 .assign(&old_stab);
252 self.table.slice_mut(s![idx_first, ..]).fill(false);
253 let idx_z_target = self.idx_z(idx_target);
254 self.table[(idx_first, idx_z_target)] = true;
255 self.table[(idx_first, idx_phase)] = result;
256 result
257 }
258}
259
260#[cfg(feature = "python")]
262#[pymethods]
263impl Tableau {
264 #[new]
268 pub fn new_py(n_qubits: usize) -> Self {
269 Self::new(n_qubits)
270 }
271}
272
273#[cfg(feature = "python")]
274#[pyproto]
275impl PyObjectProtocol for Tableau {
276 fn __repr__(&self) -> String {
277 format!("<{:?}>", self)
278 }
279
280 fn __str__(&self) -> String {
281 format!("{}", self)
282 }
283}
284
285#[cfg(test)]
286mod tests {
287 use super::*;
288
289 #[test]
290 fn bell_pair_meas_agree() {
291 let mut t = Tableau::new(2);
292 t.apply_h_mut(0);
293 t.apply_cnot_mut(0, 1);
294 let left = t.meas_mut(0);
295 let right = t.meas_mut(1);
296 assert_eq!(left, right)
297 }
298}