qec-code 0.3.0

Rust primitives for constructing and analyzing quantum error-correcting codes
Documentation
use qec_code::binary::binary_rank;
use qec_code::{Pauli, StabilizerCode};

fn pauli(n: usize, x_support: &[usize], z_support: &[usize]) -> Pauli {
    let mut x = vec![0; n];
    let mut z = vec![0; n];
    for &qubit in x_support {
        x[qubit] = 1;
    }
    for &qubit in z_support {
        z[qubit] = 1;
    }
    Pauli::from_xz_bits(x, z).unwrap()
}

fn logical_rows(code: &StabilizerCode) -> Vec<Vec<u8>> {
    let basis = code.logical_basis().unwrap();
    basis
        .logical_x
        .iter()
        .chain(&basis.logical_z)
        .map(Pauli::to_symplectic_row)
        .collect()
}

fn assert_canonical_commutation_matrix(basis: &qec_code::logical::LogicalBasis) {
    for x_index in 0..basis.k {
        for z_index in 0..basis.k {
            if x_index == z_index {
                assert!(basis.logical_x[x_index].anticommutes_with(&basis.logical_z[z_index]));
            } else {
                assert!(basis.logical_x[x_index].commutes_with(&basis.logical_z[z_index]));
            }
        }
    }

    for first in 0..basis.k {
        for second in (first + 1)..basis.k {
            assert!(basis.logical_x[first].commutes_with(&basis.logical_x[second]));
            assert!(basis.logical_z[first].commutes_with(&basis.logical_z[second]));
        }
    }
}

#[test]
fn trivial_k2_code_returns_two_canonical_logical_pairs() {
    let stabilizers = vec![pauli(4, &[], &[0]), pauli(4, &[], &[1])];
    let code = StabilizerCode::from_stabilizers(4, stabilizers).unwrap();

    let basis = code.logical_basis().unwrap();

    assert_eq!(basis.k, 2);
    assert_eq!(basis.logical_x.len(), 2);
    assert_eq!(basis.logical_z.len(), 2);
    assert_canonical_commutation_matrix(&basis);

    let mut rows = code.stabilizer_rows();
    rows.extend(logical_rows(&code));
    assert_eq!(binary_rank(&rows), 6);
}

#[test]
fn canonical_pairs_commute_off_diagonal_for_k_two_codes() {
    let stabilizers = vec![pauli(4, &[], &[0]), pauli(4, &[], &[1])];
    let code = StabilizerCode::from_stabilizers(4, stabilizers).unwrap();
    let basis = code.canonical_logical_basis().unwrap();

    assert_eq!(basis.k, 2);
    assert!(basis.logical_x[0].anticommutes_with(&basis.logical_z[0]));
    assert!(basis.logical_x[1].anticommutes_with(&basis.logical_z[1]));
    assert!(basis.logical_x[0].commutes_with(&basis.logical_z[1]));
    assert!(basis.logical_x[1].commutes_with(&basis.logical_z[0]));
    assert!(basis.logical_x[0].commutes_with(&basis.logical_x[1]));
    assert!(basis.logical_z[0].commutes_with(&basis.logical_z[1]));
}

#[test]
fn non_css_k1_code_returns_commuting_anticommuting_logicals() {
    let stabilizers = vec![pauli(2, &[0], &[0])];
    let code = StabilizerCode::from_stabilizers(2, stabilizers).unwrap();

    let basis = code.canonical_logical_basis().unwrap();

    assert_eq!(basis.k, 1);
    assert_eq!(basis.logical_x.len(), 1);
    assert_eq!(basis.logical_z.len(), 1);
    assert_canonical_commutation_matrix(&basis);
    for stabilizer in code.stabilizers() {
        assert!(basis.logical_x[0].commutes_with(stabilizer));
        assert!(basis.logical_z[0].commutes_with(stabilizer));
    }
}

#[test]
fn empty_stabilizer_code_returns_one_logical_pair_per_qubit() {
    let code = StabilizerCode::from_stabilizers(2, vec![]).unwrap();

    let basis = code.logical_basis().unwrap();

    assert_eq!(basis.k, 2);
    assert_eq!(basis.logical_x.len(), 2);
    assert_eq!(basis.logical_z.len(), 2);
    assert_canonical_commutation_matrix(&basis);
}