1use super::pointers::*;
12use super::util::*;
13use super::visualize::*;
14use crate::derivative::Derivative;
15use crate::rand_xoshiro::rand_core::SeedableRng;
16use crate::rayon::prelude::*;
17use crate::serde_json;
18#[cfg(feature = "python_binding")]
19use pyo3::prelude::*;
20use std::collections::HashMap;
21use std::fs::File;
22use std::io::{self, BufRead};
23
24#[derive(Derivative, Clone)]
26#[derivative(Debug)]
27#[cfg_attr(feature = "python_binding", cfg_eval)]
28#[cfg_attr(feature = "python_binding", pyclass)]
29pub struct CodeVertex {
30 #[cfg_attr(feature = "python_binding", pyo3(get, set))]
32 pub position: VisualizePosition,
33 #[cfg_attr(feature = "python_binding", pyo3(get, set))]
35 pub neighbor_edges: Vec<EdgeIndex>,
36 #[cfg_attr(feature = "python_binding", pyo3(get, set))]
38 pub is_virtual: bool,
39 #[cfg_attr(feature = "python_binding", pyo3(get, set))]
41 pub is_defect: bool,
42}
43
44#[cfg_attr(feature = "python_binding", cfg_eval)]
45#[cfg_attr(feature = "python_binding", pymethods)]
46impl CodeVertex {
47 #[cfg(feature = "python_binding")]
48 fn __repr__(&self) -> String {
49 format!("{:?}", self)
50 }
51}
52
53#[derive(Derivative, Clone)]
55#[derivative(Debug)]
56#[cfg_attr(feature = "python_binding", cfg_eval)]
57#[cfg_attr(feature = "python_binding", pyclass)]
58pub struct CodeEdge {
59 #[cfg_attr(feature = "python_binding", pyo3(get, set))]
61 pub vertices: (VertexIndex, VertexIndex),
62 #[cfg_attr(feature = "python_binding", pyo3(get, set))]
64 pub p: f64,
65 #[cfg_attr(feature = "python_binding", pyo3(get, set))]
67 pub pe: f64,
68 #[cfg_attr(feature = "python_binding", pyo3(get, set))]
70 pub half_weight: Weight,
71 #[cfg_attr(feature = "python_binding", pyo3(get, set))]
73 pub is_erasure: bool,
74}
75
76#[cfg_attr(feature = "python_binding", cfg_eval)]
77#[cfg_attr(feature = "python_binding", pymethods)]
78impl CodeEdge {
79 #[cfg_attr(feature = "python_binding", new)]
80 pub fn new(a: VertexIndex, b: VertexIndex) -> Self {
81 Self {
82 vertices: (a, b),
83 p: 0.,
84 pe: 0.,
85 half_weight: 0,
86 is_erasure: false,
87 }
88 }
89 #[cfg(feature = "python_binding")]
90 fn __repr__(&self) -> String {
91 format!("{:?}", self)
92 }
93}
94
95#[cfg_attr(feature = "python_binding", pyfunction)]
97pub fn weight_of_p(p: f64) -> f64 {
98 assert!((0. ..=0.5).contains(&p), "p must be a reasonable value between 0 and 50%");
99 ((1. - p) / p).ln()
100}
101
102pub trait ExampleCode {
103 fn vertices_edges(&mut self) -> (&mut Vec<CodeVertex>, &mut Vec<CodeEdge>);
105 fn immutable_vertices_edges(&self) -> (&Vec<CodeVertex>, &Vec<CodeEdge>);
106
107 fn vertex_num(&self) -> VertexNum {
109 self.immutable_vertices_edges().0.len() as VertexNum
110 }
111
112 fn compute_weights(&mut self, max_half_weight: Weight) {
115 let (_vertices, edges) = self.vertices_edges();
116 let mut max_weight = 0.;
117 for edge in edges.iter() {
118 let weight = weight_of_p(edge.p);
119 if weight > max_weight {
120 max_weight = weight;
121 }
122 }
123 assert!(max_weight > 0., "max weight is not expected to be 0.");
124 for edge in edges.iter_mut() {
126 let weight = weight_of_p(edge.p);
127 let half_weight: Weight = ((max_half_weight as f64) * weight / max_weight).round() as Weight;
128 edge.half_weight = if half_weight == 0 { 1 } else { half_weight }; }
130 }
131
132 fn sanity_check(&self) -> Result<(), String> {
134 let (vertices, edges) = self.immutable_vertices_edges();
135 if vertices.is_empty() || edges.is_empty() {
137 return Err("empty graph".to_string());
138 }
139 let mut existing_edges = HashMap::<(VertexIndex, VertexIndex), EdgeIndex>::with_capacity(edges.len() * 2);
141 for (edge_idx, edge) in edges.iter().enumerate() {
142 let (v1, v2) = edge.vertices;
143 let unique_edge = if v1 < v2 { (v1, v2) } else { (v2, v1) };
144 if existing_edges.contains_key(&unique_edge) {
145 let previous_idx = existing_edges[&unique_edge];
146 return Err(format!(
147 "duplicate edge {} and {} with incident vertices {} and {}",
148 previous_idx, edge_idx, v1, v2
149 ));
150 }
151 existing_edges.insert(unique_edge, edge_idx as EdgeIndex);
152 }
153 for (vertex_idx, vertex) in vertices.iter().enumerate() {
155 let mut existing_edges = HashMap::<EdgeIndex, ()>::new();
156 if vertex.neighbor_edges.is_empty() {
157 return Err(format!("vertex {} do not have any neighbor edges", vertex_idx));
158 }
159 for edge_idx in vertex.neighbor_edges.iter() {
160 if existing_edges.contains_key(edge_idx) {
161 return Err(format!("duplicate referred edge {} from vertex {}", edge_idx, vertex_idx));
162 }
163 existing_edges.insert(*edge_idx, ());
164 }
165 }
166 Ok(())
167 }
168
169 fn set_probability(&mut self, p: f64) {
171 let (_vertices, edges) = self.vertices_edges();
172 for edge in edges.iter_mut() {
173 edge.p = p;
174 }
175 }
176
177 fn set_erasure_probability(&mut self, pe: f64) {
179 let (_vertices, edges) = self.vertices_edges();
180 for edge in edges.iter_mut() {
181 edge.pe = pe;
182 }
183 }
184
185 #[allow(clippy::unnecessary_cast)]
187 fn fill_vertices(&mut self, vertex_num: VertexNum) {
188 let (vertices, edges) = self.vertices_edges();
189 vertices.clear();
190 vertices.reserve(vertex_num as usize);
191 for _ in 0..vertex_num {
192 vertices.push(CodeVertex {
193 position: VisualizePosition::new(0., 0., 0.),
194 neighbor_edges: Vec::new(),
195 is_virtual: false,
196 is_defect: false,
197 });
198 }
199 for (edge_idx, edge) in edges.iter().enumerate() {
200 let vertex_1 = &mut vertices[edge.vertices.0 as usize];
201 vertex_1.neighbor_edges.push(edge_idx as EdgeIndex);
202 let vertex_2 = &mut vertices[edge.vertices.1 as usize];
203 vertex_2.neighbor_edges.push(edge_idx as EdgeIndex);
204 }
205 }
206
207 fn get_positions(&self) -> Vec<VisualizePosition> {
209 let (vertices, _edges) = self.immutable_vertices_edges();
210 let mut positions = Vec::with_capacity(vertices.len());
211 for vertex in vertices.iter() {
212 positions.push(vertex.position.clone());
213 }
214 positions
215 }
216
217 fn get_initializer(&self) -> SolverInitializer {
219 let (vertices, edges) = self.immutable_vertices_edges();
220 let vertex_num = vertices.len() as VertexIndex;
221 let mut weighted_edges = Vec::with_capacity(edges.len());
222 for edge in edges.iter() {
223 weighted_edges.push((edge.vertices.0, edge.vertices.1, edge.half_weight * 2));
224 }
225 let mut virtual_vertices = Vec::new();
226 for (vertex_idx, vertex) in vertices.iter().enumerate() {
227 if vertex.is_virtual {
228 virtual_vertices.push(vertex_idx as VertexIndex);
229 }
230 }
231 SolverInitializer {
232 vertex_num,
233 weighted_edges,
234 virtual_vertices,
235 }
236 }
237
238 #[allow(clippy::unnecessary_cast)]
241 fn set_defect_vertices(&mut self, defect_vertices: &[VertexIndex]) {
242 let (vertices, _edges) = self.vertices_edges();
243 for vertex in vertices.iter_mut() {
244 vertex.is_defect = false;
245 }
246 for vertex_idx in defect_vertices.iter() {
247 let vertex = &mut vertices[*vertex_idx as usize];
248 vertex.is_defect = true;
249 }
250 }
251
252 #[allow(clippy::unnecessary_cast)]
254 fn set_erasures(&mut self, erasures: &[EdgeIndex]) {
255 let (_vertices, edges) = self.vertices_edges();
256 for edge in edges.iter_mut() {
257 edge.is_erasure = false;
258 }
259 for edge_idx in erasures.iter() {
260 let edge = &mut edges[*edge_idx as usize];
261 edge.is_erasure = true;
262 }
263 }
264
265 fn set_syndrome(&mut self, syndrome_pattern: &SyndromePattern) {
267 self.set_defect_vertices(&syndrome_pattern.defect_vertices);
268 self.set_erasures(&syndrome_pattern.erasures);
269 }
270
271 fn get_defect_vertices(&self) -> Vec<VertexIndex> {
273 let (vertices, _edges) = self.immutable_vertices_edges();
274 let mut syndrome = Vec::new();
275 for (vertex_idx, vertex) in vertices.iter().enumerate() {
276 if vertex.is_defect {
277 syndrome.push(vertex_idx as VertexIndex);
278 }
279 }
280 syndrome
281 }
282
283 fn get_erasures(&self) -> Vec<EdgeIndex> {
285 let (_vertices, edges) = self.immutable_vertices_edges();
286 let mut erasures = Vec::new();
287 for (edge_idx, edge) in edges.iter().enumerate() {
288 if edge.is_erasure {
289 erasures.push(edge_idx as EdgeIndex);
290 }
291 }
292 erasures
293 }
294
295 fn get_syndrome(&self) -> SyndromePattern {
297 SyndromePattern::new(self.get_defect_vertices(), self.get_erasures())
298 }
299
300 #[allow(clippy::unnecessary_cast)]
302 fn generate_random_errors(&mut self, seed: u64) -> SyndromePattern {
303 let mut rng = DeterministicRng::seed_from_u64(seed);
304 let (vertices, edges) = self.vertices_edges();
305 for vertex in vertices.iter_mut() {
306 vertex.is_defect = false;
307 }
308 for edge in edges.iter_mut() {
309 let p = if rng.next_f64() < edge.pe {
310 edge.is_erasure = true;
311 0.5 } else {
313 edge.is_erasure = false;
314 edge.p
315 };
316 if rng.next_f64() < p {
317 let (v1, v2) = edge.vertices;
318 let vertex_1 = &mut vertices[v1 as usize];
319 if !vertex_1.is_virtual {
320 vertex_1.is_defect = !vertex_1.is_defect;
321 }
322 let vertex_2 = &mut vertices[v2 as usize];
323 if !vertex_2.is_virtual {
324 vertex_2.is_defect = !vertex_2.is_defect;
325 }
326 }
327 }
328 self.get_syndrome()
329 }
330
331 #[allow(clippy::unnecessary_cast)]
332 fn generate_errors(&mut self, edge_indices: &[EdgeIndex]) -> SyndromePattern {
333 let (vertices, edges) = self.vertices_edges();
334 for &edge_index in edge_indices {
335 let edge = &mut edges.get_mut(edge_index as usize).unwrap();
336 let (v1, v2) = edge.vertices;
337 let vertex_1 = &mut vertices[v1 as usize];
338 if !vertex_1.is_virtual {
339 vertex_1.is_defect = !vertex_1.is_defect;
340 }
341 let vertex_2 = &mut vertices[v2 as usize];
342 if !vertex_2.is_virtual {
343 vertex_2.is_defect = !vertex_2.is_defect;
344 }
345 }
346 self.get_syndrome()
347 }
348
349 fn clear_errors(&mut self) {
350 let (vertices, edges) = self.vertices_edges();
351 for vertex in vertices.iter_mut() {
352 vertex.is_defect = false;
353 }
354 for edge in edges.iter_mut() {
355 edge.is_erasure = true;
356 }
357 }
358
359 fn is_virtual(&self, vertex_idx: usize) -> bool {
360 let (vertices, _edges) = self.immutable_vertices_edges();
361 vertices[vertex_idx].is_virtual
362 }
363
364 fn is_defect(&self, vertex_idx: usize) -> bool {
365 let (vertices, _edges) = self.immutable_vertices_edges();
366 vertices[vertex_idx].is_defect
367 }
368
369 #[allow(clippy::unnecessary_cast)]
371 fn reorder_vertices(&mut self, sequential_vertices: &[VertexIndex]) {
372 let (vertices, edges) = self.vertices_edges();
373 assert_eq!(vertices.len(), sequential_vertices.len(), "amount of vertices must be same");
374 let old_to_new = build_old_to_new(sequential_vertices);
375 *vertices = (0..vertices.len())
377 .map(|new_index| vertices[sequential_vertices[new_index] as usize].clone())
378 .collect();
379 for edge in edges.iter_mut() {
380 let (old_left, old_right) = edge.vertices;
381 edge.vertices = (
382 old_to_new[old_left as usize].unwrap(),
383 old_to_new[old_right as usize].unwrap(),
384 );
385 }
386 }
387}
388
389#[cfg(feature = "python_binding")]
390use rand::{thread_rng, Rng};
391
392#[cfg(feature = "python_binding")]
393macro_rules! bind_trait_example_code {
394 ($struct_name:ident) => {
395 #[pymethods]
396 impl $struct_name {
397 fn __repr__(&self) -> String {
398 format!("{:?}", self)
399 }
400 #[pyo3(name = "vertex_num")]
401 fn trait_vertex_num(&self) -> VertexNum {
402 self.vertex_num()
403 }
404 #[pyo3(name = "compute_weights")]
405 fn trait_compute_weights(&mut self, max_half_weight: Weight) {
406 self.compute_weights(max_half_weight)
407 }
408 #[pyo3(name = "sanity_check")]
409 fn trait_sanity_check(&self) -> Option<String> {
410 self.sanity_check().err()
411 }
412 #[pyo3(name = "set_probability")]
413 fn trait_set_probability(&mut self, p: f64) {
414 self.set_probability(p)
415 }
416 #[pyo3(name = "set_erasure_probability")]
417 fn trait_set_erasure_probability(&mut self, p: f64) {
418 self.set_erasure_probability(p)
419 }
420 #[pyo3(name = "fill_vertices")]
421 fn trait_fill_vertices(&mut self, vertex_num: VertexNum) {
422 self.fill_vertices(vertex_num)
423 }
424 #[pyo3(name = "get_positions")]
425 fn trait_get_positions(&self) -> Vec<VisualizePosition> {
426 self.get_positions()
427 }
428 #[pyo3(name = "get_initializer")]
429 fn trait_get_initializer(&self) -> SolverInitializer {
430 self.get_initializer()
431 }
432 #[pyo3(name = "set_defect_vertices")]
433 fn trait_set_defect_vertices(&mut self, defect_vertices: Vec<VertexIndex>) {
434 self.set_defect_vertices(&defect_vertices)
435 }
436 #[pyo3(name = "set_erasures")]
437 fn trait_set_erasures(&mut self, erasures: Vec<EdgeIndex>) {
438 self.set_erasures(&erasures)
439 }
440 #[pyo3(name = "set_syndrome")]
441 fn trait_set_syndrome(&mut self, syndrome_pattern: &SyndromePattern) {
442 self.set_syndrome(syndrome_pattern)
443 }
444 #[pyo3(name = "get_defect_vertices")]
445 fn trait_get_defect_vertices(&self) -> Vec<VertexIndex> {
446 self.get_defect_vertices()
447 }
448 #[pyo3(name = "get_erasures")]
449 fn trait_get_erasures(&self) -> Vec<EdgeIndex> {
450 self.get_erasures()
451 }
452 #[pyo3(name = "get_syndrome")]
453 fn trait_get_syndrome(&self) -> SyndromePattern {
454 self.get_syndrome()
455 }
456 #[pyo3(name = "generate_random_errors", signature = (seed=thread_rng().gen()))]
457 fn trait_generate_random_errors(&mut self, seed: u64) -> SyndromePattern {
458 self.generate_random_errors(seed)
459 }
460 #[pyo3(name = "generate_errors")]
461 fn trait_generate_errors(&mut self, edge_indices: Vec<EdgeIndex>) -> SyndromePattern {
462 self.generate_errors(&edge_indices)
463 }
464 #[pyo3(name = "clear_errors")]
465 fn trait_clear_errors(&mut self) {
466 self.clear_errors()
467 }
468 #[pyo3(name = "is_virtual")]
469 fn trait_is_virtual(&mut self, vertex_idx: usize) -> bool {
470 self.is_virtual(vertex_idx)
471 }
472 #[pyo3(name = "is_defect")]
473 fn trait_is_defect(&mut self, vertex_idx: usize) -> bool {
474 self.is_defect(vertex_idx)
475 }
476 #[pyo3(name = "reorder_vertices")]
477 fn trait_reorder_vertices(&mut self, sequential_vertices: Vec<VertexIndex>) {
478 self.reorder_vertices(&sequential_vertices)
479 }
480 #[pyo3(name = "snapshot", signature = (abbrev=true))]
481 fn trait_snapshot(&mut self, abbrev: bool) -> PyObject {
482 json_to_pyobject(self.snapshot(abbrev))
483 }
484 }
485 };
486}
487
488impl<T> FusionVisualizer for T
489where
490 T: ExampleCode,
491{
492 fn snapshot(&self, abbrev: bool) -> serde_json::Value {
493 let (self_vertices, self_edges) = self.immutable_vertices_edges();
494 let mut vertices = Vec::<serde_json::Value>::new();
495 for vertex in self_vertices.iter() {
496 vertices.push(json!({
497 if abbrev { "v" } else { "is_virtual" }: i32::from(vertex.is_virtual),
498 if abbrev { "s" } else { "is_defect" }: i32::from(vertex.is_defect),
499 }));
500 }
501 let mut edges = Vec::<serde_json::Value>::new();
502 for edge in self_edges.iter() {
503 edges.push(json!({
504 if abbrev { "w" } else { "weight" }: edge.half_weight * 2,
505 if abbrev { "l" } else { "left" }: edge.vertices.0,
506 if abbrev { "r" } else { "right" }: edge.vertices.1,
507 }));
509 }
510 json!({
511 "vertices": vertices, "edges": edges,
513 })
514 }
515}
516
517#[derive(Clone, Debug)]
519#[cfg_attr(feature = "python_binding", cfg_eval)]
520#[cfg_attr(feature = "python_binding", pyclass)]
521pub struct CodeCapacityRepetitionCode {
522 #[cfg_attr(feature = "python_binding", pyo3(get, set))]
524 pub vertices: Vec<CodeVertex>,
525 #[cfg_attr(feature = "python_binding", pyo3(get, set))]
527 pub edges: Vec<CodeEdge>,
528}
529
530impl ExampleCode for CodeCapacityRepetitionCode {
531 fn vertices_edges(&mut self) -> (&mut Vec<CodeVertex>, &mut Vec<CodeEdge>) {
532 (&mut self.vertices, &mut self.edges)
533 }
534 fn immutable_vertices_edges(&self) -> (&Vec<CodeVertex>, &Vec<CodeEdge>) {
535 (&self.vertices, &self.edges)
536 }
537}
538
539#[cfg(feature = "python_binding")]
540bind_trait_example_code! {CodeCapacityRepetitionCode}
541
542#[cfg_attr(feature = "python_binding", cfg_eval)]
543#[cfg_attr(feature = "python_binding", pymethods)]
544impl CodeCapacityRepetitionCode {
545 #[cfg_attr(feature = "python_binding", new)]
546 #[cfg_attr(feature = "python_binding", pyo3(signature = (d, p, max_half_weight = 500)))]
547 pub fn new(d: VertexNum, p: f64, max_half_weight: Weight) -> Self {
548 let mut code = Self::create_code(d);
549 code.set_probability(p);
550 code.compute_weights(max_half_weight);
551 code
552 }
553
554 #[cfg_attr(feature = "python_binding", staticmethod)]
555 #[allow(clippy::unnecessary_cast)]
556 pub fn create_code(d: VertexNum) -> Self {
557 assert!(d >= 3 && d % 2 == 1, "d must be odd integer >= 3");
558 let vertex_num = (d - 1) + 2; let mut edges = Vec::new();
561 for i in 0..d - 1 {
562 edges.push(CodeEdge::new(i, i + 1));
563 }
564 edges.push(CodeEdge::new(0, d)); let mut code = Self {
566 vertices: Vec::new(),
567 edges,
568 };
569 code.fill_vertices(vertex_num);
571 code.vertices[d as usize - 1].is_virtual = true;
572 code.vertices[d as usize].is_virtual = true;
573 let mut positions = Vec::new();
574 for i in 0..d {
575 positions.push(VisualizePosition::new(0., i as f64, 0.));
576 }
577 positions.push(VisualizePosition::new(0., -1., 0.));
578 for (i, position) in positions.into_iter().enumerate() {
579 code.vertices[i].position = position;
580 }
581 code
582 }
583}
584
585#[derive(Clone, Debug)]
588#[cfg_attr(feature = "python_binding", cfg_eval)]
589#[cfg_attr(feature = "python_binding", pyclass)]
590pub struct CodeCapacityPlanarCode {
591 #[cfg_attr(feature = "python_binding", pyo3(get, set))]
593 pub vertices: Vec<CodeVertex>,
594 #[cfg_attr(feature = "python_binding", pyo3(get, set))]
596 pub edges: Vec<CodeEdge>,
597}
598
599impl ExampleCode for CodeCapacityPlanarCode {
600 fn vertices_edges(&mut self) -> (&mut Vec<CodeVertex>, &mut Vec<CodeEdge>) {
601 (&mut self.vertices, &mut self.edges)
602 }
603 fn immutable_vertices_edges(&self) -> (&Vec<CodeVertex>, &Vec<CodeEdge>) {
604 (&self.vertices, &self.edges)
605 }
606}
607
608#[cfg(feature = "python_binding")]
609bind_trait_example_code! {CodeCapacityPlanarCode}
610
611#[cfg_attr(feature = "python_binding", cfg_eval)]
612#[cfg_attr(feature = "python_binding", pymethods)]
613impl CodeCapacityPlanarCode {
614 #[cfg_attr(feature = "python_binding", new)]
615 #[cfg_attr(feature = "python_binding", pyo3(signature = (d, p, max_half_weight = 500)))]
616 pub fn new(d: VertexNum, p: f64, max_half_weight: Weight) -> Self {
617 let mut code = Self::create_code(d);
618 code.set_probability(p);
619 code.compute_weights(max_half_weight);
620 code
621 }
622
623 #[cfg_attr(feature = "python_binding", staticmethod)]
624 #[allow(clippy::unnecessary_cast)]
625 pub fn create_code(d: VertexNum) -> Self {
626 assert!(d >= 3 && d % 2 == 1, "d must be odd integer >= 3");
627 let row_vertex_num = (d - 1) + 2; let vertex_num = row_vertex_num * d; let mut edges = Vec::new();
631 for row in 0..d {
632 let bias = row * row_vertex_num;
633 for i in 0..d - 1 {
634 edges.push(CodeEdge::new(bias + i, bias + i + 1));
635 }
636 edges.push(CodeEdge::new(bias, bias + d)); if row + 1 < d {
638 for i in 0..d - 1 {
639 edges.push(CodeEdge::new(bias + i, bias + i + row_vertex_num));
640 }
641 }
642 }
643 let mut code = Self {
644 vertices: Vec::new(),
645 edges,
646 };
647 code.fill_vertices(vertex_num);
649 for row in 0..d {
650 let bias = row * row_vertex_num;
651 code.vertices[(bias + d - 1) as usize].is_virtual = true;
652 code.vertices[(bias + d) as usize].is_virtual = true;
653 }
654 let mut positions = Vec::new();
655 for row in 0..d {
656 let pos_i = row as f64;
657 for i in 0..d {
658 positions.push(VisualizePosition::new(pos_i, i as f64, 0.));
659 }
660 positions.push(VisualizePosition::new(pos_i, -1., 0.));
661 }
662 for (i, position) in positions.into_iter().enumerate() {
663 code.vertices[i].position = position;
664 }
665 code
666 }
667}
668
669#[derive(Clone, Debug)]
672#[cfg_attr(feature = "python_binding", cfg_eval)]
673#[cfg_attr(feature = "python_binding", pyclass)]
674pub struct PhenomenologicalPlanarCode {
675 #[cfg_attr(feature = "python_binding", pyo3(get, set))]
677 pub vertices: Vec<CodeVertex>,
678 #[cfg_attr(feature = "python_binding", pyo3(get, set))]
680 pub edges: Vec<CodeEdge>,
681}
682
683impl ExampleCode for PhenomenologicalPlanarCode {
684 fn vertices_edges(&mut self) -> (&mut Vec<CodeVertex>, &mut Vec<CodeEdge>) {
685 (&mut self.vertices, &mut self.edges)
686 }
687 fn immutable_vertices_edges(&self) -> (&Vec<CodeVertex>, &Vec<CodeEdge>) {
688 (&self.vertices, &self.edges)
689 }
690}
691
692#[cfg(feature = "python_binding")]
693bind_trait_example_code! {PhenomenologicalPlanarCode}
694
695#[cfg_attr(feature = "python_binding", cfg_eval)]
696#[cfg_attr(feature = "python_binding", pymethods)]
697impl PhenomenologicalPlanarCode {
698 #[cfg_attr(feature = "python_binding", new)]
699 #[cfg_attr(feature = "python_binding", pyo3(signature = (d, noisy_measurements, p, max_half_weight = 500)))]
700 pub fn new(d: VertexNum, noisy_measurements: VertexNum, p: f64, max_half_weight: Weight) -> Self {
701 let mut code = Self::create_code(d, noisy_measurements);
702 code.set_probability(p);
703 code.compute_weights(max_half_weight);
704 code
705 }
706
707 #[cfg_attr(feature = "python_binding", staticmethod)]
708 #[allow(clippy::unnecessary_cast)]
709 pub fn create_code(d: VertexNum, noisy_measurements: VertexNum) -> Self {
710 assert!(d >= 3 && d % 2 == 1, "d must be odd integer >= 3");
711 let row_vertex_num = (d - 1) + 2; let t_vertex_num = row_vertex_num * d; let td = noisy_measurements + 1; let vertex_num = t_vertex_num * td; let mut edges = Vec::new();
717 for t in 0..td {
718 let t_bias = t * t_vertex_num;
719 for row in 0..d {
720 let bias = t_bias + row * row_vertex_num;
721 for i in 0..d - 1 {
722 edges.push(CodeEdge::new(bias + i, bias + i + 1));
723 }
724 edges.push(CodeEdge::new(bias, bias + d)); if row + 1 < d {
726 for i in 0..d - 1 {
727 edges.push(CodeEdge::new(bias + i, bias + i + row_vertex_num));
728 }
729 }
730 }
731 if t + 1 < td {
733 for row in 0..d {
734 let bias = t_bias + row * row_vertex_num;
735 for i in 0..d - 1 {
736 edges.push(CodeEdge::new(bias + i, bias + i + t_vertex_num));
737 }
738 }
739 }
740 }
741 let mut code = Self {
742 vertices: Vec::new(),
743 edges,
744 };
745 code.fill_vertices(vertex_num);
747 for t in 0..td {
748 let t_bias = t * t_vertex_num;
749 for row in 0..d {
750 let bias = t_bias + row * row_vertex_num;
751 code.vertices[(bias + d - 1) as usize].is_virtual = true;
752 code.vertices[(bias + d) as usize].is_virtual = true;
753 }
754 }
755 let mut positions = Vec::new();
756 for t in 0..td {
757 let pos_t = t as f64;
758 for row in 0..d {
759 let pos_i = row as f64;
760 for i in 0..d {
761 positions.push(VisualizePosition::new(pos_i, i as f64 + 0.5, pos_t));
762 }
763 positions.push(VisualizePosition::new(pos_i, -1. + 0.5, pos_t));
764 }
765 }
766 for (i, position) in positions.into_iter().enumerate() {
767 code.vertices[i].position = position;
768 }
769 code
770 }
771}
772
773#[derive(Clone, Debug)]
776#[cfg_attr(feature = "python_binding", cfg_eval)]
777#[cfg_attr(feature = "python_binding", pyclass)]
778pub struct CircuitLevelPlanarCode {
779 #[cfg_attr(feature = "python_binding", pyo3(get, set))]
781 pub vertices: Vec<CodeVertex>,
782 #[cfg_attr(feature = "python_binding", pyo3(get, set))]
784 pub edges: Vec<CodeEdge>,
785}
786
787impl ExampleCode for CircuitLevelPlanarCode {
788 fn vertices_edges(&mut self) -> (&mut Vec<CodeVertex>, &mut Vec<CodeEdge>) {
789 (&mut self.vertices, &mut self.edges)
790 }
791 fn immutable_vertices_edges(&self) -> (&Vec<CodeVertex>, &Vec<CodeEdge>) {
792 (&self.vertices, &self.edges)
793 }
794}
795
796#[cfg(feature = "python_binding")]
797bind_trait_example_code! {CircuitLevelPlanarCode}
798
799#[cfg_attr(feature = "python_binding", cfg_eval)]
800#[cfg_attr(feature = "python_binding", pymethods)]
801impl CircuitLevelPlanarCode {
802 #[cfg_attr(feature = "python_binding", new)]
804 #[cfg_attr(feature = "python_binding", pyo3(signature = (d, noisy_measurements, p, max_half_weight = 500)))]
805 pub fn new(d: VertexNum, noisy_measurements: VertexNum, p: f64, max_half_weight: Weight) -> Self {
806 Self::new_diagonal(d, noisy_measurements, p, max_half_weight, Some(p / 3.))
807 }
808
809 #[cfg_attr(feature = "python_binding", staticmethod)]
810 #[cfg_attr(feature = "python_binding", pyo3(signature = (d, noisy_measurements, p, max_half_weight = 500, diagonal_p = None)))]
811 #[allow(clippy::unnecessary_cast)]
812 pub fn new_diagonal(
813 d: VertexNum,
814 noisy_measurements: VertexNum,
815 p: f64,
816 max_half_weight: Weight,
817 diagonal_p: Option<f64>,
818 ) -> Self {
819 let mut code = Self::create_code(d, noisy_measurements);
820 code.set_probability(p);
821 if let Some(diagonal_p) = diagonal_p {
822 let (vertices, edges) = code.vertices_edges();
823 for edge in edges.iter_mut() {
824 let (v1, v2) = edge.vertices;
825 let v1p = &vertices[v1 as usize].position;
826 let v2p = &vertices[v2 as usize].position;
827 let manhattan_distance = (v1p.i - v2p.i).abs() + (v1p.j - v2p.j).abs() + (v1p.t - v2p.t).abs();
828 if manhattan_distance > 1. {
829 edge.p = diagonal_p;
830 }
831 }
832 }
833 code.compute_weights(max_half_weight);
834 code
835 }
836
837 #[cfg_attr(feature = "python_binding", staticmethod)]
838 #[allow(clippy::unnecessary_cast)]
839 pub fn create_code(d: VertexNum, noisy_measurements: VertexNum) -> Self {
840 assert!(d >= 3 && d % 2 == 1, "d must be odd integer >= 3");
841 let row_vertex_num = (d - 1) + 2; let t_vertex_num = row_vertex_num * d; let td = noisy_measurements + 1; let vertex_num = t_vertex_num * td; let mut edges = Vec::new();
847 for t in 0..td {
848 let t_bias = t * t_vertex_num;
849 for row in 0..d {
850 let bias = t_bias + row * row_vertex_num;
851 for i in 0..d - 1 {
852 edges.push(CodeEdge::new(bias + i, bias + i + 1));
853 }
854 edges.push(CodeEdge::new(bias, bias + d)); if row + 1 < d {
856 for i in 0..d - 1 {
857 edges.push(CodeEdge::new(bias + i, bias + i + row_vertex_num));
858 }
859 }
860 }
861 if t + 1 < td {
863 for row in 0..d {
864 let bias = t_bias + row * row_vertex_num;
865 for i in 0..d - 1 {
866 edges.push(CodeEdge::new(bias + i, bias + i + t_vertex_num));
867 let diagonal_diffs: Vec<(isize, isize)> = vec![(0, 1), (1, 0), (1, 1)];
868 for (di, dj) in diagonal_diffs {
869 let new_row = row as isize + di; let new_i = i as isize + dj; if new_row >= 0 && new_i >= 0 && new_row < d as isize && new_i < (d - 1) as isize {
872 let new_bias = t_bias + (new_row as VertexNum) * row_vertex_num + t_vertex_num;
873 edges.push(CodeEdge::new(bias + i, new_bias + new_i as VertexNum));
874 }
875 }
876 }
877 }
878 }
879 }
880 let mut code = Self {
881 vertices: Vec::new(),
882 edges,
883 };
884 code.fill_vertices(vertex_num);
886 for t in 0..td {
887 let t_bias = t * t_vertex_num;
888 for row in 0..d {
889 let bias = t_bias + row * row_vertex_num;
890 code.vertices[(bias + d - 1) as usize].is_virtual = true;
891 code.vertices[(bias + d) as usize].is_virtual = true;
892 }
893 }
894 let mut positions = Vec::new();
895 for t in 0..td {
896 let pos_t = t as f64;
897 for row in 0..d {
898 let pos_i = row as f64;
899 for i in 0..d {
900 positions.push(VisualizePosition::new(pos_i, i as f64 + 0.5, pos_t));
901 }
902 positions.push(VisualizePosition::new(pos_i, -1. + 0.5, pos_t));
903 }
904 }
905 for (i, position) in positions.into_iter().enumerate() {
906 code.vertices[i].position = position;
907 }
908 code
909 }
910}
911
912#[derive(Clone, Debug)]
914#[cfg_attr(feature = "python_binding", cfg_eval)]
915#[cfg_attr(feature = "python_binding", pyclass)]
916pub struct CodeCapacityRotatedCode {
917 #[cfg_attr(feature = "python_binding", pyo3(get, set))]
919 pub vertices: Vec<CodeVertex>,
920 #[cfg_attr(feature = "python_binding", pyo3(get, set))]
922 pub edges: Vec<CodeEdge>,
923}
924
925impl ExampleCode for CodeCapacityRotatedCode {
926 fn vertices_edges(&mut self) -> (&mut Vec<CodeVertex>, &mut Vec<CodeEdge>) {
927 (&mut self.vertices, &mut self.edges)
928 }
929 fn immutable_vertices_edges(&self) -> (&Vec<CodeVertex>, &Vec<CodeEdge>) {
930 (&self.vertices, &self.edges)
931 }
932}
933
934#[cfg(feature = "python_binding")]
935bind_trait_example_code! {CodeCapacityRotatedCode}
936
937#[cfg_attr(feature = "python_binding", cfg_eval)]
938#[cfg_attr(feature = "python_binding", pymethods)]
939impl CodeCapacityRotatedCode {
940 #[cfg_attr(feature = "python_binding", new)]
941 #[cfg_attr(feature = "python_binding", pyo3(signature = (d, p, max_half_weight = 500)))]
942 pub fn new(d: VertexNum, p: f64, max_half_weight: Weight) -> Self {
943 let mut code = Self::create_code(d);
944 code.set_probability(p);
945 code.compute_weights(max_half_weight);
946 code
947 }
948
949 #[cfg_attr(feature = "python_binding", staticmethod)]
950 #[allow(clippy::unnecessary_cast)]
951 pub fn create_code(d: VertexNum) -> Self {
952 assert!(d >= 3 && d % 2 == 1, "d must be odd integer >= 3");
953 let row_vertex_num = (d - 1) / 2 + 1; let vertex_num = row_vertex_num * (d + 1); let mut edges = Vec::new();
957 for row in 0..d {
958 let bias = row * row_vertex_num;
959 if row % 2 == 0 {
960 for i in 0..d {
961 if i % 2 == 0 {
962 edges.push(CodeEdge::new(bias + i / 2, bias + row_vertex_num + i / 2));
963 } else {
964 edges.push(CodeEdge::new(bias + (i - 1) / 2, bias + row_vertex_num + (i + 1) / 2));
965 }
966 }
967 } else {
968 for i in 0..d {
969 if i % 2 == 0 {
970 edges.push(CodeEdge::new(bias + i / 2, bias + row_vertex_num + i / 2));
971 } else {
972 edges.push(CodeEdge::new(bias + (i + 1) / 2, bias + row_vertex_num + (i - 1) / 2));
973 }
974 }
975 }
976 }
977 let mut code = Self {
978 vertices: Vec::new(),
979 edges,
980 };
981 code.fill_vertices(vertex_num);
983 for row in 0..d + 1 {
984 let bias = row * row_vertex_num;
985 if row % 2 == 0 {
986 code.vertices[(bias + row_vertex_num - 1) as usize].is_virtual = true;
987 } else {
988 code.vertices[(bias) as usize].is_virtual = true;
989 }
990 }
991 let mut positions = Vec::new();
992 for row in 0..d + 1 {
993 let pos_i = row as f64;
994 for i in 0..row_vertex_num {
995 let pos_bias = (row % 2 == 0) as VertexNum;
996 positions.push(VisualizePosition::new(pos_i, (i * 2 + pos_bias) as f64, 0.));
997 }
998 }
999 for (i, position) in positions.into_iter().enumerate() {
1000 code.vertices[i].position = position;
1001 }
1002 code
1003 }
1004}
1005
1006#[derive(Clone, Debug)]
1008#[cfg_attr(feature = "python_binding", cfg_eval)]
1009#[cfg_attr(feature = "python_binding", pyclass)]
1010pub struct PhenomenologicalRotatedCode {
1011 #[cfg_attr(feature = "python_binding", pyo3(get, set))]
1013 pub vertices: Vec<CodeVertex>,
1014 #[cfg_attr(feature = "python_binding", pyo3(get, set))]
1016 pub edges: Vec<CodeEdge>,
1017}
1018
1019impl ExampleCode for PhenomenologicalRotatedCode {
1020 fn vertices_edges(&mut self) -> (&mut Vec<CodeVertex>, &mut Vec<CodeEdge>) {
1021 (&mut self.vertices, &mut self.edges)
1022 }
1023 fn immutable_vertices_edges(&self) -> (&Vec<CodeVertex>, &Vec<CodeEdge>) {
1024 (&self.vertices, &self.edges)
1025 }
1026}
1027
1028#[cfg(feature = "python_binding")]
1029bind_trait_example_code! {PhenomenologicalRotatedCode}
1030
1031#[cfg_attr(feature = "python_binding", cfg_eval)]
1032#[cfg_attr(feature = "python_binding", pymethods)]
1033impl PhenomenologicalRotatedCode {
1034 #[cfg_attr(feature = "python_binding", new)]
1035 #[cfg_attr(feature = "python_binding", pyo3(signature = (d, noisy_measurements, p, max_half_weight = 500)))]
1036 pub fn new(d: VertexNum, noisy_measurements: VertexNum, p: f64, max_half_weight: Weight) -> Self {
1037 let mut code = Self::create_code(d, noisy_measurements);
1038 code.set_probability(p);
1039 code.compute_weights(max_half_weight);
1040 code
1041 }
1042
1043 #[cfg_attr(feature = "python_binding", staticmethod)]
1044 #[allow(clippy::unnecessary_cast)]
1045 pub fn create_code(d: VertexNum, noisy_measurements: VertexNum) -> Self {
1046 assert!(d >= 3 && d % 2 == 1, "d must be odd integer >= 3");
1047 let row_vertex_num = (d - 1) / 2 + 1; let t_vertex_num = row_vertex_num * (d + 1); let td = noisy_measurements + 1; let vertex_num = t_vertex_num * td; let mut edges = Vec::new();
1053 for t in 0..td {
1054 let t_bias = t * t_vertex_num;
1055 for row in 0..d {
1056 let bias = t_bias + row * row_vertex_num;
1057 if row % 2 == 0 {
1058 for i in 0..d {
1059 if i % 2 == 0 {
1060 edges.push(CodeEdge::new(bias + i / 2, bias + row_vertex_num + i / 2));
1061 } else {
1062 edges.push(CodeEdge::new(bias + (i - 1) / 2, bias + row_vertex_num + (i + 1) / 2));
1063 }
1064 }
1065 } else {
1066 for i in 0..d {
1067 if i % 2 == 0 {
1068 edges.push(CodeEdge::new(bias + i / 2, bias + row_vertex_num + i / 2));
1069 } else {
1070 edges.push(CodeEdge::new(bias + (i + 1) / 2, bias + row_vertex_num + (i - 1) / 2));
1071 }
1072 }
1073 }
1074 }
1075 if t + 1 < td {
1077 for row in 0..d + 1 {
1078 let bias = t_bias + row * row_vertex_num;
1079 for i in 0..row_vertex_num {
1080 edges.push(CodeEdge::new(bias + i, bias + i + t_vertex_num));
1081 }
1082 }
1083 }
1084 }
1085 let mut code = Self {
1086 vertices: Vec::new(),
1087 edges,
1088 };
1089 code.fill_vertices(vertex_num);
1091 for t in 0..td {
1092 let t_bias = t * t_vertex_num;
1093 for row in 0..d + 1 {
1094 let bias = t_bias + row * row_vertex_num;
1095 if row % 2 == 0 {
1096 code.vertices[(bias + row_vertex_num - 1) as usize].is_virtual = true;
1097 } else {
1098 code.vertices[(bias) as usize].is_virtual = true;
1099 }
1100 }
1101 }
1102 let mut positions = Vec::new();
1103 for t in 0..td {
1104 let pos_t = t as f64 * 2f64.sqrt();
1105 for row in 0..d + 1 {
1106 let pos_i = row as f64;
1107 for i in 0..row_vertex_num {
1108 let pos_bias = (row % 2 == 0) as VertexNum;
1109 positions.push(VisualizePosition::new(pos_i, (i * 2 + pos_bias) as f64, pos_t));
1110 }
1111 }
1112 }
1113 for (i, position) in positions.into_iter().enumerate() {
1114 code.vertices[i].position = position;
1115 }
1116 code
1117 }
1118}
1119
1120#[cfg(feature = "qecp_integrate")]
1122#[cfg_attr(feature = "python_binding", cfg_eval)]
1123#[cfg_attr(feature = "python_binding", pyclass)]
1124pub struct QECPlaygroundCode {
1125 simulator: qecp::simulator::Simulator,
1126 noise_model: std::sync::Arc<qecp::noise_model::NoiseModel>,
1127 adaptor: std::sync::Arc<qecp::decoder_fusion::FusionBlossomAdaptor>,
1128 vertex_index_map: std::sync::Arc<HashMap<usize, VertexIndex>>,
1129 edge_index_map: std::sync::Arc<HashMap<usize, EdgeIndex>>,
1130 #[cfg_attr(feature = "python_binding", pyo3(get, set))]
1132 pub vertices: Vec<CodeVertex>,
1133 #[cfg_attr(feature = "python_binding", pyo3(get, set))]
1135 pub edges: Vec<CodeEdge>,
1136}
1137
1138#[cfg(feature = "qecp_integrate")]
1139impl ExampleCode for QECPlaygroundCode {
1140 fn vertices_edges(&mut self) -> (&mut Vec<CodeVertex>, &mut Vec<CodeEdge>) {
1141 (&mut self.vertices, &mut self.edges)
1142 }
1143 fn immutable_vertices_edges(&self) -> (&Vec<CodeVertex>, &Vec<CodeEdge>) {
1144 (&self.vertices, &self.edges)
1145 }
1146 #[allow(clippy::unnecessary_cast)]
1148 fn generate_random_errors(&mut self, seed: u64) -> SyndromePattern {
1149 use qecp::simulator::SimulatorGenerics;
1150 let rng = qecp::reproducible_rand::Xoroshiro128StarStar::seed_from_u64(seed);
1151 self.simulator.set_rng(rng);
1152 let (error_count, erasure_count) = self.simulator.generate_random_errors(&self.noise_model);
1153 let sparse_detected_erasures = if erasure_count != 0 {
1154 self.simulator.generate_sparse_detected_erasures()
1155 } else {
1156 qecp::simulator::SparseErasures::new()
1157 };
1158 let sparse_measurement = if error_count != 0 {
1159 self.simulator.generate_sparse_measurement()
1160 } else {
1161 qecp::simulator::SparseMeasurement::new()
1162 };
1163 let syndrome_pattern = self
1164 .adaptor
1165 .generate_syndrome_pattern(&sparse_measurement, &sparse_detected_erasures);
1166 for vertex in self.vertices.iter_mut() {
1167 vertex.is_defect = false;
1168 }
1169 for &vertex_index in syndrome_pattern.defect_vertices.iter() {
1170 if let Some(new_index) = self.vertex_index_map.get(&vertex_index) {
1171 self.vertices[*new_index as usize].is_defect = true;
1172 }
1173 }
1174 for edge in self.edges.iter_mut() {
1175 edge.is_erasure = false;
1176 }
1177 for &edge_index in syndrome_pattern.erasures.iter() {
1178 if let Some(new_index) = self.edge_index_map.get(&edge_index) {
1179 self.edges[*new_index as usize].is_erasure = true;
1180 }
1181 }
1182 self.get_syndrome()
1183 }
1184}
1185
1186#[cfg(all(feature = "qecp_integrate", feature = "python_binding"))]
1187bind_trait_example_code! {QECPlaygroundCode}
1188
1189#[cfg(feature = "qecp_integrate")]
1190#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1191#[serde(deny_unknown_fields)]
1192pub struct QECPlaygroundCodeConfig {
1193 pub di: Option<usize>,
1195 pub dj: Option<usize>,
1196 pub nm: Option<usize>,
1197 #[serde(default = "qec_playground_default_configs::pe")]
1198 pub pe: f64,
1199 pub noise_model_modifier: Option<serde_json::Value>,
1200 #[serde(default = "qec_playground_default_configs::code_type")]
1201 pub code_type: qecp::code_builder::CodeType,
1202 #[serde(default = "qec_playground_default_configs::bias_eta")]
1203 pub bias_eta: f64,
1204 pub noise_model: Option<qecp::noise_model_builder::NoiseModelBuilder>,
1205 #[serde(default = "qec_playground_default_configs::noise_model_configuration")]
1206 pub noise_model_configuration: serde_json::Value,
1207 #[serde(default = "qec_playground_default_configs::parallel_init")]
1208 pub parallel_init: usize,
1209 #[serde(default = "qec_playground_default_configs::use_brief_edge")]
1210 pub use_brief_edge: bool,
1211 pub qubit_type: Option<qecp::types::QubitType>,
1213 #[serde(default = "qecp::decoder_fusion::fusion_default_configs::max_half_weight")]
1214 pub max_half_weight: usize,
1215 #[serde(default = "qec_playground_default_configs::trim_isolated_vertices")]
1216 pub trim_isolated_vertices: bool,
1217}
1218
1219#[cfg(feature = "qecp_integrate")]
1220pub mod qec_playground_default_configs {
1221 pub fn pe() -> f64 {
1222 0.
1223 }
1224 pub fn bias_eta() -> f64 {
1225 0.5
1226 }
1227 pub fn noise_model_configuration() -> serde_json::Value {
1228 json!({})
1229 }
1230 pub fn code_type() -> qecp::code_builder::CodeType {
1231 qecp::code_builder::CodeType::StandardPlanarCode
1232 }
1233 pub fn parallel_init() -> usize {
1234 1
1235 }
1236 pub fn use_brief_edge() -> bool {
1237 false
1238 }
1239 pub fn trim_isolated_vertices() -> bool {
1240 true
1241 }
1242}
1243
1244#[cfg(feature = "qecp_integrate")]
1245impl QECPlaygroundCode {
1246 #[allow(clippy::unnecessary_cast)]
1247 pub fn new(d: usize, p: f64, config: serde_json::Value) -> Self {
1248 let config: QECPlaygroundCodeConfig = serde_json::from_value(config).unwrap();
1249 let di = config.di.unwrap_or(d);
1250 let dj = config.dj.unwrap_or(d);
1251 let nm = config.nm.unwrap_or(d);
1252 let mut simulator = qecp::simulator::Simulator::new(config.code_type, qecp::code_builder::CodeSize::new(nm, di, dj));
1253 let mut noise_model = qecp::noise_model::NoiseModel::new(&simulator);
1254 let px = p / (1. + config.bias_eta) / 2.;
1255 let py = px;
1256 let pz = p - 2. * px;
1257 simulator.set_error_rates(&mut noise_model, px, py, pz, config.pe);
1258 if let Some(noise_model_builder) = &config.noise_model {
1260 noise_model_builder.apply(
1261 &mut simulator,
1262 &mut noise_model,
1263 &config.noise_model_configuration,
1264 p,
1265 config.bias_eta,
1266 config.pe,
1267 );
1268 }
1269 simulator.compress_error_rates(&mut noise_model); let noise_model = std::sync::Arc::new(noise_model);
1271 let fusion_decoder = qecp::decoder_fusion::FusionDecoder::new(
1273 &simulator,
1274 noise_model.clone(),
1275 &serde_json::from_value(json!({
1276 "max_half_weight": config.max_half_weight
1277 }))
1278 .unwrap(),
1279 config.parallel_init,
1280 config.use_brief_edge,
1281 );
1282 let adaptor = fusion_decoder.adaptor;
1283 let initializer = &adaptor.initializer;
1284 let positions = &adaptor.positions;
1285 let mut vertex_index_map = HashMap::new();
1286 let is_vertex_isolated = if config.trim_isolated_vertices {
1288 let mut is_vertex_isolated = vec![true; initializer.vertex_num];
1289 for (left_vertex, right_vertex, _) in initializer.weighted_edges.iter().cloned() {
1290 is_vertex_isolated[left_vertex] = false;
1291 is_vertex_isolated[right_vertex] = false;
1292 }
1293 is_vertex_isolated
1294 } else {
1295 vec![false; initializer.vertex_num]
1296 };
1297 for (vertex_index, is_isolated) in is_vertex_isolated.iter().cloned().enumerate() {
1298 let position = &adaptor.vertex_to_position_mapping[vertex_index];
1299 let qubit_type = simulator.get_node(position).as_ref().unwrap().qubit_type;
1300 if !config.qubit_type.is_some_and(|expect| expect != qubit_type) && !is_isolated {
1301 let new_index = vertex_index_map.len() as VertexIndex;
1302 vertex_index_map.insert(vertex_index, new_index);
1303 }
1304 }
1305 let mut code = Self {
1306 simulator,
1307 noise_model,
1308 adaptor: adaptor.clone(),
1309 vertex_index_map: std::sync::Arc::new(vertex_index_map),
1310 edge_index_map: std::sync::Arc::new(HashMap::new()), vertices: Vec::with_capacity(initializer.vertex_num),
1312 edges: Vec::with_capacity(initializer.weighted_edges.len()),
1313 };
1314 let mut edge_index_map = HashMap::new();
1315 for (edge_index, (left_vertex, right_vertex, weight)) in initializer.weighted_edges.iter().cloned().enumerate() {
1316 assert!(weight % 2 == 0, "weight must be even number");
1317 let contains_left = code.vertex_index_map.contains_key(&left_vertex);
1318 let contains_right = code.vertex_index_map.contains_key(&right_vertex);
1319 assert_eq!(contains_left, contains_right, "should not connect different type of qubits");
1320 if contains_left {
1321 let new_index = edge_index_map.len() as EdgeIndex;
1322 edge_index_map.insert(edge_index, new_index);
1323 code.edges.push(CodeEdge {
1324 vertices: (code.vertex_index_map[&left_vertex], code.vertex_index_map[&right_vertex]),
1325 p: 0., pe: 0., half_weight: (weight as Weight) / 2,
1328 is_erasure: false, });
1330 }
1331 }
1332 code.edge_index_map = std::sync::Arc::new(edge_index_map);
1333 code.fill_vertices(code.vertex_index_map.len() as VertexNum);
1335 for (vertex_index, position) in positions.iter().cloned().enumerate() {
1337 if let Some(new_index) = code.vertex_index_map.get(&vertex_index) {
1338 code.vertices[*new_index as usize].position = VisualizePosition::new(position.i, position.j, position.t);
1339 }
1340 }
1341 for vertex_index in initializer.virtual_vertices.iter() {
1342 if let Some(new_index) = code.vertex_index_map.get(vertex_index) {
1343 code.vertices[*new_index as usize].is_virtual = true;
1344 }
1345 }
1346 code
1347 }
1348}
1349
1350#[derive(Clone, Debug)]
1355#[cfg_attr(feature = "python_binding", cfg_eval)]
1356#[cfg_attr(feature = "python_binding", pyclass)]
1357pub struct ErrorPatternReader {
1358 #[cfg_attr(feature = "python_binding", pyo3(get, set))]
1360 pub vertices: Vec<CodeVertex>,
1361 #[cfg_attr(feature = "python_binding", pyo3(get, set))]
1363 pub edges: Vec<CodeEdge>,
1364 #[cfg_attr(feature = "python_binding", pyo3(get, set))]
1366 pub syndrome_patterns: Vec<SyndromePattern>,
1367 #[cfg_attr(feature = "python_binding", pyo3(get, set))]
1369 pub defect_index: usize,
1370 #[cfg_attr(feature = "python_binding", pyo3(get, set))]
1371 pub cyclic_syndrome: bool,
1372}
1373
1374impl ExampleCode for ErrorPatternReader {
1375 fn vertices_edges(&mut self) -> (&mut Vec<CodeVertex>, &mut Vec<CodeEdge>) {
1376 (&mut self.vertices, &mut self.edges)
1377 }
1378 fn immutable_vertices_edges(&self) -> (&Vec<CodeVertex>, &Vec<CodeEdge>) {
1379 (&self.vertices, &self.edges)
1380 }
1381 fn generate_random_errors(&mut self, _seed: u64) -> SyndromePattern {
1382 if self.cyclic_syndrome {
1383 if self.defect_index >= self.syndrome_patterns.len() {
1384 self.defect_index = 0; }
1386 } else {
1387 assert!(
1388 self.defect_index < self.syndrome_patterns.len(),
1389 "reading syndrome pattern more than in the file, consider generate the file with more data points"
1390 );
1391 }
1392 let syndrome_pattern = self.syndrome_patterns[self.defect_index].clone();
1393 self.defect_index += 1;
1394 syndrome_pattern
1395 }
1396}
1397
1398#[cfg_attr(feature = "python_binding", cfg_eval)]
1399#[cfg_attr(feature = "python_binding", pymethods)]
1400impl ErrorPatternReader {
1401 #[allow(clippy::unnecessary_cast)]
1402 #[cfg_attr(feature = "python_binding", new)]
1403 #[cfg_attr(feature = "python_binding", pyo3(signature = (filename, cyclic_syndrome = false)))]
1404 pub fn py_new(filename: String, cyclic_syndrome: bool) -> Self {
1405 Self::new(json!({
1406 "filename": filename,
1407 "cyclic_syndrome": cyclic_syndrome,
1408 }))
1409 }
1410}
1411
1412#[cfg(feature = "python_binding")]
1413bind_trait_example_code! {ErrorPatternReader}
1414
1415impl ErrorPatternReader {
1416 #[allow(clippy::unnecessary_cast)]
1417 pub fn new(mut config: serde_json::Value) -> Self {
1418 let mut filename = "tmp/syndrome_patterns.txt".to_string();
1419 let config = config.as_object_mut().expect("config must be JSON object");
1420 if let Some(value) = config.remove("filename") {
1421 filename = value.as_str().expect("filename string").to_string();
1422 }
1423 let cyclic_syndrome = if let Some(cyclic_syndrome) = config.remove("cyclic_syndrome") {
1424 cyclic_syndrome.as_bool().expect("cyclic_syndrome: bool")
1425 } else {
1426 false
1427 }; if !config.is_empty() {
1429 panic!("unknown config keys: {:?}", config.keys().collect::<Vec<&String>>());
1430 }
1431 let file = File::open(filename).unwrap();
1432 let mut syndrome_patterns = vec![];
1433 let mut initializer: Option<SolverInitializer> = None;
1434 let mut positions: Option<Vec<VisualizePosition>> = None;
1435 for (line_index, line) in io::BufReader::new(file).lines().enumerate() {
1436 if let Ok(value) = line {
1437 match line_index {
1438 0 => {
1439 assert!(value.starts_with("Syndrome Pattern v1.0 "), "incompatible file version");
1440 }
1441 1 => {
1442 initializer = Some(serde_json::from_str(&value).unwrap());
1443 }
1444 2 => {
1445 positions = Some(serde_json::from_str(&value).unwrap());
1446 }
1447 _ => {
1448 let syndrome_pattern: SyndromePattern = serde_json::from_str(&value).unwrap();
1449 syndrome_patterns.push(syndrome_pattern);
1450 }
1451 }
1452 }
1453 }
1454 let initializer = initializer.expect("initializer not present in file");
1455 let positions = positions.expect("positions not present in file");
1456 assert_eq!(positions.len(), initializer.vertex_num as usize);
1457 let mut code = Self {
1458 vertices: Vec::with_capacity(initializer.vertex_num as usize),
1459 edges: Vec::with_capacity(initializer.weighted_edges.len()),
1460 syndrome_patterns,
1461 defect_index: 0,
1462 cyclic_syndrome,
1463 };
1464 for (left_vertex, right_vertex, weight) in initializer.weighted_edges.iter() {
1465 assert!(weight % 2 == 0, "weight must be even number");
1466 code.edges.push(CodeEdge {
1467 vertices: (*left_vertex, *right_vertex),
1468 p: 0., pe: 0., half_weight: weight / 2,
1471 is_erasure: false, });
1473 }
1474 code.fill_vertices(initializer.vertex_num);
1476 for (vertex_index, position) in positions.into_iter().enumerate() {
1478 code.vertices[vertex_index].position = position;
1479 }
1480 for vertex_index in initializer.virtual_vertices {
1481 code.vertices[vertex_index as usize].is_virtual = true;
1482 }
1483 code
1484 }
1485}
1486
1487pub struct ExampleCodeParallel<CodeType: ExampleCode + Sync + Send + Clone> {
1489 pub example: CodeType,
1491 pub codes: Vec<ArcRwLock<CodeType>>,
1493 pub syndrome_patterns: Vec<SyndromePattern>,
1495 pub code_index: usize,
1497}
1498
1499impl<CodeType: ExampleCode + Sync + Send + Clone> ExampleCodeParallel<CodeType> {
1500 pub fn new(example: CodeType, code_count: usize) -> Self {
1501 let mut codes = vec![];
1502 for _ in 0..code_count {
1503 codes.push(ArcRwLock::<CodeType>::new_value(example.clone()));
1504 }
1505 Self {
1506 example,
1507 codes,
1508 syndrome_patterns: vec![],
1509 code_index: 0,
1510 }
1511 }
1512}
1513
1514impl<CodeType: ExampleCode + Sync + Send + Clone> ExampleCode for ExampleCodeParallel<CodeType> {
1515 fn vertices_edges(&mut self) -> (&mut Vec<CodeVertex>, &mut Vec<CodeEdge>) {
1516 self.example.vertices_edges()
1517 }
1518 fn immutable_vertices_edges(&self) -> (&Vec<CodeVertex>, &Vec<CodeEdge>) {
1519 self.example.immutable_vertices_edges()
1520 }
1521 fn generate_random_errors(&mut self, seed: u64) -> SyndromePattern {
1522 if self.code_index == 0 {
1523 (0..self.codes.len())
1525 .into_par_iter()
1526 .map(|code_index| {
1527 self.codes[code_index]
1528 .write()
1529 .generate_random_errors(seed + (code_index * 1_000_000_000) as u64)
1530 })
1531 .collect_into_vec(&mut self.syndrome_patterns);
1532 }
1533 let syndrome_pattern = self.syndrome_patterns[self.code_index].clone();
1534 self.code_index = (self.code_index + 1) % self.codes.len();
1535 syndrome_pattern
1536 }
1537}
1538
1539#[cfg(feature = "python_binding")]
1540#[pyfunction]
1541pub(crate) fn register(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
1542 m.add_class::<CodeVertex>()?;
1543 m.add_class::<CodeEdge>()?;
1544 m.add_function(wrap_pyfunction!(weight_of_p, m)?)?;
1545 m.add_class::<CodeCapacityRepetitionCode>()?;
1546 m.add_class::<CodeCapacityPlanarCode>()?;
1547 m.add_class::<PhenomenologicalPlanarCode>()?;
1548 m.add_class::<CircuitLevelPlanarCode>()?;
1549 m.add_class::<CodeCapacityRotatedCode>()?;
1550 m.add_class::<PhenomenologicalRotatedCode>()?;
1551 m.add_class::<ErrorPatternReader>()?;
1552 Ok(())
1553}
1554
1555pub fn visualize_code(code: &mut impl ExampleCode, visualize_filename: String) {
1556 print_visualize_link(visualize_filename.clone());
1557 let mut visualizer = Visualizer::new(
1558 Some(visualize_data_folder() + visualize_filename.as_str()),
1559 code.get_positions(),
1560 true,
1561 )
1562 .unwrap();
1563 visualizer.snapshot("code".to_string(), code).unwrap();
1564 for round in 0..3 {
1565 code.generate_random_errors(round);
1566 visualizer.snapshot(format!("syndrome {}", round + 1), code).unwrap();
1567 }
1568}
1569
1570#[cfg(test)]
1571mod tests {
1572 use super::*;
1573
1574 #[test]
1575 fn example_code_capacity_repetition_code() {
1576 let mut code = CodeCapacityRepetitionCode::new(7, 0.2, 500);
1578 code.sanity_check().unwrap();
1579 visualize_code(&mut code, "example_code_capacity_repetition_code.json".to_string());
1580 }
1581
1582 #[test]
1583 fn example_code_capacity_planar_code() {
1584 let mut code = CodeCapacityPlanarCode::new(7, 0.1, 500);
1586 code.sanity_check().unwrap();
1587 visualize_code(&mut code, "example_code_capacity_planar_code.json".to_string());
1588 }
1589
1590 #[test]
1591 fn example_phenomenological_planar_code() {
1592 let mut code = PhenomenologicalPlanarCode::new(7, 7, 0.01, 500);
1594 code.sanity_check().unwrap();
1595 visualize_code(&mut code, "example_phenomenological_planar_code.json".to_string());
1596 }
1597
1598 #[test]
1599 fn example_large_phenomenological_planar_code() {
1600 let mut code = PhenomenologicalPlanarCode::new(7, 30, 0.01, 500);
1602 code.sanity_check().unwrap();
1603 visualize_code(&mut code, "example_large_phenomenological_planar_code.json".to_string());
1604 }
1605
1606 #[test]
1607 fn example_circuit_level_planar_code() {
1608 let mut code = CircuitLevelPlanarCode::new(7, 7, 0.01, 500);
1610 code.sanity_check().unwrap();
1611 visualize_code(&mut code, "example_circuit_level_planar_code.json".to_string());
1612 }
1613
1614 #[test]
1615 fn example_code_capacity_rotated_code() {
1616 let mut code = CodeCapacityRotatedCode::new(5, 0.1, 500);
1618 code.sanity_check().unwrap();
1619 visualize_code(&mut code, "example_code_capacity_rotated_code.json".to_string());
1620 }
1621
1622 #[test]
1623 fn example_code_phenomenological_rotated_code() {
1624 let mut code = PhenomenologicalRotatedCode::new(5, 5, 0.01, 500);
1626 code.sanity_check().unwrap();
1627 visualize_code(&mut code, "example_code_phenomenological_rotated_code.json".to_string());
1628 }
1629
1630 #[cfg(feature = "qecp_integrate")]
1631 #[test]
1632 fn example_qec_playground_code() {
1633 let config = json!({
1635 "qubit_type": qecp::types::QubitType::StabZ,
1636 "max_half_weight": 50,
1637 });
1638 let mut code = QECPlaygroundCode::new(5, 0.001, config);
1639 code.sanity_check().unwrap();
1640 visualize_code(&mut code, "example_qec_playground_code.json".to_string());
1641 }
1642}