fusion_blossom/
example_partition.rs

1//! Example Partition
2//!
3//! This module contains example partition for some of the example codes
4//!
5
6use super::example_codes::*;
7use super::util::*;
8use clap::Parser;
9use serde::Serialize;
10use std::collections::VecDeque;
11
12pub trait ExamplePartition {
13    /// customize partition, note that this process may re-order the vertices in `code`
14    fn build_apply(&mut self, code: &mut dyn ExampleCode) -> PartitionConfig {
15        // first apply reorder
16        if let Some(reordered_vertices) = self.build_reordered_vertices(code) {
17            code.reorder_vertices(&reordered_vertices);
18        }
19        self.build_partition(code)
20    }
21
22    fn re_index_defect_vertices(&mut self, code: &dyn ExampleCode, defect_vertices: &[VertexIndex]) -> Vec<VertexIndex> {
23        if let Some(reordered_vertices) = self.build_reordered_vertices(code) {
24            translated_defect_to_reordered(&reordered_vertices, defect_vertices)
25        } else {
26            defect_vertices.into()
27        }
28    }
29
30    /// build reorder vertices
31    fn build_reordered_vertices(&mut self, _code: &dyn ExampleCode) -> Option<Vec<VertexIndex>> {
32        None
33    }
34
35    /// build the partition, using the indices after reordered vertices
36    fn build_partition(&mut self, code: &dyn ExampleCode) -> PartitionConfig;
37}
38
39/// no partition
40pub struct NoPartition {}
41
42impl Default for NoPartition {
43    fn default() -> Self {
44        Self::new()
45    }
46}
47
48impl NoPartition {
49    pub fn new() -> Self {
50        Self {}
51    }
52}
53
54impl ExamplePartition for NoPartition {
55    fn build_partition(&mut self, code: &dyn ExampleCode) -> PartitionConfig {
56        PartitionConfig::new(code.vertex_num())
57    }
58}
59
60/// partition into top half and bottom half
61#[derive(Default)]
62pub struct CodeCapacityPlanarCodeVerticalPartitionHalf {
63    d: VertexNum,
64    /// the row of splitting: in the visualization tool, the top row is the 1st row, the bottom row is the d-th row
65    partition_row: VertexNum,
66}
67
68impl CodeCapacityPlanarCodeVerticalPartitionHalf {
69    pub fn new(d: VertexNum, partition_row: VertexNum) -> Self {
70        Self { d, partition_row }
71    }
72}
73
74impl ExamplePartition for CodeCapacityPlanarCodeVerticalPartitionHalf {
75    fn build_partition(&mut self, code: &dyn ExampleCode) -> PartitionConfig {
76        let (d, partition_row) = (self.d, self.partition_row);
77        assert_eq!(code.vertex_num(), d * (d + 1), "code size incompatible");
78        let mut config = PartitionConfig::new(code.vertex_num());
79        assert!(partition_row > 1 && partition_row < d);
80        config.partitions = vec![
81            VertexRange::new(0, (partition_row - 1) * (d + 1)),
82            VertexRange::new(partition_row * (d + 1), d * (d + 1)),
83        ];
84        config.fusions = vec![(0, 1)];
85        config
86    }
87}
88
89/// partition into top half and bottom half
90#[derive(Default)]
91pub struct CodeCapacityRotatedCodeVerticalPartitionHalf {
92    d: VertexNum,
93    /// the row of splitting: in the visualization tool, the top row is the 1st row, the bottom row is the d-th row
94    partition_row: VertexNum,
95}
96
97impl CodeCapacityRotatedCodeVerticalPartitionHalf {
98    pub fn new(d: VertexNum, partition_row: VertexNum) -> Self {
99        Self { d, partition_row }
100    }
101}
102
103impl ExamplePartition for CodeCapacityRotatedCodeVerticalPartitionHalf {
104    fn build_partition(&mut self, code: &dyn ExampleCode) -> PartitionConfig {
105        let (d, partition_row) = (self.d, self.partition_row);
106        let row_vertex_num = (d - 1) / 2 + 1;
107        assert_eq!(code.vertex_num(), row_vertex_num * (d + 1), "code size incompatible");
108        let mut config = PartitionConfig::new(code.vertex_num());
109        assert!(partition_row >= 1 && partition_row < d);
110        config.partitions = vec![
111            VertexRange::new(0, partition_row * row_vertex_num),
112            VertexRange::new((partition_row + 1) * row_vertex_num, row_vertex_num * (d + 1)),
113        ];
114        config.fusions = vec![(0, 1)];
115        config
116    }
117}
118
119/// partition into 4 pieces: top left and right, bottom left and right
120#[derive(Default)]
121pub struct CodeCapacityPlanarCodeVerticalPartitionFour {
122    d: VertexNum,
123    /// the row of splitting: in the visualization tool, the top row is the 1st row, the bottom row is the d-th row
124    partition_row: VertexNum,
125    /// the row of splitting: in the visualization tool, the left (non-virtual) column is the 1st column, the right (non-virtual) column is the (d-1)-th column
126    partition_column: VertexNum,
127}
128
129impl CodeCapacityPlanarCodeVerticalPartitionFour {
130    pub fn new(d: VertexNum, partition_row: VertexNum, partition_column: VertexNum) -> Self {
131        Self {
132            d,
133            partition_row,
134            partition_column,
135        }
136    }
137}
138
139impl ExamplePartition for CodeCapacityPlanarCodeVerticalPartitionFour {
140    fn build_reordered_vertices(&mut self, code: &dyn ExampleCode) -> Option<Vec<VertexIndex>> {
141        let (d, partition_row, partition_column) = (self.d, self.partition_row, self.partition_column);
142        assert_eq!(code.vertex_num(), d * (d + 1), "code size incompatible");
143        assert!(partition_row > 1 && partition_row < d);
144        let mut reordered_vertices = vec![];
145        let split_horizontal = partition_row - 1;
146        let split_vertical = partition_column - 1;
147        for i in 0..split_horizontal {
148            // left-top block
149            for j in 0..split_vertical {
150                reordered_vertices.push(i * (d + 1) + j);
151            }
152            reordered_vertices.push(i * (d + 1) + d);
153        }
154        for i in 0..split_horizontal {
155            // interface between the left-top block and the right-top block
156            reordered_vertices.push(i * (d + 1) + split_vertical);
157        }
158        for i in 0..split_horizontal {
159            // right-top block
160            for j in (split_vertical + 1)..d {
161                reordered_vertices.push(i * (d + 1) + j);
162            }
163        }
164        {
165            // the big interface between top and bottom
166            for j in 0..(d + 1) {
167                reordered_vertices.push(split_horizontal * (d + 1) + j);
168            }
169        }
170        for i in (split_horizontal + 1)..d {
171            // left-bottom block
172            for j in 0..split_vertical {
173                reordered_vertices.push(i * (d + 1) + j);
174            }
175            reordered_vertices.push(i * (d + 1) + d);
176        }
177        for i in (split_horizontal + 1)..d {
178            // interface between the left-bottom block and the right-bottom block
179            reordered_vertices.push(i * (d + 1) + split_vertical);
180        }
181        for i in (split_horizontal + 1)..d {
182            // right-bottom block
183            for j in (split_vertical + 1)..d {
184                reordered_vertices.push(i * (d + 1) + j);
185            }
186        }
187        Some(reordered_vertices)
188    }
189    fn build_partition(&mut self, _code: &dyn ExampleCode) -> PartitionConfig {
190        let (d, partition_row, partition_column) = (self.d, self.partition_row, self.partition_column);
191        let mut config = PartitionConfig::new(d * (d + 1));
192        let b0_count = (partition_row - 1) * partition_column;
193        let b1_count = (partition_row - 1) * (d - partition_column);
194        let b2_count = (d - partition_row) * partition_column;
195        let b3_count = (d - partition_row) * (d - partition_column);
196        config.partitions = vec![
197            VertexRange::new_length(0, b0_count),
198            VertexRange::new_length(b0_count + (partition_row - 1), b1_count),
199            VertexRange::new_length(partition_row * (d + 1), b2_count),
200            VertexRange::new_length(partition_row * (d + 1) + b2_count + (d - partition_row), b3_count),
201        ];
202        config.fusions = vec![(0, 1), (2, 3), (4, 5)];
203        config
204    }
205}
206
207/// partition into top half and bottom half
208#[derive(Default)]
209pub struct CodeCapacityRepetitionCodePartitionHalf {
210    d: VertexNum,
211    /// the position of splitting: in the visualization tool, the left (non-virtual) vertex is the 1st column, the right (non-virtual) vertex is the (d-1)-th column
212    partition_index: VertexIndex,
213}
214
215impl CodeCapacityRepetitionCodePartitionHalf {
216    pub fn new(d: VertexNum, partition_index: VertexIndex) -> Self {
217        Self { d, partition_index }
218    }
219}
220
221impl ExamplePartition for CodeCapacityRepetitionCodePartitionHalf {
222    fn build_reordered_vertices(&mut self, code: &dyn ExampleCode) -> Option<Vec<VertexIndex>> {
223        let (d, partition_index) = (self.d, self.partition_index);
224        assert_eq!(code.vertex_num(), d + 1, "code size incompatible");
225        assert!(partition_index > 1 && partition_index < d);
226        let mut reordered_vertices = vec![];
227        let split_vertical = partition_index - 1;
228        for j in 0..split_vertical {
229            reordered_vertices.push(j);
230        }
231        reordered_vertices.push(d);
232        for j in split_vertical..d {
233            reordered_vertices.push(j);
234        }
235        Some(reordered_vertices)
236    }
237    fn build_partition(&mut self, _code: &dyn ExampleCode) -> PartitionConfig {
238        let (d, partition_index) = (self.d, self.partition_index);
239        let mut config = PartitionConfig::new(d + 1);
240        config.partitions = vec![
241            VertexRange::new(0, partition_index),
242            VertexRange::new(partition_index + 1, d + 1),
243        ];
244        config.fusions = vec![(0, 1)];
245        config
246    }
247}
248
249/// evenly partition along the time axis
250pub struct PhenomenologicalPlanarCodeTimePartition {
251    d: VertexNum,
252    noisy_measurements: VertexNum,
253    /// the number of partition
254    partition_num: usize,
255    /// enable tree fusion (to minimize latency but incur log(partition_num) more memory copy)
256    enable_tree_fusion: bool,
257    /// maximum amount of tree leaf; if the total partition is greater than this, it will be cut into multiple regions and each region is a separate tree;
258    /// those trees are then fused sequentially
259    maximum_tree_leaf_size: usize,
260}
261
262impl PhenomenologicalPlanarCodeTimePartition {
263    pub fn new_tree(
264        d: VertexNum,
265        noisy_measurements: VertexNum,
266        partition_num: usize,
267        enable_tree_fusion: bool,
268        maximum_tree_leaf_size: usize,
269    ) -> Self {
270        Self {
271            d,
272            noisy_measurements,
273            partition_num,
274            enable_tree_fusion,
275            maximum_tree_leaf_size,
276        }
277    }
278    pub fn new(d: VertexNum, noisy_measurements: VertexNum, partition_num: usize) -> Self {
279        Self::new_tree(d, noisy_measurements, partition_num, false, usize::MAX)
280    }
281}
282
283impl ExamplePartition for PhenomenologicalPlanarCodeTimePartition {
284    #[allow(clippy::unnecessary_cast)]
285    fn build_partition(&mut self, code: &dyn ExampleCode) -> PartitionConfig {
286        let (d, noisy_measurements, partition_num) = (self.d, self.noisy_measurements, self.partition_num);
287        let round_vertex_num = d * (d + 1);
288        let vertex_num = round_vertex_num * (noisy_measurements + 1);
289        assert_eq!(code.vertex_num(), vertex_num, "code size incompatible");
290        assert!(partition_num >= 1 && partition_num <= noisy_measurements as usize + 1);
291        // do not use fixed partition_length, because it would introduce super long partition; do it on the fly
292        let mut config = PartitionConfig::new(vertex_num);
293        config.partitions.clear();
294        for partition_index in 0..partition_num as VertexIndex {
295            let start_round_index = partition_index * (noisy_measurements + 1) / partition_num as VertexNum;
296            let end_round_index = (partition_index + 1) * (noisy_measurements + 1) / partition_num as VertexNum;
297            assert!(end_round_index > start_round_index, "empty partition occurs");
298            if partition_index == 0 {
299                config.partitions.push(VertexRange::new(
300                    start_round_index * round_vertex_num,
301                    end_round_index * round_vertex_num,
302                ));
303            } else {
304                config.partitions.push(VertexRange::new(
305                    (start_round_index + 1) * round_vertex_num,
306                    end_round_index * round_vertex_num,
307                ));
308            }
309        }
310        config.fusions.clear();
311        if !self.enable_tree_fusion || self.maximum_tree_leaf_size == 1 {
312            for unit_index in partition_num..(2 * partition_num - 1) {
313                if unit_index == partition_num {
314                    config.fusions.push((0, 1));
315                } else {
316                    config.fusions.push((unit_index as usize - 1, unit_index - partition_num + 1));
317                }
318            }
319        } else {
320            let mut whole_ranges = vec![];
321            let mut left_right_leaf = vec![];
322            for (unit_index, partition) in config.partitions.iter().enumerate() {
323                assert!(
324                    partition.end() <= vertex_num,
325                    "invalid vertex index {} in partitions",
326                    partition.end()
327                );
328                whole_ranges.push(*partition);
329                left_right_leaf.push((unit_index, unit_index));
330            }
331            // first cut into multiple regions
332            let region_count = if config.partitions.len() <= self.maximum_tree_leaf_size {
333                1
334            } else {
335                (config.partitions.len() + self.maximum_tree_leaf_size - 1) / self.maximum_tree_leaf_size
336            };
337            let mut last_sequential_unit: Option<usize> = None;
338            for region_index in 0..region_count {
339                let region_start = region_index * self.maximum_tree_leaf_size;
340                let region_end = std::cmp::min((region_index + 1) * self.maximum_tree_leaf_size, config.partitions.len());
341                // build the local tree
342                let mut pending_fusion = VecDeque::new();
343                for unit_index in region_start..region_end {
344                    pending_fusion.push_back(unit_index);
345                }
346                let local_fusion_start_index = whole_ranges.len();
347                for unit_index in local_fusion_start_index..(local_fusion_start_index + region_end - region_start - 1) {
348                    let mut unit_index_1 = pending_fusion.pop_front().unwrap();
349                    // iterate over all pending fusions to find a neighboring one
350                    for i in 0..pending_fusion.len() {
351                        let mut unit_index_2 = pending_fusion[i];
352                        let is_neighbor = left_right_leaf[unit_index_1].0 == left_right_leaf[unit_index_2].1 + 1
353                            || left_right_leaf[unit_index_2].0 == left_right_leaf[unit_index_1].1 + 1;
354                        if is_neighbor {
355                            pending_fusion.remove(i);
356                            if whole_ranges[unit_index_1].start() > whole_ranges[unit_index_2].start() {
357                                (unit_index_1, unit_index_2) = (unit_index_2, unit_index_1);
358                                // only lower range can fuse higher range
359                            }
360                            config.fusions.push((unit_index_1, unit_index_2));
361                            pending_fusion.push_back(unit_index);
362                            // println!("unit_index_1: {unit_index_1} {:?}, unit_index_2: {unit_index_2} {:?}", whole_ranges[unit_index_1], whole_ranges[unit_index_2]);
363                            let (whole_range, _) = whole_ranges[unit_index_1].fuse(&whole_ranges[unit_index_2]);
364                            whole_ranges.push(whole_range);
365                            left_right_leaf.push((left_right_leaf[unit_index_1].0, left_right_leaf[unit_index_2].1));
366                            break;
367                        }
368                        assert!(i != pending_fusion.len() - 1, "unreachable: cannot find a neighbor");
369                    }
370                }
371                assert!(pending_fusion.len() == 1, "only the final unit is left");
372                let tree_root_unit_index = pending_fusion.pop_front().unwrap();
373                if let Some(last_sequential_unit) = last_sequential_unit.as_mut() {
374                    config.fusions.push((*last_sequential_unit, tree_root_unit_index));
375                    let (whole_range, _) = whole_ranges[*last_sequential_unit].fuse(&whole_ranges[tree_root_unit_index]);
376                    whole_ranges.push(whole_range);
377                    left_right_leaf.push((
378                        left_right_leaf[*last_sequential_unit].0,
379                        left_right_leaf[tree_root_unit_index].1,
380                    ));
381                    *last_sequential_unit = tree_root_unit_index + 1;
382                } else {
383                    last_sequential_unit = Some(tree_root_unit_index);
384                }
385            }
386        }
387        config
388    }
389}
390
391/// evenly partition along the time axis
392#[derive(Parser, Clone, Serialize)]
393pub struct PhenomenologicalRotatedCodeTimePartition {
394    /// code distance
395    #[clap(value_parser)]
396    pub d: VertexNum,
397    /// rounds of noisy measurement, valid only when multiple rounds
398    #[clap(value_parser)]
399    pub noisy_measurements: VertexNum,
400    /// the number of partition
401    #[clap(value_parser)]
402    pub partition_num: usize,
403    /// enable tree fusion (to minimize latency but incur log(partition_num) more memory copy)
404    #[clap(short = 't', long, default_value_t = false)]
405    pub enable_tree_fusion: bool,
406    /// maximum amount of tree leaf; if the total partition is greater than this, it will be cut into multiple regions and each region is a separate tree;
407    /// those trees are then fused sequentially
408    #[clap(short = 'l', long, default_value_t = usize::MAX)]
409    pub maximum_tree_leaf_size: usize,
410}
411
412impl PhenomenologicalRotatedCodeTimePartition {
413    pub fn new_tree(
414        d: VertexNum,
415        noisy_measurements: VertexNum,
416        partition_num: usize,
417        enable_tree_fusion: bool,
418        maximum_tree_leaf_size: usize,
419    ) -> Self {
420        Self {
421            d,
422            noisy_measurements,
423            partition_num,
424            enable_tree_fusion,
425            maximum_tree_leaf_size,
426        }
427    }
428    pub fn new(d: VertexNum, noisy_measurements: VertexNum, partition_num: usize) -> Self {
429        Self::new_tree(d, noisy_measurements, partition_num, false, usize::MAX)
430    }
431}
432
433impl ExamplePartition for PhenomenologicalRotatedCodeTimePartition {
434    #[allow(clippy::unnecessary_cast)]
435    fn build_partition(&mut self, code: &dyn ExampleCode) -> PartitionConfig {
436        let (d, noisy_measurements, partition_num) = (self.d, self.noisy_measurements, self.partition_num);
437        let row_vertex_num = (d - 1) / 2 + 1;
438        let round_vertex_num = row_vertex_num * (d + 1);
439        let vertex_num = round_vertex_num * (noisy_measurements + 1);
440        assert_eq!(code.vertex_num(), vertex_num, "code size incompatible");
441        assert!(partition_num >= 1 && partition_num <= noisy_measurements as usize + 1);
442        // do not use fixed partition_length, because it would introduce super long partition; do it on the fly
443        let mut config = PartitionConfig::new(vertex_num);
444        config.partitions.clear();
445        for partition_index in 0..partition_num as VertexIndex {
446            let start_round_index = partition_index * (noisy_measurements + 1) / partition_num as VertexNum;
447            let end_round_index = (partition_index + 1) * (noisy_measurements + 1) / partition_num as VertexNum;
448            assert!(end_round_index > start_round_index, "empty partition occurs");
449            if partition_index == 0 {
450                config.partitions.push(VertexRange::new(
451                    start_round_index * round_vertex_num,
452                    end_round_index * round_vertex_num,
453                ));
454            } else {
455                config.partitions.push(VertexRange::new(
456                    (start_round_index + 1) * round_vertex_num,
457                    end_round_index * round_vertex_num,
458                ));
459            }
460        }
461        config.fusions.clear();
462        if !self.enable_tree_fusion || self.maximum_tree_leaf_size == 1 {
463            for unit_index in partition_num..(2 * partition_num - 1) {
464                if unit_index == partition_num {
465                    config.fusions.push((0, 1));
466                } else {
467                    config.fusions.push((unit_index as usize - 1, unit_index - partition_num + 1));
468                }
469            }
470        } else {
471            let mut whole_ranges = vec![];
472            let mut left_right_leaf = vec![];
473            for (unit_index, partition) in config.partitions.iter().enumerate() {
474                assert!(
475                    partition.end() <= vertex_num,
476                    "invalid vertex index {} in partitions",
477                    partition.end()
478                );
479                whole_ranges.push(*partition);
480                left_right_leaf.push((unit_index, unit_index));
481            }
482            // first cut into multiple regions
483            let region_count = if config.partitions.len() <= self.maximum_tree_leaf_size {
484                1
485            } else {
486                (config.partitions.len() + self.maximum_tree_leaf_size - 1) / self.maximum_tree_leaf_size
487            };
488            let mut last_sequential_unit: Option<usize> = None;
489            for region_index in 0..region_count {
490                let region_start = region_index * self.maximum_tree_leaf_size;
491                let region_end = std::cmp::min((region_index + 1) * self.maximum_tree_leaf_size, config.partitions.len());
492                // build the local tree
493                let mut pending_fusion = VecDeque::new();
494                for unit_index in region_start..region_end {
495                    pending_fusion.push_back(unit_index);
496                }
497                let local_fusion_start_index = whole_ranges.len();
498                for unit_index in local_fusion_start_index..(local_fusion_start_index + region_end - region_start - 1) {
499                    let mut unit_index_1 = pending_fusion.pop_front().unwrap();
500                    // iterate over all pending fusions to find a neighboring one
501                    for i in 0..pending_fusion.len() {
502                        let mut unit_index_2 = pending_fusion[i];
503                        let is_neighbor = left_right_leaf[unit_index_1].0 == left_right_leaf[unit_index_2].1 + 1
504                            || left_right_leaf[unit_index_2].0 == left_right_leaf[unit_index_1].1 + 1;
505                        if is_neighbor {
506                            pending_fusion.remove(i);
507                            if whole_ranges[unit_index_1].start() > whole_ranges[unit_index_2].start() {
508                                (unit_index_1, unit_index_2) = (unit_index_2, unit_index_1);
509                                // only lower range can fuse higher range
510                            }
511                            config.fusions.push((unit_index_1, unit_index_2));
512                            pending_fusion.push_back(unit_index);
513                            // println!("unit_index_1: {unit_index_1} {:?}, unit_index_2: {unit_index_2} {:?}", whole_ranges[unit_index_1], whole_ranges[unit_index_2]);
514                            let (whole_range, _) = whole_ranges[unit_index_1].fuse(&whole_ranges[unit_index_2]);
515                            whole_ranges.push(whole_range);
516                            left_right_leaf.push((left_right_leaf[unit_index_1].0, left_right_leaf[unit_index_2].1));
517                            break;
518                        }
519                        assert!(i != pending_fusion.len() - 1, "unreachable: cannot find a neighbor");
520                    }
521                }
522                assert!(pending_fusion.len() == 1, "only the final unit is left");
523                let tree_root_unit_index = pending_fusion.pop_front().unwrap();
524                if let Some(last_sequential_unit) = last_sequential_unit.as_mut() {
525                    config.fusions.push((*last_sequential_unit, tree_root_unit_index));
526                    let (whole_range, _) = whole_ranges[*last_sequential_unit].fuse(&whole_ranges[tree_root_unit_index]);
527                    whole_ranges.push(whole_range);
528                    left_right_leaf.push((
529                        left_right_leaf[*last_sequential_unit].0,
530                        left_right_leaf[tree_root_unit_index].1,
531                    ));
532                    *last_sequential_unit = tree_root_unit_index + 1;
533                } else {
534                    last_sequential_unit = Some(tree_root_unit_index);
535                }
536            }
537        }
538        config
539    }
540}
541
542#[cfg(test)]
543pub mod tests {
544    use super::super::dual_module::*;
545    use super::super::dual_module_parallel::*;
546    use super::super::dual_module_serial::*;
547    #[cfg(feature = "unsafe_pointer")]
548    use super::super::pointers::UnsafePtr;
549    use super::super::primal_module::*;
550    use super::super::primal_module_parallel::*;
551    use super::super::visualize::*;
552    use super::*;
553
554    pub fn example_partition_basic_standard_syndrome_optional_viz(
555        code: &mut dyn ExampleCode,
556        visualize_filename: Option<String>,
557        mut defect_vertices: Vec<VertexIndex>,
558        re_index_syndrome: bool,
559        final_dual: Weight,
560        mut partition: impl ExamplePartition,
561    ) -> (PrimalModuleParallel, DualModuleParallel<DualModuleSerial>) {
562        println!("{defect_vertices:?}");
563        if re_index_syndrome {
564            defect_vertices = partition.re_index_defect_vertices(code, &defect_vertices);
565        }
566        let partition_config = partition.build_apply(code);
567        let mut visualizer = match visualize_filename.as_ref() {
568            Some(visualize_filename) => {
569                let visualizer = Visualizer::new(
570                    Some(visualize_data_folder() + visualize_filename.as_str()),
571                    code.get_positions(),
572                    true,
573                )
574                .unwrap();
575                print_visualize_link(visualize_filename.clone());
576                Some(visualizer)
577            }
578            None => None,
579        };
580        let initializer = code.get_initializer();
581        let partition_info = partition_config.info();
582        let mut dual_module =
583            DualModuleParallel::new_config(&initializer, &partition_info, DualModuleParallelConfig::default());
584        let primal_config = PrimalModuleParallelConfig {
585            debug_sequential: true,
586            ..Default::default()
587        };
588        let mut primal_module = PrimalModuleParallel::new_config(&initializer, &partition_info, primal_config);
589        code.set_defect_vertices(&defect_vertices);
590        primal_module.parallel_solve_visualizer(&code.get_syndrome(), &dual_module, visualizer.as_mut());
591        let useless_interface_ptr = DualModuleInterfacePtr::new_empty(); // don't actually use it
592        let perfect_matching = primal_module.perfect_matching(&useless_interface_ptr, &mut dual_module);
593        let mut subgraph_builder = SubGraphBuilder::new(&initializer);
594        subgraph_builder.load_perfect_matching(&perfect_matching);
595        let subgraph = subgraph_builder.get_subgraph();
596        if let Some(visualizer) = visualizer.as_mut() {
597            let last_interface_ptr = &primal_module.units.last().unwrap().read_recursive().interface_ptr;
598            visualizer
599                .snapshot_combined(
600                    "perfect matching and subgraph".to_string(),
601                    vec![
602                        last_interface_ptr,
603                        &dual_module,
604                        &perfect_matching,
605                        &VisualizeSubgraph::new(&subgraph),
606                    ],
607                )
608                .unwrap();
609        }
610        let sum_dual_variables = primal_module
611            .units
612            .last()
613            .unwrap()
614            .read_recursive()
615            .interface_ptr
616            .sum_dual_variables();
617        assert_eq!(
618            sum_dual_variables,
619            subgraph_builder.total_weight(),
620            "unmatched sum dual variables"
621        );
622        assert_eq!(sum_dual_variables, final_dual * 2, "unexpected final dual variable sum");
623        (primal_module, dual_module)
624    }
625
626    pub fn example_partition_standard_syndrome(
627        code: &mut dyn ExampleCode,
628        visualize_filename: String,
629        defect_vertices: Vec<VertexIndex>,
630        re_index_syndrome: bool,
631        final_dual: Weight,
632        partition: impl ExamplePartition,
633    ) -> (PrimalModuleParallel, DualModuleParallel<DualModuleSerial>) {
634        example_partition_basic_standard_syndrome_optional_viz(
635            code,
636            Some(visualize_filename),
637            defect_vertices,
638            re_index_syndrome,
639            final_dual,
640            partition,
641        )
642    }
643
644    /// test a simple case
645    #[test]
646    fn example_partition_basic_1() {
647        // cargo test example_partition_basic_1 -- --nocapture
648        let visualize_filename = "example_partition_basic_1.json".to_string();
649        let defect_vertices = vec![39, 52, 63, 90, 100];
650        let half_weight = 500;
651        example_partition_standard_syndrome(
652            &mut CodeCapacityPlanarCode::new(11, 0.1, half_weight),
653            visualize_filename,
654            defect_vertices,
655            true,
656            9 * half_weight,
657            NoPartition::new(),
658        );
659    }
660
661    /// split into 2
662    #[test]
663    fn example_partition_basic_2() {
664        // cargo test example_partition_basic_2 -- --nocapture
665        let visualize_filename = "example_partition_basic_2.json".to_string();
666        let defect_vertices = vec![39, 52, 63, 90, 100];
667        let half_weight = 500;
668        example_partition_standard_syndrome(
669            &mut CodeCapacityPlanarCode::new(11, 0.1, half_weight),
670            visualize_filename,
671            defect_vertices,
672            true,
673            9 * half_weight,
674            CodeCapacityPlanarCodeVerticalPartitionHalf { d: 11, partition_row: 7 },
675        );
676    }
677
678    /// split a repetition code into 2 parts
679    #[test]
680    fn example_partition_basic_3() {
681        // cargo test example_partition_basic_3 -- --nocapture
682        let visualize_filename = "example_partition_basic_3.json".to_string();
683        // reorder vertices to enable the partition;
684        let defect_vertices = vec![2, 3, 4, 5, 6, 7, 8]; // indices are before the reorder
685        let half_weight = 500;
686        example_partition_standard_syndrome(
687            &mut CodeCapacityRepetitionCode::new(11, 0.1, half_weight),
688            visualize_filename,
689            defect_vertices,
690            true,
691            5 * half_weight,
692            CodeCapacityRepetitionCodePartitionHalf {
693                d: 11,
694                partition_index: 6,
695            },
696        );
697    }
698
699    /// split into 4
700    #[test]
701    fn example_partition_basic_4() {
702        // cargo test example_partition_basic_4 -- --nocapture
703        let visualize_filename = "example_partition_basic_4.json".to_string();
704        // reorder vertices to enable the partition;
705        let defect_vertices = vec![39, 52, 63, 90, 100]; // indices are before the reorder
706        let half_weight = 500;
707        example_partition_standard_syndrome(
708            &mut CodeCapacityPlanarCode::new(11, 0.1, half_weight),
709            visualize_filename,
710            defect_vertices,
711            true,
712            9 * half_weight,
713            CodeCapacityPlanarCodeVerticalPartitionFour {
714                d: 11,
715                partition_row: 7,
716                partition_column: 6,
717            },
718        );
719    }
720
721    /// phenomenological time axis split
722    #[test]
723    fn example_partition_basic_5() {
724        // cargo test example_partition_basic_5 -- --nocapture
725        let visualize_filename = "example_partition_basic_5.json".to_string();
726        // reorder vertices to enable the partition;
727        let defect_vertices = vec![352, 365]; // indices are before the reorder
728        let half_weight = 500;
729        let noisy_measurements = 10;
730        example_partition_standard_syndrome(
731            &mut PhenomenologicalPlanarCode::new(11, noisy_measurements, 0.1, half_weight),
732            visualize_filename,
733            defect_vertices,
734            true,
735            2 * half_weight,
736            PhenomenologicalPlanarCodeTimePartition::new(11, noisy_measurements, 2),
737        );
738    }
739
740    /// a demo to show how partition works in phenomenological planar code
741    #[test]
742    fn example_partition_demo_1() {
743        // cargo test example_partition_demo_1 -- --nocapture
744        let visualize_filename = "example_partition_demo_1.json".to_string();
745        // reorder vertices to enable the partition;
746        let defect_vertices = vec![
747            57, 113, 289, 304, 305, 331, 345, 387, 485, 493, 528, 536, 569, 570, 587, 588, 696, 745, 801, 833, 834, 884,
748            904, 940, 1152, 1184, 1208, 1258, 1266, 1344, 1413, 1421, 1481, 1489, 1490, 1546, 1690, 1733, 1740, 1746, 1796,
749            1825, 1826, 1856, 1857, 1996, 2004, 2020, 2028, 2140, 2196, 2306, 2307, 2394, 2395, 2413, 2417, 2425, 2496,
750            2497, 2731, 2739, 2818, 2874,
751        ]; // indices are before the reorder
752        let half_weight = 500;
753        let noisy_measurements = 51;
754        example_partition_standard_syndrome(
755            &mut PhenomenologicalPlanarCode::new(7, noisy_measurements, 0.005, half_weight),
756            visualize_filename,
757            defect_vertices,
758            true,
759            35 * half_weight,
760            PhenomenologicalPlanarCodeTimePartition::new(7, noisy_measurements, 3),
761        );
762    }
763
764    /// a demo to show how partition works in circuit-level planar code
765    #[test]
766    fn example_partition_demo_2() {
767        // cargo test example_partition_demo_2 -- --nocapture
768        let visualize_filename = "example_partition_demo_2.json".to_string();
769        // reorder vertices to enable the partition;
770        let defect_vertices = vec![
771            57, 113, 289, 304, 305, 331, 345, 387, 485, 493, 528, 536, 569, 570, 587, 588, 696, 745, 801, 833, 834, 884,
772            904, 940, 1152, 1184, 1208, 1258, 1266, 1344, 1413, 1421, 1481, 1489, 1490, 1546, 1690, 1733, 1740, 1746, 1796,
773            1825, 1826, 1856, 1857, 1996, 2004, 2020, 2028, 2140, 2196, 2306, 2307, 2394, 2395, 2413, 2417, 2425, 2496,
774            2497, 2731, 2739, 2818, 2874,
775        ]; // indices are before the reorder
776        let half_weight = 500;
777        let noisy_measurements = 51;
778        example_partition_standard_syndrome(
779            &mut CircuitLevelPlanarCode::new(7, noisy_measurements, 0.005, half_weight),
780            visualize_filename,
781            defect_vertices,
782            true,
783            28980 / 2,
784            PhenomenologicalPlanarCodeTimePartition::new(7, noisy_measurements, 3),
785        );
786    }
787
788    /// demo of tree fusion
789    #[test]
790    fn example_partition_demo_3() {
791        // cargo test example_partition_demo_3 -- --nocapture
792        let visualize_filename = "example_partition_demo_3.json".to_string();
793        // reorder vertices to enable the partition;
794        let defect_vertices = vec![
795            57, 113, 289, 304, 305, 331, 345, 387, 485, 493, 528, 536, 569, 570, 587, 588, 696, 745, 801, 833, 834, 884,
796            904, 940, 1152, 1184, 1208, 1258, 1266, 1344, 1413, 1421, 1481, 1489, 1490, 1546, 1690, 1733, 1740, 1746, 1796,
797            1825, 1826, 1856, 1857, 1996, 2004, 2020, 2028, 2140, 2196, 2306, 2307, 2394, 2395, 2413, 2417, 2425, 2496,
798            2497, 2731, 2739, 2818, 2874,
799        ]; // indices are before the reorder
800        let half_weight = 500;
801        let noisy_measurements = 51;
802        example_partition_standard_syndrome(
803            &mut PhenomenologicalPlanarCode::new(7, noisy_measurements, 0.005, half_weight),
804            visualize_filename,
805            defect_vertices,
806            true,
807            35 * half_weight,
808            PhenomenologicalPlanarCodeTimePartition::new_tree(7, noisy_measurements, 8, true, usize::MAX),
809        );
810    }
811
812    /// demo of sequential fuse
813    #[test]
814    fn example_partition_demo_4() {
815        // cargo test example_partition_demo_4 -- --nocapture
816        let visualize_filename = "example_partition_demo_4.json".to_string();
817        // reorder vertices to enable the partition;
818        let defect_vertices = vec![
819            57, 113, 289, 304, 305, 331, 345, 387, 485, 493, 528, 536, 569, 570, 587, 588, 696, 745, 801, 833, 834, 884,
820            904, 940, 1152, 1184, 1208, 1258, 1266, 1344, 1413, 1421, 1481, 1489, 1490, 1546, 1690, 1733, 1740, 1746, 1796,
821            1825, 1826, 1856, 1857, 1996, 2004, 2020, 2028, 2140, 2196, 2306, 2307, 2394, 2395, 2413, 2417, 2425, 2496,
822            2497, 2731, 2739, 2818, 2874,
823        ]; // indices are before the reorder
824        let half_weight = 500;
825        let noisy_measurements = 51;
826        example_partition_standard_syndrome(
827            &mut PhenomenologicalPlanarCode::new(7, noisy_measurements, 0.005, half_weight),
828            visualize_filename,
829            defect_vertices,
830            true,
831            35 * half_weight,
832            PhenomenologicalPlanarCodeTimePartition::new(7, noisy_measurements, 8),
833        );
834    }
835
836    /// demo of tree + sequential fuse
837    #[test]
838    fn example_partition_demo_5() {
839        // cargo test example_partition_demo_5 -- --nocapture
840        let visualize_filename = "example_partition_demo_5.json".to_string();
841        // reorder vertices to enable the partition;
842        let defect_vertices = vec![
843            57, 113, 289, 304, 305, 331, 345, 387, 485, 493, 528, 536, 569, 570, 587, 588, 696, 745, 801, 833, 834, 884,
844            904, 940, 1152, 1184, 1208, 1258, 1266, 1344, 1413, 1421, 1481, 1489, 1490, 1546, 1690, 1733, 1740, 1746, 1796,
845            1825, 1826, 1856, 1857, 1996, 2004, 2020, 2028, 2140, 2196, 2306, 2307, 2394, 2395, 2413, 2417, 2425, 2496,
846            2497, 2731, 2739, 2818, 2874,
847        ]; // indices are before the reorder
848        let half_weight = 500;
849        let noisy_measurements = 51;
850        example_partition_standard_syndrome(
851            &mut PhenomenologicalPlanarCode::new(7, noisy_measurements, 0.005, half_weight),
852            visualize_filename,
853            defect_vertices,
854            true,
855            35 * half_weight,
856            PhenomenologicalPlanarCodeTimePartition::new_tree(7, noisy_measurements, 8, true, 3),
857        );
858    }
859}