Skip to main content

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}