use super::code::{min_weight_outside, Distance, StabilizerCode, MAX_DISTANCE_SEARCH_DIM};
use super::pauli::PauliOperator;
use super::symplectic::independent_rows;
use super::{bit_rank, matrix_cols, matrix_from_rows, matrix_rows, require_gf2, StabilizerError};
use crate::ffield::GfMatrix;
use std::fmt;
#[derive(Clone, Debug)]
pub struct CssCode {
n: usize,
hx: GfMatrix,
hz: GfMatrix,
rx: usize,
rz: usize,
k: usize,
logical_x: Vec<Vec<u8>>,
logical_z: Vec<Vec<u8>>,
}
fn quotient_basis(
sub: &[Vec<u8>],
candidates: &[Vec<u8>],
width: usize,
) -> Result<Vec<Vec<u8>>, StabilizerError> {
let mut rows: Vec<Vec<u8>> = sub.to_vec();
let mut r = bit_rank(&rows, width)?;
let mut picked = Vec::new();
for c in candidates {
rows.push(c.clone());
let r2 = bit_rank(&rows, width)?;
if r2 > r {
r = r2;
picked.push(c.clone());
} else {
rows.pop();
}
}
Ok(picked)
}
fn kernel_basis(h: &GfMatrix) -> Result<Vec<Vec<u8>>, StabilizerError> {
let (rows, n) = h.shape();
if rows == 0 {
return Ok((0..n)
.map(|i| {
let mut e = vec![0u8; n];
e[i] = 1;
e
})
.collect());
}
matrix_cols(&h.nullspace()?)
}
impl CssCode {
pub fn new(hx: &GfMatrix, hz: &GfMatrix) -> Result<Self, StabilizerError> {
require_gf2(hx)?;
require_gf2(hz)?;
let (_, n) = hx.shape();
let (_, nz) = hz.shape();
if n != nz {
return Err(StabilizerError::ShapeMismatch {
op: "build a CSS code",
expected: format!("H_Z with {n} columns, matching H_X"),
got: format!("{nz} columns"),
});
}
super::check_qubits(n)?;
let product = hx.mul(&hz.transpose())?;
if !product.is_zero() {
let pr = matrix_rows(&product)?;
for (i, row) in pr.iter().enumerate() {
if let Some(j) = row.iter().position(|&b| b == 1) {
return Err(StabilizerError::CssConditionViolated { row: i, col: j });
}
}
return Err(StabilizerError::InconsistentResult {
reason: "H_X · H_Zᵀ is non-zero but no non-zero entry was found".to_string(),
});
}
let rx = hx.rank();
let rz = hz.rank();
let k = n
.checked_sub(rx + rz)
.ok_or_else(|| StabilizerError::InconsistentResult {
reason: format!(
"rank(H_X) + rank(H_Z) = {} exceeds n = {n}, which the CSS condition forbids",
rx + rz
),
})?;
let hx_rows = matrix_rows(hx)?;
let hz_rows = matrix_rows(hz)?;
let logical_x = quotient_basis(&hx_rows, &kernel_basis(hz)?, n)?;
let logical_z = quotient_basis(&hz_rows, &kernel_basis(hx)?, n)?;
if logical_x.len() != k || logical_z.len() != k {
return Err(StabilizerError::InconsistentResult {
reason: format!(
"expected {k} logical operators of each type, got {} X-type and {} Z-type",
logical_x.len(),
logical_z.len()
),
});
}
let logical_z = pair_up(&logical_x, &logical_z, n, k)?;
let code = Self {
n,
hx: hx.clone(),
hz: hz.clone(),
rx,
rz,
k,
logical_x,
logical_z,
};
code.verify()?;
Ok(code)
}
pub fn n(&self) -> usize {
self.n
}
pub fn k(&self) -> usize {
self.k
}
pub fn x_rank(&self) -> usize {
self.rx
}
pub fn z_rank(&self) -> usize {
self.rz
}
pub fn hx(&self) -> &GfMatrix {
&self.hx
}
pub fn hz(&self) -> &GfMatrix {
&self.hz
}
pub fn logical_x_bits(&self) -> &[Vec<u8>] {
&self.logical_x
}
pub fn logical_z_bits(&self) -> &[Vec<u8>] {
&self.logical_z
}
pub fn logical_x(&self) -> Result<Vec<PauliOperator>, StabilizerError> {
let zero = vec![0u8; self.n];
self.logical_x
.iter()
.map(|v| PauliOperator::hermitian(v, &zero, false))
.collect()
}
pub fn logical_z(&self) -> Result<Vec<PauliOperator>, StabilizerError> {
let zero = vec![0u8; self.n];
self.logical_z
.iter()
.map(|v| PauliOperator::hermitian(&zero, v, false))
.collect()
}
pub fn stabilizer_generators(&self) -> Result<Vec<PauliOperator>, StabilizerError> {
let zero = vec![0u8; self.n];
let mut gens = Vec::new();
for r in matrix_rows(&self.hx)? {
gens.push(PauliOperator::hermitian(&r, &zero, false)?);
}
for r in matrix_rows(&self.hz)? {
gens.push(PauliOperator::hermitian(&zero, &r, false)?);
}
Ok(gens)
}
pub fn to_stabilizer_code(&self) -> Result<StabilizerCode, StabilizerError> {
let code = StabilizerCode::from_generators_on(self.n, self.stabilizer_generators()?)?;
if code.k() != self.k {
return Err(StabilizerError::InconsistentResult {
reason: format!(
"the CSS parameters give k = {}, but n − rank(S) = {}",
self.k,
code.k()
),
});
}
Ok(code)
}
pub fn minimum_distance(&self) -> Result<Distance, StabilizerError> {
self.minimum_distance_with_cap(MAX_DISTANCE_SEARCH_DIM)
}
pub fn minimum_distance_with_cap(&self, cap: usize) -> Result<Distance, StabilizerError> {
if self.k == 0 {
return Err(StabilizerError::NoLogicalQubits { n: self.n });
}
let zero = vec![0u8; self.n];
let embed_x = |v: &Vec<u8>| {
let mut w = v.clone();
w.extend_from_slice(&zero);
w
};
let embed_z = |v: &Vec<u8>| {
let mut w = zero.clone();
w.extend_from_slice(v);
w
};
let hx_basis = independent_rows(&matrix_rows(&self.hx)?, self.n)?;
let hz_basis = independent_rows(&matrix_rows(&self.hz)?, self.n)?;
let lx: Vec<Vec<u8>> = self.logical_x.iter().map(&embed_x).collect();
let sx: Vec<Vec<u8>> = hx_basis.iter().map(&embed_x).collect();
let dx = min_weight_outside(&lx, &sx, self.n, cap)?;
let lz: Vec<Vec<u8>> = self.logical_z.iter().map(&embed_z).collect();
let sz: Vec<Vec<u8>> = hz_basis.iter().map(&embed_z).collect();
let dz = min_weight_outside(&lz, &sz, self.n, cap)?;
Ok(Distance::Exact(dx.min(dz)))
}
pub fn distance_upper_bound(&self) -> Result<Distance, StabilizerError> {
if self.k == 0 {
return Err(StabilizerError::NoLogicalQubits { n: self.n });
}
let best = self
.logical_x
.iter()
.chain(self.logical_z.iter())
.map(|v| v.iter().filter(|&&b| b == 1).count())
.min()
.unwrap_or(0);
Ok(Distance::UpperBound(best))
}
fn verify(&self) -> Result<(), StabilizerError> {
for (i, v) in self.logical_x.iter().enumerate() {
let prod = self
.hz
.mul(&matrix_from_rows(std::slice::from_ref(v), self.n)?.transpose())?;
if !prod.is_zero() {
return Err(StabilizerError::InconsistentResult {
reason: format!("X-type logical {i} does not commute with every Z stabilizer"),
});
}
}
for (i, v) in self.logical_z.iter().enumerate() {
let prod = self
.hx
.mul(&matrix_from_rows(std::slice::from_ref(v), self.n)?.transpose())?;
if !prod.is_zero() {
return Err(StabilizerError::InconsistentResult {
reason: format!("Z-type logical {i} does not commute with every X stabilizer"),
});
}
}
for i in 0..self.k {
for j in 0..self.k {
let dot: u8 = self.logical_x[i]
.iter()
.zip(&self.logical_z[j])
.fold(0u8, |acc, (&a, &b)| acc ^ (a & b));
if dot != u8::from(i == j) {
return Err(StabilizerError::InconsistentResult {
reason: format!("⟨X̄_{i}, Z̄_{j}⟩ is not {}", u8::from(i == j)),
});
}
}
}
Ok(())
}
}
fn pair_up(
lx: &[Vec<u8>],
lz: &[Vec<u8>],
n: usize,
k: usize,
) -> Result<Vec<Vec<u8>>, StabilizerError> {
if k == 0 {
return Ok(Vec::new());
}
let mut m = vec![vec![0u8; k]; k];
for i in 0..k {
for j in 0..k {
m[i][j] = lx[i]
.iter()
.zip(&lz[j])
.fold(0u8, |acc, (&a, &b)| acc ^ (a & b));
}
}
let gm = matrix_from_rows(&m, k)?;
let inv = gm.inverse().map_err(|e| match e {
crate::ffield::FiniteFieldError::Singular { .. } => StabilizerError::InconsistentResult {
reason: "the pairing matrix ⟨X̄_i, Z̄_j⟩ is singular; the symplectic form is \
degenerate on the logical quotient, which cannot happen for a valid \
CSS pair"
.to_string(),
},
other => StabilizerError::FiniteField(other),
})?;
let a = matrix_rows(&inv.transpose())?;
let mut out = Vec::with_capacity(k);
for row in a.iter().take(k) {
let mut v = vec![0u8; n];
for (j, &c) in row.iter().enumerate() {
if c == 1 {
for t in 0..n {
v[t] ^= lz[j][t];
}
}
}
out.push(v);
}
Ok(out)
}
impl fmt::Display for CssCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "CSS[[{}, {}]]", self.n, self.k)
}
}