Skip to main content

bloq_compile/
config.rs

1use bloq_graph::{BlockGraph, ModuleCertificationLimits};
2
3use crate::error::InvalidDistance;
4
5/// Largest supported surface code distance.
6///
7/// Fixed-bulk lowering materializes grids with area quadratic in the distance;
8/// this practical limit rejects inputs that would otherwise exhaust memory.
9pub const MAX_CODE_DISTANCE: u32 = 255;
10
11/// Warning shown by front ends when a fixed-bulk graph contains a spatial
12/// Hadamard wall. The wall's measured effective distance can be below the
13/// requested code distance.
14const SPATIAL_HADAMARD_DISTANCE_WARNING: &str = "fixed-bulk spatial Hadamard pipes can reduce the effective circuit distance below the requested code distance; check the emitted circuit's distance before relying on it";
15
16/// Parameters that determine how a [`BlockGraph`](bloq_graph::BlockGraph) is
17/// lowered: the surface code distance and T-source mode.
18///
19/// A valid surface code distance is odd and in `3..=MAX_CODE_DISTANCE`; the
20/// upper bound limits the compiler's quadratic grid materialization. The fields
21/// are private so this invariant holds by construction. Build one with
22/// [`try_new`] (the fallible public path) or [`new`] (panics on an invalid
23/// distance, for known-good internal literals).
24///
25/// [`try_new`]: CompileConfig::try_new
26/// [`new`]: CompileConfig::new
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
28pub struct CompileConfig {
29    code_distance: u32,
30    prepare_t_with_mpps: bool,
31    certification_limits: ModuleCertificationLimits,
32}
33
34impl CompileConfig {
35    /// Whether `code_distance` is a distance the compiler accepts: odd and in
36    /// `3..=`[`MAX_CODE_DISTANCE`].
37    ///
38    /// The predicate form of [`try_new`](Self::try_new), for front ends that
39    /// validate an input field before there is a graph to compile.
40    ///
41    /// # Examples
42    ///
43    /// ```
44    /// use bloq_compile::CompileConfig;
45    ///
46    /// assert!(CompileConfig::is_valid_distance(3));
47    /// assert!(!CompileConfig::is_valid_distance(4));
48    /// assert!(!CompileConfig::is_valid_distance(1));
49    /// ```
50    #[must_use]
51    pub const fn is_valid_distance(code_distance: u32) -> bool {
52        code_distance >= 3 && code_distance <= MAX_CODE_DISTANCE && code_distance % 2 == 1
53    }
54
55    /// Builds a config, validating the documented distance range and parity.
56    ///
57    /// # Errors
58    ///
59    /// Returns [`InvalidDistance`] unless
60    /// [`is_valid_distance`](Self::is_valid_distance) accepts `code_distance`.
61    pub fn try_new(code_distance: u32) -> Result<Self, InvalidDistance> {
62        if !Self::is_valid_distance(code_distance) {
63            return Err(InvalidDistance(code_distance));
64        }
65        Ok(Self {
66            code_distance,
67            prepare_t_with_mpps: false,
68            certification_limits: ModuleCertificationLimits::DEFAULT,
69        })
70    }
71
72    /// Builds a config from a known-valid distance.
73    ///
74    /// # Panics
75    ///
76    /// Panics if `code_distance` is not odd and in `3..=255`; use [`try_new`](Self::try_new)
77    /// for distances that come from user input.
78    #[must_use]
79    pub fn new(code_distance: u32) -> Self {
80        Self::try_new(code_distance).expect("code distance must be odd and in 3..=255")
81    }
82
83    /// The surface code distance (odd and in `3..=MAX_CODE_DISTANCE`).
84    #[must_use]
85    pub fn code_distance(&self) -> u32 {
86        self.code_distance
87    }
88
89    /// Whether T blocks use the compact product-state preparation followed by
90    /// one stabilizer `MPP`, instead of MSC-LS cultivation.
91    #[must_use]
92    pub fn prepare_t_with_mpps(&self) -> bool {
93        self.prepare_t_with_mpps
94    }
95
96    /// Returns this config with compact T-block preparation enabled or disabled.
97    #[must_use]
98    pub const fn with_prepare_t_with_mpps(mut self, enabled: bool) -> Self {
99        self.prepare_t_with_mpps = enabled;
100        self
101    }
102
103    /// Limits applied while certifying a module hierarchy.
104    #[must_use]
105    pub const fn certification_limits(&self) -> ModuleCertificationLimits {
106        self.certification_limits
107    }
108
109    /// Returns this config with explicit module-certification limits.
110    #[must_use]
111    pub const fn with_certification_limits(mut self, limits: ModuleCertificationLimits) -> Self {
112        self.certification_limits = limits;
113        self
114    }
115}
116
117/// Return the user-facing distance warning when `graph` uses a construction
118/// whose fixed-bulk circuit distance is not guaranteed to equal `d`.
119///
120/// This is the pre-flight form, for front ends that warn before compiling. A
121/// completed compilation reports the same advisory in
122/// [`CompileArtifacts::warnings`](crate::CompileArtifacts::warnings), which
123/// cannot be forgotten.
124#[must_use]
125pub fn spatial_hadamard_distance_warning(graph: &BlockGraph) -> Option<&'static str> {
126    graph
127        .pipes()
128        .any(|pipe| pipe.is_hadamard() && pipe.dir().is_spatial())
129        .then_some(SPATIAL_HADAMARD_DISTANCE_WARNING)
130}
131
132impl Default for CompileConfig {
133    fn default() -> Self {
134        Self::new(3)
135    }
136}
137
138#[cfg(test)]
139mod tests {
140    use super::*;
141    use bloq_graph::{Block, BlockKind, Direction, Pipe};
142    use glam::IVec3;
143
144    #[test]
145    fn distance_bound_limits_quadratic_grid_materialization() {
146        assert_eq!(
147            CompileConfig::try_new(MAX_CODE_DISTANCE)
148                .unwrap()
149                .code_distance(),
150            MAX_CODE_DISTANCE
151        );
152        assert_eq!(
153            CompileConfig::try_new(MAX_CODE_DISTANCE + 2),
154            Err(InvalidDistance(MAX_CODE_DISTANCE + 2))
155        );
156    }
157
158    #[test]
159    fn spatial_hadamard_distance_warning_only_matches_fixed_bulk_walls() {
160        let mut graph = BlockGraph::new();
161        graph.add_block(Block::new(
162            IVec3::ZERO,
163            BlockKind::Cube(bloq_graph::CubeKind::XZX),
164        ));
165        graph.add_block(Block::new(
166            IVec3::X,
167            BlockKind::Cube(bloq_graph::CubeKind::XXZ),
168        ));
169        graph.add_pipe(Pipe::new(IVec3::ZERO, Direction::XPLUS).with_hadamard());
170
171        assert_eq!(
172            spatial_hadamard_distance_warning(&graph),
173            Some(SPATIAL_HADAMARD_DISTANCE_WARNING)
174        );
175        graph
176            .set_pipe_hadamard(IVec3::ZERO, IVec3::X, false)
177            .unwrap();
178        graph.add_block(Block::new(
179            IVec3::Z,
180            BlockKind::Cube(bloq_graph::CubeKind::XZX),
181        ));
182        graph.add_pipe(Pipe::new(IVec3::ZERO, Direction::ZPLUS).with_hadamard());
183        assert!(spatial_hadamard_distance_warning(&graph).is_none());
184    }
185}