use crate::numerical::BVP_sci::BVP_sci_faer::{faer_col, faer_mat};
use crate::somelinalg::banded::{Banded, BandedError, LapackStyleBandedLuFaithful};
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct BvpSciBandedProfile {
pub nrows: usize,
pub ncols: usize,
pub nnz: usize,
pub kl: usize,
pub ku: usize,
pub nonfinite_values: usize,
}
impl BvpSciBandedProfile {
#[inline]
pub fn is_square(&self) -> bool {
self.nrows == self.ncols
}
#[inline]
pub fn scalar_band_rows(&self) -> usize {
self.kl + self.ku + 1
}
pub fn storage_amplification(&self) -> Option<f64> {
if !self.is_square() || self.nnz == 0 {
return None;
}
Some((self.scalar_band_rows() * self.ncols) as f64 / self.nnz as f64)
}
}
pub fn infer_banded_profile(mat: &faer_mat) -> BvpSciBandedProfile {
let (nrows, _) = mat.shape();
infer_banded_profile_for_row_range(mat, 0, nrows)
}
pub fn infer_banded_profile_for_row_range(
mat: &faer_mat,
row_start: usize,
row_end: usize,
) -> BvpSciBandedProfile {
let (nrows, ncols) = mat.shape();
let row_start = row_start.min(nrows);
let row_end = row_end.min(nrows).max(row_start);
let mut profile = BvpSciBandedProfile {
nrows,
ncols,
..BvpSciBandedProfile::default()
};
let dyn_mat = mat.as_dyn();
for col in 0..ncols {
for row in dyn_mat.row_idx_of_col(col) {
if row < row_start || row >= row_end {
continue;
}
profile.nnz += 1;
if row >= col {
profile.kl = profile.kl.max(row - col);
} else {
profile.ku = profile.ku.max(col - row);
}
let value = mat[(row, col)];
if !value.is_finite() {
profile.nonfinite_values += 1;
}
}
}
profile
}
pub fn sparse_global_jac_to_banded(mat: &faer_mat) -> Result<Banded<f64>, BandedError> {
let profile = infer_banded_profile(mat);
if !profile.is_square() || profile.nonfinite_values > 0 {
return Err(BandedError::DimensionMismatch);
}
let mut banded = Banded::<f64>::zeros(profile.ncols, profile.kl, profile.ku)?;
let dyn_mat = mat.as_dyn();
for col in 0..profile.ncols {
for row in dyn_mat.row_idx_of_col(col) {
banded.set(row, col, mat[(row, col)])?;
}
}
Ok(banded)
}
pub fn solve_banded_lapack_faithful(
mat: &faer_mat,
rhs: &faer_col,
) -> Result<faer_col, BandedError> {
let banded = sparse_global_jac_to_banded(mat)?;
if rhs.nrows() != banded.n() {
return Err(BandedError::DimensionMismatch);
}
let mut lu = LapackStyleBandedLuFaithful::new(banded.n(), banded.kl(), banded.ku())?;
lu.factor_from(&banded)?;
let mut x = (0..rhs.nrows()).map(|i| rhs[i]).collect::<Vec<_>>();
lu.solve_in_place(&mut x)?;
Ok(faer_col::from_fn(x.len(), |i| x[i]))
}