use faer::sparse::SymbolicSparseColMatRef;
use faer_traits::Index;
use super::Ilu0Error;
#[derive(Debug, Clone)]
pub struct SymbolicIlu0<I> {
pub(crate) dim: usize,
pub(crate) a_nnz: usize,
pub(crate) diag_pos: Vec<usize>,
pub(crate) l_col_ptr: Vec<I>,
pub(crate) l_row_idx: Vec<I>,
pub(crate) u_col_ptr: Vec<I>,
pub(crate) u_row_idx: Vec<I>,
}
impl<I: Index> SymbolicIlu0<I> {
pub fn try_new(pattern: SymbolicSparseColMatRef<'_, I>) -> Result<Self, Ilu0Error> {
let nrows = pattern.nrows();
let ncols = pattern.ncols();
if nrows != ncols {
return Err(Ilu0Error::NonSquareMatrix { nrows, ncols });
}
let n = nrows;
let mut diag_pos = Vec::with_capacity(n);
let mut l_col_ptr: Vec<I> = Vec::with_capacity(n + 1);
l_col_ptr.push(I::truncate(0));
let mut u_col_ptr: Vec<I> = Vec::with_capacity(n + 1);
u_col_ptr.push(I::truncate(0));
let mut a_nnz = 0usize;
let mut l_running = 0usize;
let mut u_running = 0usize;
for j in 0..n {
let row_idx = pattern.row_idx_of_col_raw(j);
let col_len = row_idx.len();
a_nnz += col_len;
let mut diag_idx: Option<usize> = None;
let mut prev: Option<usize> = None;
for (k, raw) in row_idx.iter().enumerate() {
let r = raw.zx();
if let Some(p) = prev
&& r <= p
{
return Err(Ilu0Error::UnsortedRowIndices { col: j });
}
if r == j {
diag_idx = Some(k);
}
prev = Some(r);
}
let d = diag_idx.ok_or(Ilu0Error::MissingDiagonal { col: j })?;
diag_pos.push(d);
u_running += d + 1;
l_running += col_len - d;
u_col_ptr.push(I::truncate(u_running));
l_col_ptr.push(I::truncate(l_running));
}
let mut l_row_idx: Vec<I> = Vec::with_capacity(l_running);
let mut u_row_idx: Vec<I> = Vec::with_capacity(u_running);
for (j, &d) in diag_pos.iter().enumerate() {
let row_idx = pattern.row_idx_of_col_raw(j);
u_row_idx.extend_from_slice(&row_idx[..=d]);
l_row_idx.push(I::truncate(j));
l_row_idx.extend_from_slice(&row_idx[d + 1..]);
}
debug_assert_eq!(l_row_idx.len(), l_running);
debug_assert_eq!(u_row_idx.len(), u_running);
Ok(Self {
dim: n,
a_nnz,
diag_pos,
l_col_ptr,
l_row_idx,
u_col_ptr,
u_row_idx,
})
}
#[inline]
pub fn dim(&self) -> usize {
self.dim
}
#[inline]
pub fn a_nnz(&self) -> usize {
self.a_nnz
}
#[inline]
pub fn l_nnz(&self) -> usize {
self.l_row_idx.len()
}
#[inline]
pub fn u_nnz(&self) -> usize {
self.u_row_idx.len()
}
}