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}