use std::collections::HashMap;
use super::types::{IlukSymbolic, SymbolicIlu0};
use crate::algorithm::sparse_linalg::IluFillLevel;
use crate::error::Result;
pub fn ilu0_symbolic_impl(n: usize, row_ptrs: &[i64], col_indices: &[i64]) -> Result<SymbolicIlu0> {
let mut col_to_idx: Vec<HashMap<usize, usize>> = vec![HashMap::new(); n];
for i in 0..n {
let start = row_ptrs[i] as usize;
let end = row_ptrs[i + 1] as usize;
for idx in start..end {
let j = col_indices[idx] as usize;
col_to_idx[i].insert(j, idx);
}
}
let mut l_row_ptrs = vec![0i64; n + 1];
let mut l_col_indices = Vec::new();
let mut u_row_ptrs = vec![0i64; n + 1];
let mut u_col_indices = Vec::new();
let mut diag_positions = vec![0usize; n];
for i in 0..n {
let start = row_ptrs[i] as usize;
let end = row_ptrs[i + 1] as usize;
for idx in start..end {
let j = col_indices[idx] as usize;
if j < i {
l_col_indices.push(j as i64);
} else {
if j == i {
diag_positions[i] = u_col_indices.len();
}
u_col_indices.push(j as i64);
}
}
l_row_ptrs[i + 1] = l_col_indices.len() as i64;
u_row_ptrs[i + 1] = u_col_indices.len() as i64;
}
let mut update_schedule = Vec::with_capacity(n);
for i in 0..n {
let mut row_updates = Vec::new();
let start_i = row_ptrs[i] as usize;
let end_i = row_ptrs[i + 1] as usize;
for idx_ik in start_i..end_i {
let k = col_indices[idx_ik] as usize;
if k >= i {
break;
}
let mut updates_for_k = Vec::new();
let start_k = row_ptrs[k] as usize;
let end_k = row_ptrs[k + 1] as usize;
for idx_kj in start_k..end_k {
let j = col_indices[idx_kj] as usize;
if j <= k {
continue;
}
if let Some(&idx_ij) = col_to_idx[i].get(&j) {
updates_for_k.push((j, idx_ij, idx_kj));
}
}
if !updates_for_k.is_empty() || col_to_idx[k].contains_key(&k) {
row_updates.push((k, idx_ik, updates_for_k));
}
}
update_schedule.push(row_updates);
}
Ok(SymbolicIlu0 {
n,
l_row_ptrs,
l_col_indices,
u_row_ptrs,
u_col_indices,
diag_positions,
update_schedule,
})
}
pub fn iluk_symbolic_impl(
n: usize,
row_ptrs: &[i64],
col_indices: &[i64],
fill_level: IluFillLevel,
) -> Result<IlukSymbolic> {
let max_level = fill_level.level();
let mut level_of_fill: Vec<HashMap<usize, usize>> = vec![HashMap::new(); n];
for i in 0..n {
let start = row_ptrs[i] as usize;
let end = row_ptrs[i + 1] as usize;
for idx in start..end {
let j = col_indices[idx] as usize;
level_of_fill[i].insert(j, 0);
}
}
for i in 0..n {
let cols_below_diag: Vec<usize> = level_of_fill[i]
.keys()
.filter(|&&k| k < i)
.copied()
.collect();
for k in cols_below_diag {
let level_ik = level_of_fill[i][&k];
let cols_in_row_k: Vec<(usize, usize)> =
level_of_fill[k].iter().map(|(&j, &lev)| (j, lev)).collect();
for (j, level_kj) in cols_in_row_k {
if j == k {
continue;
}
let new_level = level_ik + level_kj + 1;
if new_level <= max_level {
let entry = level_of_fill[i].entry(j).or_insert(new_level);
if new_level < *entry {
*entry = new_level;
}
}
}
}
}
let mut l_row_ptrs = vec![0i64; n + 1];
let mut l_col_indices = Vec::new();
let mut u_row_ptrs = vec![0i64; n + 1];
let mut u_col_indices = Vec::new();
for i in 0..n {
let mut cols: Vec<usize> = level_of_fill[i].keys().copied().collect();
cols.sort_unstable();
for j in cols {
if j < i {
l_col_indices.push(j as i64);
} else {
u_col_indices.push(j as i64);
}
}
l_row_ptrs[i + 1] = l_col_indices.len() as i64;
u_row_ptrs[i + 1] = u_col_indices.len() as i64;
}
Ok(IlukSymbolic {
n,
level_of_fill,
row_ptrs_l: l_row_ptrs,
col_indices_l: l_col_indices,
row_ptrs_u: u_row_ptrs,
col_indices_u: u_col_indices,
fill_level,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ilu0_symbolic_basic() {
let row_ptrs = vec![0i64, 2, 5, 7];
let col_indices = vec![0i64, 1, 0, 1, 2, 1, 2];
let symbolic = ilu0_symbolic_impl(3, &row_ptrs, &col_indices).unwrap();
assert_eq!(symbolic.n, 3);
assert_eq!(symbolic.l_row_ptrs.len(), 4);
assert_eq!(symbolic.u_row_ptrs.len(), 4);
}
#[test]
fn test_iluk_symbolic_level0() {
let row_ptrs = vec![0i64, 2, 5, 7];
let col_indices = vec![0i64, 1, 0, 1, 2, 1, 2];
let symbolic = iluk_symbolic_impl(3, &row_ptrs, &col_indices, IluFillLevel::Zero).unwrap();
assert_eq!(symbolic.n, 3);
assert_eq!(symbolic.col_indices_l.len(), 2);
assert_eq!(symbolic.col_indices_u.len(), 5);
}
#[test]
fn test_iluk_symbolic_level1_fill() {
let row_ptrs = vec![0i64, 2, 5, 7];
let col_indices = vec![0i64, 1, 0, 1, 2, 1, 2];
let symbolic = iluk_symbolic_impl(3, &row_ptrs, &col_indices, IluFillLevel::One).unwrap();
assert!(symbolic.level_of_fill[2].contains_key(&0));
}
}