1#![cfg_attr(feature = "unsafe_pointer", feature(get_mut_unchecked))]
2#![cfg_attr(feature = "unsafe_pointer", allow(unused_mut))]
3#![cfg_attr(feature = "python_binding", feature(cfg_eval))]
4
5extern crate cfg_if;
6extern crate libc;
7extern crate parking_lot;
8extern crate priority_queue;
9extern crate rand_xoshiro;
10extern crate serde;
11#[macro_use]
12extern crate serde_json;
13extern crate chrono;
14extern crate clap;
15extern crate core_affinity;
16extern crate derivative;
17extern crate pbr;
18#[cfg(test)]
19extern crate petgraph;
20#[cfg(feature = "python_binding")]
21extern crate pyo3;
22#[cfg(feature = "qecp_integrate")]
23pub extern crate qecp;
24extern crate rand;
25extern crate rayon;
26extern crate urlencoding;
27extern crate weak_table;
28
29pub mod blossom_v;
30pub mod cli;
31pub mod complete_graph;
32pub mod dual_module;
33pub mod dual_module_parallel;
34pub mod dual_module_serial;
35pub mod example_codes;
36pub mod example_partition;
37pub mod mwpm_solver;
38pub mod pointers;
39pub mod primal_module;
40pub mod primal_module_parallel;
41pub mod primal_module_serial;
42pub mod util;
43pub mod visualize;
44#[cfg(feature = "python_binding")]
45use pyo3::prelude::*;
46
47use complete_graph::*;
48use util::*;
49
50#[cfg(feature = "python_binding")]
51#[pymodule]
52fn fusion_blossom(py: Python<'_>, m: &PyModule) -> PyResult<()> {
53 util::register(py, m)?;
54 mwpm_solver::register(py, m)?;
55 example_codes::register(py, m)?;
56 visualize::register(py, m)?;
57 primal_module::register(py, m)?;
58 let helper_code = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/helper.py"));
59 let helper_module = PyModule::from_code(py, helper_code, "helper", "helper")?;
60 helper_module.add("visualizer_website", generate_visualizer_website(py))?;
61 let bottle_code = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/bottle.py")); helper_module.add_submodule(PyModule::from_code(py, bottle_code, "bottle", "bottle")?)?;
63 m.add_submodule(helper_module)?;
64 let helper_register = helper_module.getattr("register")?;
65 helper_register.call1((m,))?;
66 Ok(())
67}
68
69#[allow(clippy::unnecessary_cast)]
71pub fn fusion_mwpm(initializer: &SolverInitializer, syndrome_pattern: &SyndromePattern) -> Vec<VertexIndex> {
72 assert!(initializer.vertex_num > 1, "at least one vertex required");
74 let max_safe_weight = ((Weight::MAX as usize) / initializer.vertex_num as usize) as Weight;
75 for (i, j, weight) in initializer.weighted_edges.iter() {
76 if weight > &max_safe_weight {
77 panic!(
78 "edge {}-{} has weight {} > max safe weight {}, it may cause fusion blossom to overflow",
79 i, j, weight, max_safe_weight
80 );
81 }
82 }
83 mwpm_solver::LegacySolverSerial::mwpm_solve(initializer, syndrome_pattern)
85}
86
87#[allow(clippy::unnecessary_cast)]
89pub fn blossom_v_mwpm(initializer: &SolverInitializer, defect_vertices: &[VertexIndex]) -> Vec<VertexIndex> {
90 if cfg!(not(feature = "blossom_v")) {
92 panic!("need blossom V library, see README.md")
93 }
94 assert!(initializer.vertex_num > 1, "at least one vertex required");
96 let max_safe_weight = ((i32::MAX as usize) / initializer.vertex_num as usize) as Weight;
97 for (i, j, weight) in initializer.weighted_edges.iter() {
98 if weight > &max_safe_weight {
99 panic!(
100 "edge {}-{} has weight {} > max safe weight {}, it may cause blossom V library to overflow",
101 i, j, weight, max_safe_weight
102 );
103 }
104 }
105 let mut complete_graph = CompleteGraph::new(initializer.vertex_num, &initializer.weighted_edges);
106 blossom_v_mwpm_reuse(&mut complete_graph, initializer, defect_vertices)
107}
108
109#[allow(clippy::unnecessary_cast)]
110pub fn blossom_v_mwpm_reuse(
111 complete_graph: &mut CompleteGraph,
112 initializer: &SolverInitializer,
113 defect_vertices: &[VertexIndex],
114) -> Vec<VertexIndex> {
115 let mut is_virtual: Vec<bool> = (0..initializer.vertex_num).map(|_| false).collect();
117 let mut is_defect: Vec<bool> = (0..initializer.vertex_num).map(|_| false).collect();
118 for &virtual_vertex in initializer.virtual_vertices.iter() {
119 assert!(virtual_vertex < initializer.vertex_num, "invalid input");
120 assert!(!is_virtual[virtual_vertex as usize], "same virtual vertex appears twice");
121 is_virtual[virtual_vertex as usize] = true;
122 }
123 let mut mapping_to_defect_vertices: Vec<usize> = (0..initializer.vertex_num).map(|_| usize::MAX).collect();
124 for (i, &defect_vertex) in defect_vertices.iter().enumerate() {
125 assert!(defect_vertex < initializer.vertex_num, "invalid input");
126 assert!(!is_virtual[defect_vertex as usize], "syndrome vertex cannot be virtual");
127 assert!(!is_defect[defect_vertex as usize], "same syndrome vertex appears twice");
128 is_defect[defect_vertex as usize] = true;
129 mapping_to_defect_vertices[defect_vertex as usize] = i;
130 }
131 let defect_num = defect_vertices.len();
133 let legacy_vertex_num = defect_num * 2;
134 let mut legacy_weighted_edges = Vec::<(usize, usize, u32)>::new();
135 let mut boundaries = Vec::<Option<(VertexIndex, Weight)>>::new();
136 for (i, &defect_vertex) in defect_vertices.iter().enumerate() {
137 let complete_graph_edges = complete_graph.all_edges(defect_vertex);
138 let mut boundary: Option<(VertexIndex, Weight)> = None;
139 for (&peer, &(_, weight)) in complete_graph_edges.iter() {
140 if is_virtual[peer as usize] && (boundary.is_none() || weight < boundary.as_ref().unwrap().1) {
141 boundary = Some((peer, weight));
142 }
143 }
144 if let Some((_, weight)) = boundary {
145 legacy_weighted_edges.push((i, i + defect_num, weight as u32));
147 }
148 boundaries.push(boundary); for (&peer, &(_, weight)) in complete_graph_edges.iter() {
150 if is_defect[peer as usize] {
151 let j = mapping_to_defect_vertices[peer as usize];
152 if i < j {
153 legacy_weighted_edges.push((i, j, weight as u32));
155 }
157 }
158 }
159 for j in (i + 1)..defect_num {
160 legacy_weighted_edges.push((i + defect_num, j + defect_num, 0));
162 }
163 }
164 let matchings = blossom_v::safe_minimum_weight_perfect_matching(legacy_vertex_num, &legacy_weighted_edges);
168 let mut mwpm_result = Vec::new();
169 for i in 0..defect_num {
170 let j = matchings[i];
171 if j < defect_num {
172 mwpm_result.push(defect_vertices[j]);
174 } else {
175 assert_eq!(
176 j,
177 i + defect_num,
178 "if not matched to another real vertex, it must match to it's corresponding virtual vertex"
179 );
180 mwpm_result.push(
181 boundaries[i]
182 .as_ref()
183 .expect("boundary must exist if match to virtual vertex")
184 .0,
185 );
186 }
187 }
188 mwpm_result
189}
190
191#[allow(dead_code)]
192#[derive(Debug, Clone)]
193pub struct DetailedMatching {
194 pub a: DefectIndex,
196 pub b: DefectIndex,
198 pub path: Vec<(DefectIndex, Weight)>,
200 pub weight: Weight,
202}
203
204#[allow(clippy::unnecessary_cast)]
206pub fn detailed_matching(
207 initializer: &SolverInitializer,
208 defect_vertices: &[DefectIndex],
209 mwpm_result: &[DefectIndex],
210) -> Vec<DetailedMatching> {
211 let defect_num = defect_vertices.len();
212 let mut is_defect: Vec<bool> = (0..initializer.vertex_num).map(|_| false).collect();
213 for &defect_vertex in defect_vertices.iter() {
214 assert!(defect_vertex < initializer.vertex_num, "invalid input");
215 assert!(!is_defect[defect_vertex as usize], "same syndrome vertex appears twice");
216 is_defect[defect_vertex as usize] = true;
217 }
218 assert_eq!(defect_num, mwpm_result.len(), "invalid mwpm result");
219 let mut complete_graph = complete_graph::CompleteGraph::new(initializer.vertex_num, &initializer.weighted_edges);
220 let mut details = Vec::new();
221 for i in 0..defect_num {
222 let a = defect_vertices[i];
223 let b = mwpm_result[i];
224 if !is_defect[b as usize] || a < b {
225 let (path, weight) = complete_graph.get_path(a, b);
226 let detail = DetailedMatching { a, b, path, weight };
227 details.push(detail);
228 }
229 }
230 details
231}
232
233#[cfg(feature = "python_binding")]
234macro_rules! include_visualize_file {
235 ($mapping:ident, $filepath:expr) => {
236 $mapping.insert($filepath.to_string(), include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/visualize/", $filepath)).to_string());
237 };
238 ($mapping:ident, $filepath:expr, $($other_filepath:expr),+) => {
239 include_visualize_file!($mapping, $filepath);
240 include_visualize_file!($mapping, $($other_filepath),+);
241 };
242}
243
244#[cfg(feature = "python_binding")]
245fn generate_visualizer_website(py: Python<'_>) -> &pyo3::types::PyDict {
246 use pyo3::types::IntoPyDict;
247 let mut mapping = std::collections::BTreeMap::<String, String>::new();
248 include_visualize_file!(
249 mapping,
250 "gui3d.js",
251 "index.js",
252 "patches.js",
253 "primal.js",
254 "cmd.js",
255 "mocker.js"
256 );
257 include_visualize_file!(mapping, "index.html", "partition-profile.html", "icon.svg");
258 include_visualize_file!(mapping, "package.json", "package-lock.json");
259 mapping.into_py_dict(py)
260}