1use crate::code::StabilizerCode;
2use crate::error::{QecError, Result};
3use crate::gf2;
4use crate::Pauli;
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct SparseRowsMatrix {
9 num_cols: usize,
10 rows: Vec<Vec<usize>>,
11}
12
13impl SparseRowsMatrix {
14 pub fn new(num_cols: usize, rows: Vec<Vec<usize>>) -> Result<Self> {
15 validate_sparse_rows(num_cols, &rows)?;
16 Ok(Self { num_cols, rows })
17 }
18
19 pub fn num_cols(&self) -> usize {
20 self.num_cols
21 }
22
23 pub fn rows(&self) -> &[Vec<usize>] {
24 &self.rows
25 }
26
27 pub fn to_dense_rows(&self) -> Vec<Vec<u8>> {
28 self.rows
29 .iter()
30 .map(|row| {
31 let mut dense = vec![0; self.num_cols];
32 for &support in row {
33 dense[support] = 1;
34 }
35 dense
36 })
37 .collect()
38 }
39
40 pub fn to_json_string(&self) -> String {
41 #[derive(Serialize)]
42 struct SparseRowsMatrixJson<'a> {
43 format: &'static str,
44 num_cols: usize,
45 rows: &'a [Vec<usize>],
46 }
47
48 let json = serde_json::to_string(&SparseRowsMatrixJson {
49 format: "sparse_rows",
50 num_cols: self.num_cols,
51 rows: &self.rows,
52 })
53 .expect("validated sparse rows matrix should always serialize");
54 json
55 }
56}
57
58#[derive(Debug, Deserialize)]
59struct SparseRowsMatrixJson {
60 format: String,
61 num_cols: usize,
62 rows: Vec<Vec<usize>>,
63}
64
65pub fn sparse_rows_matrix_from_json_str(input: &str) -> Result<SparseRowsMatrix> {
66 let value: serde_json::Value = serde_json::from_str(input)
67 .map_err(|err| QecError::InvalidCssMatrixJson(err.to_string()))?;
68
69 let format = value
70 .get("format")
71 .and_then(serde_json::Value::as_str)
72 .ok_or(QecError::MissingCssMatrixFormat)?;
73
74 if format != "sparse_rows" {
75 return Err(QecError::UnsupportedCssMatrixFormat {
76 format: format.to_owned(),
77 });
78 }
79
80 let parsed: SparseRowsMatrixJson = serde_json::from_value(value)
81 .map_err(|err| QecError::InvalidCssMatrixJson(err.to_string()))?;
82 let SparseRowsMatrixJson {
83 format: _format,
84 num_cols,
85 rows,
86 } = parsed;
87
88 SparseRowsMatrix::new(num_cols, rows)
89}
90
91#[derive(Debug, Clone, PartialEq, Eq)]
92pub struct CssCode {
93 code: StabilizerCode,
94 hx: Vec<Vec<u8>>,
95 hz: Vec<Vec<u8>>,
96}
97
98impl CssCode {
99 pub fn from_hx_hz(hx: Vec<Vec<u8>>, hz: Vec<Vec<u8>>) -> Result<Self> {
100 let n = shared_width(&hx, &hz)?;
101
102 if !checks_are_orthogonal(&hx, &hz) {
103 return Err(QecError::InvalidCssOrthogonality);
104 }
105
106 let validated_hx = hx.clone();
107 let validated_hz = hz.clone();
108
109 let mut stabilizer_rows = Vec::with_capacity(hx.len() + hz.len());
110 for row in hx {
111 let mut symplectic_row = row;
112 symplectic_row.extend(vec![0; n]);
113 stabilizer_rows.push(symplectic_row);
114 }
115 for row in hz {
116 let mut symplectic_row = vec![0; n];
117 symplectic_row.extend(row);
118 stabilizer_rows.push(symplectic_row);
119 }
120
121 let stabilizers = gf2::try_select_independent_rows(&stabilizer_rows)?
122 .into_iter()
123 .map(Pauli::from_symplectic_row)
124 .collect::<Result<Vec<_>>>()?;
125
126 Ok(Self {
127 code: StabilizerCode::from_stabilizers(n, stabilizers)?,
128 hx: validated_hx,
129 hz: validated_hz,
130 })
131 }
132
133 pub fn code(&self) -> &StabilizerCode {
134 &self.code
135 }
136
137 pub fn hx(&self) -> &[Vec<u8>] {
138 &self.hx
139 }
140
141 pub fn hz(&self) -> &[Vec<u8>] {
142 &self.hz
143 }
144}
145
146fn validate_sparse_rows(num_cols: usize, rows: &[Vec<usize>]) -> Result<()> {
147 if num_cols == 0 {
148 return Err(QecError::InvalidSparseRowsWidth { num_cols });
149 }
150
151 for (row_index, row) in rows.iter().enumerate() {
152 let mut seen = std::collections::BTreeSet::new();
153 for &support in row {
154 if support >= num_cols {
155 return Err(QecError::SparseRowSupportOutOfRange {
156 row: row_index,
157 support,
158 num_cols,
159 });
160 }
161 if !seen.insert(support) {
162 return Err(QecError::DuplicateSparseRowSupport {
163 row: row_index,
164 support,
165 });
166 }
167 }
168 }
169 Ok(())
170}
171
172fn shared_width(hx: &[Vec<u8>], hz: &[Vec<u8>]) -> Result<usize> {
173 let n = hx
174 .first()
175 .map(Vec::len)
176 .or_else(|| hz.first().map(Vec::len))
177 .unwrap_or(0);
178
179 validate_rows(hx, n)?;
180 validate_rows(hz, n)?;
181
182 Ok(n)
183}
184
185fn validate_rows(matrix: &[Vec<u8>], expected_width: usize) -> Result<()> {
186 for (row_index, row) in matrix.iter().enumerate() {
187 if row.len() != expected_width {
188 return Err(QecError::RowWidthMismatch {
189 expected: expected_width,
190 actual: row.len(),
191 });
192 }
193
194 for (col_index, bit) in row.iter().enumerate() {
195 if *bit > 1 {
196 return Err(QecError::InvalidBinaryEntry {
197 row: row_index,
198 col: col_index,
199 value: *bit,
200 });
201 }
202 }
203 }
204
205 Ok(())
206}
207
208fn checks_are_orthogonal(hx: &[Vec<u8>], hz: &[Vec<u8>]) -> bool {
209 hx.iter()
210 .all(|x_row| hz.iter().all(|z_row| dot_product_mod_2(x_row, z_row) == 0))
211}
212
213fn dot_product_mod_2(lhs: &[u8], rhs: &[u8]) -> u8 {
214 lhs.iter()
215 .zip(rhs)
216 .fold(0, |parity, (left, right)| parity ^ (*left & *right))
217}