bloq_circuit/chunk.rs
1use glam::IVec2;
2use smallvec::SmallVec;
3
4use crate::{CoordCircuit, PauliMap};
5
6const FLOW_MEASUREMENTS_INLINE_CAP: usize = 4;
7
8/// The measurement ids a [`Flow`] carries, kept inline for the common small
9/// case.
10pub type FlowMeasurements = SmallVec<[u32; FLOW_MEASUREMENTS_INLINE_CAP]>;
11
12/// How a completed flow chain's parity is consumed downstream.
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
14pub enum FlowMarker {
15 /// The completed chain emits a detector.
16 #[default]
17 Detector,
18 /// The chain still participates in matching/fusion (so an incoming
19 /// stabilizer is consumed and nothing dangles) but emits no detector. A
20 /// transversal Pauli measurement uses this to suppress the opposite-basis
21 /// stabilizer parities it randomizes.
22 Discard,
23 /// The chain emits a **restart parity** (a `RepeatUntilSuccess`
24 /// post-selection syndrome) instead of a detector: odd parity restarts the
25 /// enclosing attempt rather than feeding the decoder.
26 Restart,
27}
28
29impl FlowMarker {
30 /// Combine a flow's marker with the marker of the chain it fuses into.
31 /// [`Detector`](FlowMarker::Detector) is the identity. Returns `None` when a
32 /// [`Discard`](FlowMarker::Discard) component meets a
33 /// [`Restart`](FlowMarker::Restart) one — a topology-dependent conflict
34 /// (not any one builder's invariant) that the caller rejects.
35 pub(crate) fn fuse(self, other: FlowMarker) -> Option<FlowMarker> {
36 match (self, other) {
37 (marker, FlowMarker::Detector) | (FlowMarker::Detector, marker) => Some(marker),
38 (a, b) if a == b => Some(a),
39 _ => None,
40 }
41 }
42}
43
44/// A stabilizer flow: a Pauli boundary condition `start` propagated through a
45/// chunk to `end`, together with the measurements it accumulates.
46///
47/// An empty `start` marks a flow created inside the chunk (nothing enters);
48/// an empty `end` marks one that terminates in the chunk (nothing leaves).
49/// [`FlowEngine`](crate::FlowEngine) fuses flows whose boundaries match across
50/// chunks into detector chains.
51#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
52pub struct Flow {
53 /// The Pauli boundary entering the chunk; empty if the flow originates
54 /// fully from here.
55 pub start: PauliMap,
56 /// The Pauli boundary leaving the chunk; empty if the flow terminates here.
57 pub end: PauliMap,
58 /// Measurement ids the flow accumulates as it crosses the chunk.
59 pub measurements: FlowMeasurements,
60 /// Whether the stabilizer relation has negative sign. Equivalently, this
61 /// is the expected XOR of `measurements` when both boundaries have positive
62 /// eigenvalue.
63 pub sign: bool,
64 /// Coordinate for the flow center, if any.
65 pub center: Option<IVec2>,
66 /// How the completed chain's parity is consumed. See [`FlowMarker`].
67 pub marker: FlowMarker,
68}
69
70impl Flow {
71 /// A flow from `start` to `end` with no measurements, no center, and a
72 /// [`FlowMarker::Detector`] marker. Chain the `with_*` methods to set the
73 /// rest, so adding a new field does not churn every construction site.
74 pub fn new(start: PauliMap, end: PauliMap) -> Self {
75 Self {
76 start,
77 end,
78 measurements: FlowMeasurements::new(),
79 sign: false,
80 center: None,
81 marker: FlowMarker::Detector,
82 }
83 }
84
85 /// Replaces the carried measurement parity.
86 pub fn with_measurements(mut self, measurements: impl IntoIterator<Item = u32>) -> Self {
87 self.measurements = measurements.into_iter().collect();
88 self
89 }
90
91 /// Sets whether the stabilizer relation is negative.
92 pub fn with_sign(mut self, sign: bool) -> Self {
93 self.sign = sign;
94 self
95 }
96
97 /// Sets the optional flow center.
98 pub fn with_center(mut self, center: impl Into<Option<IVec2>>) -> Self {
99 self.center = center.into();
100 self
101 }
102
103 /// Sets how downstream compilation consumes this flow.
104 pub fn with_marker(mut self, marker: FlowMarker) -> Self {
105 self.marker = marker;
106 self
107 }
108}
109
110impl std::fmt::Display for Flow {
111 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112 write!(f, "{} -> {}", self.start, self.end)?;
113 for m in &self.measurements {
114 write!(f, "*meas[{m:?}]")?;
115 }
116 if self.sign {
117 write!(f, " [negative]")?;
118 }
119 if let Some(c) = self.center {
120 write!(f, ", center=({},{})", c.x, c.y)?;
121 }
122 match self.marker {
123 FlowMarker::Detector => {}
124 FlowMarker::Discard => write!(f, " [discard]")?,
125 FlowMarker::Restart => write!(f, " [restart]")?,
126 }
127 Ok(())
128 }
129}
130
131/// A circuit fragment paired with the stabilizer flows crossing its
132/// boundaries — the unit the compiler composes into a full program.
133#[derive(Debug, Clone)]
134pub struct Chunk {
135 /// The chunk's operations in absolute coordinates.
136 pub circuit: CoordCircuit,
137 /// Flows entering, crossing, and leaving this chunk.
138 pub flows: Vec<Flow>,
139}
140
141/// A chunk or a loop of chunks (for repeated syndrome extraction rounds).
142#[derive(Debug, Clone)]
143pub enum ChunkOrLoop {
144 /// A single, non-repeated chunk.
145 Single(Box<Chunk>),
146 /// A body of chunks executed `repetitions` times.
147 Loop {
148 /// Ordered loop body.
149 body: Vec<Chunk>,
150 /// Iteration count.
151 repetitions: u32,
152 },
153}