qecp/
code_builder.rs

1//! # Code Builder
2//!
3//! Given known a `code_type: CodeType` for a simulator, this will build the proper code.
4//! It will ignore `CodeType::Customized` and leave it to user
5//!
6//! TODO: add svg picture to show example of different code types, see <https://docs.rs/embed-doc-image-showcase/latest/embed_doc_image_showcase/>
7//! for how to embed picture in cargo doc
8//!
9
10use super::simulator::*;
11use super::types::*;
12use super::util_macros::*;
13use super::visualize::*;
14use clap::ValueEnum;
15#[cfg(feature = "python_binding")]
16use pyo3::prelude::*;
17use serde::{Deserialize, Serialize};
18use ErrorType::*;
19
20/// commonly used code type that has built-in functions to automatically build up the simulator.
21/// other type of code type is also feasible, but one needs to implement the generation of code patch.
22#[cfg_attr(feature = "python_binding", pyclass)]
23#[derive(Debug, Clone, Serialize, Deserialize, ValueEnum, PartialEq, Eq, PartialOrd, Ord, Copy)]
24#[serde(deny_unknown_fields)]
25pub enum CodeType {
26    ///noisy measurement rounds (excluding the final perfect measurement cap), vertical code distance, horizontal code distance
27    StandardPlanarCode,
28    /// noisy measurement rounds (excluding the final perfect measurement cap), +i+j axis code distance, +i-j axis code distance
29    RotatedPlanarCode,
30    /// noisy measurement rounds (excluding the final perfect measurement cap), vertical code distance, horizontal code distance
31    StandardXZZXCode,
32    /// noisy measurement rounds (excluding the final perfect measurement cap), +i+j axis code distance, +i-j axis code distance
33    RotatedXZZXCode,
34    /// noisy measurement rounds (excluding the final perfect measurement cap), vertical code distance, horizontal code distance
35    StandardTailoredCode,
36    /// noisy measurement rounds (excluding the final perfect measurement cap), +i+j axis code distance, +i-j axis code distance
37    RotatedTailoredCode,
38    /// same as RotatedTailoredCode but with first measurement cycle modified for bell state initialization
39    RotatedTailoredCodeBellInit,
40    /// periodic boundary condition of rotated tailored surface code, code distances must be even number
41    PeriodicRotatedTailoredCode,
42    /// unknown code type, user must provide necessary information and build circuit-level implementation
43    Customized,
44}
45
46/// code size information
47#[cfg_attr(feature = "python_binding", cfg_eval)]
48#[cfg_attr(feature = "python_binding", pyclass)]
49#[derive(Debug, Serialize, Clone)]
50pub struct CodeSize {
51    #[cfg_attr(feature = "python_binding", pyo3(get, set))]
52    pub noisy_measurements: usize,
53    #[cfg_attr(feature = "python_binding", pyo3(get, set))]
54    pub di: usize,
55    #[cfg_attr(feature = "python_binding", pyo3(get, set))]
56    pub dj: usize,
57}
58
59#[cfg_attr(feature = "python_binding", cfg_eval)]
60#[cfg_attr(feature = "python_binding", pymethods)]
61impl CodeSize {
62    #[cfg_attr(feature = "python_binding", new)]
63    pub fn new(noisy_measurements: usize, di: usize, dj: usize) -> Self {
64        CodeSize {
65            noisy_measurements,
66            di,
67            dj,
68        }
69    }
70}
71
72#[cfg_attr(feature = "python_binding", pymethods)]
73impl CodeType {
74    /// get position on the left of (i, j), note that this position may be invalid for open-boundary code if it doesn't exist
75    pub fn get_left(&self, i: usize, j: usize, code_size: &CodeSize) -> (usize, usize) {
76        match self {
77            &CodeType::RotatedTailoredCode | &CodeType::RotatedTailoredCodeBellInit => {
78                if j > 0 {
79                    (i, j - 1)
80                } else {
81                    (i, usize::MAX)
82                }
83            }
84            &CodeType::PeriodicRotatedTailoredCode => {
85                let dp = code_size.di;
86                let dn = code_size.dj;
87                let (di, dj) = (dp - 1, dn - 1);
88                if i + j == dj {
89                    (i + (di + 1), j + di)
90                } else if i == j + dj + 1 {
91                    (i - (dj + 1), j + dj)
92                } else {
93                    (i, j - 1)
94                }
95            }
96            _ => unimplemented!("left position not implemented for this code type, please fill the implementation"),
97        }
98    }
99
100    /// get position up the position (i, j), note that this position may be invalid for open-boundary code if it doesn't exist
101    pub fn get_up(&self, i: usize, j: usize, code_size: &CodeSize) -> (usize, usize) {
102        match self {
103            &CodeType::RotatedTailoredCode | &CodeType::RotatedTailoredCodeBellInit => {
104                if i > 0 {
105                    (i - 1, j)
106                } else {
107                    (usize::MAX, j)
108                }
109            }
110            &CodeType::PeriodicRotatedTailoredCode => {
111                let dp = code_size.di;
112                let dn = code_size.dj;
113                let (di, dj) = (dp - 1, dn - 1);
114                if i == 0 && j == dj {
115                    (di + dj + 1, di)
116                } else if i + j == dj {
117                    (i + di, j + (di + 1))
118                } else if j == i + dj {
119                    (i + dj, j - (dj + 1))
120                } else {
121                    (i - 1, j)
122                }
123            }
124            _ => unimplemented!("left position not implemented for this code type, please fill the implementation"),
125        }
126    }
127
128    /// get position on the right of (i, j), note that this position may be invalid for open-boundary code if it doesn't exist
129    pub fn get_right(&self, i: usize, j: usize, code_size: &CodeSize) -> (usize, usize) {
130        match self {
131            &CodeType::RotatedTailoredCode | &CodeType::RotatedTailoredCodeBellInit => (i, j + 1),
132            &CodeType::PeriodicRotatedTailoredCode => {
133                let dp = code_size.di;
134                let dn = code_size.dj;
135                let (di, dj) = (dp - 1, dn - 1);
136                if i + j == 2 * di + dj + 1 {
137                    (i - (di + 1), j - di)
138                } else if j == i + dj {
139                    (i + (dj + 1), j - dj)
140                } else {
141                    (i, j + 1)
142                }
143            }
144            _ => unimplemented!("left position not implemented for this code type, please fill the implementation"),
145        }
146    }
147
148    /// get position down the position (i, j), note that this position may be invalid for open-boundary code if it doesn't exist
149    pub fn get_down(&self, i: usize, j: usize, code_size: &CodeSize) -> (usize, usize) {
150        match self {
151            &CodeType::RotatedTailoredCode | &CodeType::RotatedTailoredCodeBellInit => (i + 1, j),
152            &CodeType::PeriodicRotatedTailoredCode => {
153                let dp = code_size.di;
154                let dn = code_size.dj;
155                let (di, dj) = (dp - 1, dn - 1);
156                if i == di + dj + 1 && j == di {
157                    (0, dj)
158                } else if i + j == 2 * di + dj + 1 {
159                    (i - di, j - (di + 1))
160                } else if i == j + dj + 1 {
161                    (i - dj, j + (dj + 1))
162                } else {
163                    (i + 1, j)
164                }
165            }
166            _ => unimplemented!("left position not implemented for this code type, please fill the implementation"),
167        }
168    }
169
170    /// convenient call to get diagonal neighbor on the left up
171    pub fn get_left_up(&self, i: usize, j: usize, code_size: &CodeSize) -> (usize, usize) {
172        let (i, j) = self.get_left(i, j, code_size);
173        self.get_up(i, j, code_size)
174    }
175
176    /// convenient call to get diagonal neighbor on the left down
177    pub fn get_left_down(&self, i: usize, j: usize, code_size: &CodeSize) -> (usize, usize) {
178        let (i, j) = self.get_left(i, j, code_size);
179        self.get_down(i, j, code_size)
180    }
181
182    /// convenient call to get diagonal neighbor on the right up
183    pub fn get_right_up(&self, i: usize, j: usize, code_size: &CodeSize) -> (usize, usize) {
184        let (i, j) = self.get_right(i, j, code_size);
185        self.get_up(i, j, code_size)
186    }
187
188    /// convenient call to get diagonal neighbor on the left down
189    pub fn get_right_down(&self, i: usize, j: usize, code_size: &CodeSize) -> (usize, usize) {
190        let (i, j) = self.get_right(i, j, code_size);
191        self.get_down(i, j, code_size)
192    }
193}
194
195pub fn build_code(simulator: &mut Simulator) {
196    let code_type = &simulator.code_type;
197    let code_size = &simulator.code_size;
198    match code_type {
199        &CodeType::StandardPlanarCode | &CodeType::RotatedPlanarCode => {
200            let di = code_size.di;
201            let dj = code_size.dj;
202            let noisy_measurements = code_size.noisy_measurements;
203            simulator.measurement_cycles = 6;
204            assert!(di > 0, "code distance must be positive integer");
205            assert!(dj > 0, "code distance must be positive integer");
206            let is_rotated = matches!(code_type, CodeType::RotatedPlanarCode { .. });
207            if is_rotated {
208                assert!(di % 2 == 1, "code distance must be odd integer, current: di = {}", di);
209                assert!(dj % 2 == 1, "code distance must be odd integer, current: dj = {}", dj);
210            }
211            // println!("noisy_measurements: {}, di: {}, dj: {}, is_rotated: {}", noisy_measurements, di, dj, is_rotated);
212            let (vertical, horizontal) = if is_rotated {
213                (di + dj + 1, di + dj + 1)
214            } else {
215                (2 * di + 1, 2 * dj + 1)
216            };
217            let height = simulator.measurement_cycles * (noisy_measurements + 1) + 1;
218            // each measurement takes 6 time steps
219            let mut nodes = Vec::with_capacity(height);
220            let is_real = |i: usize, j: usize| -> bool {
221                if is_rotated {
222                    let is_real_dj = |pi, pj| pi + pj < dj || (pi + pj == dj && pi % 2 == 0 && pi > 0);
223                    let is_real_di = |pi, pj| pi + pj < di || (pi + pj == di && pj % 2 == 0 && pj > 0);
224                    if i <= dj && j <= dj {
225                        is_real_dj(dj - i, dj - j)
226                    } else if i >= di && j >= di {
227                        is_real_dj(i - di, j - di)
228                    } else if i >= dj && j <= di {
229                        is_real_di(i - dj, di - j)
230                    } else if i <= di && j >= dj {
231                        is_real_di(di - i, j - dj)
232                    } else {
233                        unreachable!()
234                    }
235                } else {
236                    i > 0 && j > 0 && i < vertical - 1 && j < horizontal - 1
237                }
238            };
239            let is_virtual = |i: usize, j: usize| -> bool {
240                if is_rotated {
241                    let is_virtual_dj = |pi, pj| pi + pj == dj && (pi % 2 == 1 || pi == 0);
242                    let is_virtual_di = |pi, pj| pi + pj == di && (pj % 2 == 1 || pj == 0);
243                    if i <= dj && j <= dj {
244                        is_virtual_dj(dj - i, dj - j)
245                    } else if i >= di && j >= di {
246                        is_virtual_dj(i - di, j - di)
247                    } else if i >= dj && j <= di {
248                        is_virtual_di(i - dj, di - j)
249                    } else if i <= di && j >= dj {
250                        is_virtual_di(di - i, j - dj)
251                    } else {
252                        unreachable!()
253                    }
254                } else if i == 0 || i == vertical - 1 {
255                    j % 2 == 1
256                } else if j == 0 || j == horizontal - 1 {
257                    i % 2 == 1
258                } else {
259                    false
260                }
261            };
262            let is_present = |i: usize, j: usize| -> bool {
263                let is_this_real = is_real(i, j);
264                let is_this_virtual = is_virtual(i, j);
265                assert!(
266                    !(is_this_real && is_this_virtual),
267                    "a position cannot be both real and virtual"
268                );
269                is_this_real || is_this_virtual
270            };
271            for t in 0..height {
272                let mut row_i = Vec::with_capacity(vertical);
273                for i in 0..vertical {
274                    let mut row_j = Vec::with_capacity(horizontal);
275                    for j in 0..horizontal {
276                        if is_present(i, j) {
277                            let qubit_type = if (i + j) % 2 == 0 {
278                                assert!(is_real(i, j), "data qubits should not be virtual");
279                                QubitType::Data
280                            } else if i % 2 == 1 {
281                                QubitType::StabZ
282                            } else {
283                                QubitType::StabX
284                            };
285                            let mut gate_type = GateType::None;
286                            let mut gate_peer = None;
287                            match t % simulator.measurement_cycles {
288                                1 => {
289                                    // initialization
290                                    match qubit_type {
291                                        QubitType::StabZ => {
292                                            gate_type = GateType::InitializeZ;
293                                        }
294                                        QubitType::StabX => {
295                                            gate_type = GateType::InitializeX;
296                                        }
297                                        QubitType::Data => {}
298                                        _ => {
299                                            unreachable!()
300                                        }
301                                    }
302                                }
303                                2 => {
304                                    // gate 1
305                                    if qubit_type == QubitType::Data {
306                                        if i + 1 < vertical && is_present(i + 1, j) {
307                                            gate_type = if j % 2 == 1 {
308                                                GateType::CXGateTarget
309                                            } else {
310                                                GateType::CXGateControl
311                                            };
312                                            gate_peer = Some(pos!(t, i + 1, j));
313                                        }
314                                    } else if i >= 1 && is_present(i - 1, j) {
315                                        gate_type = if j % 2 == 1 {
316                                            GateType::CXGateControl
317                                        } else {
318                                            GateType::CXGateTarget
319                                        };
320                                        gate_peer = Some(pos!(t, i - 1, j));
321                                    }
322                                }
323                                3 => {
324                                    // gate 2
325                                    if j % 2 == 1 {
326                                        // operate with right
327                                        if is_present(i, j + 1) {
328                                            gate_type = GateType::CXGateControl;
329                                            gate_peer = Some(pos!(t, i, j + 1));
330                                        }
331                                    } else {
332                                        // operate with left
333                                        if j >= 1 && is_present(i, j - 1) {
334                                            gate_type = GateType::CXGateTarget;
335                                            gate_peer = Some(pos!(t, i, j - 1));
336                                        }
337                                    }
338                                }
339                                4 => {
340                                    // gate 3
341                                    if j % 2 == 1 {
342                                        // operate with left
343                                        if j >= 1 && is_present(i, j - 1) {
344                                            gate_type = GateType::CXGateControl;
345                                            gate_peer = Some(pos!(t, i, j - 1));
346                                        }
347                                    } else {
348                                        // operate with right
349                                        if is_present(i, j + 1) {
350                                            gate_type = GateType::CXGateTarget;
351                                            gate_peer = Some(pos!(t, i, j + 1));
352                                        }
353                                    }
354                                }
355                                5 => {
356                                    // gate 4
357                                    if qubit_type == QubitType::Data {
358                                        if i >= 1 && is_present(i - 1, j) {
359                                            gate_type = if j % 2 == 1 {
360                                                GateType::CXGateTarget
361                                            } else {
362                                                GateType::CXGateControl
363                                            };
364                                            gate_peer = Some(pos!(t, i - 1, j));
365                                        }
366                                    } else if i + 1 < vertical && is_present(i + 1, j) {
367                                        gate_type = if j % 2 == 1 {
368                                            GateType::CXGateControl
369                                        } else {
370                                            GateType::CXGateTarget
371                                        };
372                                        gate_peer = Some(pos!(t, i + 1, j));
373                                    }
374                                }
375                                0 => {
376                                    // measurement
377                                    match qubit_type {
378                                        QubitType::StabZ => {
379                                            gate_type = GateType::MeasureZ;
380                                        }
381                                        QubitType::StabX => {
382                                            gate_type = GateType::MeasureX;
383                                        }
384                                        QubitType::Data => {}
385                                        _ => {
386                                            unreachable!()
387                                        }
388                                    }
389                                }
390                                _ => unreachable!(),
391                            }
392                            row_j.push(Some(Box::new(
393                                SimulatorNode::new(qubit_type, gate_type, gate_peer.clone()).set_virtual(
394                                    is_virtual(i, j),
395                                    gate_peer.map_or(false, |peer| is_virtual(peer.i, peer.j)),
396                                ),
397                            )));
398                        } else {
399                            row_j.push(None);
400                        }
401                    }
402                    row_i.push(row_j);
403                }
404                nodes.push(row_i)
405            }
406            simulator.vertical = vertical;
407            simulator.horizontal = horizontal;
408            simulator.height = height;
409            simulator.nodes = nodes;
410        }
411        &CodeType::StandardTailoredCode | &CodeType::RotatedTailoredCode | &CodeType::RotatedTailoredCodeBellInit => {
412            let di = code_size.di;
413            let dj = code_size.dj;
414            let noisy_measurements = code_size.noisy_measurements;
415            simulator.measurement_cycles = 6;
416            assert!(di > 0, "code distance must be positive integer");
417            assert!(dj > 0, "code distance must be positive integer");
418            let is_rotated = matches!(code_type, CodeType::RotatedTailoredCode { .. })
419                || matches!(code_type, CodeType::RotatedTailoredCodeBellInit { .. });
420            let is_bell_init = matches!(code_type, CodeType::RotatedTailoredCodeBellInit { .. });
421            if is_rotated {
422                assert!(di % 2 == 1, "code distance must be odd integer, current: di = {}", di);
423                assert!(dj % 2 == 1, "code distance must be odd integer, current: dj = {}", dj);
424            }
425            // println!("noisy_measurements: {}, di: {}, dj: {}, is_rotated: {}", noisy_measurements, di, dj, is_rotated);
426            let (vertical, horizontal) = if is_rotated {
427                (di + dj + 1, di + dj + 1)
428            } else {
429                (2 * di + 1, 2 * dj + 1)
430            };
431            let height = simulator.measurement_cycles * (noisy_measurements + 1) + 1;
432            // each measurement takes 6 time steps
433            let mut nodes = Vec::with_capacity(height);
434            let is_real = |i: usize, j: usize| -> bool {
435                if is_rotated {
436                    let is_real_dj = |pi, pj| pi + pj < dj || (pi + pj == dj && pi % 2 == 0 && pi > 0);
437                    let is_real_di = |pi, pj| pi + pj < di || (pi + pj == di && pj % 2 == 0 && pj > 0);
438                    if i <= dj && j <= dj {
439                        is_real_dj(dj - i, dj - j)
440                    } else if i >= di && j >= di {
441                        is_real_dj(i - di, j - di)
442                    } else if i >= dj && j <= di {
443                        is_real_di(i - dj, di - j)
444                    } else if i <= di && j >= dj {
445                        is_real_di(di - i, j - dj)
446                    } else {
447                        unreachable!()
448                    }
449                } else {
450                    i > 0 && j > 0 && i < vertical - 1 && j < horizontal - 1
451                }
452            };
453            let is_virtual = |i: usize, j: usize| -> bool {
454                if is_rotated {
455                    let is_virtual_dj = |pi, pj| pi + pj == dj && (pi % 2 == 1 || pi == 0);
456                    let is_virtual_di = |pi, pj| pi + pj == di && (pj % 2 == 1 || pj == 0);
457                    if i <= dj && j <= dj {
458                        is_virtual_dj(dj - i, dj - j)
459                    } else if i >= di && j >= di {
460                        is_virtual_dj(i - di, j - di)
461                    } else if i >= dj && j <= di {
462                        is_virtual_di(i - dj, di - j)
463                    } else if i <= di && j >= dj {
464                        is_virtual_di(di - i, j - dj)
465                    } else {
466                        unreachable!()
467                    }
468                } else if i == 0 || i == vertical - 1 {
469                    j % 2 == 1
470                } else if j == 0 || j == horizontal - 1 {
471                    i % 2 == 1
472                } else {
473                    false
474                }
475            };
476            let is_present = |i: usize, j: usize| -> bool {
477                let is_this_real = is_real(i, j);
478                let is_this_virtual = is_virtual(i, j);
479                assert!(
480                    !(is_this_real && is_this_virtual),
481                    "a position cannot be both real and virtual"
482                );
483                is_this_real || is_this_virtual
484            };
485            // some criteria for bell init
486            let is_bell_init_anc = |i: usize, j: usize| -> bool {
487                is_real(i, j) && i < j + dj - 3 && ((i % 4 == 1 && j % 4 == 0) || (i % 4 == 3 && j % 4 == 2))
488            };
489            let is_bell_init_top = |i: usize, j: usize| -> bool {
490                is_real(i, j) && i < j + dj - 1 && ((i % 4 == 0 && j % 4 == 0) || (i % 4 == 2 && j % 4 == 2))
491            };
492            let is_bell_init_left = |i: usize, j: usize| -> bool {
493                is_real(i, j) && i < j + dj - 1 && ((i % 4 == 1 && j % 4 == 3) || (i % 4 == 3 && j % 4 == 1))
494            };
495            let is_bell_init_right = |i: usize, j: usize| -> bool {
496                is_real(i, j) && i < j + dj - 1 && ((i % 4 == 1 && j % 4 == 1) || (i % 4 == 3 && j % 4 == 3))
497            };
498            let is_bell_init_bot = |i: usize, j: usize| -> bool {
499                is_real(i, j) && i < j + dj - 1 && ((i % 4 == 2 && j % 4 == 0) || (i % 4 == 0 && j % 4 == 2))
500            };
501
502            for t in 0..height {
503                let mut row_i = Vec::with_capacity(vertical);
504                for i in 0..vertical {
505                    let mut row_j = Vec::with_capacity(horizontal);
506                    for j in 0..horizontal {
507                        if is_present(i, j) {
508                            let qubit_type = if (i + j) % 2 == 0 {
509                                assert!(is_real(i, j), "data qubits should not be virtual");
510                                QubitType::Data
511                            } else if i % 2 == 1 {
512                                QubitType::StabY
513                            } else {
514                                QubitType::StabX
515                            };
516                            let mut gate_type = GateType::None;
517                            let mut gate_peer = None;
518                            // see residual decoding of https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.124.130501
519                            let (is_corner, peer_corner): (bool, Option<Position>) = if is_rotated {
520                                if i == 0 && j == dj {
521                                    (true, Some(pos!(t, 1, dj + 1)))
522                                } else if j == 0 && i == dj {
523                                    (true, Some(pos!(t, dj - 1, 1)))
524                                } else if i == vertical - 1 && j == di {
525                                    (true, Some(pos!(t, vertical - 2, di - 1)))
526                                } else if i == di && j == vertical - 1 {
527                                    (true, Some(pos!(t, di + 1, vertical - 2)))
528                                } else {
529                                    (false, None)
530                                }
531                            } else if i == 0 && j == 1 {
532                                (true, Some(pos!(t, 1, 0)))
533                            } else if i == 1 && j == horizontal - 1 {
534                                (true, Some(pos!(t, 0, horizontal - 2)))
535                            } else if i == vertical - 2 && j == 0 {
536                                (true, Some(pos!(t, vertical - 1, 1)))
537                            } else if i == vertical - 1 && j == horizontal - 2 {
538                                (true, Some(pos!(t, vertical - 2, horizontal - 1)))
539                            } else {
540                                (false, None)
541                            };
542                            if is_bell_init && t > 0 && t <= simulator.measurement_cycles {
543                                // code_type is BellInit and t < measurement_cycle for init circuit
544                                match t % simulator.measurement_cycles {
545                                    1 => {
546                                        // anc to top
547                                        if is_bell_init_anc(i, j) && is_bell_init_top(i - 1, j) {
548                                            gate_type = GateType::CXGateControl;
549                                            gate_peer = Some(pos!(t, i - 1, j));
550                                        } else if is_bell_init_top(i, j) && is_bell_init_anc(i + 1, j) {
551                                            gate_type = GateType::CXGateTarget;
552                                            gate_peer = Some(pos!(t, i + 1, j));
553                                        } else {
554                                            match qubit_type {
555                                                QubitType::StabY => {
556                                                    gate_type = GateType::InitializeX;
557                                                }
558                                                QubitType::StabX => {
559                                                    gate_type = GateType::InitializeX;
560                                                }
561                                                QubitType::Data => {}
562                                                _ => {
563                                                    unreachable!()
564                                                }
565                                            }
566                                        }
567                                    }
568                                    2 => {
569                                        // anc to left
570                                        if is_bell_init_anc(i, j) && is_bell_init_left(i, j - 1) {
571                                            gate_type = GateType::CXGateControl;
572                                            gate_peer = Some(pos!(t, i, j - 1));
573                                        }
574                                        if is_bell_init_left(i, j) && is_bell_init_anc(i, j + 1) {
575                                            gate_type = GateType::CXGateTarget;
576                                            gate_peer = Some(pos!(t, i, j + 1));
577                                        }
578                                    }
579                                    3 => {
580                                        // anc to right
581                                        if is_bell_init_anc(i, j) && is_bell_init_right(i, j + 1) {
582                                            gate_type = GateType::CXGateControl;
583                                            gate_peer = Some(pos!(t, i, j + 1));
584                                        }
585                                        if is_bell_init_right(i, j) && is_bell_init_anc(i, j - 1) {
586                                            gate_type = GateType::CXGateTarget;
587                                            gate_peer = Some(pos!(t, i, j - 1));
588                                        }
589                                    }
590                                    4 | 0 => {
591                                        // anc to bot
592                                        if is_bell_init_anc(i, j) && is_bell_init_bot(i + 1, j) {
593                                            gate_type = GateType::CXGateControl;
594                                            gate_peer = Some(pos!(t, i + 1, j));
595                                        }
596                                        if is_bell_init_bot(i, j) && is_bell_init_anc(i - 1, j) {
597                                            gate_type = GateType::CXGateTarget;
598                                            gate_peer = Some(pos!(t, i - 1, j));
599                                        }
600                                    }
601                                    5 => {
602                                        // bot to anc
603                                        if is_bell_init_anc(i, j) && is_bell_init_bot(i + 1, j) {
604                                            gate_type = GateType::CXGateTarget;
605                                            gate_peer = Some(pos!(t, i + 1, j));
606                                        }
607                                        if is_bell_init_bot(i, j) && is_bell_init_anc(i - 1, j) {
608                                            gate_type = GateType::CXGateControl;
609                                            gate_peer = Some(pos!(t, i - 1, j));
610                                        }
611                                    }
612                                    _ => {
613                                        unreachable!()
614                                    }
615                                }
616                            } else {
617                                // normal cycles
618                                match t % simulator.measurement_cycles {
619                                    1 => {
620                                        // initialization
621                                        match qubit_type {
622                                            QubitType::StabY => {
623                                                gate_type = GateType::InitializeX;
624                                            }
625                                            QubitType::StabX => {
626                                                gate_type = GateType::InitializeX;
627                                            }
628                                            QubitType::Data => {}
629                                            _ => {
630                                                unreachable!()
631                                            }
632                                        }
633                                    }
634                                    2 => {
635                                        // gate 1
636                                        if qubit_type == QubitType::Data {
637                                            if i + 1 < vertical && is_present(i + 1, j) {
638                                                gate_type = if j % 2 == 1 {
639                                                    GateType::CXGateTarget
640                                                } else {
641                                                    GateType::CYGateTarget
642                                                };
643                                                gate_peer = Some(pos!(t, i + 1, j));
644                                            }
645                                        } else if i >= 1 && is_present(i - 1, j) {
646                                            gate_type = if j % 2 == 1 {
647                                                GateType::CXGateControl
648                                            } else {
649                                                GateType::CYGateControl
650                                            };
651                                            gate_peer = Some(pos!(t, i - 1, j));
652                                        }
653                                    }
654                                    3 => {
655                                        // gate 2
656                                        if j % 2 == 1 {
657                                            // operate with right
658                                            if is_present(i, j + 1) {
659                                                gate_type = if qubit_type == QubitType::Data {
660                                                    GateType::CYGateTarget
661                                                } else {
662                                                    GateType::CXGateControl
663                                                };
664                                                gate_peer = Some(pos!(t, i, j + 1));
665                                            }
666                                        } else {
667                                            // operate with left
668                                            if j >= 1 && is_present(i, j - 1) {
669                                                gate_type = if qubit_type == QubitType::Data {
670                                                    GateType::CXGateTarget
671                                                } else {
672                                                    GateType::CYGateControl
673                                                };
674                                                gate_peer = Some(pos!(t, i, j - 1));
675                                            }
676                                        }
677                                    }
678                                    4 => {
679                                        // gate 3
680                                        if j % 2 == 1 {
681                                            // operate with left
682                                            if j >= 1 && is_present(i, j - 1) {
683                                                gate_type = if qubit_type == QubitType::Data {
684                                                    GateType::CYGateTarget
685                                                } else {
686                                                    GateType::CXGateControl
687                                                };
688                                                gate_peer = Some(pos!(t, i, j - 1));
689                                            }
690                                        } else {
691                                            // operate with right
692                                            if is_present(i, j + 1) {
693                                                gate_type = if qubit_type == QubitType::Data {
694                                                    GateType::CXGateTarget
695                                                } else {
696                                                    GateType::CYGateControl
697                                                };
698                                                gate_peer = Some(pos!(t, i, j + 1));
699                                            }
700                                        }
701                                    }
702                                    5 => {
703                                        // gate 4
704                                        if qubit_type == QubitType::Data {
705                                            if i >= 1 && is_present(i - 1, j) {
706                                                gate_type = if j % 2 == 1 {
707                                                    GateType::CXGateTarget
708                                                } else {
709                                                    GateType::CYGateTarget
710                                                };
711                                                gate_peer = Some(pos!(t, i - 1, j));
712                                            }
713                                        } else if i + 1 < vertical && is_present(i + 1, j) {
714                                            gate_type = if j % 2 == 1 {
715                                                GateType::CXGateControl
716                                            } else {
717                                                GateType::CYGateControl
718                                            };
719                                            gate_peer = Some(pos!(t, i + 1, j));
720                                        }
721                                    }
722                                    0 => {
723                                        // measurement
724                                        match qubit_type {
725                                            QubitType::StabY => {
726                                                gate_type = GateType::MeasureX;
727                                            }
728                                            QubitType::StabX => {
729                                                gate_type = GateType::MeasureX;
730                                            }
731                                            QubitType::Data => {}
732                                            _ => {
733                                                unreachable!()
734                                            }
735                                        }
736                                    }
737                                    _ => unreachable!(),
738                                }
739                            }
740                            row_j.push(Some(Box::new(
741                                SimulatorNode::new(qubit_type, gate_type, gate_peer.clone())
742                                    .set_virtual(
743                                        is_virtual(i, j),
744                                        gate_peer.map_or(false, |peer| is_virtual(peer.i, peer.j)),
745                                    )
746                                    .with_miscellaneous(if is_corner {
747                                        Some(json!({ "is_corner": true, "peer_corner": peer_corner.unwrap() }))
748                                    } else {
749                                        None
750                                    }),
751                            )));
752                        } else {
753                            row_j.push(None);
754                        }
755                    }
756                    row_i.push(row_j);
757                }
758                nodes.push(row_i)
759            }
760            simulator.vertical = vertical;
761            simulator.horizontal = horizontal;
762            simulator.height = height;
763            simulator.nodes = nodes;
764        }
765        &CodeType::PeriodicRotatedTailoredCode => {
766            let dp = code_size.di;
767            let dn = code_size.dj;
768            let noisy_measurements = code_size.noisy_measurements;
769            simulator.measurement_cycles = 6;
770            assert!(dp > 0, "code distance must be positive integer");
771            assert!(dn > 0, "code distance must be positive integer");
772            assert!(dp % 2 == 0, "code distance must be even integer, current: dp = {}", dp);
773            assert!(dn % 2 == 0, "code distance must be even integer, current: dn = {}", dn);
774            // println!("noisy_measurements: {}, dp: {}, dn: {}, is_rotated: {}", noisy_measurements, dp, dn);
775            let di = dp - 1; // to use previously developed functions
776            let dj = dn - 1;
777            let (vertical, horizontal) = (di + dj + 2, di + dj + 1);
778            let height = simulator.measurement_cycles * (noisy_measurements + 1) + 1;
779            // each measurement takes 6 time steps
780            let mut nodes = Vec::with_capacity(height);
781            let is_present = |i: usize, j: usize| -> bool {
782                let is_present_dj = |pi, pj| pi + pj <= dj;
783                let is_present_di = |pi, pj| pi + pj <= di;
784                let presented = if i <= dj && j <= dj {
785                    is_present_dj(dj - i, dj - j)
786                } else if i >= di && j >= di {
787                    is_present_dj(i - di, j - di)
788                } else if i >= dj && j <= di {
789                    is_present_di(i - dj, di - j)
790                } else if i <= di && j >= dj {
791                    is_present_di(di - i, j - dj)
792                } else {
793                    unreachable!()
794                };
795                presented || i == j + dj + 1 || i + j == 2 * di + dj + 1
796            };
797            for t in 0..height {
798                let mut row_i = Vec::with_capacity(vertical);
799                for i in 0..vertical {
800                    let mut row_j = Vec::with_capacity(horizontal);
801                    for j in 0..horizontal {
802                        if is_present(i, j) {
803                            let qubit_type = if (i + j) % 2 == 0 {
804                                QubitType::Data
805                            } else if i % 2 == 1 {
806                                QubitType::StabY
807                            } else {
808                                QubitType::StabX
809                            };
810                            let mut gate_type = GateType::None;
811                            let mut gate_peer = None;
812                            match t % simulator.measurement_cycles {
813                                1 => {
814                                    // initialization
815                                    match qubit_type {
816                                        QubitType::StabY => {
817                                            gate_type = GateType::InitializeX;
818                                        }
819                                        QubitType::StabX => {
820                                            gate_type = GateType::InitializeX;
821                                        }
822                                        QubitType::Data => {}
823                                        _ => {
824                                            unreachable!()
825                                        }
826                                    }
827                                }
828                                2 => {
829                                    // gate 1
830                                    if qubit_type == QubitType::Data {
831                                        let (pi, pj) = code_type.get_down(i, j, code_size);
832                                        gate_type = if j % 2 == 1 {
833                                            GateType::CXGateTarget
834                                        } else {
835                                            GateType::CYGateTarget
836                                        };
837                                        gate_peer = Some(pos!(t, pi, pj));
838                                    } else {
839                                        let (pi, pj) = code_type.get_up(i, j, code_size);
840                                        gate_type = if j % 2 == 1 {
841                                            GateType::CXGateControl
842                                        } else {
843                                            GateType::CYGateControl
844                                        };
845                                        gate_peer = Some(pos!(t, pi, pj));
846                                    }
847                                }
848                                3 => {
849                                    // gate 2
850                                    if j % 2 == 1 {
851                                        // operate with right
852                                        let (pi, pj) = code_type.get_right(i, j, code_size);
853                                        gate_type = if qubit_type == QubitType::Data {
854                                            GateType::CYGateTarget
855                                        } else {
856                                            GateType::CXGateControl
857                                        };
858                                        gate_peer = Some(pos!(t, pi, pj));
859                                    } else {
860                                        // operate with left
861                                        let (pi, pj) = code_type.get_left(i, j, code_size);
862                                        gate_type = if qubit_type == QubitType::Data {
863                                            GateType::CXGateTarget
864                                        } else {
865                                            GateType::CYGateControl
866                                        };
867                                        gate_peer = Some(pos!(t, pi, pj));
868                                    }
869                                }
870                                4 => {
871                                    // gate 3
872                                    if j % 2 == 1 {
873                                        // operate with left
874                                        let (pi, pj) = code_type.get_left(i, j, code_size);
875                                        gate_type = if qubit_type == QubitType::Data {
876                                            GateType::CYGateTarget
877                                        } else {
878                                            GateType::CXGateControl
879                                        };
880                                        gate_peer = Some(pos!(t, pi, pj));
881                                    } else {
882                                        // operate with right
883                                        let (pi, pj) = code_type.get_right(i, j, code_size);
884                                        gate_type = if qubit_type == QubitType::Data {
885                                            GateType::CXGateTarget
886                                        } else {
887                                            GateType::CYGateControl
888                                        };
889                                        gate_peer = Some(pos!(t, pi, pj));
890                                    }
891                                }
892                                5 => {
893                                    // gate 4
894                                    if qubit_type == QubitType::Data {
895                                        let (pi, pj) = code_type.get_up(i, j, code_size);
896                                        gate_type = if j % 2 == 1 {
897                                            GateType::CXGateTarget
898                                        } else {
899                                            GateType::CYGateTarget
900                                        };
901                                        gate_peer = Some(pos!(t, pi, pj));
902                                    } else {
903                                        let (pi, pj) = code_type.get_down(i, j, code_size);
904                                        gate_type = if j % 2 == 1 {
905                                            GateType::CXGateControl
906                                        } else {
907                                            GateType::CYGateControl
908                                        };
909                                        gate_peer = Some(pos!(t, pi, pj));
910                                    }
911                                }
912                                0 => {
913                                    // measurement
914                                    match qubit_type {
915                                        QubitType::StabY => {
916                                            gate_type = GateType::MeasureX;
917                                        }
918                                        QubitType::StabX => {
919                                            gate_type = GateType::MeasureX;
920                                        }
921                                        QubitType::Data => {}
922                                        _ => {
923                                            unreachable!()
924                                        }
925                                    }
926                                }
927                                _ => unreachable!(),
928                            }
929                            row_j.push(Some(Box::new(SimulatorNode::new(qubit_type, gate_type, gate_peer.clone()))));
930                        } else {
931                            row_j.push(None);
932                        }
933                    }
934                    row_i.push(row_j);
935                }
936                nodes.push(row_i)
937            }
938            simulator.vertical = vertical;
939            simulator.horizontal = horizontal;
940            simulator.height = height;
941            simulator.nodes = nodes;
942        }
943        CodeType::Customized => {
944            // skip user customized code
945        }
946        &CodeType::StandardXZZXCode | &CodeType::RotatedXZZXCode => {
947            let di = code_size.di;
948            let dj = code_size.dj;
949            let noisy_measurements = code_size.noisy_measurements;
950            simulator.measurement_cycles = 6;
951            assert!(di > 0, "code distance must be positive integer");
952            assert!(dj > 0, "code distance must be positive integer");
953            let is_rotated = matches!(code_type, CodeType::RotatedXZZXCode { .. });
954            if is_rotated {
955                assert!(di % 2 == 1, "code distance must be odd integer, current: di = {}", di);
956                assert!(dj % 2 == 1, "code distance must be odd integer, current: dj = {}", dj);
957            }
958            // println!("noisy_measurements: {}, di: {}, dj: {}, is_rotated: {}", noisy_measurements, di, dj, is_rotated);
959            let (vertical, horizontal) = if is_rotated {
960                (di + dj + 1, di + dj + 1)
961            } else {
962                (2 * di + 1, 2 * dj + 1)
963            };
964            let height = simulator.measurement_cycles * (noisy_measurements + 1) + 1;
965            // each measurement takes 6 time steps
966            let mut nodes = Vec::with_capacity(height);
967            let is_real = |i: usize, j: usize| -> bool {
968                if is_rotated {
969                    let is_real_dj = |pi, pj| pi + pj < dj || (pi + pj == dj && pi % 2 == 0 && pi > 0);
970                    let is_real_di = |pi, pj| pi + pj < di || (pi + pj == di && pj % 2 == 0 && pj > 0);
971                    if i <= dj && j <= dj {
972                        is_real_dj(dj - i, dj - j)
973                    } else if i >= di && j >= di {
974                        is_real_dj(i - di, j - di)
975                    } else if i >= dj && j <= di {
976                        is_real_di(i - dj, di - j)
977                    } else if i <= di && j >= dj {
978                        is_real_di(di - i, j - dj)
979                    } else {
980                        unreachable!()
981                    }
982                } else {
983                    i > 0 && j > 0 && i < vertical - 1 && j < horizontal - 1
984                }
985            };
986            let is_virtual = |i: usize, j: usize| -> bool {
987                if is_rotated {
988                    let is_virtual_dj = |pi, pj| pi + pj == dj && (pi % 2 == 1 || pi == 0);
989                    let is_virtual_di = |pi, pj| pi + pj == di && (pj % 2 == 1 || pj == 0);
990                    if i <= dj && j <= dj {
991                        is_virtual_dj(dj - i, dj - j)
992                    } else if i >= di && j >= di {
993                        is_virtual_dj(i - di, j - di)
994                    } else if i >= dj && j <= di {
995                        is_virtual_di(i - dj, di - j)
996                    } else if i <= di && j >= dj {
997                        is_virtual_di(di - i, j - dj)
998                    } else {
999                        unreachable!()
1000                    }
1001                } else if i == 0 || i == vertical - 1 {
1002                    j % 2 == 1
1003                } else if j == 0 || j == horizontal - 1 {
1004                    i % 2 == 1
1005                } else {
1006                    false
1007                }
1008            };
1009            let is_present = |i: usize, j: usize| -> bool {
1010                let is_this_real = is_real(i, j);
1011                let is_this_virtual = is_virtual(i, j);
1012                assert!(
1013                    !(is_this_real && is_this_virtual),
1014                    "a position cannot be both real and virtual"
1015                );
1016                is_this_real || is_this_virtual
1017            };
1018            for t in 0..height {
1019                let mut row_i = Vec::with_capacity(vertical);
1020                for i in 0..vertical {
1021                    let mut row_j = Vec::with_capacity(horizontal);
1022                    for j in 0..horizontal {
1023                        if is_present(i, j) {
1024                            let qubit_type = if (i + j) % 2 == 0 {
1025                                assert!(is_real(i, j), "data qubits should not be virtual");
1026                                QubitType::Data
1027                            } else if i % 2 == 1 {
1028                                QubitType::StabXZZXLogicalZ
1029                            } else {
1030                                QubitType::StabXZZXLogicalX
1031                            };
1032                            let mut gate_type = GateType::None;
1033                            let mut gate_peer = None;
1034                            match t % simulator.measurement_cycles {
1035                                1 => {
1036                                    // initialization
1037                                    match qubit_type {
1038                                        QubitType::StabXZZXLogicalZ => {
1039                                            gate_type = GateType::InitializeX;
1040                                        }
1041                                        QubitType::StabXZZXLogicalX => {
1042                                            gate_type = GateType::InitializeX;
1043                                        }
1044                                        QubitType::Data => {}
1045                                        _ => {
1046                                            unreachable!()
1047                                        }
1048                                    }
1049                                }
1050                                2 => {
1051                                    // gate 1
1052                                    if qubit_type == QubitType::Data {
1053                                        if i + 1 < vertical && is_present(i + 1, j) {
1054                                            gate_type = GateType::CZGate;
1055                                            gate_peer = Some(pos!(t, i + 1, j));
1056                                        }
1057                                    } else if i >= 1 && is_present(i - 1, j) {
1058                                        gate_type = GateType::CZGate;
1059                                        gate_peer = Some(pos!(t, i - 1, j));
1060                                    }
1061                                }
1062                                3 => {
1063                                    // gate 2
1064                                    if qubit_type == QubitType::Data {
1065                                        if j + 1 < horizontal && is_present(i, j + 1) {
1066                                            gate_type = GateType::CXGateTarget;
1067                                            gate_peer = Some(pos!(t, i, j + 1));
1068                                        }
1069                                    } else if j >= 1 && is_present(i, j - 1) {
1070                                        gate_type = GateType::CXGateControl;
1071                                        gate_peer = Some(pos!(t, i, j - 1));
1072                                    }
1073                                }
1074                                4 => {
1075                                    // gate 3
1076                                    if qubit_type == QubitType::Data {
1077                                        if j >= 1 && is_present(i, j - 1) {
1078                                            gate_type = GateType::CXGateTarget;
1079                                            gate_peer = Some(pos!(t, i, j - 1));
1080                                        }
1081                                    } else if j + 1 < horizontal && is_present(i, j + 1) {
1082                                        gate_type = GateType::CXGateControl;
1083                                        gate_peer = Some(pos!(t, i, j + 1));
1084                                    }
1085                                }
1086                                5 => {
1087                                    // gate 4
1088                                    if qubit_type == QubitType::Data {
1089                                        if i >= 1 && is_present(i - 1, j) {
1090                                            gate_type = GateType::CZGate;
1091                                            gate_peer = Some(pos!(t, i - 1, j));
1092                                        }
1093                                    } else if i + 1 < vertical && is_present(i + 1, j) {
1094                                        gate_type = GateType::CZGate;
1095                                        gate_peer = Some(pos!(t, i + 1, j));
1096                                    }
1097                                }
1098                                0 => {
1099                                    // measurement
1100                                    match qubit_type {
1101                                        QubitType::StabXZZXLogicalZ => {
1102                                            gate_type = GateType::MeasureX;
1103                                        }
1104                                        QubitType::StabXZZXLogicalX => {
1105                                            gate_type = GateType::MeasureX;
1106                                        }
1107                                        QubitType::Data => {}
1108                                        _ => {
1109                                            unreachable!()
1110                                        }
1111                                    }
1112                                }
1113                                _ => unreachable!(),
1114                            }
1115                            row_j.push(Some(Box::new(
1116                                SimulatorNode::new(qubit_type, gate_type, gate_peer.clone()).set_virtual(
1117                                    is_virtual(i, j),
1118                                    gate_peer.map_or(false, |peer| is_virtual(peer.i, peer.j)),
1119                                ),
1120                            )));
1121                        } else {
1122                            row_j.push(None);
1123                        }
1124                    }
1125                    row_i.push(row_j);
1126                }
1127                nodes.push(row_i)
1128            }
1129            simulator.vertical = vertical;
1130            simulator.horizontal = horizontal;
1131            simulator.height = height;
1132            simulator.nodes = nodes;
1133        }
1134    }
1135}
1136
1137/// 2D position of the qubits; time axis is always pointing up
1138pub fn visualize_positions(simulator: &Simulator) -> Vec<Vec<VisualizePosition>> {
1139    (0..simulator.vertical)
1140        .map(|i| {
1141            let x = i as f64 - (simulator.vertical as f64 - 1.) / 2.;
1142            (0..simulator.horizontal)
1143                .map(|j| {
1144                    let y = j as f64 - (simulator.horizontal as f64 - 1.) / 2.;
1145                    VisualizePosition::new(x, y)
1146                })
1147                .collect::<Vec<VisualizePosition>>()
1148        })
1149        .collect::<Vec<Vec<VisualizePosition>>>()
1150}
1151
1152/// detect common bugs of code building, e.g. peer gate invalid type, is_virtual not correct, etc...
1153pub fn code_builder_sanity_check(simulator: &Simulator) -> Result<(), String> {
1154    simulator_iter!(simulator, position, node, {
1155        // println!("{}", node);
1156        if node.qubit_type == QubitType::Data {
1157            if node.gate_type.is_initialization() {
1158                return Err(format!(
1159                    "data qubit at {} cannot be initialized: gate_type = {:?}",
1160                    position, node.gate_type
1161                ));
1162            }
1163            if node.gate_type.is_measurement() {
1164                return Err(format!(
1165                    "data qubit at {} cannot be initialized: gate_type = {:?}",
1166                    position, node.gate_type
1167                ));
1168            }
1169        }
1170        match node.gate_peer.as_ref() {
1171            Some(peer_position) => {
1172                if node.gate_type.is_single_qubit_gate() {
1173                    return Err(format!(
1174                        "{} has single qubit gate {:?} should not have peer",
1175                        position, node.gate_type
1176                    ));
1177                }
1178                if !simulator.is_node_exist(peer_position) {
1179                    return Err(format!("{}'s peer not exist: {}", position, peer_position));
1180                }
1181                let peer_node = simulator.get_node_unwrap(peer_position);
1182                match &peer_node.gate_peer {
1183                    Some(peer_peer_position) => {
1184                        if peer_peer_position.as_ref() != position {
1185                            return Err(format!(
1186                                "{}, as the peer of {}, doesn't have correct peer but {}",
1187                                peer_position, position, peer_peer_position
1188                            ));
1189                        }
1190                        if peer_node.gate_type.is_single_qubit_gate() {
1191                            return Err(format!(
1192                                "{}, as the peer of {}, doesn't have two-qubit gate",
1193                                peer_position, position
1194                            ));
1195                        }
1196                        if node.gate_type.peer_gate() != peer_node.gate_type {
1197                            return Err(format!(
1198                                "{}, as the peer of {}, doesn't have correct peer gate {:?}, the correct one should be {:?}",
1199                                peer_position,
1200                                position,
1201                                node.gate_type.peer_gate(),
1202                                peer_node.gate_type
1203                            ));
1204                        }
1205                    }
1206                    None => {
1207                        return Err(format!(
1208                            "{}, as the peer of {}, doesn't have peer which is invalid",
1209                            peer_position, position
1210                        ))
1211                    }
1212                }
1213            }
1214            None => {
1215                if !node.gate_type.is_single_qubit_gate() {
1216                    return Err(format!("two qubit gate {:?} should have peer", node.gate_type));
1217                }
1218            }
1219        }
1220    });
1221    simulator_iter!(simulator, base_position, _base_node, t => 0, {
1222        // check that initialization and measurement are always in the same basis
1223        let mut previous_initialization = GateType::None;
1224        for t in 1..simulator.height {
1225            let position = &mut base_position.clone();
1226            position.t = t;
1227            let node = simulator.get_node_unwrap(position);
1228            if node.gate_type.is_initialization() {
1229                previous_initialization = node.gate_type;
1230            }
1231            if node.gate_type.is_measurement() && !node.gate_type.is_corresponding_initialization(&previous_initialization) {
1232                return Err(format!("measurement and initialization not in the same basis: node {} has gate type {:?} but previous initialization is {:?}"
1233                    , position, node.gate_type, previous_initialization))
1234            }
1235        }
1236    });
1237    Ok(())
1238}
1239
1240pub fn code_builder_validate_correction(simulator: &mut Simulator, correction: &SparseCorrection) -> Option<(bool, bool)> {
1241    // apply the correction directly to the top layer
1242    let top_t = simulator.height - 1;
1243    for (position, error) in correction.iter() {
1244        assert_eq!(position.t, top_t, "correction pattern must only be at top layer");
1245        let node = simulator.get_node_mut_unwrap(position);
1246        node.propagated = node.propagated.multiply(error);
1247    }
1248    // validate the result
1249    let code_type = &simulator.code_type;
1250    let code_size = &simulator.code_size;
1251    let result = match code_type {
1252        &CodeType::StandardPlanarCode => {
1253            // check cardinality of top boundary for logical_i
1254            let mut top_cardinality = 0;
1255            for j in (1..simulator.horizontal).step_by(2) {
1256                let node = simulator.get_node_unwrap(&pos!(top_t, 1, j));
1257                if node.propagated == Z || node.propagated == Y {
1258                    top_cardinality += 1;
1259                }
1260            }
1261            let logical_i = top_cardinality % 2 != 0; // odd cardinality means there is a logical Z error
1262                                                      // check cardinality of left boundary for logical_j
1263            let mut left_cardinality = 0;
1264            for i in (1..simulator.vertical).step_by(2) {
1265                let node = simulator.get_node_unwrap(&pos!(top_t, i, 1));
1266                if node.propagated == X || node.propagated == Y {
1267                    left_cardinality += 1;
1268                }
1269            }
1270            let logical_j = left_cardinality % 2 != 0; // odd cardinality means there is a logical X error
1271            Some((logical_i, logical_j))
1272        }
1273        &CodeType::RotatedPlanarCode => {
1274            // check cardinality of top boundary for logical_i
1275            let dp = code_size.di;
1276            let dn = code_size.dj;
1277            let mut top_cardinality = 0;
1278            for delta in 0..dn {
1279                let node = simulator.get_node_unwrap(&pos!(top_t, dn - delta, 1 + delta));
1280                if node.propagated == Z || node.propagated == Y {
1281                    top_cardinality += 1;
1282                }
1283            }
1284            let logical_p = top_cardinality % 2 != 0; // odd cardinality means there is a logical Z error
1285                                                      // check cardinality of left boundary for logical_j
1286            let mut left_cardinality = 0;
1287            for delta in 0..dp {
1288                let node = simulator.get_node_unwrap(&pos!(top_t, dn + delta, 1 + delta));
1289                if node.propagated == X || node.propagated == Y {
1290                    left_cardinality += 1;
1291                }
1292            }
1293            let logical_n = left_cardinality % 2 != 0; // odd cardinality means there is a logical X error
1294            Some((logical_p, logical_n))
1295        }
1296        &CodeType::StandardTailoredCode => {
1297            // check cardinality of top boundary for logical_i
1298            let mut top_cardinality = 0;
1299            for j in (1..simulator.horizontal).step_by(2) {
1300                let node = simulator.get_node_unwrap(&pos!(top_t, 1, j));
1301                if node.propagated == Y || node.propagated == Z {
1302                    top_cardinality += 1;
1303                }
1304            }
1305            let logical_i = top_cardinality % 2 != 0; // odd cardinality means there is a logical Z error
1306                                                      // check cardinality of left boundary for logical_j
1307            let mut left_cardinality = 0;
1308            for i in (1..simulator.vertical).step_by(2) {
1309                let node = simulator.get_node_unwrap(&pos!(top_t, i, 1));
1310                if node.propagated == X || node.propagated == Z {
1311                    left_cardinality += 1;
1312                }
1313            }
1314            let logical_j = left_cardinality % 2 != 0; // odd cardinality means there is a logical X error
1315            Some((logical_i, logical_j))
1316        }
1317        &CodeType::RotatedTailoredCode | &CodeType::RotatedTailoredCodeBellInit => {
1318            // check cardinality of top boundary for logical_i
1319            let dp = code_size.di;
1320            let dn = code_size.dj;
1321            let mut top_cardinality = 0;
1322            for delta in 0..dn {
1323                let node = simulator.get_node_unwrap(&pos!(top_t, dn - delta, 1 + delta));
1324                if node.propagated == Y || node.propagated == Z {
1325                    top_cardinality += 1;
1326                }
1327            }
1328            let logical_p = top_cardinality % 2 != 0; // odd cardinality means there is a logical Z error
1329                                                      // check cardinality of left boundary for logical_j
1330            let mut left_cardinality = 0;
1331            for delta in 0..dp {
1332                let node = simulator.get_node_unwrap(&pos!(top_t, dn + delta, 1 + delta));
1333                if node.propagated == X || node.propagated == Z {
1334                    left_cardinality += 1;
1335                }
1336            }
1337            let logical_n = left_cardinality % 2 != 0; // odd cardinality means there is a logical X error
1338            Some((logical_p, logical_n))
1339        }
1340        &CodeType::PeriodicRotatedTailoredCode => {
1341            let dp = code_size.di;
1342            let dn = code_size.dj;
1343            // check cardinality of top boundary for logical_i
1344            let mut top_cardinality_y = 0;
1345            let mut top_cardinality_x = 0;
1346            for delta in 0..dn {
1347                let node = simulator.get_node_unwrap(&pos!(top_t, dn - delta, delta));
1348                if node.propagated == Y || node.propagated == Z {
1349                    top_cardinality_y += 1;
1350                }
1351                if node.propagated == X || node.propagated == Z {
1352                    top_cardinality_x += 1;
1353                }
1354            }
1355            // check cardinality of left boundary for logical_j
1356            let mut left_cardinality_y = 0;
1357            let mut left_cardinality_x = 0;
1358            for delta in 0..dp {
1359                let node = simulator.get_node_unwrap(&pos!(top_t, dn + delta, delta));
1360                if node.propagated == Y || node.propagated == Z {
1361                    left_cardinality_y += 1;
1362                }
1363                if node.propagated == X || node.propagated == Z {
1364                    left_cardinality_x += 1;
1365                }
1366            }
1367            // odd cardinality means there is a logical error; there are two logical qubits so either error
1368            let logical_p = top_cardinality_y % 2 != 0 || left_cardinality_y % 2 != 0;
1369            let logical_n = top_cardinality_x % 2 != 0 || left_cardinality_x % 2 != 0;
1370            Some((logical_p, logical_n))
1371        }
1372        &CodeType::StandardXZZXCode => {
1373            // check cardinality of top boundary for logical_i
1374            let mut top_cardinality = 0;
1375            for j in (1..simulator.horizontal).step_by(2) {
1376                let node = simulator.get_node_unwrap(&pos!(top_t, 1, j));
1377                if node.propagated == X || node.propagated == Y {
1378                    top_cardinality += 1;
1379                }
1380            }
1381            let logical_i = top_cardinality % 2 != 0; // odd cardinality means there is a logical Z error
1382                                                      // check cardinality of left boundary for logical_j
1383            let mut left_cardinality = 0;
1384            for i in (1..simulator.vertical).step_by(2) {
1385                let node = simulator.get_node_unwrap(&pos!(top_t, i, 1));
1386                if node.propagated == Z || node.propagated == Y {
1387                    left_cardinality += 1;
1388                }
1389            }
1390            let logical_j = left_cardinality % 2 != 0; // odd cardinality means there is a logical X error
1391            Some((logical_i, logical_j))
1392        }
1393        &CodeType::RotatedXZZXCode => {
1394            let dp = code_size.di;
1395            let dn = code_size.dj;
1396            // check cardinality of top boundary for logical_i
1397            let mut top_cardinality = 0;
1398            for delta in 0..dn {
1399                let node = simulator.get_node_unwrap(&pos!(top_t, dn - delta, 1 + delta));
1400                if node.propagated == X || node.propagated == Y {
1401                    top_cardinality += 1;
1402                }
1403            }
1404            let logical_p = top_cardinality % 2 != 0; // odd cardinality means there is a logical Z error
1405                                                      // check cardinality of left boundary for logical_j
1406            let mut left_cardinality = 0;
1407            for delta in 0..dp {
1408                let node = simulator.get_node_unwrap(&pos!(top_t, dn + delta, 1 + delta));
1409                if node.propagated == Z || node.propagated == Y {
1410                    left_cardinality += 1;
1411                }
1412            }
1413            let logical_n = left_cardinality % 2 != 0; // odd cardinality means there is a logical X error
1414            Some((logical_p, logical_n))
1415        }
1416        _ => None,
1417    };
1418    // recover the errors
1419    for (position, error) in correction.iter() {
1420        let node = simulator.get_node_mut_unwrap(position);
1421        node.propagated = node.propagated.multiply(error);
1422    }
1423    result
1424}
1425
1426/// check if correction indeed recover all stabilizer measurements (this is expensive for runtime)
1427#[allow(dead_code)]
1428pub fn code_builder_sanity_check_correction(
1429    simulator: &mut Simulator,
1430    correction: &SparseCorrection,
1431) -> Result<(), Vec<Position>> {
1432    // apply the correction directly to the top layer
1433    let top_t = simulator.height - 1;
1434    for (position, error) in correction.iter() {
1435        assert_eq!(position.t, top_t, "correction pattern must only be at top layer");
1436        let mut position = position.clone();
1437        position.t -= simulator.measurement_cycles; // apply it before the final perfect measurement
1438        let node = simulator.get_node_mut_unwrap(&position);
1439        node.error = node.error.multiply(error);
1440    }
1441    simulator.clear_propagate_errors();
1442    simulator.propagate_errors();
1443    // check if all stabilizers at the final measurement round don't detect errors
1444    let mut violating_positions = Vec::new();
1445    simulator_iter_real!(simulator, position, node, t => top_t, {
1446        if node.gate_type.is_measurement() {
1447            let minus_one = node.gate_type.stabilizer_measurement(&node.propagated);
1448            if minus_one {
1449                violating_positions.push(position.clone());
1450            }
1451        }
1452    });
1453    // recover the errors
1454    for (position, error) in correction.iter() {
1455        let mut position = position.clone();
1456        position.t -= simulator.measurement_cycles; // apply it before the final perfect measurement
1457        let node = simulator.get_node_mut_unwrap(&position);
1458        node.error = node.error.multiply(error);
1459    }
1460    simulator.clear_propagate_errors();
1461    simulator.propagate_errors();
1462    if !violating_positions.is_empty() {
1463        Err(violating_positions)
1464    } else {
1465        Ok(())
1466    }
1467}
1468
1469#[cfg(feature = "python_binding")]
1470#[pyfunction]
1471pub(crate) fn register(py: Python<'_>, m: &PyModule) -> PyResult<()> {
1472    m.add_class::<CodeType>()?;
1473    m.add_class::<CodeSize>()?;
1474    use crate::pyo3::PyTypeInfo;
1475    m.add("BuiltinCodeInformation", CodeSize::type_object(py))?; // backward compatibility
1476    Ok(())
1477}
1478
1479#[cfg(test)]
1480mod tests {
1481    use super::*;
1482
1483    #[macro_export]
1484    macro_rules! assert_measurement {
1485        ($simulator:ident, $errors:expr, $expected_measurements:expr) => {
1486            $simulator.clear_all_errors();
1487            for (position, error) in $errors.iter() {
1488                let node = $simulator.get_node_mut_unwrap(position);
1489                assert_eq!(
1490                    node.error,
1491                    ErrorType::I,
1492                    "do not set the error at a same position twice: {} {}",
1493                    position,
1494                    error
1495                );
1496                node.error = *error;
1497            }
1498            $simulator.propagate_errors();
1499            assert_eq!(
1500                $simulator.generate_sparse_measurement().to_vec(),
1501                $expected_measurements
1502            );
1503        };
1504    }
1505
1506    #[test]
1507    fn code_builder_standard_planar_code() {
1508        // cargo test code_builder_standard_planar_code -- --nocapture
1509        let di = 7;
1510        let dj = 5;
1511        let noisy_measurements = 3;
1512        let mut simulator = Simulator::new(CodeType::StandardPlanarCode, CodeSize::new(noisy_measurements, di, dj));
1513        code_builder_sanity_check(&simulator).unwrap();
1514        {
1515            // count how many nodes
1516            let mut nodes_count = 0;
1517            let mut virtual_nodes_count = 0;
1518            simulator_iter!(simulator, position, node, {
1519                // println!("{}", node);
1520                nodes_count += 1;
1521                if node.is_virtual {
1522                    virtual_nodes_count += 1;
1523                }
1524            });
1525            let each_layer_real_node_count = (2 * di - 1) * (2 * dj - 1);
1526            let each_layer_virtual_node_count = 2 * (di + dj);
1527            let layer_count = 6 * (noisy_measurements + 1) + 1;
1528            assert_eq!(
1529                nodes_count,
1530                layer_count * (each_layer_real_node_count + each_layer_virtual_node_count)
1531            );
1532            assert_eq!(virtual_nodes_count, layer_count * each_layer_virtual_node_count);
1533        }
1534        {
1535            // check individual qubit type
1536            {
1537                let node = simulator.get_node_unwrap(&pos!(0, 0, 1));
1538                assert_eq!(node.qubit_type, QubitType::StabX);
1539                assert_eq!(node.gate_type, GateType::MeasureX);
1540                assert!(node.is_virtual);
1541            }
1542            {
1543                let node = simulator.get_node_unwrap(&pos!(0, 0, 2 * dj - 1));
1544                assert_eq!(node.qubit_type, QubitType::StabX);
1545                assert_eq!(node.gate_type, GateType::MeasureX);
1546                assert!(node.is_virtual);
1547            }
1548            {
1549                let node = simulator.get_node_unwrap(&pos!(0, 1, 0));
1550                assert_eq!(node.qubit_type, QubitType::StabZ);
1551                assert_eq!(node.gate_type, GateType::MeasureZ);
1552                assert!(node.is_virtual);
1553            }
1554            {
1555                let node = simulator.get_node_unwrap(&pos!(0, 2 * di - 1, 0));
1556                assert_eq!(node.qubit_type, QubitType::StabZ);
1557                assert_eq!(node.gate_type, GateType::MeasureZ);
1558                assert!(node.is_virtual);
1559            }
1560            {
1561                let node = simulator.get_node_unwrap(&pos!(0, 1, 1));
1562                assert_eq!(node.qubit_type, QubitType::Data);
1563                assert_eq!(node.gate_type, GateType::None);
1564                assert!(!node.is_virtual);
1565            }
1566            {
1567                let node = simulator.get_node_unwrap(&pos!(0, 1, 2));
1568                assert_eq!(node.qubit_type, QubitType::StabZ);
1569                assert_eq!(node.gate_type, GateType::MeasureZ);
1570                assert!(!node.is_virtual);
1571            }
1572            {
1573                let node = simulator.get_node_unwrap(&pos!(0, 2, 1));
1574                assert_eq!(node.qubit_type, QubitType::StabX);
1575                assert_eq!(node.gate_type, GateType::MeasureX);
1576                assert!(!node.is_virtual);
1577            }
1578        }
1579        {
1580            // check gate sequence
1581            {
1582                // data qubit
1583                let node = simulator.get_node_unwrap(&pos!(1, 1, 1));
1584                assert!(!node.is_peer_virtual);
1585                assert_eq!(node.gate_type, GateType::None);
1586                let node = simulator.get_node_unwrap(&pos!(2, 1, 1));
1587                assert!(!node.is_peer_virtual);
1588                assert_eq!(node.gate_type, GateType::CXGateTarget);
1589                assert_eq!(node.gate_peer.as_ref().map(|x| (**x).clone()), Some(pos!(2, 2, 1)));
1590                let node = simulator.get_node_unwrap(&pos!(3, 1, 1));
1591                assert!(!node.is_peer_virtual);
1592                assert_eq!(node.gate_type, GateType::CXGateControl);
1593                assert_eq!(node.gate_peer.as_ref().map(|x| (**x).clone()), Some(pos!(3, 1, 2)));
1594                let node = simulator.get_node_unwrap(&pos!(4, 1, 1));
1595                assert!(node.is_peer_virtual);
1596                assert_eq!(node.gate_type, GateType::CXGateControl);
1597                assert_eq!(node.gate_peer.as_ref().map(|x| (**x).clone()), Some(pos!(4, 1, 0)));
1598                let node = simulator.get_node_unwrap(&pos!(5, 1, 1));
1599                assert!(node.is_peer_virtual);
1600                assert_eq!(node.gate_type, GateType::CXGateTarget);
1601                assert_eq!(node.gate_peer.as_ref().map(|x| (**x).clone()), Some(pos!(5, 0, 1)));
1602            }
1603        }
1604        {
1605            // check stabilizer measurements
1606            // data qubit at corner
1607            assert_measurement!(simulator, [(pos!(0, 1, 1), X)], [pos!(6, 1, 2)]);
1608            assert_measurement!(simulator, [(pos!(0, 1, 1), Z)], [pos!(6, 2, 1)]);
1609            assert_measurement!(simulator, [(pos!(0, 1, 1), Y)], [pos!(6, 1, 2), pos!(6, 2, 1)]);
1610            // data qubit at center
1611            assert_measurement!(simulator, [(pos!(0, 2, 2), X)], [pos!(6, 1, 2), pos!(6, 3, 2)]);
1612            assert_measurement!(simulator, [(pos!(0, 2, 2), Z)], [pos!(6, 2, 1), pos!(6, 2, 3)]);
1613            assert_measurement!(
1614                simulator,
1615                [(pos!(0, 2, 2), Y)],
1616                [pos!(6, 1, 2), pos!(6, 2, 1), pos!(6, 2, 3), pos!(6, 3, 2)]
1617            );
1618            // Z stabilizer measurement error
1619            assert_measurement!(simulator, [(pos!(5, 1, 2), X)], [pos!(6, 1, 2), pos!(12, 1, 2)]);
1620            assert_measurement!(simulator, [(pos!(5, 1, 2), Z)], []); // not sensitive to Z error
1621            assert_measurement!(simulator, [(pos!(5, 1, 2), Y)], [pos!(6, 1, 2), pos!(12, 1, 2)]);
1622            // X stabilizer measurement error
1623            assert_measurement!(simulator, [(pos!(5, 2, 1), X)], []); // not sensitive to X error
1624            assert_measurement!(simulator, [(pos!(5, 2, 1), Z)], [pos!(6, 2, 1), pos!(12, 2, 1)]);
1625            assert_measurement!(simulator, [(pos!(5, 2, 1), Y)], [pos!(6, 2, 1), pos!(12, 2, 1)]);
1626        }
1627    }
1628
1629    #[test]
1630    fn code_builder_standard_tailored_code() {
1631        // cargo test code_builder_standard_tailored_code -- --nocapture
1632        let di = 7;
1633        let dj = 5;
1634        let noisy_measurements = 3;
1635        let mut simulator = Simulator::new(CodeType::StandardTailoredCode, CodeSize::new(noisy_measurements, di, dj));
1636        code_builder_sanity_check(&simulator).unwrap();
1637        {
1638            // check stabilizer measurements
1639            // data qubit at corner
1640            assert_measurement!(simulator, [(pos!(0, 1, 1), X)], [pos!(6, 1, 2)]);
1641            assert_measurement!(simulator, [(pos!(0, 1, 1), Z)], [pos!(6, 1, 2), pos!(6, 2, 1)]);
1642            assert_measurement!(simulator, [(pos!(0, 1, 1), Y)], [pos!(6, 2, 1)]);
1643            // data qubit at center
1644            assert_measurement!(simulator, [(pos!(0, 2, 2), X)], [pos!(6, 1, 2), pos!(6, 3, 2)]);
1645            assert_measurement!(
1646                simulator,
1647                [(pos!(0, 2, 2), Z)],
1648                [pos!(6, 1, 2), pos!(6, 2, 1), pos!(6, 2, 3), pos!(6, 3, 2)]
1649            );
1650            assert_measurement!(simulator, [(pos!(0, 2, 2), Y)], [pos!(6, 2, 1), pos!(6, 2, 3)]);
1651            // Y stabilizer measurement error
1652            assert_measurement!(simulator, [(pos!(5, 1, 2), X)], []);
1653            assert_measurement!(simulator, [(pos!(5, 1, 2), Z)], [pos!(6, 1, 2), pos!(12, 1, 2)]); // not sensitive to Z error
1654            assert_measurement!(simulator, [(pos!(5, 1, 2), Y)], [pos!(6, 1, 2), pos!(12, 1, 2)]);
1655            // X stabilizer measurement error
1656            assert_measurement!(simulator, [(pos!(5, 2, 1), X)], []); // not sensitive to X error
1657            assert_measurement!(simulator, [(pos!(5, 2, 1), Z)], [pos!(6, 2, 1), pos!(12, 2, 1)]);
1658            assert_measurement!(simulator, [(pos!(5, 2, 1), Y)], [pos!(6, 2, 1), pos!(12, 2, 1)]);
1659        }
1660    }
1661
1662    #[test]
1663    fn code_builder_periodic_rotated_tailored_code() {
1664        // cargo test code_builder_periodic_rotated_tailored_code -- --nocapture
1665        let di = 7;
1666        let dj = 5;
1667        let noisy_measurements = 0;
1668        let mut simulator = Simulator::new(
1669            CodeType::PeriodicRotatedTailoredCode,
1670            CodeSize::new(noisy_measurements, di + 1, dj + 1),
1671        );
1672        code_builder_sanity_check(&simulator).unwrap();
1673        {
1674            // check stabilizer measurements
1675            // data qubit at center
1676            assert_measurement!(simulator, [(pos!(0, 1, 5), X)], [pos!(6, 1, 4), pos!(6, 1, 6)]);
1677            assert_measurement!(
1678                simulator,
1679                [(pos!(0, 1, 5), Z)],
1680                [pos!(6, 0, 5), pos!(6, 1, 4), pos!(6, 1, 6), pos!(6, 2, 5)]
1681            );
1682            assert_measurement!(simulator, [(pos!(0, 1, 5), Y)], [pos!(6, 0, 5), pos!(6, 2, 5)]);
1683            // data qubit at periodic boundary
1684            assert_measurement!(simulator, [(pos!(0, 6, 0), X)], [pos!(6, 1, 6), pos!(6, 5, 0)]);
1685            assert_measurement!(
1686                simulator,
1687                [(pos!(0, 6, 0), Z)],
1688                [pos!(6, 0, 5), pos!(6, 1, 6), pos!(6, 5, 0), pos!(6, 6, 1)]
1689            );
1690            assert_measurement!(simulator, [(pos!(0, 6, 0), Y)], [pos!(6, 0, 5), pos!(6, 6, 1)]);
1691            // data qubit at periodic boundary
1692            assert_measurement!(simulator, [(pos!(0, 7, 1), X)], [pos!(6, 1, 6), pos!(6, 7, 2)]);
1693            assert_measurement!(
1694                simulator,
1695                [(pos!(0, 7, 1), Z)],
1696                [pos!(6, 1, 6), pos!(6, 2, 7), pos!(6, 6, 1), pos!(6, 7, 2)]
1697            );
1698            assert_measurement!(simulator, [(pos!(0, 7, 1), Y)], [pos!(6, 2, 7), pos!(6, 6, 1)]);
1699            // data qubit at periodic boundary
1700            assert_measurement!(simulator, [(pos!(0, 13, 7), X)], [pos!(6, 5, 0), pos!(6, 7, 12)]);
1701            assert_measurement!(
1702                simulator,
1703                [(pos!(0, 13, 7), Z)],
1704                [pos!(6, 0, 5), pos!(6, 5, 0), pos!(6, 7, 12), pos!(6, 12, 7)]
1705            );
1706            assert_measurement!(simulator, [(pos!(0, 13, 7), Y)], [pos!(6, 0, 5), pos!(6, 12, 7)]);
1707            // data qubit at periodic boundary
1708            assert_measurement!(simulator, [(pos!(0, 8, 12), X)], [pos!(6, 1, 4), pos!(6, 7, 12)]);
1709            assert_measurement!(
1710                simulator,
1711                [(pos!(0, 8, 12), Z)],
1712                [pos!(6, 0, 5), pos!(6, 1, 4), pos!(6, 7, 12), pos!(6, 8, 11)]
1713            );
1714            assert_measurement!(simulator, [(pos!(0, 8, 12), Y)], [pos!(6, 0, 5), pos!(6, 8, 11)]);
1715        }
1716    }
1717
1718    #[test]
1719    fn code_builder_visualize_standard_planar_code() {
1720        // cargo test code_builder_visualize_standard_planar_code -- --nocapture
1721        let visualize_filename = "code_builder_visualize_standard_planar_code.json".to_string();
1722        print_visualize_link(visualize_filename.clone());
1723        let di = 7;
1724        let dj = 5;
1725        let noisy_measurements = 0;
1726        let simulator = Simulator::new(CodeType::StandardPlanarCode, CodeSize::new(noisy_measurements, di, dj));
1727        code_builder_sanity_check(&simulator).unwrap();
1728        let mut visualizer = Visualizer::new(Some(visualize_data_folder() + visualize_filename.as_str())).unwrap();
1729        visualizer.add_component(&simulator).unwrap();
1730    }
1731
1732    #[test]
1733    fn code_builder_visualize_rotated_planar_code() {
1734        // cargo test code_builder_visualize_rotated_planar_code -- --nocapture
1735        let visualize_filename = "code_builder_visualize_rotated_planar_code.json".to_string();
1736        print_visualize_link(visualize_filename.clone());
1737        let di = 7;
1738        let dj = 5;
1739        let noisy_measurements = 0;
1740        let simulator = Simulator::new(CodeType::RotatedPlanarCode, CodeSize::new(noisy_measurements, di, dj));
1741        code_builder_sanity_check(&simulator).unwrap();
1742        let mut visualizer = Visualizer::new(Some(visualize_data_folder() + visualize_filename.as_str())).unwrap();
1743        visualizer.add_component(&simulator).unwrap();
1744    }
1745
1746    #[test]
1747    fn code_builder_visualize_standard_planar_code_noisy() {
1748        // cargo test code_builder_visualize_standard_planar_code_noisy -- --nocapture
1749        let visualize_filename = "code_builder_visualize_standard_planar_code_noisy.json".to_string();
1750        print_visualize_link(visualize_filename.clone());
1751        let di = 5;
1752        let dj = 5;
1753        let noisy_measurements = 2;
1754        let simulator = Simulator::new(CodeType::StandardPlanarCode, CodeSize::new(noisy_measurements, di, dj));
1755        code_builder_sanity_check(&simulator).unwrap();
1756        let mut visualizer = Visualizer::new(Some(visualize_data_folder() + visualize_filename.as_str())).unwrap();
1757        visualizer.add_component(&simulator).unwrap();
1758    }
1759
1760    #[test]
1761    fn code_builder_visualize_rotated_planar_code_noisy() {
1762        // cargo test code_builder_visualize_rotated_planar_code_noisy -- --nocapture
1763        let visualize_filename = "code_builder_visualize_rotated_planar_code_noisy.json".to_string();
1764        print_visualize_link(visualize_filename.clone());
1765        let di = 5;
1766        let dj = 5;
1767        let noisy_measurements = 2;
1768        let simulator = Simulator::new(CodeType::RotatedPlanarCode, CodeSize::new(noisy_measurements, di, dj));
1769        code_builder_sanity_check(&simulator).unwrap();
1770        let mut visualizer = Visualizer::new(Some(visualize_data_folder() + visualize_filename.as_str())).unwrap();
1771        visualizer.add_component(&simulator).unwrap();
1772    }
1773
1774    #[test]
1775    fn code_builder_visualize_standard_xzzx_code() {
1776        // cargo test code_builder_visualize_standard_xzzx_code -- --nocapture
1777        let visualize_filename = "code_builder_visualize_standard_xzzx_code.json".to_string();
1778        print_visualize_link(visualize_filename.clone());
1779        let di = 7;
1780        let dj = 5;
1781        let noisy_measurements = 0;
1782        let simulator = Simulator::new(CodeType::StandardXZZXCode, CodeSize::new(noisy_measurements, di, dj));
1783        code_builder_sanity_check(&simulator).unwrap();
1784        let mut visualizer = Visualizer::new(Some(visualize_data_folder() + visualize_filename.as_str())).unwrap();
1785        visualizer.add_component(&simulator).unwrap();
1786    }
1787
1788    #[test]
1789    fn code_builder_visualize_rotated_xzzx_code() {
1790        // cargo test code_builder_visualize_rotated_xzzx_code -- --nocapture
1791        let visualize_filename = "code_builder_visualize_rotated_xzzx_code.json".to_string();
1792        print_visualize_link(visualize_filename.clone());
1793        let di = 7;
1794        let dj = 5;
1795        let noisy_measurements = 0;
1796        let simulator = Simulator::new(CodeType::RotatedXZZXCode, CodeSize::new(noisy_measurements, di, dj));
1797        code_builder_sanity_check(&simulator).unwrap();
1798        let mut visualizer = Visualizer::new(Some(visualize_data_folder() + visualize_filename.as_str())).unwrap();
1799        visualizer.add_component(&simulator).unwrap();
1800    }
1801
1802    #[test]
1803    fn code_builder_visualize_standard_tailored_code() {
1804        // cargo test code_builder_visualize_standard_tailored_code -- --nocapture
1805        let visualize_filename = "code_builder_visualize_standard_tailored_code.json".to_string();
1806        print_visualize_link(visualize_filename.clone());
1807        let di = 7;
1808        let dj = 5;
1809        let noisy_measurements = 0;
1810        let simulator = Simulator::new(CodeType::StandardTailoredCode, CodeSize::new(noisy_measurements, di, dj));
1811        code_builder_sanity_check(&simulator).unwrap();
1812        let mut visualizer = Visualizer::new(Some(visualize_data_folder() + visualize_filename.as_str())).unwrap();
1813        visualizer.add_component(&simulator).unwrap();
1814    }
1815
1816    #[test]
1817    fn code_builder_visualize_rotated_tailored_code() {
1818        // cargo test code_builder_visualize_rotated_tailored_code -- --nocapture
1819        let visualize_filename = "code_builder_visualize_rotated_tailored_code.json".to_string();
1820        print_visualize_link(visualize_filename.clone());
1821        let di = 7;
1822        let dj = 5;
1823        let noisy_measurements = 0;
1824        let simulator = Simulator::new(CodeType::RotatedTailoredCode, CodeSize::new(noisy_measurements, di, dj));
1825        code_builder_sanity_check(&simulator).unwrap();
1826        let mut visualizer = Visualizer::new(Some(visualize_data_folder() + visualize_filename.as_str())).unwrap();
1827        visualizer.add_component(&simulator).unwrap();
1828    }
1829}