use crate::noise::NoiseModel;
use itertools::Itertools;
use rand::Rng;
use serde::{Deserialize, Serialize};
use sparse_bin_mat::{SparseBinMat, SparseBinSlice, SparseBinVec, SparseBinVecBase};
mod edges;
pub use edges::{Edge, Edges};
mod random;
pub use self::random::RandomRegularCode;
#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)]
pub struct LinearCode {
parity_check_matrix: SparseBinMat,
generator_matrix: SparseBinMat,
bit_adjacencies: SparseBinMat,
}
impl LinearCode {
pub fn from_both_matrices(generator_matrix: SparseBinMat, parity_check_matrix: SparseBinMat) -> Self {
if generator_matrix.number_of_columns() != parity_check_matrix.number_of_columns() {
panic!("matrics have different number of bits (columns)");
}
let bit_adjacencies = parity_check_matrix.transposed();
if !(&generator_matrix * &bit_adjacencies).is_zero() {
panic!("matrices are non orthogonal");
}
Self {
generator_matrix,
parity_check_matrix,
bit_adjacencies
}
}
pub fn from_parity_check_matrix(parity_check_matrix: SparseBinMat) -> Self {
let generator_matrix = parity_check_matrix.nullspace();
let bit_adjacencies = parity_check_matrix.transposed();
Self {
parity_check_matrix,
generator_matrix,
bit_adjacencies,
}
}
pub fn from_generator_matrix(generator_matrix: SparseBinMat) -> Self {
let parity_check_matrix = generator_matrix.nullspace();
let bit_adjacencies = parity_check_matrix.transposed();
Self {
parity_check_matrix,
generator_matrix,
bit_adjacencies,
}
}
pub fn repetition_code(length: usize) -> Self {
let checks = (0..length - 1).map(|c| vec![c, c + 1]).collect();
let matrix = SparseBinMat::new(length, checks);
Self::from_parity_check_matrix(matrix)
}
pub fn hamming_code() -> Self {
let parity_check_matrix = SparseBinMat::new(
7,
vec![vec![3, 4, 5, 6], vec![1, 2, 5, 6], vec![0, 2, 4, 6]],
);
Self::from_parity_check_matrix(parity_check_matrix)
}
pub fn empty() -> Self {
let matrix = SparseBinMat::empty();
Self::from_parity_check_matrix(matrix)
}
pub fn random_regular_code() -> RandomRegularCode {
RandomRegularCode::default()
}
pub fn parity_check_matrix(&self) -> &SparseBinMat {
&self.parity_check_matrix
}
pub fn check(&self, index: usize) -> Option<SparseBinSlice> {
self.parity_check_matrix.row(index)
}
pub fn generator_matrix(&self) -> &SparseBinMat {
&self.generator_matrix
}
pub fn generator(&self, index: usize) -> Option<SparseBinSlice> {
self.generator_matrix.row(index)
}
pub fn bit_adjacencies(&self) -> &SparseBinMat {
&self.bit_adjacencies
}
pub fn checks_adjacent_to_bit(&self, bit: usize) -> Option<SparseBinSlice> {
self.bit_adjacencies.row(bit)
}
pub fn has_same_codespace(&self, other: &Self) -> bool {
self.len() == other.len()
&& (&self.parity_check_matrix * &other.generator_matrix.transposed()).is_zero()
}
pub fn len(&self) -> usize {
self.parity_check_matrix.number_of_columns()
}
pub fn num_checks(&self) -> usize {
self.parity_check_matrix.number_of_rows()
}
pub fn num_generators(&self) -> usize {
self.generator_matrix.number_of_rows()
}
pub fn dimension(&self) -> usize {
self.generator_matrix.rank()
}
pub fn minimal_distance(&self) -> Option<usize> {
(1..=self.num_generators())
.flat_map(|n| self.generator_matrix.rows().combinations(n))
.filter_map(|generators| {
let weight = generators
.into_iter()
.fold(SparseBinVec::zeros(self.len()), |sum, generator| {
&sum + &generator
})
.weight();
if weight > 0 {
Some(weight)
} else {
None
}
})
.min()
}
pub fn edges(&self) -> Edges {
Edges::new(self)
}
pub fn syndrome_of<T>(&self, message: &SparseBinVecBase<T>) -> SparseBinVec
where
T: std::ops::Deref<Target = [usize]>,
{
if message.len() != self.len() {
panic!(
"message of length {} is invalid for code with length {}",
message.len(),
self.len()
);
}
&self.parity_check_matrix * message
}
pub fn has_codeword<T>(&self, operator: &SparseBinVecBase<T>) -> bool
where
T: std::ops::Deref<Target = [usize]>,
{
self.syndrome_of(operator).is_zero()
}
pub fn random_error<N, R>(&self, noise_model: &N, rng: &mut R) -> SparseBinVec
where
N: NoiseModel<Error = SparseBinVec>,
R: Rng,
{
noise_model.sample_error_of_length(self.len(), rng)
}
pub fn as_json(&self) -> serde_json::Result<String> {
serde_json::to_string(self)
}
}