Skip to main content

qec_code/codes/
la_cross.rs

1use serde::{Deserialize, Serialize};
2
3use crate::error::{QecError, Result};
4use crate::family_contract::CssClassicalCheckSpec;
5use crate::sparse_gf2::SparseGf2Matrix;
6
7pub const LA_CROSS_CONSTRUCTION_ID: &str = "la_cross";
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
10#[serde(rename_all = "snake_case")]
11pub enum LaCrossBoundary {
12    Open,
13    Periodic,
14}
15
16impl LaCrossBoundary {
17    pub const fn as_str(self) -> &'static str {
18        match self {
19            Self::Open => "open",
20            Self::Periodic => "periodic",
21        }
22    }
23
24    pub fn parse(value: &str) -> Result<Self> {
25        match value {
26            "open" => Ok(Self::Open),
27            "periodic" => Ok(Self::Periodic),
28            _ => Err(invalid(format!("unknown la_cross boundary {value}"))),
29        }
30    }
31}
32
33#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
34pub struct LaCrossSpec {
35    pub seed_length: usize,
36    pub reach: usize,
37    pub boundary: LaCrossBoundary,
38}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
41pub(crate) struct LaCrossClassicalCheck {
42    pub spec: LaCrossSpec,
43    pub check: CssClassicalCheckSpec,
44}
45
46pub(crate) fn la_cross_classical_check(spec: &LaCrossSpec) -> Result<LaCrossClassicalCheck> {
47    validate_la_cross_spec(spec)?;
48    let rows = match spec.boundary {
49        LaCrossBoundary::Open => open_rows(spec.seed_length, spec.reach)?,
50        LaCrossBoundary::Periodic => periodic_rows(spec.seed_length, spec.reach)?,
51    };
52    let matrix = SparseGf2Matrix::new(rows.len(), spec.seed_length, rows)?;
53    Ok(LaCrossClassicalCheck {
54        spec: spec.clone(),
55        check: CssClassicalCheckSpec {
56            num_cols: matrix.num_cols(),
57            rows: matrix.rows().to_vec(),
58        },
59    })
60}
61
62pub(crate) fn la_cross_known_distances(spec: &LaCrossSpec) -> Option<(usize, usize)> {
63    (spec.seed_length == 5 && spec.reach == 2 && spec.boundary == LaCrossBoundary::Open)
64        .then_some((3, 3))
65}
66
67fn validate_la_cross_spec(spec: &LaCrossSpec) -> Result<()> {
68    if spec.seed_length < 2 {
69        return Err(invalid(format!(
70            "seed_length must be at least 2, got {}",
71            spec.seed_length
72        )));
73    }
74    if spec.reach == 0 {
75        return Err(invalid("reach must be nonzero"));
76    }
77    if spec.reach >= spec.seed_length {
78        return Err(invalid(format!(
79            "reach must be less than seed_length, got reach {} and seed_length {}",
80            spec.reach, spec.seed_length
81        )));
82    }
83    preflight_hgp_dimensions(spec)
84}
85
86fn preflight_hgp_dimensions(spec: &LaCrossSpec) -> Result<()> {
87    let row_count = classical_row_count(spec);
88    spec.seed_length
89        .checked_mul(spec.seed_length)
90        .and_then(|left| {
91            row_count
92                .checked_mul(row_count)
93                .and_then(|right| left.checked_add(right))
94        })
95        .ok_or_else(|| overflow("HGP data qubit count"))?;
96    row_count
97        .checked_mul(spec.seed_length)
98        .ok_or_else(|| overflow("HGP check count"))?;
99    Ok(())
100}
101
102fn classical_row_count(spec: &LaCrossSpec) -> usize {
103    match spec.boundary {
104        LaCrossBoundary::Open => spec.seed_length - spec.reach,
105        LaCrossBoundary::Periodic => spec.seed_length,
106    }
107}
108
109fn open_rows(seed_length: usize, reach: usize) -> Result<Vec<Vec<usize>>> {
110    let row_count = seed_length - reach;
111    let mut rows = Vec::new();
112    rows.try_reserve_exact(row_count)
113        .map_err(|_| overflow("classical row allocation"))?;
114    for row in 0..row_count {
115        rows.push(vec![row, row + 1, row + reach]);
116    }
117    Ok(rows)
118}
119
120fn periodic_rows(seed_length: usize, reach: usize) -> Result<Vec<Vec<usize>>> {
121    let mut rows = Vec::new();
122    rows.try_reserve_exact(seed_length)
123        .map_err(|_| overflow("classical row allocation"))?;
124    for row in 0..seed_length {
125        rows.push(vec![
126            row,
127            periodic_add(row, 1, seed_length),
128            periodic_add(row, reach, seed_length),
129        ]);
130    }
131    Ok(rows)
132}
133
134fn periodic_add(value: usize, shift: usize, period: usize) -> usize {
135    let shift = shift % period;
136    if shift == 0 {
137        value
138    } else if value >= period - shift {
139        value - (period - shift)
140    } else {
141        value + shift
142    }
143}
144
145fn invalid(reason: impl Into<String>) -> QecError {
146    QecError::InvalidCssConstruction {
147        construction: LA_CROSS_CONSTRUCTION_ID.to_owned(),
148        reason: reason.into(),
149    }
150}
151
152fn overflow(operation: &'static str) -> QecError {
153    invalid(format!("la_cross dimension overflow during {operation}"))
154}
155
156#[cfg(test)]
157mod tests {
158    use super::*;
159
160    #[test]
161    fn open_5_2_classical_rows_match_issue_fixture() {
162        let check = la_cross_classical_check(&LaCrossSpec {
163            seed_length: 5,
164            reach: 2,
165            boundary: LaCrossBoundary::Open,
166        })
167        .unwrap();
168
169        assert_eq!(check.check.num_cols, 5);
170        assert_eq!(
171            check.check.rows,
172            vec![vec![0, 1, 2], vec![1, 2, 3], vec![2, 3, 4]]
173        );
174    }
175
176    #[test]
177    fn periodic_5_2_rows_wrap_deterministically() {
178        let check = la_cross_classical_check(&LaCrossSpec {
179            seed_length: 5,
180            reach: 2,
181            boundary: LaCrossBoundary::Periodic,
182        })
183        .unwrap();
184
185        assert_eq!(
186            check.check.rows,
187            vec![
188                vec![0, 1, 2],
189                vec![1, 2, 3],
190                vec![2, 3, 4],
191                vec![0, 3, 4],
192                vec![0, 1, 4],
193            ]
194        );
195    }
196}