use alloc::vec::Vec;
use core::cmp::Ordering;
use crate::{float::SemiflowFloat, pcg::Preconditioner};
#[derive(Clone)]
pub(crate) struct Ic0<F> {
row_ptr: Vec<usize>,
col_idx: Vec<u32>,
vals: Vec<F>,
}
fn sparse_dot<F: SemiflowFloat>(ca: &[u32], va: &[F], cb: &[u32], vb: &[F]) -> F {
let (mut p, mut q, mut acc) = (0, 0, F::zero());
while p < ca.len() && q < cb.len() {
match ca[p].cmp(&cb[q]) {
Ordering::Less => p += 1,
Ordering::Greater => q += 1,
Ordering::Equal => {
acc += va[p] * vb[q];
p += 1;
q += 1;
}
}
}
acc
}
impl<F: SemiflowFloat> Ic0<F> {
pub(crate) fn build(
n: usize,
row_ptr: &[usize],
col_idx: &[u32],
vals: &[F],
shift: &[F],
) -> Option<Self> {
let mut lp = Vec::with_capacity(n + 1);
let mut lc: Vec<u32> = Vec::with_capacity(vals.len());
let mut lv: Vec<F> = Vec::with_capacity(vals.len());
lp.push(0_usize);
for i in 0..n {
let start = lc.len();
let mut a_ii = shift[i];
for k in row_ptr[i]..row_ptr[i + 1] {
let j = col_idx[k] as usize;
if j == i {
a_ii += vals[k];
} else if j < i {
let (cj, vj) = (&lc[lp[j]..lp[j + 1] - 1], &lv[lp[j]..lp[j + 1] - 1]);
let dot = sparse_dot(&lc[start..], &lv[start..], cj, vj);
let l_jj = lv[lp[j + 1] - 1];
lc.push(col_idx[k]);
lv.push((vals[k] - dot) / l_jj);
}
}
let sq: F = lv[start..].iter().fold(F::zero(), |s, &l| s + l * l);
let pivot = a_ii - sq;
if !(pivot > F::zero() && pivot.is_finite()) {
return None;
}
#[allow(clippy::cast_possible_truncation)]
lc.push(i as u32);
lv.push(pivot.sqrt());
lp.push(lc.len());
}
Some(Self {
row_ptr: lp,
col_idx: lc,
vals: lv,
})
}
}
impl<F: SemiflowFloat> Preconditioner<F> for Ic0<F> {
fn apply(&self, rhs: &[F], out: &mut [F]) {
let len = rhs.len();
out.copy_from_slice(rhs);
for i in 0..len {
let (first, diag) = (self.row_ptr[i], self.row_ptr[i + 1] - 1);
let mut acc = out[i];
for k in first..diag {
acc -= self.vals[k] * out[self.col_idx[k] as usize];
}
out[i] = acc / self.vals[diag];
}
for i in (0..len).rev() {
let (first, diag) = (self.row_ptr[i], self.row_ptr[i + 1] - 1);
let zi = out[i] / self.vals[diag];
out[i] = zi;
for k in first..diag {
out[self.col_idx[k] as usize] -= self.vals[k] * zi;
}
}
}
}