Skip to main content

qec_code/codes/
directional.rs

1use std::collections::{BTreeMap, BTreeSet};
2
3use serde::{Deserialize, Serialize};
4
5use crate::error::{QecError, Result};
6
7pub const DIRECTIONAL_CSS_CONSTRUCTION_ID: &str = "directional";
8
9const HEX_COMPATIBLE_NORMALIZED_ROUTES: &[&str] = &["NE3N"];
10
11type Coordinate = (i64, i64);
12
13#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
14#[serde(deny_unknown_fields)]
15pub struct DirectionalCssSpec {
16    pub torus: DirectionalTorusSpec,
17    pub route: String,
18    #[serde(default)]
19    pub layout: DirectionalLayoutSpec,
20    #[serde(default)]
21    pub connectivity: DirectionalConnectivity,
22}
23
24#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
25#[serde(deny_unknown_fields)]
26pub struct DirectionalTorusSpec {
27    pub period_x: usize,
28    pub period_y: usize,
29    #[serde(default)]
30    pub vertical_period_x_shift: usize,
31}
32
33#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
34#[serde(deny_unknown_fields)]
35pub struct DirectionalLayoutSpec {
36    #[serde(default = "default_x_ancilla_coset")]
37    pub x_ancilla_coset: DirectionalAncillaCoset,
38    #[serde(default = "default_z_ancilla_coset")]
39    pub z_ancilla_coset: DirectionalAncillaCoset,
40}
41
42impl Default for DirectionalLayoutSpec {
43    fn default() -> Self {
44        Self {
45            x_ancilla_coset: default_x_ancilla_coset(),
46            z_ancilla_coset: default_z_ancilla_coset(),
47        }
48    }
49}
50
51fn default_x_ancilla_coset() -> DirectionalAncillaCoset {
52    DirectionalAncillaCoset::OddEven
53}
54
55fn default_z_ancilla_coset() -> DirectionalAncillaCoset {
56    DirectionalAncillaCoset::EvenOdd
57}
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
60#[serde(rename_all = "snake_case")]
61pub enum DirectionalAncillaCoset {
62    OddEven,
63    EvenOdd,
64}
65
66impl DirectionalAncillaCoset {
67    fn contains(self, (x, y): Coordinate) -> bool {
68        match self {
69            Self::OddEven => x.rem_euclid(2) == 1 && y.rem_euclid(2) == 0,
70            Self::EvenOdd => x.rem_euclid(2) == 0 && y.rem_euclid(2) == 1,
71        }
72    }
73
74    fn translated(self, (x, y): Coordinate) -> Self {
75        match (self, x.rem_euclid(2), y.rem_euclid(2)) {
76            (Self::OddEven, 0, 0) | (Self::EvenOdd, 0, 0) => self,
77            (Self::OddEven, 1, 1) => Self::EvenOdd,
78            (Self::EvenOdd, 1, 1) => Self::OddEven,
79            _ => self,
80        }
81    }
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
85#[serde(rename_all = "snake_case")]
86pub enum DirectionalConnectivity {
87    #[default]
88    Square,
89    Hex,
90}
91
92#[derive(Debug, Clone, PartialEq, Eq)]
93pub struct DirectionalCssChecks {
94    pub code_id: &'static str,
95    pub num_cols: usize,
96    pub hx: Vec<Vec<usize>>,
97    pub hz: Vec<Vec<usize>>,
98    pub route_support: Vec<Coordinate>,
99    pub normalized_route: String,
100}
101
102pub fn parse_directional_route_support(route: &str) -> Result<Vec<Coordinate>> {
103    parse_route(route).map(|parsed| parsed.support)
104}
105
106pub fn build_directional_css_checks(spec: &DirectionalCssSpec) -> Result<DirectionalCssChecks> {
107    validate_torus(&spec.torus)?;
108    validate_layout(&spec.layout)?;
109
110    let parsed_route = parse_route(&spec.route)?;
111    validate_connectivity(spec.connectivity, &parsed_route.normalized)?;
112    validate_infinite_support(&parsed_route.support)?;
113    validate_odd_overlap(&parsed_route.support, &spec.layout)?;
114    validate_finite_torus(&parsed_route.support, &spec.torus)?;
115
116    let data_index = data_index(&spec.torus)?;
117    let hx = build_check_rows(
118        spec.layout.x_ancilla_coset,
119        &parsed_route.support,
120        &spec.torus,
121        &data_index,
122    )?;
123    let hz = build_check_rows(
124        spec.layout.z_ancilla_coset,
125        &parsed_route.support,
126        &spec.torus,
127        &data_index,
128    )?;
129
130    Ok(DirectionalCssChecks {
131        code_id: DIRECTIONAL_CSS_CONSTRUCTION_ID,
132        num_cols: data_index.len(),
133        hx,
134        hz,
135        route_support: parsed_route.support,
136        normalized_route: parsed_route.normalized,
137    })
138}
139
140#[derive(Debug)]
141struct ParsedRoute {
142    support: Vec<Coordinate>,
143    normalized: String,
144}
145
146fn parse_route(route: &str) -> Result<ParsedRoute> {
147    if route.is_empty() {
148        return invalid_route(route, "route must contain at least one direction");
149    }
150
151    let chars: Vec<char> = route.chars().collect();
152    let mut index = 0;
153    let mut previous = (0_i64, 0_i64);
154    let mut support = Vec::new();
155    let mut normalized_runs: Vec<(char, usize)> = Vec::new();
156    while index < chars.len() {
157        let direction = chars[index];
158        let displacement = match direction {
159            'N' => (0, 1),
160            'E' => (1, 0),
161            'S' => (0, -1),
162            'W' => (-1, 0),
163            _ => return invalid_route(route, format!("unexpected symbol {direction:?}")),
164        };
165        index += 1;
166
167        let digits_start = index;
168        while index < chars.len() && chars[index].is_ascii_digit() {
169            index += 1;
170        }
171        let repetitions = if digits_start == index {
172            1
173        } else {
174            let digits: String = chars[digits_start..index].iter().collect();
175            let repetitions =
176                digits
177                    .parse::<usize>()
178                    .map_err(|_| QecError::InvalidDirectionalRoute {
179                        route: route.to_owned(),
180                        reason: format!("repetition suffix {digits:?} is out of range"),
181                    })?;
182            if repetitions == 0 {
183                return invalid_route(route, "repetition suffix must be positive");
184            }
185            repetitions
186        };
187        if normalized_runs
188            .last()
189            .is_some_and(|(last_direction, _)| *last_direction == direction)
190        {
191            let (_, last_repetitions) = normalized_runs
192                .last_mut()
193                .expect("last normalized route run should exist");
194            *last_repetitions = last_repetitions.checked_add(repetitions).ok_or_else(|| {
195                QecError::InvalidDirectionalRoute {
196                    route: route.to_owned(),
197                    reason: "normalized route repetition overflow".to_owned(),
198                }
199            })?;
200        } else {
201            normalized_runs.push((direction, repetitions));
202        }
203
204        for _ in 0..repetitions {
205            let offset = (
206                previous
207                    .0
208                    .checked_mul(2)
209                    .and_then(|x| x.checked_add(displacement.0)),
210                previous
211                    .1
212                    .checked_mul(2)
213                    .and_then(|y| y.checked_add(displacement.1)),
214            );
215            let (Some(x), Some(y)) = offset else {
216                return invalid_route(route, "support offset overflow");
217            };
218            support.push((x, y));
219            previous.0 = previous.0.checked_add(displacement.0).ok_or_else(|| {
220                QecError::InvalidDirectionalRoute {
221                    route: route.to_owned(),
222                    reason: "route displacement overflow".to_owned(),
223                }
224            })?;
225            previous.1 = previous.1.checked_add(displacement.1).ok_or_else(|| {
226                QecError::InvalidDirectionalRoute {
227                    route: route.to_owned(),
228                    reason: "route displacement overflow".to_owned(),
229                }
230            })?;
231        }
232    }
233    let mut normalized = String::new();
234    for (direction, repetitions) in normalized_runs {
235        normalized.push(direction);
236        if repetitions > 1 {
237            normalized.push_str(&repetitions.to_string());
238        }
239    }
240
241    Ok(ParsedRoute {
242        support,
243        normalized,
244    })
245}
246
247fn invalid_route<T>(route: &str, reason: impl Into<String>) -> Result<T> {
248    Err(QecError::InvalidDirectionalRoute {
249        route: route.to_owned(),
250        reason: reason.into(),
251    })
252}
253
254fn validate_torus(torus: &DirectionalTorusSpec) -> Result<()> {
255    if torus.period_x == 0 || torus.period_x % 2 != 0 {
256        return invalid_spec("period_x must be positive and even");
257    }
258    if torus.period_y == 0 || torus.period_y % 2 != 0 {
259        return invalid_spec("period_y must be positive and even");
260    }
261    if torus.vertical_period_x_shift % 2 != 0 {
262        return invalid_spec(
263            "vertical_period_x_shift must be even to preserve checkerboard parity",
264        );
265    }
266    Ok(())
267}
268
269fn validate_layout(layout: &DirectionalLayoutSpec) -> Result<()> {
270    if layout.x_ancilla_coset == layout.z_ancilla_coset {
271        return invalid_spec("X and Z checks must use distinct ancilla cosets");
272    }
273    Ok(())
274}
275
276fn validate_connectivity(
277    connectivity: DirectionalConnectivity,
278    normalized_route: &str,
279) -> Result<()> {
280    if matches!(connectivity, DirectionalConnectivity::Hex)
281        && !HEX_COMPATIBLE_NORMALIZED_ROUTES.contains(&normalized_route)
282    {
283        return invalid_spec(format!(
284            "hex connectivity does not support normalized route {normalized_route}"
285        ));
286    }
287    Ok(())
288}
289
290fn validate_infinite_support(support: &[Coordinate]) -> Result<()> {
291    let unique: BTreeSet<_> = support.iter().copied().collect();
292    if unique.len() != support.len() {
293        return invalid_spec("route support contains duplicate offsets");
294    }
295    Ok(())
296}
297
298fn validate_odd_overlap(support: &[Coordinate], layout: &DirectionalLayoutSpec) -> Result<()> {
299    let mut delta_counts = BTreeMap::new();
300    for &left in support {
301        for &right in support {
302            if left != right {
303                *delta_counts.entry(subtract(left, right)).or_insert(0_usize) += 1;
304            }
305        }
306    }
307
308    for (delta, count) in delta_counts {
309        if count % 2 == 1 && layout.x_ancilla_coset.translated(delta) == layout.z_ancilla_coset {
310            return invalid_spec(format!(
311                "odd route-overlap delta ({}, {}) conflicts with the selected ancilla layout",
312                delta.0, delta.1
313            ));
314        }
315    }
316    Ok(())
317}
318
319fn validate_finite_torus(support: &[Coordinate], torus: &DirectionalTorusSpec) -> Result<()> {
320    let reduced: BTreeSet<_> = support
321        .iter()
322        .map(|&coordinate| reduce_coordinate(coordinate, torus))
323        .collect::<Result<_>>()?;
324    if reduced.len() != support.len() {
325        return invalid_spec("the finite torus identifies route support offsets");
326    }
327
328    let deltas: BTreeSet<_> = support
329        .iter()
330        .enumerate()
331        .flat_map(|(index, &left)| {
332            support[index + 1..]
333                .iter()
334                .map(move |&right| subtract(left, right))
335        })
336        .collect();
337    for &delta in &deltas {
338        if in_period_lattice(delta, torus)? {
339            return invalid_spec("a route delta is in the torus period lattice");
340        }
341    }
342    for &u in &deltas {
343        for &w in &deltas {
344            if u == w {
345                continue;
346            }
347            for collision in [add(u, w), subtract(u, w)] {
348                if collision != (0, 0) && in_period_lattice(collision, torus)? {
349                    return invalid_spec("route delta vectors collide on the finite torus");
350                }
351            }
352        }
353    }
354    Ok(())
355}
356
357fn data_index(torus: &DirectionalTorusSpec) -> Result<BTreeMap<Coordinate, usize>> {
358    let period_x =
359        i64::try_from(torus.period_x).map_err(|_| QecError::InvalidDirectionalCssSpec {
360            reason: "period_x is too large".to_owned(),
361        })?;
362    let period_y =
363        i64::try_from(torus.period_y).map_err(|_| QecError::InvalidDirectionalCssSpec {
364            reason: "period_y is too large".to_owned(),
365        })?;
366    let mut data_index = BTreeMap::new();
367    for y in 0..period_y {
368        for x in 0..period_x {
369            if (x + y).rem_euclid(2) == 0 {
370                let next = data_index.len();
371                data_index.insert((x, y), next);
372            }
373        }
374    }
375    Ok(data_index)
376}
377
378fn build_check_rows(
379    selected_coset: DirectionalAncillaCoset,
380    support: &[Coordinate],
381    torus: &DirectionalTorusSpec,
382    data_index: &BTreeMap<Coordinate, usize>,
383) -> Result<Vec<Vec<usize>>> {
384    let period_x =
385        i64::try_from(torus.period_x).map_err(|_| QecError::InvalidDirectionalCssSpec {
386            reason: "period_x is too large".to_owned(),
387        })?;
388    let period_y =
389        i64::try_from(torus.period_y).map_err(|_| QecError::InvalidDirectionalCssSpec {
390            reason: "period_y is too large".to_owned(),
391        })?;
392    let mut rows = Vec::new();
393    for y in 0..period_y {
394        for x in 0..period_x {
395            let ancilla = (x, y);
396            if !selected_coset.contains(ancilla) {
397                continue;
398            }
399            let mut row = Vec::with_capacity(support.len());
400            for &offset in support {
401                let data = reduce_coordinate(add(ancilla, offset), torus)?;
402                let column = data_index.get(&data).copied().ok_or_else(|| {
403                    QecError::InvalidDirectionalCssSpec {
404                        reason: format!(
405                            "route support maps ancilla ({x}, {y}) to non-data coordinate ({}, {})",
406                            data.0, data.1
407                        ),
408                    }
409                })?;
410                row.push(column);
411            }
412            row.sort_unstable();
413            if row.windows(2).any(|pair| pair[0] == pair[1]) {
414                return invalid_spec("a generated finite-torus check has duplicate support");
415            }
416            rows.push(row);
417        }
418    }
419    Ok(rows)
420}
421
422fn reduce_coordinate((x, y): Coordinate, torus: &DirectionalTorusSpec) -> Result<Coordinate> {
423    let period_x =
424        i64::try_from(torus.period_x).map_err(|_| QecError::InvalidDirectionalCssSpec {
425            reason: "period_x is too large".to_owned(),
426        })?;
427    let period_y =
428        i64::try_from(torus.period_y).map_err(|_| QecError::InvalidDirectionalCssSpec {
429            reason: "period_y is too large".to_owned(),
430        })?;
431    let shift = i64::try_from(torus.vertical_period_x_shift).map_err(|_| {
432        QecError::InvalidDirectionalCssSpec {
433            reason: "vertical_period_x_shift is too large".to_owned(),
434        }
435    })?;
436    let vertical_periods = y.div_euclid(period_y);
437    let reduced_x = x
438        .checked_sub(vertical_periods.checked_mul(shift).ok_or_else(|| {
439            QecError::InvalidDirectionalCssSpec {
440                reason: "coordinate reduction overflow".to_owned(),
441            }
442        })?)
443        .ok_or_else(|| QecError::InvalidDirectionalCssSpec {
444            reason: "coordinate reduction overflow".to_owned(),
445        })?
446        .rem_euclid(period_x);
447    Ok((reduced_x, y.rem_euclid(period_y)))
448}
449
450fn in_period_lattice((x, y): Coordinate, torus: &DirectionalTorusSpec) -> Result<bool> {
451    let period_x =
452        i64::try_from(torus.period_x).map_err(|_| QecError::InvalidDirectionalCssSpec {
453            reason: "period_x is too large".to_owned(),
454        })?;
455    let period_y =
456        i64::try_from(torus.period_y).map_err(|_| QecError::InvalidDirectionalCssSpec {
457            reason: "period_y is too large".to_owned(),
458        })?;
459    let shift = i64::try_from(torus.vertical_period_x_shift).map_err(|_| {
460        QecError::InvalidDirectionalCssSpec {
461            reason: "vertical_period_x_shift is too large".to_owned(),
462        }
463    })?;
464    if y.rem_euclid(period_y) != 0 {
465        return Ok(false);
466    }
467    let vertical_periods = y.div_euclid(period_y);
468    let horizontal_remainder = x
469        .checked_sub(vertical_periods.checked_mul(shift).ok_or_else(|| {
470            QecError::InvalidDirectionalCssSpec {
471                reason: "period lattice overflow".to_owned(),
472            }
473        })?)
474        .ok_or_else(|| QecError::InvalidDirectionalCssSpec {
475            reason: "period lattice overflow".to_owned(),
476        })?;
477    Ok(horizontal_remainder.rem_euclid(period_x) == 0)
478}
479
480fn add(left: Coordinate, right: Coordinate) -> Coordinate {
481    (left.0 + right.0, left.1 + right.1)
482}
483
484fn subtract(left: Coordinate, right: Coordinate) -> Coordinate {
485    (left.0 - right.0, left.1 - right.1)
486}
487
488fn invalid_spec<T>(reason: impl Into<String>) -> Result<T> {
489    Err(QecError::InvalidDirectionalCssSpec {
490        reason: reason.into(),
491    })
492}
493
494#[cfg(test)]
495mod tests {
496    use super::*;
497
498    fn square_spec(route: &str) -> DirectionalCssSpec {
499        DirectionalCssSpec {
500            torus: DirectionalTorusSpec {
501                period_x: 8,
502                period_y: 6,
503                vertical_period_x_shift: 4,
504            },
505            route: route.to_owned(),
506            layout: DirectionalLayoutSpec::default(),
507            connectivity: DirectionalConnectivity::Square,
508        }
509    }
510
511    fn assert_spec_error_contains<T: std::fmt::Debug>(result: Result<T>, expected: &str) {
512        let error = result.unwrap_err();
513        assert!(
514            format!("{error:?}").contains(expected),
515            "expected {error:?} to contain {expected:?}"
516        );
517    }
518
519    #[test]
520    fn parses_repeated_route_with_paper_offsets() {
521        assert_eq!(
522            parse_directional_route_support("NE2N").unwrap(),
523            vec![(0, 1), (1, 2), (3, 2), (4, 3)]
524        );
525        assert_eq!(
526            parse_directional_route_support("NE2EN").unwrap(),
527            vec![(0, 1), (1, 2), (3, 2), (5, 2), (6, 3)]
528        );
529        assert_eq!(
530            parse_directional_route_support("SW").unwrap(),
531            vec![(0, -1), (-1, -2)]
532        );
533        assert_spec_error_contains(
534            parse_directional_route_support(""),
535            "route must contain at least one direction",
536        );
537        assert_spec_error_contains(
538            parse_directional_route_support("N999999999999999999999999999999999999"),
539            "is out of range",
540        );
541        assert!(parse_directional_route_support("N0E").is_err());
542        assert!(parse_directional_route_support("NX").is_err());
543    }
544
545    #[test]
546    fn ancilla_coset_translation_covers_checkerboard_cases() {
547        assert_eq!(
548            DirectionalAncillaCoset::EvenOdd.translated((1, 1)),
549            DirectionalAncillaCoset::OddEven
550        );
551        assert_eq!(
552            DirectionalAncillaCoset::OddEven.translated((1, 0)),
553            DirectionalAncillaCoset::OddEven
554        );
555    }
556
557    #[test]
558    fn generates_square_ne2n_checks_in_hardware_order() {
559        let checks = build_directional_css_checks(&square_spec("NE2N")).unwrap();
560
561        assert_eq!(checks.num_cols, 24);
562        assert_eq!(checks.hx[0], vec![4, 9, 10, 14]);
563        assert_eq!(checks.hz[0], vec![8, 12, 13, 18]);
564    }
565
566    #[test]
567    fn generates_hex_ne3n_checks_in_hardware_order() {
568        let spec = DirectionalCssSpec {
569            torus: DirectionalTorusSpec {
570                period_x: 18,
571                period_y: 4,
572                vertical_period_x_shift: 0,
573            },
574            route: "NE3N".to_owned(),
575            layout: DirectionalLayoutSpec::default(),
576            connectivity: DirectionalConnectivity::Hex,
577        };
578        let checks = build_directional_css_checks(&spec).unwrap();
579
580        assert_eq!(checks.num_cols, 36);
581        assert_eq!(checks.hx[0], vec![9, 19, 20, 21, 30]);
582        assert_eq!(checks.hz[0], vec![3, 18, 27, 28, 29]);
583    }
584
585    #[test]
586    fn canonicalizes_route_spellings_before_hex_compatibility() {
587        let canonical = build_directional_css_checks(&DirectionalCssSpec {
588            torus: DirectionalTorusSpec {
589                period_x: 18,
590                period_y: 4,
591                vertical_period_x_shift: 0,
592            },
593            route: "NE3N".to_owned(),
594            layout: DirectionalLayoutSpec::default(),
595            connectivity: DirectionalConnectivity::Hex,
596        })
597        .unwrap();
598
599        for route in ["NEEEN", "NE2EN"] {
600            let checks = build_directional_css_checks(&DirectionalCssSpec {
601                route: route.to_owned(),
602                torus: DirectionalTorusSpec {
603                    period_x: 18,
604                    period_y: 4,
605                    vertical_period_x_shift: 0,
606                },
607                layout: DirectionalLayoutSpec::default(),
608                connectivity: DirectionalConnectivity::Hex,
609            })
610            .unwrap();
611
612            assert_eq!(checks.normalized_route, "NE3N");
613            assert_eq!(checks.route_support, canonical.route_support);
614            assert_eq!(checks.hx, canonical.hx);
615            assert_eq!(checks.hz, canonical.hz);
616        }
617    }
618
619    #[test]
620    fn rejects_invalid_directional_specs() {
621        assert_spec_error_contains(
622            build_directional_css_checks(&square_spec("NE")),
623            "odd route-overlap delta",
624        );
625        assert_spec_error_contains(
626            build_directional_css_checks(&DirectionalCssSpec {
627                connectivity: DirectionalConnectivity::Hex,
628                route: "NE2N".to_owned(),
629                ..square_spec("NE2N")
630            }),
631            "hex connectivity does not support normalized route NE2N",
632        );
633        assert_spec_error_contains(
634            build_directional_css_checks(&DirectionalCssSpec {
635                torus: DirectionalTorusSpec {
636                    period_x: 8,
637                    period_y: 6,
638                    vertical_period_x_shift: 1,
639                },
640                ..square_spec("NE2N")
641            }),
642            "vertical_period_x_shift must be even",
643        );
644        assert_spec_error_contains(
645            build_directional_css_checks(&DirectionalCssSpec {
646                torus: DirectionalTorusSpec {
647                    period_x: 0,
648                    ..square_spec("NE2N").torus
649                },
650                ..square_spec("NE2N")
651            }),
652            "period_x must be positive and even",
653        );
654        assert_spec_error_contains(
655            build_directional_css_checks(&DirectionalCssSpec {
656                torus: DirectionalTorusSpec {
657                    period_x: 7,
658                    ..square_spec("NE2N").torus
659                },
660                ..square_spec("NE2N")
661            }),
662            "period_x must be positive and even",
663        );
664        assert_spec_error_contains(
665            build_directional_css_checks(&DirectionalCssSpec {
666                torus: DirectionalTorusSpec {
667                    period_y: 0,
668                    ..square_spec("NE2N").torus
669                },
670                ..square_spec("NE2N")
671            }),
672            "period_y must be positive and even",
673        );
674        assert_spec_error_contains(
675            build_directional_css_checks(&DirectionalCssSpec {
676                torus: DirectionalTorusSpec {
677                    period_y: 5,
678                    ..square_spec("NE2N").torus
679                },
680                ..square_spec("NE2N")
681            }),
682            "period_y must be positive and even",
683        );
684        assert_spec_error_contains(
685            build_directional_css_checks(&DirectionalCssSpec {
686                layout: DirectionalLayoutSpec {
687                    x_ancilla_coset: DirectionalAncillaCoset::OddEven,
688                    z_ancilla_coset: DirectionalAncillaCoset::OddEven,
689                },
690                ..square_spec("NE2N")
691            }),
692            "X and Z checks must use distinct ancilla cosets",
693        );
694        assert_spec_error_contains(
695            build_directional_css_checks(&square_spec("NS")),
696            "route support contains duplicate offsets",
697        );
698    }
699
700    #[test]
701    fn finite_torus_and_row_builders_report_specific_errors() {
702        let torus = DirectionalTorusSpec {
703            period_x: 8,
704            period_y: 6,
705            vertical_period_x_shift: 0,
706        };
707        let data_index = data_index(&torus).unwrap();
708
709        assert_spec_error_contains(
710            validate_finite_torus(&[(0, 0), (8, 0)], &torus),
711            "finite torus identifies route support offsets",
712        );
713        assert_spec_error_contains(
714            validate_finite_torus(&[(0, 0), (3, 0), (5, 0)], &torus),
715            "route delta vectors collide on the finite torus",
716        );
717        assert_spec_error_contains(
718            build_check_rows(
719                DirectionalAncillaCoset::OddEven,
720                &[(0, 0)],
721                &torus,
722                &data_index,
723            ),
724            "to non-data coordinate",
725        );
726        assert_spec_error_contains(
727            build_check_rows(
728                DirectionalAncillaCoset::OddEven,
729                &[(0, 1), (0, 1)],
730                &torus,
731                &data_index,
732            ),
733            "generated finite-torus check has duplicate support",
734        );
735    }
736
737    #[cfg(target_pointer_width = "64")]
738    #[test]
739    fn lattice_helpers_report_overflow_errors() {
740        let too_large = usize::MAX;
741        let normal = DirectionalTorusSpec {
742            period_x: 8,
743            period_y: 2,
744            vertical_period_x_shift: 0,
745        };
746
747        assert_spec_error_contains(
748            data_index(&DirectionalTorusSpec {
749                period_x: too_large,
750                ..normal.clone()
751            }),
752            "period_x is too large",
753        );
754        assert_spec_error_contains(
755            data_index(&DirectionalTorusSpec {
756                period_y: too_large,
757                ..normal.clone()
758            }),
759            "period_y is too large",
760        );
761        assert_spec_error_contains(
762            build_check_rows(
763                DirectionalAncillaCoset::OddEven,
764                &[(0, 1)],
765                &DirectionalTorusSpec {
766                    period_x: too_large,
767                    ..normal.clone()
768                },
769                &BTreeMap::new(),
770            ),
771            "period_x is too large",
772        );
773        assert_spec_error_contains(
774            build_check_rows(
775                DirectionalAncillaCoset::OddEven,
776                &[(0, 1)],
777                &DirectionalTorusSpec {
778                    period_y: too_large,
779                    ..normal.clone()
780                },
781                &BTreeMap::new(),
782            ),
783            "period_y is too large",
784        );
785        assert_spec_error_contains(
786            reduce_coordinate(
787                (0, 0),
788                &DirectionalTorusSpec {
789                    period_x: too_large,
790                    ..normal.clone()
791                },
792            ),
793            "period_x is too large",
794        );
795        assert_spec_error_contains(
796            reduce_coordinate(
797                (0, 0),
798                &DirectionalTorusSpec {
799                    period_y: too_large,
800                    ..normal.clone()
801                },
802            ),
803            "period_y is too large",
804        );
805        assert_spec_error_contains(
806            reduce_coordinate(
807                (0, 0),
808                &DirectionalTorusSpec {
809                    vertical_period_x_shift: too_large,
810                    ..normal.clone()
811                },
812            ),
813            "vertical_period_x_shift is too large",
814        );
815        assert_spec_error_contains(
816            reduce_coordinate(
817                (0, i64::MAX - 1),
818                &DirectionalTorusSpec {
819                    vertical_period_x_shift: 4,
820                    ..normal.clone()
821                },
822            ),
823            "coordinate reduction overflow",
824        );
825        assert_spec_error_contains(
826            reduce_coordinate(
827                (i64::MIN, i64::MAX - 1),
828                &DirectionalTorusSpec {
829                    vertical_period_x_shift: 2,
830                    ..normal.clone()
831                },
832            ),
833            "coordinate reduction overflow",
834        );
835        assert_spec_error_contains(
836            in_period_lattice(
837                (0, 0),
838                &DirectionalTorusSpec {
839                    period_x: too_large,
840                    ..normal.clone()
841                },
842            ),
843            "period_x is too large",
844        );
845        assert_spec_error_contains(
846            in_period_lattice(
847                (0, 0),
848                &DirectionalTorusSpec {
849                    period_y: too_large,
850                    ..normal.clone()
851                },
852            ),
853            "period_y is too large",
854        );
855        assert_spec_error_contains(
856            in_period_lattice(
857                (0, 0),
858                &DirectionalTorusSpec {
859                    vertical_period_x_shift: too_large,
860                    ..normal.clone()
861                },
862            ),
863            "vertical_period_x_shift is too large",
864        );
865        assert_spec_error_contains(
866            in_period_lattice(
867                (0, i64::MAX - 1),
868                &DirectionalTorusSpec {
869                    vertical_period_x_shift: 4,
870                    ..normal.clone()
871                },
872            ),
873            "period lattice overflow",
874        );
875        assert_spec_error_contains(
876            in_period_lattice(
877                (i64::MIN, i64::MAX - 1),
878                &DirectionalTorusSpec {
879                    vertical_period_x_shift: 2,
880                    ..normal
881                },
882            ),
883            "period lattice overflow",
884        );
885    }
886
887    #[test]
888    fn generates_checks_for_a_swapped_valid_layout() {
889        let checks = build_directional_css_checks(&DirectionalCssSpec {
890            layout: DirectionalLayoutSpec {
891                x_ancilla_coset: DirectionalAncillaCoset::EvenOdd,
892                z_ancilla_coset: DirectionalAncillaCoset::OddEven,
893            },
894            ..square_spec("NE2N")
895        })
896        .unwrap();
897
898        assert_eq!(checks.hx.len(), 12);
899        assert_eq!(checks.hz.len(), 12);
900        assert_eq!(checks.hx[0], vec![8, 12, 13, 18]);
901    }
902}