1use super::pauli_like::PauliLike;
2use super::pauli_set::PauliSet;
3use super::{CliffordCircuit, IsometryTableau};
4use rand::Rng;
5
6fn compute_phase_product_pauli(pset0: &PauliSet, vec: &[bool]) -> bool {
7 let mut phase = false;
8 for (j, item) in vec.iter().enumerate().take(2 * pset0.n) {
9 phase ^= pset0.get_phase(j) & item;
10 }
11 let mut ifact: u8 = 0;
12 for i in 0..pset0.n {
13 if vec[i] & vec[i + pset0.n] {
14 ifact += 1;
15 }
16 }
17 ifact %= 4;
18 for j in 0..pset0.n {
19 let mut x: bool = false;
20 let mut z: bool = false;
21 for (i, item) in vec.iter().enumerate().take(2 * pset0.n) {
22 if *item {
23 let x1: bool = pset0.get_entry(j, i);
24 let z1: bool = pset0.get_entry(j + pset0.n, i);
25 let entry = (x1, z1, x, z);
26 if LOOKUP_0.contains(&entry) {
27 ifact += 1;
28 }
29 if LOOKUP_1.contains(&entry) {
30 ifact += 3;
31 }
32 x ^= x1;
33 z ^= z1;
34 ifact %= 4;
35 }
36 }
37 }
38 (((ifact % 4) >> 1) != 0) ^ phase
39}
40
41#[derive(Debug, Clone, PartialEq)]
42pub struct Tableau {
43 pub logicals: PauliSet,
44}
45
46impl Tableau {
47 pub fn new(n: usize) -> Self {
49 let mut logicals = PauliSet::new(n);
50 for i in 0..2 * n {
51 let mut vecbool = vec![false; 2 * n];
53 vecbool[i] = true;
54 logicals.insert_vec_bool(&vecbool, false);
55 }
56 Tableau { logicals }
57 }
58 pub fn from_circuit(circuit: &CliffordCircuit) -> Self {
60 let mut tab = Self::new(circuit.nqbits);
61 tab.conjugate_with_circuit(circuit);
62 tab
63 }
64 pub fn random(n: usize) -> Self {
66 let mut rng = rand::thread_rng();
67 let mut iso = Self::new(n);
68 for _ in 0..(n) * (n) {
69 let i = rng.gen::<usize>() % (n);
70 loop {
71 let j = rng.gen::<usize>() % (n);
72 if i == j {
73 continue;
74 }
75 iso.cnot(i, j);
76 break;
77 }
78 for _ in 0..(n) {
79 let gate_i = rng.gen::<u8>() % 3;
80 if gate_i == 1 {
81 let q = rng.gen::<usize>() % (n);
82 iso.h(q);
83 }
84 if gate_i == 2 {
85 let q = rng.gen::<usize>() % (n);
86 iso.s(q);
87 }
88 }
89 }
90 iso
91 }
92 pub fn from_operators(logicals: &Vec<(bool, String)>) -> Self {
94 if logicals.is_empty() {
95 return Self::new(0);
96 }
97 let nqbits = logicals[0].1.len();
98 let mut pset = PauliSet::new(nqbits);
99 for (phase, string) in logicals {
100 pset.insert(string, *phase);
101 }
102 Self { logicals: pset }
103 }
104 pub fn adjoint(&self) -> Self {
106 let mut new_logicals = PauliSet::new(self.logicals.n);
107 for i in 0..self.logicals.n {
108 let (_, string) = self.logicals.get_inverse_x(i);
109 new_logicals.insert(&string, self.logicals.get_phase(i));
110 }
111 for i in 0..self.logicals.n {
112 let (_, string) = self.logicals.get_inverse_z(i);
113 new_logicals.insert(&string, self.logicals.get_phase(i + self.logicals.n));
114 }
115 let prod = self.clone()
116 * Tableau {
117 logicals: new_logicals.clone(),
118 };
119 for i in 0..2 * self.logicals.n {
120 new_logicals.set_phase(i, new_logicals.get_phase(i) ^ prod.logicals.get_phase(i));
121 }
122 Self {
123 logicals: new_logicals,
124 }
125 }
126
127 pub fn get_inverse_z(&self, qbit: usize) -> (bool, String) {
128 let (_, string) = self.logicals.get_inverse_z(qbit);
129 let mut as_vec_bool = vec![false; 2 * self.logicals.n];
130 for qbit in 0..self.logicals.n {
131 match string.chars().nth(qbit).unwrap() {
132 'X' => {
133 as_vec_bool[qbit] = true;
134 }
135 'Y' => {
136 as_vec_bool[qbit] = true;
137 as_vec_bool[qbit + self.logicals.n] = true;
138 }
139 'Z' => {
140 as_vec_bool[qbit + self.logicals.n] = true;
141 }
142 _ => {}
143 }
144 }
145 let phase = compute_phase_product_pauli(&self.logicals, &as_vec_bool);
146 (phase, string)
147 }
148 pub fn get_inverse_x(&self, qbit: usize) -> (bool, String) {
149 let (_, string) = self.logicals.get_inverse_x(qbit);
150 let mut as_vec_bool = vec![false; 2 * self.logicals.n];
151 for qbit in 0..self.logicals.n {
152 match string.chars().nth(qbit).unwrap() {
153 'X' => {
154 as_vec_bool[qbit] = true;
155 }
156 'Y' => {
157 as_vec_bool[qbit] = true;
158 as_vec_bool[qbit + self.logicals.n] = true;
159 }
160 'Z' => {
161 as_vec_bool[qbit + self.logicals.n] = true;
162 }
163 _ => {}
164 }
165 }
166 let phase = compute_phase_product_pauli(&self.logicals, &as_vec_bool);
167 (phase, string)
168 }
169 pub fn to_isometry(self) -> IsometryTableau {
171 IsometryTableau {
172 n: self.logicals.n,
173 k: 0,
174 stabilizers: PauliSet::new(self.logicals.n),
175 logicals: self.logicals,
176 }
177 }
178}
179
180impl PauliLike for Tableau {
181 fn h(&mut self, i: usize) {
182 self.logicals.h(i);
183 }
184
185 fn s(&mut self, i: usize) {
186 self.logicals.s(i);
187 }
188
189 fn sd(&mut self, i: usize) {
190 self.logicals.sd(i);
191 }
192
193 fn sqrt_x(&mut self, i: usize) {
194 self.logicals.sqrt_x(i);
195 }
196
197 fn sqrt_xd(&mut self, i: usize) {
198 self.logicals.sqrt_xd(i);
199 }
200
201 fn cnot(&mut self, i: usize, j: usize) {
202 self.logicals.cnot(i, j);
203 }
204}
205
206const LOOKUP_0: [(bool, bool, bool, bool); 3] = [
207 (false, true, true, true),
208 (true, false, false, true),
209 (true, true, true, false),
210];
211
212const LOOKUP_1: [(bool, bool, bool, bool); 3] = [
213 (false, true, true, false),
214 (true, false, true, true),
215 (true, true, false, true),
216];
217
218impl std::ops::Mul<Tableau> for Tableau {
219 type Output = Tableau;
220 fn mul(self, rhs: Tableau) -> Self::Output {
221 assert_eq!(self.logicals.n, rhs.logicals.n);
222 let mut new_tableau = Tableau::new(self.logicals.n);
223 for i in 0..2 * self.logicals.n {
224 let (mut phase, col) = rhs.logicals.get_as_vec_bool(i);
225 for (j, item) in col.iter().enumerate().take(2 * self.logicals.n) {
226 phase ^= self.logicals.get_phase(j) & item;
227 }
228 new_tableau.logicals.set_phase(i, phase);
229 }
230 let mut ifacts = rhs.logicals.get_i_factors();
231 for (k, item) in ifacts.iter_mut().enumerate().take(2 * self.logicals.n) {
232 for j in 0..self.logicals.n {
233 let mut x: bool = false;
234 let mut z: bool = false;
235 for i in 0..2 * self.logicals.n {
236 if rhs.logicals.get_entry(i, k) {
237 let x1: bool = self.logicals.get_entry(j, i);
238 let z1: bool = self.logicals.get_entry(j + self.logicals.n, i);
239 let entry = (x1, z1, x, z);
240 if LOOKUP_0.contains(&entry) {
241 *item += 1;
242 }
243 if LOOKUP_1.contains(&entry) {
244 *item += 3;
245 }
246 x ^= x1;
247 z ^= z1;
248 *item %= 4;
249 }
250 }
251 }
252 *item %= 4;
253 }
254 let p: Vec<bool> = ifacts.into_iter().map(|v| 0 != ((v % 4) >> 1)).collect();
255 for (i, ph) in p.iter().enumerate() {
256 new_tableau
257 .logicals
258 .set_phase(i, new_tableau.logicals.get_phase(i) ^ ph);
259 }
260
261 for i in 0..2 * self.logicals.n {
262 for j in 0..2 * self.logicals.n {
263 let (_, col) = rhs.logicals.get_as_vec_bool(j);
264 new_tableau
265 .logicals
266 .set_raw_entry(i, j, self.logicals.and_row_acc(i, &col));
267 }
268 }
269 new_tableau
270 }
271}
272
273#[cfg(test)]
274mod tests {
275 use super::Tableau;
276
277 #[test]
278 fn test_mul_adjoint() {
279 let t1 = Tableau::random(5);
280 let t2 = t1.adjoint();
281 let t3 = t1 * t2;
282 let t4 = Tableau::new(5);
283 assert_eq!(t3, t4);
284 }
285}