use crate::algebra::linear_algebra::Matrix;
use crate::core::binary_matrix::BinaryMatrix;
use crate::data_manager::DataManager;
use crate::traits::HDPC;
use crate::types::CodeParams;
use crate::algebra::finite_field::GF2;
pub struct InactiveSystem {
params: CodeParams,
num_inactive: usize,
matrix_f_packed: Option<BinaryMatrix>,
matrix_f: Vec<Vec<u8>>,
rhs_data_ids: Vec<usize>,
q: Vec<usize>,
num_binary_rows: usize,
r: usize,
}
impl InactiveSystem {
pub fn new(params: &CodeParams) -> Self {
Self {
params: params.clone(),
num_inactive: 0,
matrix_f_packed: None,
matrix_f: Vec::new(),
rhs_data_ids: Vec::new(),
q: Vec::new(),
num_binary_rows: 0,
r: 0,
}
}
pub fn init(&mut self, num_inactive: usize) {
self.num_inactive = num_inactive;
self.matrix_f = Vec::new();
self.rhs_data_ids = Vec::new();
self.q = (0..num_inactive).collect::<Vec<_>>();
self.num_binary_rows = 0;
self.r = 0;
self.matrix_f_packed = Some(BinaryMatrix::new(num_inactive));
}
fn add_row(&mut self, row: Vec<u8>, rhs_id: usize) {
self.matrix_f.push(row);
self.rhs_data_ids.push(rhs_id);
}
pub fn add_row_from_master(&mut self, master: &BinaryMatrix, equ_id: usize, rhs_id: usize) {
if let Some(packed) = &mut self.matrix_f_packed {
let n = self.num_inactive;
let mut words = vec![0_u64; packed.words_per_row()];
master.copy_row_words(equ_id, &mut words, n);
packed.append_row_from_words(&words, n);
self.rhs_data_ids.push(rhs_id);
} else {
self.add_row(master.row_bytes(equ_id, self.num_inactive), rhs_id);
}
}
fn materialize_packed_prefix(&mut self) {
if let Some(packed) = self.matrix_f_packed.take() {
let mut prefix = packed.to_dense_rows(self.num_inactive);
prefix.append(&mut self.matrix_f);
self.matrix_f = prefix;
}
}
fn matrix_row_count(&self) -> usize {
self.matrix_f_packed
.as_ref()
.map_or(0, BinaryMatrix::num_rows)
+ self.matrix_f.len()
}
fn has_stale_hdpc_tail(&self) -> bool {
!self.matrix_f.is_empty()
&& self
.matrix_f_packed
.as_ref()
.is_some_and(|packed| self.r < packed.num_rows())
}
fn clear_hdpc_tail(&mut self) {
let tail = self.matrix_f.len();
if tail == 0 {
return;
}
let keep = self.rhs_data_ids.len().saturating_sub(tail);
self.rhs_data_ids.truncate(keep);
self.matrix_f.clear();
}
fn lu_get_coeff(&self, row: usize, logical_col: usize) -> u8 {
if let Some(packed) = &self.matrix_f_packed {
if row < packed.num_rows() {
u8::from(packed.get_logical(row, &self.q, logical_col))
} else {
self.matrix_f[row - packed.num_rows()][self.q[logical_col]]
}
} else {
self.matrix_f[row][self.q[logical_col]]
}
}
pub fn append_hdpc_constraints(
&mut self,
hdpc: &dyn HDPC,
tilde_g_rows: &[Vec<u8>],
manager: &mut DataManager,
) {
let mut hdpc_constraint_data_ids = Vec::with_capacity(self.params.h);
for _ in 0..self.params.h {
hdpc_constraint_data_ids.push(manager.temp_data_id());
}
let variable_ids_active: Vec<usize> = manager.data_id_range_of_active_variable();
hdpc.mul_data(
manager,
&self.params,
&variable_ids_active,
&hdpc_constraint_data_ids,
);
self.rhs_data_ids.append(&mut hdpc_constraint_data_ids);
let mut matrix_d = hdpc.mul_binary_from_rows(manager.gf256(), &self.params, tilde_g_rows);
let b = self.params.b;
for (i, row) in matrix_d.iter_mut().enumerate().take(self.params.h) {
row[i + b] ^= 1;
}
self.num_binary_rows = self.matrix_row_count();
self.matrix_f.append(&mut matrix_d);
}
pub fn append_hdpc_constraints_packed(
&mut self,
hdpc: &dyn HDPC,
tilde_g: &BinaryMatrix,
manager: &mut DataManager,
) {
let mut hdpc_constraint_data_ids = Vec::with_capacity(self.params.h);
for _ in 0..self.params.h {
hdpc_constraint_data_ids.push(manager.temp_data_id());
}
let variable_ids_active: Vec<usize> = manager.data_id_range_of_active_variable();
hdpc.mul_data(
manager,
&self.params,
&variable_ids_active,
&hdpc_constraint_data_ids,
);
self.rhs_data_ids.append(&mut hdpc_constraint_data_ids);
let mut matrix_d =
hdpc.mul_binary_packed(manager.gf256(), &self.params, self.num_inactive, tilde_g);
let b = self.params.b;
for i in 0..self.params.h {
matrix_d.flip(i, i + b);
}
if let Some(packed) = &mut self.matrix_f_packed {
packed.append_all_rows_from(&matrix_d, self.num_inactive);
} else {
self.matrix_f
.extend(matrix_d.to_dense_rows(self.num_inactive));
}
self.num_binary_rows = self.matrix_row_count();
}
pub(crate) fn has_full_rank_factorization(&self) -> bool {
if self.matrix_row_count() < self.num_inactive {
return false;
}
(0..self.num_inactive).all(|j| self.lu_get_coeff(j, j) != 0)
}
pub fn lu_decomposition(
&mut self,
manager: &mut DataManager,
remove_redundant_rhs: bool,
) -> usize {
if self.has_stale_hdpc_tail() {
self.clear_hdpc_tail();
}
let (p, r) = if let Some(packed) = &mut self.matrix_f_packed {
if self.matrix_f.is_empty() {
packed.lu_decomp_incr_gf2(&mut self.q, self.r, self.num_inactive)
} else {
self.materialize_packed_prefix();
Matrix::lu_decomp_incr_mixed(
manager.gf256().unwrap(),
&mut self.matrix_f,
&mut self.q,
self.r,
self.num_binary_rows,
)
}
} else if manager.gf256().is_some() {
Matrix::lu_decomp_incr_mixed(
manager.gf256().unwrap(),
&mut self.matrix_f,
&mut self.q,
self.r,
self.num_binary_rows,
)
} else {
Matrix::lu_decomp_incr(&GF2::new(), &mut self.matrix_f, &mut self.q, self.r)
};
let ids_new: Vec<_> = p.iter().take(r).map(|&pi| self.rhs_data_ids[pi]).collect();
for &pi in p.iter().skip(r) {
if remove_redundant_rhs {
manager.remove(self.rhs_data_ids[pi]);
}
}
self.rhs_data_ids = ids_new;
if let Some(packed) = &mut self.matrix_f_packed {
let packed_keep = r.min(packed.num_rows());
packed.truncate_rows(packed_keep);
self.matrix_f.truncate(r.saturating_sub(packed_keep));
} else {
self.matrix_f.truncate(r);
}
if self.num_binary_rows > r {
self.num_binary_rows = r;
}
self.r = r;
r
}
pub(crate) fn is_binary_only(&self) -> bool {
self.matrix_f.is_empty() && self.matrix_f_packed.is_some()
}
pub fn lu_solve(&mut self, manager: &mut DataManager) -> Vec<usize> {
if self.is_binary_only() {
self.lu_solve_gf2_packed(manager)
} else {
if self.matrix_f_packed.is_some() {
self.materialize_packed_prefix();
}
self.lu_solve_dense(manager)
}
}
fn lu_solve_gf2_packed(&mut self, manager: &mut DataManager) -> Vec<usize> {
let q = self.q.clone();
let n = self.rhs_data_ids.len();
if self
.matrix_f_packed
.as_ref()
.is_none_or(|p| p.num_rows() != n)
{
panic!("The number of rows in F must equal the number of RHS vectors");
}
for (j, &phys_col) in q.iter().enumerate().take(n - 1) {
for i in j + 1..n {
let add = self.matrix_f_packed.as_ref().unwrap().get(i, phys_col);
if add {
let src = self.rhs_data_ids[j];
let dst = self.rhs_data_ids[i];
manager.add_one_to_vector(src, dst);
}
}
}
for j in (0..n).rev() {
let phys_col = q[j];
if !self.matrix_f_packed.as_ref().unwrap().get(j, phys_col) {
panic!(
"Singular matrix in inactive system: diagonal element at position {} is zero",
j
);
}
for i in (0..j).rev() {
if self.matrix_f_packed.as_ref().unwrap().get(i, phys_col) {
let src = self.rhs_data_ids[j];
let dst = self.rhs_data_ids[i];
manager.add_one_to_vector(src, dst);
}
}
}
let mut ids_new = vec![0; self.r];
for i in 0..self.r {
ids_new[q[i]] = self.rhs_data_ids[i];
}
ids_new
}
fn lu_solve_dense(&mut self, manager: &mut DataManager) -> Vec<usize> {
let n = self.matrix_row_count();
if n != self.rhs_data_ids.len() {
panic!("The number of rows in F must equal the number of RHS vectors");
}
for j in 0..n - 1 {
for i in j + 1..n {
let l = self.lu_get_coeff(i, j);
if l == 0 {
continue;
}
if l == 1 {
manager.add_one_to_vector(self.rhs_data_ids[j], self.rhs_data_ids[i]);
} else {
manager.mul_add(self.rhs_data_ids[j], l, self.rhs_data_ids[i]);
}
}
}
for j in (0..n).rev() {
let l = self.lu_get_coeff(j, j);
if l == 0 {
panic!(
"Singular matrix in inactive system: diagonal element at position {} is zero",
j
);
}
if l != 1 {
manager.divide_scalar(l, self.rhs_data_ids[j]);
}
for i in (0..j).rev() {
let l = self.lu_get_coeff(i, j);
if l == 0 {
continue;
}
if l == 1 {
manager.add_one_to_vector(self.rhs_data_ids[j], self.rhs_data_ids[i]);
} else {
manager.mul_add(self.rhs_data_ids[j], l, self.rhs_data_ids[i]);
}
}
}
let mut ids_new = vec![0; self.r];
for i in 0..self.r {
ids_new[self.q[i]] = self.rhs_data_ids[i];
}
ids_new
}
}