ldpc 0.7.0

A toolbox for classical and quantum LDPC codes.
Documentation
use sparse_bin_mat::{SparseBinMat, SparseBinSlice};

use super::ErasureDecoder;
use crate::codes::CssCode;

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct CssErasureDecoder<'c> {
    code: &'c CssCode,
}

impl<'c> CssErasureDecoder<'c> {
    pub fn new(code: &'c CssCode) -> Self {
        Self { code }
    }

    pub fn error_basis(&self, erasure: SparseBinSlice) -> SparseBinMat {
        let rows = erasure
            .non_trivial_positions()
            .map(|pos| vec![pos])
            .collect();
        SparseBinMat::new(self.code.len(), rows)
    }

    fn num_bad_x_errors(&self, errors: &SparseBinMat) -> usize {
        Self::num_bad_errors(
            errors,
            self.code.z_stabs_binary(),
            self.code.z_logicals_binary(),
        )
    }

    fn num_bad_z_errors(&self, errors: &SparseBinMat) -> usize {
        Self::num_bad_errors(
            errors,
            self.code.x_stabs_binary(),
            self.code.x_logicals_binary(),
        )
    }

    fn num_bad_errors(
        errors: &SparseBinMat,
        stabs: &SparseBinMat,
        logicals: &SparseBinMat,
    ) -> usize {
        let syndrome_rows = errors
            .rows()
            .map(|error| (stabs * &error).to_positions_vec())
            .collect();
        let syndrome_matrix = SparseBinMat::new(stabs.number_of_columns(), syndrome_rows);
        let logical_rows = errors
            .rows()
            .map(|error| (logicals * &error).to_positions_vec())
            .collect();
        let logical_matrix = SparseBinMat::new(logicals.number_of_columns(), logical_rows);
        let total_matrix = syndrome_matrix.horizontal_concat_with(&logical_matrix);
        total_matrix.rank() - syndrome_matrix.rank()
    }
}

impl<'c> ErasureDecoder for CssErasureDecoder<'c> {
    fn recovery_probability(&self, erasure: SparseBinSlice) -> f64 {
        let errors = self.error_basis(erasure);
        1.0 / 2.0_f64.powi((self.num_bad_x_errors(&errors) + self.num_bad_z_errors(&errors)) as i32)
    }
}

#[cfg(test)]
mod test {
    use sparse_bin_mat::SparseBinVec;

    use super::*;

    #[test]
    fn erasure_failures_in_shor_code() {
        let code = CssCode::shor_code();
        let decoder = CssErasureDecoder::new(&code);

        let erasure = SparseBinVec::new(9, vec![0, 4, 8]);
        let error_basis = decoder.error_basis(erasure.as_view());
        assert_eq!(decoder.num_bad_x_errors(&error_basis), 0);
        assert_eq!(decoder.num_bad_z_errors(&error_basis), 1);
        assert_eq!(decoder.recovery_probability(erasure.as_view()), 0.5);

        let erasure = SparseBinVec::new(9, vec![0, 1, 2]);
        let error_basis = decoder.error_basis(erasure.as_view());
        assert_eq!(decoder.num_bad_x_errors(&error_basis), 1);
        assert_eq!(decoder.num_bad_z_errors(&error_basis), 0);
        assert_eq!(decoder.recovery_probability(erasure.as_view()), 0.5);
    }

    #[test]
    fn erasure_successes_in_shor_code() {
        let code = CssCode::shor_code();
        let decoder = CssErasureDecoder::new(&code);

        let erasure = SparseBinVec::new(9, vec![0, 1, 3, 4]);
        let error_basis = decoder.error_basis(erasure.as_view());
        assert_eq!(decoder.num_bad_x_errors(&error_basis), 0);
        assert_eq!(decoder.num_bad_z_errors(&error_basis), 0);
        assert_eq!(decoder.recovery_probability(erasure.as_view()), 1.0);

        let erasure = SparseBinVec::new(9, vec![0, 6, 7]);
        let error_basis = decoder.error_basis(erasure.as_view());
        assert_eq!(decoder.num_bad_x_errors(&error_basis), 0);
        assert_eq!(decoder.num_bad_z_errors(&error_basis), 0);
        assert_eq!(decoder.recovery_probability(erasure.as_view()), 1.0);
    }

    #[test]
    fn empty_erasure_in_shor_code() {
        let code = CssCode::shor_code();
        let decoder = CssErasureDecoder::new(&code);

        let erasure = SparseBinVec::new(9, vec![]);
        let error_basis = decoder.error_basis(erasure.as_view());
        assert_eq!(decoder.num_bad_x_errors(&error_basis), 0);
        assert_eq!(decoder.num_bad_z_errors(&error_basis), 0);
        assert_eq!(decoder.recovery_probability(erasure.as_view()), 1.0);
    }

    #[test]
    fn erasure_failures_in_steane_code() {
        let code = CssCode::steane_code();
        let decoder = CssErasureDecoder::new(&code);

        let erasure = SparseBinVec::new(7, vec![0, 1, 2]);
        let error_basis = decoder.error_basis(erasure.as_view());
        assert_eq!(decoder.num_bad_x_errors(&error_basis), 1);
        assert_eq!(decoder.num_bad_z_errors(&error_basis), 1);
        assert_eq!(decoder.recovery_probability(erasure.as_view()), 0.25);

        let erasure = SparseBinVec::new(7, vec![0, 3, 4, 5]);
        let error_basis = decoder.error_basis(erasure.as_view());
        assert_eq!(decoder.num_bad_x_errors(&error_basis), 1);
        assert_eq!(decoder.num_bad_z_errors(&error_basis), 1);
        assert_eq!(decoder.recovery_probability(erasure.as_view()), 0.25);
    }

    #[test]
    fn erasure_failures_in_toric_code() {
        let code = CssCode::toric_code(3);
        let decoder = CssErasureDecoder::new(&code);

        let erasure = SparseBinVec::new(18, vec![0, 1, 2]);
        let error_basis = decoder.error_basis(erasure.as_view());
        assert_eq!(decoder.num_bad_x_errors(&error_basis), 0);
        assert_eq!(decoder.num_bad_z_errors(&error_basis), 1);
        assert_eq!(decoder.recovery_probability(erasure.as_view()), 0.5);

        let erasure = SparseBinVec::new(18, vec![9, 10, 11]);
        let error_basis = decoder.error_basis(erasure.as_view());
        assert_eq!(decoder.num_bad_x_errors(&error_basis), 1);
        assert_eq!(decoder.num_bad_z_errors(&error_basis), 0);
        assert_eq!(decoder.recovery_probability(erasure.as_view()), 0.5);

        let erasure = SparseBinVec::new(18, vec![1, 4, 6, 7, 8, 9, 12, 15]);
        let error_basis = decoder.error_basis(erasure.as_view());
        assert_eq!(decoder.num_bad_x_errors(&error_basis), 1);
        assert_eq!(decoder.num_bad_z_errors(&error_basis), 2);
        assert_eq!(decoder.recovery_probability(erasure.as_view()), 0.125);

        let erasure = SparseBinVec::new(18, vec![1, 4, 6, 7, 8, 9, 10, 11, 12, 15]);
        let error_basis = decoder.error_basis(erasure.as_view());
        assert_eq!(decoder.num_bad_x_errors(&error_basis), 2);
        assert_eq!(decoder.num_bad_z_errors(&error_basis), 2);
        assert_eq!(decoder.recovery_probability(erasure.as_view()), 0.0625);
    }

    #[test]
    fn erasure_successes_in_toric_code() {
        let code = CssCode::toric_code(3);
        println!(
            "x: {} \n {}",
            code.x_stabs_binary(),
            code.x_logicals_binary()
        );
        println!(
            "z: {} \n {}",
            code.z_stabs_binary(),
            code.z_logicals_binary()
        );
        let decoder = CssErasureDecoder::new(&code);

        let erasure = SparseBinVec::new(18, vec![0, 1]);
        let error_basis = decoder.error_basis(erasure.as_view());
        assert_eq!(decoder.num_bad_x_errors(&error_basis), 0);
        assert_eq!(decoder.num_bad_z_errors(&error_basis), 0);
        assert_eq!(decoder.recovery_probability(erasure.as_view()), 1.0);

        let erasure = SparseBinVec::new(18, vec![0, 1, 3, 4, 9, 10, 12, 13]);
        let error_basis = decoder.error_basis(erasure.as_view());
        assert_eq!(decoder.num_bad_x_errors(&error_basis), 0);
        assert_eq!(decoder.num_bad_z_errors(&error_basis), 0);
        assert_eq!(decoder.recovery_probability(erasure.as_view()), 1.0);
    }
}