1use std::collections::hash_map::Entry;
4use std::fmt;
5use std::vec::Drain;
6
7use glam::IVec2;
8use rustc_hash::FxHashMap;
9use smallvec::SmallVec;
10
11use crate::chunk::{Flow, FlowMarker};
12use crate::error::FlowError;
13use crate::pauli_map::PauliMap;
14use crate::{
15 DetectorParity, DetectorTerm, LoopCarriedDetectorState, LoopStateId,
16 checked_translate_coordinate,
17};
18
19pub type FlowEngineMeasurements<M> = SmallVec<[M; 2]>;
27
28#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct CompletedFlow<M> {
31 pub measurements: FlowEngineMeasurements<M>,
33 pub sign: bool,
35 pub center: Option<IVec2>,
37 pub marker: FlowMarker,
41}
42
43#[derive(Debug, Clone, PartialEq, Eq)]
49pub enum ComposedChain<M> {
50 Detector {
52 parity: DetectorParity<M>,
54 center: Option<IVec2>,
56 },
57 Restart {
60 parity: DetectorParity<M>,
62 },
63}
64
65#[derive(Debug, Clone, PartialEq, Eq)]
72pub struct ResidualFlow<M> {
73 pub start: PauliMap,
75 pub end: PauliMap,
77 pub measurements: FlowEngineMeasurements<M>,
79 pub sign: bool,
81 pub center: Option<IVec2>,
83 pub marker: FlowMarker,
87}
88
89#[derive(Clone)]
90struct OpenFlow<M> {
91 measurements: FlowEngineMeasurements<M>,
92 sign: bool,
93 center: Option<IVec2>,
94 marker: FlowMarker,
96 origin_start: Option<Box<PauliMap>>,
103}
104
105#[derive(Debug, Clone, Copy)]
111pub struct OffsetFlows<'c> {
112 pub flows: &'c [Flow],
114 pub offset: IVec2,
116}
117
118impl<'c> OffsetFlows<'c> {
119 pub fn new(flows: &'c [Flow], offset: IVec2) -> Self {
121 Self { flows, offset }
122 }
123}
124
125#[derive(Debug, Clone, Copy)]
135pub struct FlowKey<'c> {
136 map: &'c PauliMap,
137 offset: IVec2,
138 hash: u64,
139}
140
141impl<'c> FlowKey<'c> {
142 pub fn try_new(
149 map: &'c PauliMap,
150 offset: IVec2,
151 ) -> Result<Self, crate::CoordinateOverflowError> {
152 for (coord, _) in map {
153 checked_translate_coordinate(*coord, offset)?;
154 }
155 Ok(Self::new(map, offset))
156 }
157
158 fn new(map: &'c PauliMap, offset: IVec2) -> Self {
159 use std::hash::Hasher as _;
160 let mut hasher = rustc_hash::FxHasher::default();
161 map.hash_translated(offset, &mut hasher);
162 Self {
163 map,
164 offset,
165 hash: hasher.finish(),
166 }
167 }
168
169 fn translated(&self) -> PauliMap {
170 self.map.translated(self.offset)
171 }
172}
173
174impl std::hash::Hash for FlowKey<'_> {
175 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
176 state.write_u64(self.hash);
177 }
178}
179
180impl PartialEq for FlowKey<'_> {
181 fn eq(&self, other: &Self) -> bool {
182 if self.hash != other.hash {
183 return false;
184 }
185 if self.offset.to_array() == other.offset.to_array() {
186 return self.map == other.map;
187 }
188 self.map.len() == other.map.len()
189 && self.map.iter().zip(other.map.iter()).all(
190 |((coord, pauli), (other_coord, other_pauli))| {
191 *coord + self.offset == *other_coord + other.offset && pauli == other_pauli
192 },
193 )
194 }
195}
196
197impl Eq for FlowKey<'_> {}
198
199impl fmt::Display for FlowKey<'_> {
200 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201 self.translated().fmt(f)
202 }
203}
204
205#[must_use]
212#[derive(Clone)]
213pub struct FlowEngine<'c, M> {
214 open_flows: FxHashMap<FlowKey<'c>, OpenFlow<M>>,
215 matched_inputs: Vec<Option<OpenFlow<M>>>,
218 latent_flows: Vec<(PauliMap, OpenFlow<M>)>,
222 completed: Vec<CompletedFlow<M>>,
223 residual_completed: Vec<ResidualFlow<M>>,
228}
229
230impl<'c, M> FlowEngine<'c, M> {
231 pub fn new() -> Self {
233 Self {
234 open_flows: FxHashMap::default(),
235 matched_inputs: Vec::new(),
236 latent_flows: Vec::new(),
237 completed: Vec::new(),
238 residual_completed: Vec::new(),
239 }
240 }
241
242 pub fn finish(self) -> Result<Vec<CompletedFlow<M>>, FlowError> {
251 let active: Vec<String> = self.open_flows.keys().map(ToString::to_string).collect();
252 if !active.is_empty() {
253 return Err(FlowError::Composition(format!(
254 "unterminated flows: {}",
255 active.join("; ")
256 )));
257 }
258 Ok(self.completed)
259 }
260
261 pub fn append_group_allowing_dangling(
273 &mut self,
274 parts: &[OffsetFlows<'c>],
275 meas_mapper: impl FnMut(usize, &u32) -> M,
276 ) -> Result<(), FlowError> {
277 self.append_group_with_mode(parts, meas_mapper, UnmatchedInputMode::Error, None)
278 }
279
280 pub fn append_group_allowing_stabilizer_transition(
290 &mut self,
291 parts: &[OffsetFlows<'c>],
292 mut meas_mapper: impl FnMut(usize, &u32) -> M,
293 ) -> Result<(), FlowError>
294 where
295 M: Copy,
296 {
297 let mut consumer_count = 0;
298 for part in parts {
299 for flow in part.flows.iter().filter(|flow| !flow.start.is_empty()) {
300 for (coord, _) in &flow.start {
301 checked_translate_coordinate(*coord, part.offset)?;
302 }
303 consumer_count += 1;
304 }
305 }
306 if !self.latent_flows.is_empty() {
307 return self.append_stabilizer_transition(parts, &mut meas_mapper);
308 }
309
310 let consumer_keys = || {
316 parts.iter().flat_map(|part| {
317 part.flows
318 .iter()
319 .filter(|flow| !flow.start.is_empty())
320 .map(|flow| FlowKey::new(&flow.start, part.offset))
321 })
322 };
323 let mut matched_inputs = std::mem::take(&mut self.matched_inputs);
324 matched_inputs.reserve(consumer_count);
325 for target in consumer_keys() {
326 let Some(open) = self.open_flows.remove(&target) else {
327 let duplicate_consumer = consumer_keys()
328 .take(matched_inputs.len())
329 .any(|seen| seen == target);
330 for (key, open) in consumer_keys().zip(matched_inputs.into_iter().flatten()) {
331 self.open_flows.insert(key, open);
332 }
333 if !duplicate_consumer {
334 return self.append_stabilizer_transition(parts, &mut meas_mapper);
335 }
336 return self.append_group_with_mode(
338 parts,
339 meas_mapper,
340 UnmatchedInputMode::Error,
341 None,
342 );
343 };
344 matched_inputs.push(Some(open));
345 }
346 self.append_group_with_mode(
347 parts,
348 meas_mapper,
349 UnmatchedInputMode::Error,
350 Some(matched_inputs),
351 )
352 }
353
354 pub fn append_group_skipping_unmatched_inputs(
363 &mut self,
364 parts: &[OffsetFlows<'c>],
365 meas_mapper: impl FnMut(usize, &u32) -> M,
366 ) -> Result<(), FlowError> {
367 self.append_group_with_mode(parts, meas_mapper, UnmatchedInputMode::Skip, None)
368 }
369
370 pub fn append_residual_group(
383 &mut self,
384 parts: &[OffsetFlows<'c>],
385 meas_mapper: impl FnMut(usize, &u32) -> M,
386 ) -> Result<(), FlowError> {
387 self.append_group_with_mode(parts, meas_mapper, UnmatchedInputMode::KeepAsResidual, None)
388 }
389
390 fn append_group_with_mode(
391 &mut self,
392 parts: &[OffsetFlows<'c>],
393 mut meas_mapper: impl FnMut(usize, &u32) -> M,
394 unmatched_input_mode: UnmatchedInputMode,
395 matched_inputs: Option<Vec<Option<OpenFlow<M>>>>,
396 ) -> Result<(), FlowError> {
397 let mut inputs = match matched_inputs {
398 Some(inputs) => inputs,
399 None => {
400 let mut inputs = std::mem::take(&mut self.matched_inputs);
401 for part in parts {
402 for flow in part.flows.iter().filter(|flow| !flow.start.is_empty()) {
403 for (coord, _) in &flow.start {
404 checked_translate_coordinate(*coord, part.offset)?;
405 }
406 inputs.push(
407 self.open_flows
408 .remove(&FlowKey::new(&flow.start, part.offset)),
409 );
410 }
411 }
412 inputs
413 }
414 };
415 let mut matched_inputs = inputs.drain(..);
416 self.open_flows.reserve(
419 parts
420 .iter()
421 .flat_map(|part| part.flows)
422 .filter(|flow| !flow.end.is_empty())
423 .count(),
424 );
425
426 for (part_index, part) in parts.iter().enumerate() {
429 for flow in part.flows {
430 if !flow.start.is_empty() {
431 let previous = matched_inputs
432 .next()
433 .expect("pre-matched inputs cover every consumer");
434 self.process_flow(
435 flow,
436 part.offset,
437 part_index,
438 &mut meas_mapper,
439 unmatched_input_mode,
440 previous,
441 )?;
442 }
443 }
444 }
445 debug_assert!(
446 matched_inputs.as_slice().is_empty(),
447 "every pre-matched input is consumed"
448 );
449 drop(matched_inputs);
450 self.matched_inputs = inputs;
451 for (part_index, part) in parts.iter().enumerate() {
452 for flow in part.flows {
453 if flow.start.is_empty() {
454 self.process_flow(
455 flow,
456 part.offset,
457 part_index,
458 &mut meas_mapper,
459 unmatched_input_mode,
460 None,
461 )?;
462 }
463 }
464 }
465
466 Ok(())
467 }
468
469 pub fn drain_completed(&mut self) -> Drain<'_, CompletedFlow<M>> {
472 self.completed.drain(..)
473 }
474
475 pub fn into_residual_flows(self) -> Vec<ResidualFlow<M>> {
486 let mut flows = self.residual_completed;
487 flows.extend(
488 self.open_flows
489 .into_iter()
490 .map(|(key, open)| (key.translated(), open))
491 .chain(self.latent_flows)
492 .map(|(end, open)| ResidualFlow {
493 start: open
494 .origin_start
495 .map_or_else(PauliMap::empty, |start| *start),
496 end,
497 measurements: open.measurements,
498 sign: open.sign,
499 center: open.center,
500 marker: open.marker,
501 }),
502 );
503 flows
504 }
505
506 fn process_flow(
507 &mut self,
508 flow: &'c Flow,
509 offset: IVec2,
510 part_index: usize,
511 meas_mapper: &mut impl FnMut(usize, &u32) -> M,
512 unmatched_input_mode: UnmatchedInputMode,
513 previous: Option<OpenFlow<M>>,
514 ) -> Result<(), FlowError> {
515 if offset != IVec2::ZERO {
519 for (coord, _) in flow.start.iter().chain(flow.end.iter()) {
520 checked_translate_coordinate(*coord, offset)?;
521 }
522 if let Some(center) = flow.center {
523 checked_translate_coordinate(center, offset)?;
524 }
525 }
526
527 let mut measurements: FlowEngineMeasurements<M> =
528 SmallVec::with_capacity(flow.measurements.len());
529 for measurement in &flow.measurements {
530 measurements.push(meas_mapper(part_index, measurement));
531 }
532
533 let mut center = flow.center.map(|center| center + offset);
534 let mut sign = flow.sign;
535 let mut marker = flow.marker;
536 let mut origin_start = None;
541
542 if !flow.start.is_empty() {
543 match previous {
545 Some(prev) => {
546 measurements.extend(prev.measurements);
549 sign ^= prev.sign;
550 center = center.or(prev.center);
551 origin_start = prev.origin_start;
552 marker = marker.fuse(prev.marker).ok_or_else(|| {
557 FlowError::Composition(
558 "fused chain mixes discard and restart components".into(),
559 )
560 })?;
561 }
562 None => match unmatched_input_mode {
563 UnmatchedInputMode::Skip => return Ok(()),
564 UnmatchedInputMode::KeepAsResidual => {
567 origin_start = Some(Box::new(flow.start.translated(offset)));
568 }
569 UnmatchedInputMode::Error => {
570 return Err(FlowError::Composition(format!(
571 "unmatched flow input {}",
572 flow.start.translated(offset)
573 )));
574 }
575 },
576 }
577 }
578
579 if !flow.end.is_empty() {
580 let end_key = FlowKey::new(&flow.end, offset);
582 let Entry::Vacant(entry) = self.open_flows.entry(end_key) else {
583 return Err(FlowError::Composition(format!(
584 "multiple flows create boundary {end_key}"
585 )));
586 };
587 entry.insert(OpenFlow {
588 measurements,
589 sign,
590 center,
591 origin_start,
592 marker,
593 });
594 } else if !measurements.is_empty() || sign || marker == FlowMarker::Discard {
595 if let Some(origin_start) = origin_start {
608 self.residual_completed.push(ResidualFlow {
609 start: *origin_start,
610 end: PauliMap::empty(),
611 measurements,
612 sign,
613 center,
614 marker,
615 });
616 } else {
617 self.completed.push(CompletedFlow {
618 measurements,
619 sign,
620 center,
621 marker,
622 });
623 }
624 }
625
626 Ok(())
627 }
628
629 fn append_stabilizer_transition(
630 &mut self,
631 parts: &[OffsetFlows<'c>],
632 meas_mapper: &mut impl FnMut(usize, &u32) -> M,
633 ) -> Result<(), FlowError>
634 where
635 M: Copy,
636 {
637 let mut incoming = Vec::new();
638 for (part_index, part) in parts.iter().enumerate() {
639 for flow in part.flows.iter().filter(|flow| !flow.start.is_empty()) {
640 if !flow.end.is_empty() {
641 return Err(FlowError::Composition(
642 "stabilizer-span transitions require boundary consumer flows".into(),
643 ));
644 }
645 let mut payload = empty_open_flow();
646 payload.measurements.extend(
647 flow.measurements
648 .iter()
649 .map(|measurement| meas_mapper(part_index, measurement)),
650 );
651 payload.sign = flow.sign;
652 payload.center = flow
653 .center
654 .map(|center| checked_translate_coordinate(center, part.offset))
655 .transpose()?;
656 payload.marker = flow.marker;
657 incoming.push((flow.start.translated(part.offset), payload));
658 }
659 }
660
661 let mut old: Vec<_> = std::mem::take(&mut self.open_flows)
662 .into_iter()
663 .map(|(key, flow)| (key.translated(), flow))
664 .chain(std::mem::take(&mut self.latent_flows))
665 .collect();
666 old.sort_by(|(a, _), (b, _)| pauli_map_cmp(a, b));
667 let old_len = old.len();
668 let generators: Vec<&PauliMap> = old
670 .iter()
671 .map(|(map, _)| map)
672 .chain(incoming.iter().map(|(start, _)| start))
673 .collect();
674 let dependencies = pauli_dependencies(&generators);
675 for (index, mask) in dependencies {
676 if index < old_len {
677 return Err(FlowError::Composition(
678 "open flow boundaries are not independent generators".into(),
679 ));
680 }
681 if first_set_bit(&mask).is_none_or(|first| first >= old_len) {
682 return Err(FlowError::Composition(
683 "incoming flow boundaries are not independent generators".into(),
684 ));
685 }
686 let mut completed = empty_open_flow();
687 let mut product = PauliMap::empty();
688 for (index, (map, flow)) in incoming.iter().enumerate() {
689 if mask[(index + old_len) / 64] & (1 << ((index + old_len) % 64)) != 0 {
690 multiply_pauli_flow(&mut product, &mut completed, map, flow)?;
691 }
692 }
693 for (index, (map, flow)) in old.iter().enumerate() {
694 if mask[index / 64] & (1 << (index % 64)) != 0 {
695 multiply_pauli_flow(&mut product, &mut completed, map, flow)?;
696 }
697 }
698 if !completed.measurements.is_empty()
699 || completed.sign
700 || completed.marker == FlowMarker::Discard
701 {
702 self.completed.push(CompletedFlow {
703 measurements: completed.measurements,
704 sign: completed.sign,
705 center: completed.center,
706 marker: completed.marker,
707 });
708 }
709 }
710
711 let mut commutations = pauli_commutation_columns(
715 old.iter().map(|(map, _)| map),
716 incoming.iter().map(|(map, _)| map),
717 );
718 for measured in 0..incoming.len() {
719 let mut targets = std::mem::take(&mut commutations[measured]);
720 let Some(pivot) = first_set_bit(&targets) else {
721 continue;
722 };
723 let last = old.len() - 1;
724 let (pivot_map, pivot_flow) = old.swap_remove(pivot);
725 swap_remove_bit(&mut targets, pivot, last);
726 for (word, &bits) in targets.iter().enumerate() {
727 let mut bits = bits;
728 while bits != 0 {
729 let target = word * 64 + bits.trailing_zeros() as usize;
730 let (known, flow) = &mut old[target];
731 multiply_pauli_flow(known, flow, &pivot_map, &pivot_flow)?;
732 bits &= bits - 1;
733 }
734 }
735 for column in &mut commutations[measured + 1..] {
738 let anticommutes = column[pivot / 64] & (1 << (pivot % 64)) != 0;
739 swap_remove_bit(column, pivot, last);
740 if anticommutes {
741 xor_words(column, &targets);
742 }
743 }
744 }
745
746 let mut basis = FxHashMap::default();
749 for (measured, flow) in &incoming {
750 let mut measured = measured.clone();
751 let mut flow = flow.clone();
752 reduce_pauli_flow(&mut measured, &mut flow, &basis)?;
753 let pivot = pauli_pivot(&measured)
754 .expect("incoming flow boundaries were checked as independent");
755 basis.insert(pivot, (measured, flow));
756 }
757 for (mut known, mut flow) in old {
758 reduce_pauli_flow(&mut known, &mut flow, &basis)?;
759 let Some(pivot) = pauli_pivot(&known) else {
760 continue;
761 };
762 basis.insert(pivot, (known.clone(), flow.clone()));
763 self.latent_flows.push((known, flow));
764 }
765
766 for (part_index, part) in parts.iter().enumerate() {
771 for flow in part.flows.iter().filter(|flow| flow.start.is_empty()) {
772 self.process_flow(
773 flow,
774 part.offset,
775 part_index,
776 meas_mapper,
777 UnmatchedInputMode::Error,
778 None,
779 )?;
780 }
781 }
782 Ok(())
783 }
784}
785
786type PauliPivot = (i32, i32, u8);
787
788fn pauli_pivot(map: &PauliMap) -> Option<PauliPivot> {
789 map.iter().next_back().map(|(coord, pauli)| {
790 let component = if *pauli & crate::Pauli::Z { 2 } else { 1 };
791 (coord.x, coord.y, component)
792 })
793}
794
795fn pauli_pivot_before(map: &PauliMap, before: PauliPivot) -> Option<PauliPivot> {
798 let maximum = pauli_pivot(map)?;
799 if maximum < before {
800 return Some(maximum);
801 }
802 let (x, y, component) = before;
803 let coord = IVec2::new(x, y);
804 if component == 2
805 && map
806 .get(&coord)
807 .is_some_and(|pauli| *pauli & crate::Pauli::X)
808 {
809 return Some((x, y, 1));
810 }
811 map.entry_before(coord).map(|(coord, pauli)| {
812 (
813 coord.x,
814 coord.y,
815 if pauli & crate::Pauli::Z { 2 } else { 1 },
816 )
817 })
818}
819
820#[cfg(test)]
821fn has_pauli_pivot(map: &PauliMap, pivot: PauliPivot) -> bool {
822 let (x, y, component) = pivot;
823 map.get(&IVec2::new(x, y)).is_some_and(|pauli| {
824 *pauli
825 & if component == 2 {
826 crate::Pauli::Z
827 } else {
828 crate::Pauli::X
829 }
830 })
831}
832
833fn pauli_commutation_columns<'a>(
836 known: impl ExactSizeIterator<Item = &'a PauliMap>,
837 measured: impl Iterator<Item = &'a PauliMap>,
838) -> Vec<Vec<u64>> {
839 let words = known.len().div_ceil(64);
840 let mut sites: FxHashMap<IVec2, SmallVec<[(usize, crate::Pauli); 4]>> = FxHashMap::default();
841 for (index, map) in known.enumerate() {
842 for (&coord, &pauli) in map {
843 sites.entry(coord).or_default().push((index, pauli));
844 }
845 }
846 measured
847 .map(|map| {
848 let mut column = vec![0; words];
849 for (coord, &pauli) in map {
850 for &(index, other) in sites.get(coord).into_iter().flatten() {
851 if pauli.anticommutes(other) {
852 column[index / 64] ^= 1 << (index % 64);
853 }
854 }
855 }
856 column
857 })
858 .collect()
859}
860
861fn swap_remove_bit(words: &mut [u64], index: usize, last: usize) {
862 let value = (words[last / 64] >> (last % 64)) & 1;
863 words[index / 64] = (words[index / 64] & !(1 << (index % 64))) | (value << (index % 64));
864 words[last / 64] &= !(1 << (last % 64));
865}
866
867fn reduce_pauli_flow<M: Copy>(
868 value: &mut PauliMap,
869 flow: &mut OpenFlow<M>,
870 basis: &FxHashMap<PauliPivot, (PauliMap, OpenFlow<M>)>,
871) -> Result<(), FlowError> {
872 let mut pivot = pauli_pivot(value);
873 while let Some(current) = pivot {
874 if let Some((row, row_flow)) = basis.get(¤t) {
875 multiply_pauli_flow(value, flow, row, row_flow)?;
876 }
877 pivot = pauli_pivot_before(value, current);
878 }
879 Ok(())
880}
881
882fn multiply_pauli_flow<M: Copy>(
883 target: &mut PauliMap,
884 flow: &mut OpenFlow<M>,
885 source: &PauliMap,
886 source_flow: &OpenFlow<M>,
887) -> Result<(), FlowError> {
888 let phase = target.product_phase(source);
889 if !phase.is_multiple_of(2) {
890 return Err(FlowError::Composition(
891 "stabilizer-span products require commuting boundaries".into(),
892 ));
893 }
894 flow.sign ^= phase == 2;
895 *target = &*target ^ source;
896 xor_open_flow(flow, source_flow)
897}
898
899fn first_set_bit(words: &[u64]) -> Option<usize> {
900 words
901 .iter()
902 .enumerate()
903 .find_map(|(word, &bits)| (bits != 0).then(|| word * 64 + bits.trailing_zeros() as usize))
904}
905
906fn xor_words(target: &mut [u64], source: &[u64]) {
907 for (target, source) in target.iter_mut().zip(source) {
908 *target ^= source;
909 }
910}
911
912#[cfg(test)]
913fn xor_bits(target: &mut [bool], source: &[bool]) {
914 for (target, source) in target.iter_mut().zip(source) {
915 *target ^= source;
916 }
917}
918
919fn pauli_dependencies(generators: &[&PauliMap]) -> Vec<(usize, Vec<u64>)> {
920 let mut basis: FxHashMap<PauliPivot, (PauliMap, Vec<u64>)> = FxHashMap::default();
921 let mut dependencies = Vec::new();
922 for (index, generator) in generators.iter().enumerate() {
923 let mut value = (*generator).clone();
924 let mut mask = vec![0; generators.len().div_ceil(64)];
925 mask[index / 64] = 1 << (index % 64);
926 let mut pivot = pauli_pivot(&value);
927 while let Some(current) = pivot {
928 if let Some((row, row_mask)) = basis.get(¤t) {
929 value = &value ^ row;
930 xor_words(&mut mask, row_mask);
931 }
932 pivot = pauli_pivot_before(&value, current);
933 }
934 if let Some(pivot) = pauli_pivot(&value) {
935 basis.insert(pivot, (value, mask));
936 } else {
937 dependencies.push((index, mask));
938 }
939 }
940 dependencies
941}
942
943#[cfg(test)]
944fn eager_pauli_dependencies(generators: &[&PauliMap]) -> Vec<(usize, Vec<bool>)> {
945 let mut basis: Vec<(PauliPivot, PauliMap, Vec<bool>)> = Vec::new();
946 let mut dependencies = Vec::new();
947 for (index, generator) in generators.iter().enumerate() {
948 let mut value = (*generator).clone();
949 let mut mask = vec![false; generators.len()];
950 mask[index] = true;
951 let start = pauli_pivot(&value).map_or(basis.len(), |maximum| {
952 basis.partition_point(|(pivot, _, _)| *pivot > maximum)
953 });
954 for (pivot, row, row_mask) in &basis[start..] {
955 if has_pauli_pivot(&value, *pivot) {
956 value = &value ^ row;
957 xor_bits(&mut mask, row_mask);
958 if value.is_empty() {
959 break;
960 }
961 }
962 }
963 if let Some(pivot) = pauli_pivot(&value) {
964 let index = basis.partition_point(|(existing, _, _)| *existing > pivot);
965 basis.insert(index, (pivot, value, mask));
966 } else {
967 dependencies.push((index, mask));
968 }
969 }
970 dependencies
971}
972
973fn empty_open_flow<M>() -> OpenFlow<M> {
974 OpenFlow {
975 measurements: SmallVec::new(),
976 sign: false,
977 center: None,
978 marker: FlowMarker::Detector,
979 origin_start: None,
980 }
981}
982
983fn xor_open_flow<M: Copy>(target: &mut OpenFlow<M>, source: &OpenFlow<M>) -> Result<(), FlowError> {
984 target.measurements.extend_from_slice(&source.measurements);
985 target.sign ^= source.sign;
986 target.center = target.center.or(source.center);
987 target.marker = target.marker.fuse(source.marker).ok_or_else(|| {
988 FlowError::Composition(
989 "stabilizer-span transition mixes discard and restart components".into(),
990 )
991 })?;
992 if let Some(source_origin) = source.origin_start.as_deref() {
993 target.origin_start = match target.origin_start.take() {
994 Some(mut target_origin) => {
995 let phase = target_origin.product_phase(source_origin);
996 if !phase.is_multiple_of(2) {
997 return Err(FlowError::Composition(
998 "stabilizer-span products require commuting origin boundaries".into(),
999 ));
1000 }
1001 target.sign ^= phase == 2;
1002 *target_origin = &*target_origin ^ source_origin;
1003 (!target_origin.is_empty()).then_some(target_origin)
1004 }
1005 None => Some(Box::new(source_origin.clone())),
1006 };
1007 }
1008 Ok(())
1009}
1010
1011fn pauli_map_cmp(a: &PauliMap, b: &PauliMap) -> std::cmp::Ordering {
1012 a.iter()
1013 .map(|(coord, pauli)| (coord.x, coord.y, *pauli as u8))
1014 .cmp(
1015 b.iter()
1016 .map(|(coord, pauli)| (coord.x, coord.y, *pauli as u8)),
1017 )
1018}
1019
1020#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1021enum UnmatchedInputMode {
1022 Error,
1023 Skip,
1024 KeepAsResidual,
1027}
1028
1029impl<M> Default for FlowEngine<'_, M> {
1030 fn default() -> Self {
1031 Self::new()
1032 }
1033}
1034
1035impl<M: fmt::Debug> fmt::Debug for FlowEngine<'_, M> {
1036 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1037 f.debug_struct("FlowEngine")
1038 .field("open_flows", &self.open_flows.len())
1039 .field("latent_flows", &self.latent_flows.len())
1040 .field("completed", &self.completed.len())
1041 .finish()
1042 }
1043}
1044
1045#[derive(Debug, Clone)]
1051pub struct PendingLoopDetectorStates<'c, M = u32> {
1052 loop_states: Vec<(FlowKey<'c>, LoopCarriedDetectorState<M>)>,
1053}
1054
1055impl<'c, M: Copy + Ord> FlowEngine<'c, DetectorTerm<M>> {
1056 pub fn start_loop_detector_states(
1072 &mut self,
1073 body_first_chunk: &[OffsetFlows<'c>],
1074 next_loop_state: &mut u32,
1075 ) -> Result<PendingLoopDetectorStates<'c, M>, FlowError> {
1076 for part in body_first_chunk
1077 .iter()
1078 .filter(|part| part.offset != IVec2::ZERO)
1079 {
1080 for flow in part.flows.iter().filter(|flow| !flow.start.is_empty()) {
1081 for (coordinate, _) in &flow.start {
1082 checked_translate_coordinate(*coordinate, part.offset)?;
1083 }
1084 }
1085 }
1086 let possible_states =
1087 loop_state_upper_bound(body_first_chunk.iter().map(|part| part.flows.len()));
1088 if possible_states == usize::MAX || possible_states > (u32::MAX - *next_loop_state) as usize
1089 {
1090 self.check_loop_state_capacity(body_first_chunk, *next_loop_state)?;
1091 }
1092
1093 let mut loop_states = Vec::with_capacity(self.open_flows.len().min(possible_states));
1094 for part in body_first_chunk {
1095 for flow in part.flows {
1096 if flow.start.is_empty() {
1097 continue;
1098 }
1099 let key = FlowKey::new(&flow.start, part.offset);
1100 let Some(open) = self.open_flows.get_mut(&key) else {
1101 continue;
1102 };
1103 let state = LoopStateId(*next_loop_state);
1104 *next_loop_state = next_loop_state
1105 .checked_add(1)
1106 .expect("loop state range was preflighted");
1107 let initial = DetectorParity::from_term_buf(std::mem::replace(
1108 &mut open.measurements,
1109 smallvec::smallvec![DetectorTerm::LoopState(state)],
1110 ))
1111 .with_sign(std::mem::take(&mut open.sign));
1112 loop_states.push((
1113 key,
1114 LoopCarriedDetectorState {
1115 state,
1116 initial,
1117 next: DetectorParity::default(),
1118 },
1119 ));
1120 }
1121 }
1122 Ok(PendingLoopDetectorStates { loop_states })
1123 }
1124
1125 #[cold]
1126 fn check_loop_state_capacity(
1127 &self,
1128 body_first_chunk: &[OffsetFlows<'c>],
1129 next_loop_state: u32,
1130 ) -> Result<(), FlowError> {
1131 body_first_chunk
1132 .iter()
1133 .flat_map(|part| {
1134 part.flows.iter().filter(|flow| {
1135 !flow.start.is_empty()
1136 && self
1137 .open_flows
1138 .contains_key(&FlowKey::new(&flow.start, part.offset))
1139 })
1140 })
1141 .try_fold(next_loop_state, |next, _| next.checked_add(1))
1142 .ok_or_else(|| {
1143 FlowError::Composition("loop detector state id space exhausted".into())
1144 })?;
1145 Ok(())
1146 }
1147
1148 pub fn drain_composed_chains(&mut self) -> impl Iterator<Item = ComposedChain<M>> + '_ {
1156 self.completed.drain(..).filter_map(|completed| {
1157 if completed.marker == FlowMarker::Discard {
1158 return None;
1159 }
1160 let parity =
1161 DetectorParity::from_term_buf(completed.measurements).with_sign(completed.sign);
1162 if parity.is_empty() {
1163 return None;
1164 }
1165 Some(if completed.marker == FlowMarker::Restart {
1166 ComposedChain::Restart { parity }
1167 } else {
1168 ComposedChain::Detector {
1169 parity,
1170 center: completed.center,
1171 }
1172 })
1173 })
1174 }
1175
1176 pub fn finish_loop_detector_states(
1179 &self,
1180 pending: PendingLoopDetectorStates<'c, M>,
1181 ) -> Vec<LoopCarriedDetectorState<M>> {
1182 pending
1183 .loop_states
1184 .into_iter()
1185 .map(|(key, mut detector_state)| {
1186 if let Some(open) = self.open_flows.get(&key) {
1187 detector_state.next =
1188 DetectorParity::from_terms(open.measurements.iter().copied())
1189 .with_sign(open.sign);
1190 }
1191 detector_state
1192 })
1193 .collect()
1194 }
1195}
1196
1197fn loop_state_upper_bound(lengths: impl IntoIterator<Item = usize>) -> usize {
1198 lengths.into_iter().fold(0, usize::saturating_add)
1199}
1200
1201#[cfg(test)]
1202mod tests {
1203 use super::*;
1204 use crate::Pauli;
1205
1206 #[test]
1207 fn repeated_flow_slices_cannot_wrap_the_loop_state_bound() {
1208 assert_eq!(
1209 loop_state_upper_bound([usize::MAX / 2, usize::MAX / 2, 2]),
1210 usize::MAX,
1211 );
1212 }
1213 fn pauli_map(entries: impl IntoIterator<Item = (IVec2, Pauli)>) -> PauliMap {
1214 entries.into_iter().collect()
1215 }
1216
1217 fn x(coord: IVec2) -> PauliMap {
1218 pauli_map([(coord, Pauli::X)])
1219 }
1220
1221 fn z(coord: IVec2) -> PauliMap {
1222 pauli_map([(coord, Pauli::Z)])
1223 }
1224
1225 fn flow(start: PauliMap, end: PauliMap, measurement: Option<u32>) -> Flow {
1226 Flow::new(start, end).with_measurements(measurement)
1227 }
1228
1229 fn append_flows<'c>(
1230 engine: &mut FlowEngine<'c, u32>,
1231 flows: &'c [Flow],
1232 mut mapper: impl FnMut(&u32) -> u32,
1233 ) -> Result<(), FlowError> {
1234 engine.append_group_allowing_dangling(&[OffsetFlows::new(flows, IVec2::ZERO)], |_, m| {
1235 mapper(m)
1236 })
1237 }
1238
1239 fn measurement_to_sequential_mapper(offset: &mut u32) -> impl FnMut(&u32) -> u32 + '_ {
1240 move |_reference| {
1241 let id = *offset;
1242 *offset += 1;
1243 id
1244 }
1245 }
1246
1247 #[test]
1248 fn offset_flows_match_equal_absolute_boundaries() {
1249 let mut engine = FlowEngine::<u32>::new();
1250 let mut offset = 0u32;
1251
1252 let creator = [Flow::new(PauliMap::empty(), x(IVec2::new(0, 0)))
1256 .with_measurements([0])
1257 .with_center(IVec2::new(1, 1))];
1258 let consumer = [flow(x(IVec2::new(2, 0)), PauliMap::empty(), Some(0))];
1259
1260 let mut mapper = measurement_to_sequential_mapper(&mut offset);
1261 engine
1262 .append_group_allowing_dangling(
1263 &[OffsetFlows::new(&creator, IVec2::new(2, 0))],
1264 |_, m| mapper(m),
1265 )
1266 .expect("creator");
1267 engine
1268 .append_group_allowing_dangling(&[OffsetFlows::new(&consumer, IVec2::ZERO)], |_, m| {
1269 mapper(m)
1270 })
1271 .expect("consumer matches translated boundary");
1272
1273 let completed = engine.finish().expect("clean finish");
1274 assert_eq!(completed.len(), 1);
1275 assert_eq!(completed[0].measurements.as_slice(), [1, 0]);
1276 assert_eq!(completed[0].center, Some(IVec2::new(3, 1)));
1277 }
1278
1279 #[test]
1280 fn offset_flow_coordinate_overflow_is_typed() {
1281 let coordinate = IVec2::new(i32::MAX, 0);
1282 let offset = IVec2::new(1, 0);
1283 let flows = [Flow::new(PauliMap::empty(), x(coordinate))];
1284 let error = FlowEngine::<u32>::new()
1285 .append_group_allowing_dangling(&[OffsetFlows::new(&flows, offset)], |_, &m| m)
1286 .unwrap_err();
1287
1288 assert_eq!(
1289 error,
1290 FlowError::CoordinateOverflow(crate::CoordinateOverflowError { coordinate, offset })
1291 );
1292 }
1293
1294 #[test]
1295 fn stabilizer_transition_center_overflow_is_typed() {
1296 let coordinate = IVec2::new(i32::MAX, 0);
1297 let offset = IVec2::X;
1298 let incoming = [flow(z(IVec2::ZERO), PauliMap::empty(), Some(1)).with_center(coordinate)];
1299 let mut engine = FlowEngine::<u32>::new();
1300
1301 let error = engine
1302 .append_group_allowing_stabilizer_transition(
1303 &[OffsetFlows::new(&incoming, offset)],
1304 |_, &measurement| measurement,
1305 )
1306 .unwrap_err();
1307
1308 assert_eq!(
1309 error,
1310 FlowError::CoordinateOverflow(crate::CoordinateOverflowError { coordinate, offset })
1311 );
1312 }
1313
1314 #[test]
1315 fn zero_offset_accepts_coordinate_extrema() {
1316 let boundary = x(IVec2::new(i32::MAX, i32::MIN));
1317 let flows = [Flow::new(PauliMap::empty(), boundary)];
1318
1319 FlowEngine::<u32>::new()
1320 .append_group_allowing_dangling(
1321 &[OffsetFlows::new(&flows, IVec2::ZERO)],
1322 |_, &measurement| measurement,
1323 )
1324 .expect("zero offset cannot overflow a coordinate");
1325 }
1326
1327 #[test]
1328 fn completes_and_fuses_flows() {
1329 let mut engine = FlowEngine::<u32>::new();
1330 let mut offset = 0u32;
1331 let boundary = x(IVec2::new(0, 0));
1332 let chunk1 = [Flow::new(PauliMap::empty(), boundary.clone())
1333 .with_measurements([0])
1334 .with_sign(true)
1335 .with_center(IVec2::new(1, 1))];
1336 let chunk2 = [Flow::new(boundary, PauliMap::empty()).with_measurements([0])];
1337
1338 append_flows(
1339 &mut engine,
1340 &chunk1,
1341 measurement_to_sequential_mapper(&mut offset),
1342 )
1343 .expect("chunk1");
1344 append_flows(
1345 &mut engine,
1346 &chunk2,
1347 measurement_to_sequential_mapper(&mut offset),
1348 )
1349 .expect("chunk2");
1350
1351 let completed = engine.finish().expect("clean finish");
1352 assert_eq!(completed.len(), 1);
1353 assert_eq!(completed[0].measurements.as_slice(), [1, 0]);
1354 assert!(completed[0].sign);
1355 assert_eq!(completed[0].center, Some(IVec2::new(1, 1)));
1356 }
1357
1358 #[test]
1359 fn boundary_composition_accepts_an_equivalent_generator_basis() {
1360 let a = x(IVec2::new(0, 0));
1361 let b = z(IVec2::new(1, 0));
1362 let ab = &a ^ &b;
1363 let creators = [
1364 flow(PauliMap::empty(), a.clone(), Some(0)),
1365 flow(PauliMap::empty(), b, Some(1)),
1366 ];
1367 let consumers = [
1368 flow(ab, PauliMap::empty(), Some(2)),
1369 flow(a, PauliMap::empty(), Some(3)),
1370 ];
1371 let mut engine = FlowEngine::<u32>::new();
1372 engine
1373 .append_group_allowing_dangling(
1374 &[OffsetFlows::new(&creators, IVec2::ZERO)],
1375 |_, &measurement| measurement,
1376 )
1377 .expect("open the original basis");
1378 engine
1379 .append_group_allowing_stabilizer_transition(
1380 &[OffsetFlows::new(&consumers, IVec2::ZERO)],
1381 |_, &measurement| measurement,
1382 )
1383 .expect("consume the equivalent basis");
1384
1385 let mut completed = engine.finish().expect("the rebased seam closes");
1386 completed.sort_by_key(|flow| flow.measurements[0]);
1387 assert_eq!(completed[0].measurements.as_slice(), [2, 0, 1]);
1388 assert_eq!(completed[1].measurements.as_slice(), [3, 0]);
1389 }
1390
1391 #[test]
1392 fn indexed_pivots_match_eager_dependency_masks() {
1393 for seed in 0..64u32 {
1394 let generators = (0..75u32)
1395 .map(|index| {
1396 let bits = seed
1397 .wrapping_mul(0x9e37_79b9)
1398 .wrapping_add(index.wrapping_mul(0x85eb_ca6b));
1399 pauli_map(
1400 [
1401 IVec2::new(i32::MIN, 0),
1402 IVec2::ZERO,
1403 IVec2::Y,
1404 IVec2::X,
1405 IVec2::new(i32::MAX, 0),
1406 ]
1407 .into_iter()
1408 .enumerate()
1409 .map(|(site, coord)| {
1410 (
1411 coord,
1412 [Pauli::I, Pauli::X, Pauli::Y, Pauli::Z]
1413 [((bits >> (2 * site)) & 3) as usize],
1414 )
1415 }),
1416 )
1417 })
1418 .collect::<Vec<_>>();
1419 let generators: Vec<&PauliMap> = generators.iter().collect();
1420 let actual = pauli_dependencies(&generators)
1421 .into_iter()
1422 .map(|(index, mask)| {
1423 (
1424 index,
1425 (0..generators.len())
1426 .map(|bit| mask[bit / 64] & (1 << (bit % 64)) != 0)
1427 .collect::<Vec<_>>(),
1428 )
1429 })
1430 .collect::<Vec<_>>();
1431 assert_eq!(actual, eager_pauli_dependencies(&generators), "seed {seed}");
1432 }
1433 }
1434
1435 #[test]
1436 fn sparse_commutation_matches_product_phase() {
1437 let maps: Vec<_> = (0..128)
1438 .map(|bits| {
1439 pauli_map(
1440 [IVec2::new(-2, 1), IVec2::ZERO, IVec2::new(5, -1)]
1441 .into_iter()
1442 .enumerate()
1443 .map(|(index, coord)| {
1444 (
1445 coord,
1446 [Pauli::I, Pauli::X, Pauli::Y, Pauli::Z][(bits >> (2 * index)) & 3],
1447 )
1448 }),
1449 )
1450 })
1451 .collect();
1452 for offset in [IVec2::ZERO, IVec2::X, IVec2::new(9, 0)] {
1453 let measured = maps
1456 .iter()
1457 .take(64)
1458 .map(|map| map.translated(offset))
1459 .collect::<Vec<_>>();
1460 let columns = pauli_commutation_columns(maps.iter(), measured.iter());
1461 for (row, a) in maps.iter().enumerate() {
1462 for (column, b) in measured.iter().enumerate() {
1463 let expected = a.product_phase(b) % 2 == 1;
1464 assert_eq!(columns[column][row / 64] & (1 << (row % 64)) != 0, expected);
1465 }
1466 }
1467 for removed in [0, 63, 64, 127] {
1468 let mut remaining = maps.clone();
1469 remaining.swap_remove(removed);
1470 let mut shortened = columns.clone();
1471 for column in &mut shortened {
1472 swap_remove_bit(column, removed, maps.len() - 1);
1473 }
1474 let expected = pauli_commutation_columns(remaining.iter(), measured.iter());
1475 assert_eq!(shortened, expected);
1476 }
1477 }
1478 }
1479
1480 #[test]
1481 fn borrowed_flow_keys_match_translated_maps_and_reject_overflow() {
1482 let local = pauli_map([(IVec2::ZERO, Pauli::X), (IVec2::X, Pauli::Z)]);
1483 let offset = IVec2::new(-7, 13);
1484 let translated = local.translated(offset);
1485 let key = FlowKey::try_new(&local, offset).unwrap();
1486 let placed = FlowKey::try_new(&translated, IVec2::ZERO).unwrap();
1487 let mut keys = FxHashMap::default();
1488 keys.insert(key, 42);
1489 assert_eq!(keys.get(&placed), Some(&42));
1490 assert_ne!(key, FlowKey::try_new(&local, IVec2::ZERO).unwrap());
1491 FlowKey::try_new(&local, IVec2::new(i32::MAX, 0)).unwrap_err();
1492 }
1493
1494 #[test]
1495 fn stabilizer_products_keep_their_sign_through_basis_and_gauge_changes() {
1496 let a = IVec2::ZERO;
1497 let b = IVec2::X;
1498 let pair = |pauli| pauli_map([(a, pauli), (b, pauli)]);
1499 let creators = [
1500 flow(PauliMap::empty(), pair(Pauli::X), Some(0)),
1501 flow(PauliMap::empty(), pair(Pauli::Z), Some(1)),
1502 ];
1503 let measured = pauli_map([(a, Pauli::X), (b, Pauli::Z)]);
1504 let gauge = [
1505 flow(measured.clone(), PauliMap::empty(), Some(2)),
1506 flow(PauliMap::empty(), measured, Some(2)),
1507 ];
1508 let consumers = [flow(pair(Pauli::Y), PauliMap::empty(), Some(3))];
1509 for change_gauge in [false, true] {
1510 let mut engine = FlowEngine::<u32>::new();
1511 engine
1512 .append_group_allowing_dangling(
1513 &[OffsetFlows::new(&creators, IVec2::ZERO)],
1514 |_, &m| m,
1515 )
1516 .unwrap();
1517 if change_gauge {
1518 engine
1519 .append_group_allowing_stabilizer_transition(
1520 &[OffsetFlows::new(&gauge, IVec2::ZERO)],
1521 |_, &m| m,
1522 )
1523 .unwrap();
1524 }
1525 engine
1526 .append_group_allowing_stabilizer_transition(
1527 &[OffsetFlows::new(&consumers, IVec2::ZERO)],
1528 |_, &m| m,
1529 )
1530 .unwrap();
1531 let completed = engine.finish().unwrap();
1532 assert_eq!(completed.len(), 1);
1533 let parity =
1534 DetectorParity::from_measurements(completed[0].measurements.iter().copied())
1535 .with_sign(completed[0].sign);
1536 assert_eq!(
1537 parity,
1538 DetectorParity::from_measurements([0, 1, 3]).with_sign(true),
1539 "XX * ZZ = -YY, including when their product survives as a latent check"
1540 );
1541 }
1542 }
1543
1544 #[test]
1545 fn a_group_reads_only_boundaries_from_earlier_groups() {
1546 let (a, b) = (x(IVec2::ZERO), x(IVec2::X));
1547 let swap = [
1548 flow(a.clone(), b.clone(), None),
1549 flow(b.clone(), a.clone(), None),
1550 ];
1551 let mut residual = FlowEngine::<u32>::new();
1552 residual
1553 .append_residual_group(&[OffsetFlows::new(&swap, IVec2::ZERO)], |_, &m| m)
1554 .unwrap();
1555 let residuals = residual.into_residual_flows();
1556 assert_eq!(residuals.len(), 2);
1557 assert!(residuals.iter().any(|f| f.start == a && f.end == b));
1558 assert!(residuals.iter().any(|f| f.start == b && f.end == a));
1559
1560 let creators = [
1561 flow(PauliMap::empty(), a.clone(), Some(0)),
1562 flow(PauliMap::empty(), b.clone(), Some(1)),
1563 ];
1564 let consumers = [
1565 flow(a, PauliMap::empty(), Some(2)),
1566 flow(b, PauliMap::empty(), Some(3)),
1567 ];
1568 let mut engine = FlowEngine::<u32>::new();
1569 for group in [&creators, &swap, &consumers] {
1570 engine
1571 .append_group_allowing_dangling(&[OffsetFlows::new(group, IVec2::ZERO)], |_, &m| m)
1572 .unwrap();
1573 }
1574 let completed = engine.finish().unwrap();
1575 assert_eq!(completed[0].measurements.as_slice(), [2, 1]);
1576 assert_eq!(completed[1].measurements.as_slice(), [3, 0]);
1577 }
1578
1579 #[test]
1580 fn boundary_composition_matches_an_exact_generator_basis() {
1581 let a = x(IVec2::new(0, 0));
1582 let b = z(IVec2::new(1, 0));
1583 let creators = [
1584 flow(PauliMap::empty(), a.clone(), Some(0)),
1585 flow(PauliMap::empty(), b.clone(), Some(1)),
1586 ];
1587 let consumers = [
1588 flow(a, PauliMap::empty(), Some(2)),
1589 flow(b, PauliMap::empty(), Some(3)),
1590 ];
1591 let mut engine = FlowEngine::<u32>::new();
1592 engine
1593 .append_group_allowing_dangling(
1594 &[OffsetFlows::new(&creators, IVec2::ZERO)],
1595 |_, &measurement| measurement,
1596 )
1597 .expect("open the basis");
1598 engine
1599 .append_group_allowing_stabilizer_transition(
1600 &[OffsetFlows::new(&consumers, IVec2::ZERO)],
1601 |_, &measurement| measurement,
1602 )
1603 .expect("consume the same basis");
1604
1605 let completed = engine.finish().expect("the seam closes");
1606 assert_eq!(completed[0].measurements.as_slice(), [2, 0]);
1607 assert_eq!(completed[1].measurements.as_slice(), [3, 1]);
1608 }
1609
1610 #[test]
1611 fn gauge_transition_carries_only_commuting_old_products() {
1612 let z0 = z(IVec2::new(0, 0));
1613 let z1 = z(IVec2::new(1, 0));
1614 let joint_x = &x(IVec2::new(0, 0)) ^ &x(IVec2::new(1, 0));
1615 let old_creators = [
1616 flow(PauliMap::empty(), z0.clone(), Some(0)),
1617 flow(PauliMap::empty(), z1.clone(), Some(1)),
1618 ];
1619 let merge = [
1620 flow(joint_x.clone(), PauliMap::empty(), Some(2)),
1621 flow(PauliMap::empty(), joint_x, Some(3)),
1622 ];
1623 let split_left = [flow(z0, PauliMap::empty(), Some(4))];
1624 let split_right = [flow(z1, PauliMap::empty(), Some(5))];
1625
1626 let mut engine = FlowEngine::<u32>::new();
1627 engine
1628 .append_group_allowing_dangling(
1629 &[OffsetFlows::new(&old_creators, IVec2::ZERO)],
1630 |_, &measurement| measurement,
1631 )
1632 .expect("open the old basis");
1633 engine
1634 .append_group_allowing_stabilizer_transition(
1635 &[OffsetFlows::new(&merge, IVec2::ZERO)],
1636 |_, &measurement| measurement,
1637 )
1638 .expect("measure the temporary gauge");
1639 engine
1640 .append_group_allowing_stabilizer_transition(
1641 &[OffsetFlows::new(&split_left, IVec2::ZERO)],
1642 |_, &measurement| measurement,
1643 )
1644 .expect("restore the left half of the old basis");
1645 engine
1646 .append_group_allowing_stabilizer_transition(
1647 &[OffsetFlows::new(&split_right, IVec2::ZERO)],
1648 |_, &measurement| measurement,
1649 )
1650 .expect("restore the right half of the old basis");
1651
1652 let completed = engine.finish().expect("the restored basis closes");
1653 assert_eq!(completed.len(), 1);
1654 let mut measurements = completed[0].measurements.to_vec();
1655 measurements.sort_unstable();
1656 assert_eq!(measurements, [0, 1, 4, 5]);
1657 }
1658
1659 #[test]
1660 fn flow_marker_survives_fusion() {
1661 for marker in [FlowMarker::Discard, FlowMarker::Restart] {
1662 let mut engine = FlowEngine::<u32>::new();
1663 let mut offset = 0u32;
1664 let boundary = x(IVec2::new(0, 0));
1665 let creator = [Flow::new(PauliMap::empty(), boundary.clone())
1666 .with_measurements([0])
1667 .with_marker(marker)];
1668 let consumer = [Flow::new(boundary, PauliMap::empty()).with_measurements([0])];
1669
1670 append_flows(
1671 &mut engine,
1672 &creator,
1673 measurement_to_sequential_mapper(&mut offset),
1674 )
1675 .expect("creator");
1676 append_flows(
1677 &mut engine,
1678 &consumer,
1679 measurement_to_sequential_mapper(&mut offset),
1680 )
1681 .expect("consumer");
1682
1683 let completed = engine.finish().expect("clean finish");
1684 assert_eq!(completed.len(), 1);
1685 assert_eq!(completed[0].marker, marker);
1686 }
1687 }
1688
1689 #[test]
1693 fn restart_marker_survives_residual_extraction() {
1694 let mut engine = FlowEngine::<u32>::new();
1695 let creator = [Flow::new(PauliMap::empty(), z(IVec2::new(0, 0)))
1696 .with_measurements([0])
1697 .with_marker(FlowMarker::Restart)];
1698 engine
1699 .append_residual_group(&[OffsetFlows::new(&creator, IVec2::ZERO)], |_, &m| m)
1700 .expect("creator");
1701
1702 let residual = engine.into_residual_flows();
1703 assert_eq!(residual.len(), 1);
1704 assert_eq!(residual[0].marker, FlowMarker::Restart);
1705 }
1706
1707 #[test]
1708 fn consumer_before_creator_ordering() {
1709 let mut engine = FlowEngine::<u32>::new();
1710 let mut offset = 0u32;
1711
1712 let shared_key = x(IVec2::new(0, 0));
1713 let predecessor = [flow(PauliMap::empty(), shared_key.clone(), Some(0))];
1714 append_flows(
1715 &mut engine,
1716 &predecessor,
1717 measurement_to_sequential_mapper(&mut offset),
1718 )
1719 .expect("predecessor");
1720
1721 let time_reversed_chunk = [
1722 flow(PauliMap::empty(), shared_key.clone(), Some(0)),
1723 flow(shared_key.clone(), PauliMap::empty(), Some(0)),
1724 ];
1725
1726 append_flows(
1727 &mut engine,
1728 &time_reversed_chunk,
1729 measurement_to_sequential_mapper(&mut offset),
1730 )
1731 .expect("time-reversed chunk should succeed when consumers are processed first");
1732
1733 let keys: Vec<_> = engine.open_flows.keys().map(FlowKey::translated).collect();
1734 assert_eq!(keys.len(), 1);
1735 assert_eq!(keys[0], shared_key);
1736 assert_eq!(engine.drain_completed().len(), 1);
1737 }
1738
1739 #[test]
1740 fn flow_errors_cover_unmatched_and_unterminated() {
1741 let mut offset = 0u32;
1742
1743 let mut unmatched = FlowEngine::<u32>::new();
1744 let orphan = [flow(x(IVec2::new(0, 0)), PauliMap::empty(), Some(0))];
1745 let err = append_flows(
1746 &mut unmatched,
1747 &orphan,
1748 measurement_to_sequential_mapper(&mut offset),
1749 )
1750 .expect_err("unmatched input");
1751 assert_eq!(
1752 err,
1753 FlowError::Composition(format!("unmatched flow input {}", orphan[0].start))
1754 );
1755
1756 let mut unterminated = FlowEngine::<u32>::new();
1757 let open_ended = [flow(PauliMap::empty(), z(IVec2::new(2, 0)), Some(0))];
1758 append_flows(
1759 &mut unterminated,
1760 &open_ended,
1761 measurement_to_sequential_mapper(&mut offset),
1762 )
1763 .expect("seed");
1764 let err = unterminated.finish().expect_err("unterminated flow");
1765 assert_eq!(
1766 err,
1767 FlowError::Composition(format!("unterminated flows: {}", open_ended[0].end))
1768 );
1769 }
1770
1771 #[test]
1772 fn duplicate_creators_are_rejected_instead_of_overwriting_parity() {
1773 let boundary = z(IVec2::new(2, 0));
1774 let creators = [
1775 flow(PauliMap::empty(), boundary.clone(), Some(0)),
1776 flow(PauliMap::empty(), boundary.clone(), Some(1)),
1777 ];
1778 let mut engine = FlowEngine::<u32>::new();
1779
1780 let error = append_flows(&mut engine, &creators, |measurement| *measurement)
1781 .expect_err("two creators of one boundary must collide");
1782
1783 assert_eq!(
1784 error,
1785 FlowError::Composition(format!("multiple flows create boundary {boundary}"))
1786 );
1787 }
1788
1789 #[test]
1790 fn loop_state_id_exhaustion_is_an_error() {
1791 let boundaries = [x(IVec2::ZERO), x(IVec2::X)];
1792 let creator = [
1793 flow(PauliMap::empty(), boundaries[0].clone(), Some(7)),
1794 flow(PauliMap::empty(), boundaries[1].clone(), Some(8)),
1795 ];
1796 let consumer = [
1797 flow(boundaries[0].clone(), PauliMap::empty(), None),
1798 flow(boundaries[1].clone(), PauliMap::empty(), None),
1799 ];
1800 let mut engine = FlowEngine::<DetectorTerm<u32>>::new();
1801 engine
1802 .append_group_allowing_dangling(
1803 &[OffsetFlows::new(&creator, IVec2::ZERO)],
1804 |_, &measurement| DetectorTerm::Measurement(measurement),
1805 )
1806 .expect("creator");
1807 let mut next_loop_state = u32::MAX - 1;
1808
1809 let error = engine
1810 .start_loop_detector_states(
1811 &[OffsetFlows::new(&consumer, IVec2::ZERO)],
1812 &mut next_loop_state,
1813 )
1814 .expect_err("loop state id space is exhausted");
1815
1816 assert_eq!(
1817 error,
1818 FlowError::Composition("loop detector state id space exhausted".into())
1819 );
1820 assert_eq!(next_loop_state, u32::MAX - 1);
1821 assert!(engine.open_flows.values().all(|flow| {
1822 matches!(
1823 flow.measurements.as_slice(),
1824 [DetectorTerm::Measurement(7 | 8)]
1825 )
1826 }));
1827 }
1828
1829 #[test]
1830 fn loop_state_start_rejects_coordinate_overflow_before_mutating() {
1831 let coordinate = IVec2::new(i32::MAX, 0);
1832 let offset = IVec2::X;
1833 let creator = [flow(PauliMap::empty(), x(IVec2::new(i32::MIN, 0)), Some(7))];
1834 let consumer = [flow(x(coordinate), PauliMap::empty(), None)];
1835 let mut engine = FlowEngine::<DetectorTerm<u32>>::new();
1836 engine
1837 .append_group_allowing_dangling(
1838 &[OffsetFlows::new(&creator, IVec2::ZERO)],
1839 |_, &measurement| DetectorTerm::Measurement(measurement),
1840 )
1841 .expect("creator");
1842 let mut next_loop_state = 0;
1843
1844 let error = engine
1845 .start_loop_detector_states(
1846 &[OffsetFlows::new(&consumer, offset)],
1847 &mut next_loop_state,
1848 )
1849 .expect_err("overflowing loop boundary");
1850
1851 assert_eq!(
1852 error,
1853 FlowError::CoordinateOverflow(crate::CoordinateOverflowError { coordinate, offset })
1854 );
1855 assert_eq!(next_loop_state, 0);
1856 assert!(matches!(
1857 engine
1858 .open_flows
1859 .values()
1860 .next()
1861 .unwrap()
1862 .measurements
1863 .as_slice(),
1864 [DetectorTerm::Measurement(7)]
1865 ));
1866 }
1867
1868 #[test]
1869 fn loop_state_preflight_counts_only_matching_flows() {
1870 let boundary = x(IVec2::ZERO);
1871 let creator = [flow(PauliMap::empty(), boundary.clone(), Some(7))];
1872 let consumers = [
1873 flow(x(IVec2::X), PauliMap::empty(), None),
1874 flow(boundary, PauliMap::empty(), None),
1875 ];
1876 let mut engine = FlowEngine::<DetectorTerm<u32>>::new();
1877 engine
1878 .append_group_allowing_dangling(
1879 &[OffsetFlows::new(&creator, IVec2::ZERO)],
1880 |_, &measurement| DetectorTerm::Measurement(measurement),
1881 )
1882 .expect("creator");
1883 let mut next_loop_state = u32::MAX - 1;
1884
1885 let pending = engine
1886 .start_loop_detector_states(
1887 &[OffsetFlows::new(&consumers, IVec2::ZERO)],
1888 &mut next_loop_state,
1889 )
1890 .expect("only the matching flow needs a state");
1891
1892 assert_eq!(pending.loop_states.len(), 1);
1893 assert_eq!(next_loop_state, u32::MAX);
1894 }
1895
1896 #[test]
1897 fn drain_completed_allows_open_boundaries() {
1898 let mut engine = FlowEngine::<u32>::new();
1899 let mut offset = 0u32;
1900
1901 let chunk = [
1902 flow(PauliMap::empty(), PauliMap::empty(), Some(0)),
1903 flow(PauliMap::empty(), x(IVec2::new(0, 0)), Some(0)),
1904 ];
1905
1906 append_flows(
1907 &mut engine,
1908 &chunk,
1909 measurement_to_sequential_mapper(&mut offset),
1910 )
1911 .expect("chunk");
1912
1913 assert_eq!(engine.drain_completed().len(), 1);
1914 }
1915
1916 #[test]
1917 fn residual_extraction_fuses_a_chain_across_chunks() {
1918 let mut engine = FlowEngine::<u32>::new();
1922 let creator = [flow(PauliMap::empty(), z(IVec2::new(0, 0)), Some(0))];
1923 let mover = [flow(z(IVec2::new(0, 0)), z(IVec2::new(1, 0)), Some(1))];
1924 engine
1925 .append_residual_group(&[OffsetFlows::new(&creator, IVec2::ZERO)], |_, &m| m)
1926 .expect("creator");
1927 engine
1928 .append_residual_group(&[OffsetFlows::new(&mover, IVec2::ZERO)], |_, &m| m)
1929 .expect("mover");
1930
1931 let residual = engine.into_residual_flows();
1932 assert_eq!(residual.len(), 1, "the chain fuses to a single flow");
1933 assert!(residual[0].start.is_empty());
1934 assert_eq!(residual[0].end, z(IVec2::new(1, 0)));
1935 let mut measurements = residual[0].measurements.to_vec();
1936 measurements.sort_unstable();
1937 assert_eq!(measurements, [0, 1]);
1938 }
1939
1940 #[test]
1941 fn residual_extraction_keeps_opposite_boundary_faces_separate() {
1942 let mut engine = FlowEngine::<u32>::new();
1947 let faces = [
1948 flow(z(IVec2::new(0, 0)), PauliMap::empty(), Some(0)),
1949 flow(PauliMap::empty(), z(IVec2::new(0, 0)), Some(1)),
1950 ];
1951 engine
1952 .append_residual_group(&[OffsetFlows::new(&faces, IVec2::ZERO)], |_, &m| m)
1953 .expect("faces");
1954
1955 let mut residual = engine.into_residual_flows();
1956 residual.sort_by_key(|flow| flow.measurements.first().copied());
1957 assert_eq!(residual.len(), 2, "opposite faces are not fused");
1958 assert_eq!(residual[0].start, z(IVec2::new(0, 0)));
1960 assert!(residual[0].end.is_empty());
1961 assert!(residual[1].start.is_empty());
1963 assert_eq!(residual[1].end, z(IVec2::new(0, 0)));
1964 }
1965}