believer 0.2.1

A toolbox to simulate error correcting code performance.
Documentation
//! Note: Prototype idea
use crate::ParityCheckMatrix;
use crate::Pauli;

pub struct GF4Stabilizers {
    x_checks: ParityCheckMatrix,
    z_checks: ParityCheckMatrix,
}

impl GF4Stabilizers {
    pub fn from_dense_paulis(stabilizers: Vec<Vec<Pauli>>, n_qubits: usize) -> Self {
        let mut x_checks = Vec::with_capacity(stabilizers.len());
        let mut z_checks = Vec::with_capacity(stabilizers.len());
        stabilizers.iter().for_each(|stab| {
            let mut x_check = Vec::new();
            let mut z_check = Vec::new();
            stab.iter().enumerate().for_each(|(index, p)| {
                let gf4_representation = p.as_gf4();
                if gf4_representation.0 == 1 {
                    x_check.push(index);
                }
                if gf4_representation.1 == 1 {
                    z_check.push(index);
                }
            });
            x_checks.push(x_check);
            z_checks.push(z_check);
        });
        Self {
            x_checks: ParityCheckMatrix::new(x_checks, n_qubits),
            z_checks: ParityCheckMatrix::new(z_checks, n_qubits),
        }
    }

    pub fn from_sparse_paulis(stabilizers: Vec<Vec<(Pauli, usize)>>, n_qubits: usize) -> Self {
        let mut x_checks = Vec::with_capacity(stabilizers.len());
        let mut z_checks = Vec::with_capacity(stabilizers.len());
        stabilizers.iter().for_each(|stab| {
            let mut x_check = Vec::new();
            let mut z_check = Vec::new();
            stab.iter().for_each(|(pauli, position)| {
                let gf4_representation = pauli.as_gf4();
                if gf4_representation.0 == 1 {
                    x_check.push(*position);
                }
                if gf4_representation.1 == 1 {
                    z_check.push(*position);
                }
            });
            x_checks.push(x_check);
            z_checks.push(z_check);
        });
        Self {
            x_checks: ParityCheckMatrix::new(x_checks, n_qubits),
            z_checks: ParityCheckMatrix::new(z_checks, n_qubits),
        }
    }

    pub fn from_parity_check_matrices(
        x_checks: ParityCheckMatrix,
        z_checks: ParityCheckMatrix,
    ) -> Self {
        if x_checks.get_n_bits() != z_checks.get_n_bits() {
            panic!("different number of bits in each parity check matrix")
        }
        Self { x_checks, z_checks }
    }

    pub fn x_checks(&self) -> &ParityCheckMatrix {
        &self.x_checks
    }

    pub fn z_checks(&self) -> &ParityCheckMatrix {
        &self.z_checks
    }

    pub fn keep(&self, qubits: &[usize]) -> Self {
        Self {
            x_checks: self.x_checks.keep(qubits),
            z_checks: self.z_checks.keep(qubits),
        }
    }

    pub fn merge(&self) -> ParityCheckMatrix {
        self.x_checks.right_concat(&self.z_checks)
    }

    pub fn n_qubits(&self) -> usize {
        self.x_checks().get_n_bits()
    }

    pub fn n_stabilizers(&self) -> usize {
        std::cmp::max(
            self.x_checks().get_n_checks(),
            self.z_checks().get_n_checks(),
        )
    }

    pub fn without(&self, qubits: &[usize]) -> Self {
        Self {
            x_checks: self.x_checks.without(qubits),
            z_checks: self.z_checks.without(qubits),
        }
    }
}