Skip to main content

qec_code/codes/
toric_3d.rs

1use crate::binary_chain_complex::{BinaryBoundaryMap, BinaryChainComplex};
2use crate::error::{QecError, Result};
3use crate::sparse_gf2::SparseGf2Matrix;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
6pub struct Toric3dSpec {
7    pub lx: usize,
8    pub ly: usize,
9    pub lz: usize,
10}
11
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct Toric3dCssChecks {
14    pub num_cols: usize,
15    pub hx: Vec<Vec<usize>>,
16    pub hz: Vec<Vec<usize>>,
17    pub distances: Toric3dDistances,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub struct Toric3dDistances {
22    pub d_x: usize,
23    pub d_z: usize,
24    pub distance: usize,
25}
26
27#[derive(Debug, Clone, Copy)]
28struct Toric3dDimensions {
29    spec: Toric3dSpec,
30    volume: usize,
31    num_edges: usize,
32    num_plaquettes: usize,
33}
34
35impl Toric3dDimensions {
36    fn new(spec: Toric3dSpec) -> Result<Self> {
37        validate_period("lx", spec.lx)?;
38        validate_period("ly", spec.ly)?;
39        validate_period("lz", spec.lz)?;
40        let xy = checked_mul(spec.lx, spec.ly)?;
41        let volume = checked_mul(xy, spec.lz)?;
42        let num_edges = checked_mul(3, volume)?;
43        let num_plaquettes = checked_mul(3, volume)?;
44        Ok(Self {
45            spec,
46            volume,
47            num_edges,
48            num_plaquettes,
49        })
50    }
51
52    fn cell(&self, x: usize, y: usize, z: usize) -> Result<usize> {
53        let xy = checked_add(checked_mul(x, self.spec.ly)?, y)?;
54        checked_add(checked_mul(xy, self.spec.lz)?, z)
55    }
56
57    fn x_edge(&self, x: usize, y: usize, z: usize) -> Result<usize> {
58        self.cell(x, y, z)
59    }
60
61    fn y_edge(&self, x: usize, y: usize, z: usize) -> Result<usize> {
62        checked_add(self.volume, self.cell(x, y, z)?)
63    }
64
65    fn z_edge(&self, x: usize, y: usize, z: usize) -> Result<usize> {
66        checked_add(checked_mul(2, self.volume)?, self.cell(x, y, z)?)
67    }
68}
69
70pub fn toric_3d_chain_complex(spec: Toric3dSpec) -> Result<BinaryChainComplex> {
71    let dims = Toric3dDimensions::new(spec)?;
72    let vertex_rows = vertex_edge_rows(&dims)?;
73    let b1_matrix = SparseGf2Matrix::new(dims.volume, dims.num_edges, vertex_rows)?;
74    let boundary_1 = BinaryBoundaryMap::new(1, 0, b1_matrix)?;
75    let edge_rows = edge_plaquette_rows(&dims)?;
76    let b2_matrix = SparseGf2Matrix::new(dims.num_edges, dims.num_plaquettes, edge_rows)?;
77    let boundary_2 = BinaryBoundaryMap::new(2, 1, b2_matrix)?;
78    BinaryChainComplex::new(vec![boundary_1, boundary_2])
79}
80
81pub fn toric_3d_css_checks(spec: Toric3dSpec) -> Result<Toric3dCssChecks> {
82    let dims = Toric3dDimensions::new(spec)?;
83    let complex = toric_3d_chain_complex(spec)?;
84    let css = complex.css_view(1)?;
85    Ok(Toric3dCssChecks {
86        num_cols: css.num_qubits(),
87        hx: css.hx().rows().to_vec(),
88        hz: css.hz().rows().to_vec(),
89        distances: analytic_distances(&dims)?,
90    })
91}
92
93fn validate_period(parameter: &str, value: usize) -> Result<()> {
94    if value < 3 {
95        return Err(QecError::OutOfRangeBuiltInCssIntegerParameter {
96            family: "toric_3d".to_owned(),
97            parameter: parameter.to_owned(),
98            value,
99        });
100    }
101    Ok(())
102}
103
104fn checked_mul(left: usize, right: usize) -> Result<usize> {
105    left.checked_mul(right)
106        .ok_or_else(toric_3d_dimension_overflow)
107}
108
109fn checked_add(left: usize, right: usize) -> Result<usize> {
110    left.checked_add(right)
111        .ok_or_else(toric_3d_dimension_overflow)
112}
113
114fn toric_3d_dimension_overflow() -> QecError {
115    QecError::SparseGf2DimensionOverflow {
116        operation: "toric_3d",
117    }
118}
119
120fn row_buffer(capacity: usize) -> Result<Vec<Vec<usize>>> {
121    let mut rows = Vec::new();
122    rows.try_reserve_exact(capacity)
123        .map_err(|_| toric_3d_dimension_overflow())?;
124    Ok(rows)
125}
126
127fn empty_rows(len: usize) -> Result<Vec<Vec<usize>>> {
128    let mut rows = row_buffer(len)?;
129    rows.resize_with(len, Vec::new);
130    Ok(rows)
131}
132
133fn vertex_edge_rows(dims: &Toric3dDimensions) -> Result<Vec<Vec<usize>>> {
134    let mut rows = row_buffer(dims.volume)?;
135    for x in 0..dims.spec.lx {
136        let previous_x = previous_coordinate(x, dims.spec.lx);
137        for y in 0..dims.spec.ly {
138            let previous_y = previous_coordinate(y, dims.spec.ly);
139            for z in 0..dims.spec.lz {
140                let previous_z = previous_coordinate(z, dims.spec.lz);
141                rows.push(vec![
142                    dims.x_edge(x, y, z)?,
143                    dims.x_edge(previous_x, y, z)?,
144                    dims.y_edge(x, y, z)?,
145                    dims.y_edge(x, previous_y, z)?,
146                    dims.z_edge(x, y, z)?,
147                    dims.z_edge(x, y, previous_z)?,
148                ]);
149            }
150        }
151    }
152    Ok(rows)
153}
154
155fn edge_plaquette_rows(dims: &Toric3dDimensions) -> Result<Vec<Vec<usize>>> {
156    let mut rows = empty_rows(dims.num_edges)?;
157    let mut plaquette = 0;
158
159    for x in 0..dims.spec.lx {
160        let next_x = next_coordinate(x, dims.spec.lx);
161        for y in 0..dims.spec.ly {
162            let next_y = next_coordinate(y, dims.spec.ly);
163            for z in 0..dims.spec.lz {
164                for edge in [
165                    dims.x_edge(x, y, z)?,
166                    dims.x_edge(x, next_y, z)?,
167                    dims.y_edge(x, y, z)?,
168                    dims.y_edge(next_x, y, z)?,
169                ] {
170                    rows[edge].push(plaquette);
171                }
172                plaquette = checked_add(plaquette, 1)?;
173            }
174        }
175    }
176
177    for x in 0..dims.spec.lx {
178        let next_x = next_coordinate(x, dims.spec.lx);
179        for y in 0..dims.spec.ly {
180            for z in 0..dims.spec.lz {
181                let next_z = next_coordinate(z, dims.spec.lz);
182                for edge in [
183                    dims.x_edge(x, y, z)?,
184                    dims.x_edge(x, y, next_z)?,
185                    dims.z_edge(x, y, z)?,
186                    dims.z_edge(next_x, y, z)?,
187                ] {
188                    rows[edge].push(plaquette);
189                }
190                plaquette = checked_add(plaquette, 1)?;
191            }
192        }
193    }
194
195    for x in 0..dims.spec.lx {
196        for y in 0..dims.spec.ly {
197            let next_y = next_coordinate(y, dims.spec.ly);
198            for z in 0..dims.spec.lz {
199                let next_z = next_coordinate(z, dims.spec.lz);
200                for edge in [
201                    dims.y_edge(x, y, z)?,
202                    dims.y_edge(x, y, next_z)?,
203                    dims.z_edge(x, y, z)?,
204                    dims.z_edge(x, next_y, z)?,
205                ] {
206                    rows[edge].push(plaquette);
207                }
208                plaquette = checked_add(plaquette, 1)?;
209            }
210        }
211    }
212
213    Ok(rows)
214}
215
216fn previous_coordinate(coordinate: usize, period: usize) -> usize {
217    if coordinate == 0 {
218        period - 1
219    } else {
220        coordinate - 1
221    }
222}
223
224fn next_coordinate(coordinate: usize, period: usize) -> usize {
225    if coordinate == period - 1 {
226        0
227    } else {
228        coordinate + 1
229    }
230}
231
232fn analytic_distances(dims: &Toric3dDimensions) -> Result<Toric3dDistances> {
233    let d_x = checked_mul(dims.spec.lx, dims.spec.ly)?
234        .min(checked_mul(dims.spec.lx, dims.spec.lz)?)
235        .min(checked_mul(dims.spec.ly, dims.spec.lz)?);
236    let d_z = dims.spec.lx.min(dims.spec.ly).min(dims.spec.lz);
237    Ok(Toric3dDistances {
238        d_x,
239        d_z,
240        distance: d_x.min(d_z),
241    })
242}
243
244#[cfg(test)]
245mod tests {
246    use super::*;
247
248    #[test]
249    fn corrupt_boundary_composition_is_rejected() {
250        let dims = Toric3dDimensions::new(Toric3dSpec {
251            lx: 3,
252            ly: 3,
253            lz: 3,
254        })
255        .unwrap();
256        let boundary_1 = BinaryBoundaryMap::new(
257            1,
258            0,
259            SparseGf2Matrix::new(
260                dims.volume,
261                dims.num_edges,
262                vertex_edge_rows(&dims).unwrap(),
263            )
264            .unwrap(),
265        )
266        .unwrap();
267        let mut rows = edge_plaquette_rows(&dims).unwrap();
268        rows[0].remove(0);
269        let boundary_2 = BinaryBoundaryMap::new(
270            2,
271            1,
272            SparseGf2Matrix::new(dims.num_edges, dims.num_plaquettes, rows).unwrap(),
273        )
274        .unwrap();
275
276        assert!(matches!(
277            BinaryChainComplex::new(vec![boundary_1, boundary_2]),
278            Err(QecError::NonzeroBoundaryComposition {
279                lower_dimension: 1,
280                upper_dimension: 2,
281                ..
282            })
283        ));
284    }
285}