t
This commit is contained in:
295
srctmp/matrix.rs
295
srctmp/matrix.rs
@ -1,295 +0,0 @@
|
||||
use crate::{Gf2, Result};
|
||||
|
||||
// Matrice creuse GF(2) — format CSR + CSC dual
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SparseMatrixGF2 {
|
||||
pub rows: usize,
|
||||
pub cols: usize,
|
||||
// CSR — accès ligne i : col_idx[ row_ptr[i] .. row_ptr[i+1] ]
|
||||
row_ptr: Vec<usize>,
|
||||
col_idx: Vec<usize>,
|
||||
// CSC — accès col j : row_idx[ col_ptr[j] .. col_ptr[j+1] ]
|
||||
col_ptr: Vec<usize>,
|
||||
row_idx: Vec<usize>,
|
||||
}
|
||||
|
||||
impl SparseMatrixGF2 {
|
||||
pub fn zeros(rows: usize, cols: usize) -> Self {
|
||||
Self {
|
||||
rows,
|
||||
cols,
|
||||
row_ptr: vec![0; rows + 1],
|
||||
col_idx: vec![],
|
||||
col_ptr: vec![0; cols + 1],
|
||||
row_idx: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
// Depuis une liste de (row, col) indiquant les positions des 1s.
|
||||
// Trie les entrées et construit CSR + CSC en un seul passage.
|
||||
pub fn from_positions(rows: usize, cols: usize, mut ones: Vec<(usize, usize)>) -> Self {
|
||||
// Construction CSR
|
||||
ones.sort_unstable();
|
||||
let mut row_ptr = vec![0usize; rows + 1];
|
||||
let mut col_idx = Vec::with_capacity(ones.len());
|
||||
for &(r, c) in &ones {
|
||||
row_ptr[r + 1] += 1;
|
||||
col_idx.push(c);
|
||||
}
|
||||
for i in 0..rows {
|
||||
row_ptr[i + 1] += row_ptr[i];
|
||||
}
|
||||
// Construction CSC
|
||||
let mut col_sorted = ones.clone();
|
||||
col_sorted.sort_unstable_by_key(|&(r, c)| (c, r));
|
||||
let mut col_ptr = vec![0usize; cols + 1];
|
||||
let mut row_idx = Vec::with_capacity(ones.len());
|
||||
for &(r, c) in &col_sorted {
|
||||
col_ptr[c + 1] += 1;
|
||||
row_idx.push(r);
|
||||
}
|
||||
for j in 0..cols {
|
||||
col_ptr[j + 1] += col_ptr[j];
|
||||
}
|
||||
Self {
|
||||
rows,
|
||||
cols,
|
||||
row_ptr,
|
||||
col_idx,
|
||||
col_ptr,
|
||||
row_idx,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_dense(dense: &[Vec<Gf2>]) -> Self {
|
||||
let rows = dense.len();
|
||||
let cols = if rows > 0 { dense[0].len() } else { 0 };
|
||||
let ones: Vec<(usize, usize)> = dense
|
||||
.iter()
|
||||
.enumerate()
|
||||
.flat_map(|(r, row)| {
|
||||
row.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, &v)| v == 1)
|
||||
.map(move |(c, _)| (r, c))
|
||||
})
|
||||
.collect();
|
||||
Self::from_positions(rows, cols, ones)
|
||||
}
|
||||
|
||||
pub fn get(&self, row: usize, col: usize) -> Gf2 {
|
||||
let slice = self.row_neighbors(row);
|
||||
if slice.binary_search(&col).is_ok() {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
// Indices des colonnes où la ligne `row` vaut 1 (voisins check→var)
|
||||
pub fn row_neighbors(&self, row: usize) -> &[usize] {
|
||||
&self.col_idx[self.row_ptr[row]..self.row_ptr[row + 1]]
|
||||
}
|
||||
|
||||
// Indices des lignes où la colonne `col` vaut 1 (voisins var→check)
|
||||
pub fn col_neighbors(&self, col: usize) -> &[usize] {
|
||||
&self.row_idx[self.col_ptr[col]..self.col_ptr[col + 1]]
|
||||
}
|
||||
|
||||
pub fn row_weight(&self, row: usize) -> usize {
|
||||
self.row_ptr[row + 1] - self.row_ptr[row]
|
||||
}
|
||||
|
||||
pub fn col_weight(&self, col: usize) -> usize {
|
||||
self.col_ptr[col + 1] - self.col_ptr[col]
|
||||
}
|
||||
|
||||
pub fn nnz(&self) -> usize {
|
||||
self.col_idx.len()
|
||||
}
|
||||
|
||||
pub fn density(&self) -> f64 {
|
||||
self.nnz() as f64 / (self.rows * self.cols) as f64
|
||||
}
|
||||
|
||||
// Produit H * x mod 2 (calcul du syndrome : s = H * c^T)
|
||||
pub fn multiply_vec(&self, x: &[Gf2]) -> Vec<Gf2> {
|
||||
(0..self.rows)
|
||||
.map(|r| {
|
||||
self.row_neighbors(r)
|
||||
.iter()
|
||||
.map(|&c| x[c])
|
||||
.fold(0u8, |acc, b| acc ^ b)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn transpose(&self) -> Self {
|
||||
Self {
|
||||
rows: self.cols,
|
||||
cols: self.rows,
|
||||
row_ptr: self.col_ptr.clone(),
|
||||
col_idx: self.row_idx.clone(),
|
||||
col_ptr: self.row_ptr.clone(),
|
||||
row_idx: self.col_idx.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
// Vérifie si deux colonnes partagent >= 2 positions de 1 -> cycle-4 détecté
|
||||
pub fn columns_share_two_ones(&self, c1: usize, c2: usize) -> bool {
|
||||
let n1 = self.col_neighbors(c1);
|
||||
let n2 = self.col_neighbors(c2);
|
||||
let mut common = 0usize;
|
||||
let (mut i, mut j) = (0, 0);
|
||||
while i < n1.len() && j < n2.len() {
|
||||
match n1[i].cmp(&n2[j]) {
|
||||
std::cmp::Ordering::Less => i += 1,
|
||||
std::cmp::Ordering::Greater => j += 1,
|
||||
std::cmp::Ordering::Equal => {
|
||||
common += 1;
|
||||
if common >= 2 {
|
||||
return true;
|
||||
}
|
||||
i += 1;
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn to_dense(&self) -> Vec<Vec<Gf2>> {
|
||||
let mut out = vec![vec![0u8; self.cols]; self.rows];
|
||||
for r in 0..self.rows {
|
||||
for &c in self.row_neighbors(r) {
|
||||
out[r][c] = 1;
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
// Matrice dense GF(2)
|
||||
// Utilisée pour la matrice génératrice G et les calculs de Gauss-Jordan
|
||||
// G = [I | P], P est souvent dense
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DenseMatrixGF2 {
|
||||
pub rows: usize,
|
||||
pub cols: usize,
|
||||
data: Vec<Vec<Gf2>>,
|
||||
}
|
||||
|
||||
impl DenseMatrixGF2 {
|
||||
pub fn zeros(rows: usize, cols: usize) -> Self {
|
||||
Self {
|
||||
rows,
|
||||
cols,
|
||||
data: vec![vec![0u8; cols]; rows],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn identity(n: usize) -> Self {
|
||||
let mut m = Self::zeros(n, n);
|
||||
for i in 0..n {
|
||||
m.data[i][i] = 1;
|
||||
}
|
||||
m
|
||||
}
|
||||
|
||||
pub fn get(&self, row: usize, col: usize) -> Gf2 {
|
||||
self.data[row][col]
|
||||
}
|
||||
pub fn set(&mut self, row: usize, col: usize, val: Gf2) {
|
||||
self.data[row][col] = val;
|
||||
}
|
||||
|
||||
// Addition de deux lignes dans GF(2)
|
||||
pub fn row_add(&mut self, dst: usize, src: usize) {
|
||||
for j in 0..self.cols {
|
||||
self.data[dst][j] ^= self.data[src][j];
|
||||
}
|
||||
}
|
||||
|
||||
pub fn row_swap(&mut self, r1: usize, r2: usize) {
|
||||
self.data.swap(r1, r2);
|
||||
}
|
||||
|
||||
pub fn multiply_vec(&self, x: &[Gf2]) -> Vec<Gf2> {
|
||||
self.data
|
||||
.iter()
|
||||
.map(|row| {
|
||||
row.iter()
|
||||
.zip(x.iter())
|
||||
.fold(0u8, |acc, (&a, &b)| acc ^ (a & b))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn into_sparse(self) -> SparseMatrixGF2 {
|
||||
SparseMatrixGF2::from_dense(&self.data)
|
||||
}
|
||||
|
||||
// Retourne la permutation de colonnes appliquée et le rang
|
||||
// self est sous forme échelonnée réduite
|
||||
pub fn gauss_jordan_gf2(&mut self) -> (Vec<usize>, usize) {
|
||||
let mut perm: Vec<usize> = (0..self.cols).collect();
|
||||
let mut pivot_row = 0;
|
||||
for col in 0..self.cols {
|
||||
// Chercher un pivot dans la colonne courante
|
||||
let pivot = (pivot_row..self.rows).find(|&r| self.data[r][col] == 1);
|
||||
let Some(p) = pivot else { continue };
|
||||
self.row_swap(pivot_row, p);
|
||||
// Éliminer dans toutes les autres lignes
|
||||
for r in 0..self.rows {
|
||||
if r != pivot_row && self.data[r][col] == 1 {
|
||||
self.row_add(r, pivot_row);
|
||||
}
|
||||
}
|
||||
pivot_row += 1;
|
||||
if pivot_row == self.rows {
|
||||
break;
|
||||
}
|
||||
}
|
||||
(perm, pivot_row)
|
||||
}
|
||||
}
|
||||
|
||||
// #[cfg(test)]
|
||||
// mod tests {
|
||||
// use super::*;
|
||||
//
|
||||
// #[test]
|
||||
// fn test_from_dense_roundtrip() {
|
||||
// let dense = vec![vec![1u8, 0, 1, 0], vec![0, 1, 0, 1], vec![1, 1, 0, 0]];
|
||||
// let sparse = SparseMatrixGF2::from_dense(&dense);
|
||||
// assert_eq!(sparse.to_dense(), dense);
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn test_multiply_vec_gf2() {
|
||||
// let dense = vec![vec![1u8, 1, 0], vec![0, 1, 1]];
|
||||
// let h = SparseMatrixGF2::from_dense(&dense);
|
||||
// let x = vec![1u8, 1, 1];
|
||||
// let s = h.multiply_vec(&x);
|
||||
// // ligne 0 : 1^1^0 = 0, ligne 1 : 0^1^1 = 0
|
||||
// assert_eq!(s, vec![0u8, 0]);
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn test_transpose_double_is_identity() {
|
||||
// let dense = vec![vec![1u8, 0, 1], vec![0, 1, 0]];
|
||||
// let h = SparseMatrixGF2::from_dense(&dense);
|
||||
// assert_eq!(h.transpose().transpose().to_dense(), dense);
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn test_columns_share_two_ones() {
|
||||
// // Colonnes 0 et 1 partagent les lignes 0 et 1 → cycle-4
|
||||
// let dense = vec![vec![1u8, 1, 0], vec![1, 1, 0], vec![0, 0, 1]];
|
||||
// let h = SparseMatrixGF2::from_dense(&dense);
|
||||
// assert!(h.columns_share_two_ones(0, 1));
|
||||
// assert!(!h.columns_share_two_ones(0, 2));
|
||||
// }
|
||||
// }
|
||||
Reference in New Issue
Block a user