use crate::klu_native::KluNativeSystem;
use crate::sparse::{LinearSolver, RealSparseSystem};
#[cfg(feature = "klu")]
use crate::sparse_klu::KluRealSystem;
#[cfg(feature = "pardiso")]
use crate::sparse_pardiso::PardisoRealSystem;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum LuBackend {
#[default]
Faer,
KluNative,
#[cfg(feature = "klu")]
Klu,
#[cfg(feature = "pardiso")]
Pardiso,
}
enum System {
Faer(RealSparseSystem),
KluNative(KluNativeSystem),
#[cfg(feature = "klu")]
Klu(KluRealSystem),
#[cfg(feature = "pardiso")]
Pardiso(PardisoRealSystem),
}
pub struct SparseLu {
backend: LuBackend,
system: Option<System>,
order: Option<Vec<usize>>,
}
impl SparseLu {
pub fn new(backend: LuBackend) -> Self {
Self {
backend,
system: None,
order: None,
}
}
pub fn backend(&self) -> LuBackend {
self.backend
}
pub fn is_analyzed(&self) -> bool {
self.system.is_some()
}
pub fn analyze(&mut self, n: usize, entries: &[(usize, usize, f64)]) -> bool {
self.system = match self.backend {
LuBackend::Faer => RealSparseSystem::new(n, entries).map(System::Faer),
LuBackend::KluNative => KluNativeSystem::new(n, entries).map(System::KluNative),
#[cfg(feature = "klu")]
LuBackend::Klu => KluRealSystem::new(n, entries).map(System::Klu),
#[cfg(feature = "pardiso")]
LuBackend::Pardiso => PardisoRealSystem::new(n, entries).map(System::Pardiso),
};
self.order = None;
self.system.is_some()
}
pub fn adopt_storage_order(&mut self) -> Option<Vec<usize>> {
let order = match self.system.as_mut()? {
System::KluNative(s) => s.adopt_storage_order(),
#[cfg(feature = "klu")]
System::Klu(s) => s.adopt_storage_order(),
_ => None,
}?;
self.order = Some(order.clone());
Some(order)
}
pub fn storage_order(&self) -> Option<&[usize]> {
self.order.as_deref()
}
pub fn reset(&mut self) {
self.system = None;
self.order = None;
}
pub fn factor_and_solve(
&mut self,
entries: &[(usize, usize, f64)],
rhs: &[f64],
) -> Option<Vec<f64>> {
match self.system.as_mut()? {
System::Faer(s) => s.factor_and_solve(entries, rhs),
System::KluNative(s) => s.factor_and_solve(entries, rhs),
#[cfg(feature = "klu")]
System::Klu(s) => s.factor_and_solve(entries, rhs),
#[cfg(feature = "pardiso")]
System::Pardiso(s) => s.factor_and_solve(entries, rhs),
}
}
pub fn factor_and_solve_values(&mut self, values: &[f64], rhs: &[f64]) -> Option<Vec<f64>> {
match self.system.as_mut()? {
System::Faer(s) => s.factor_and_solve_values(values, rhs),
System::KluNative(s) => s.factor_and_solve_values(values, rhs),
#[cfg(feature = "klu")]
System::Klu(s) => s.factor_and_solve_values(values, rhs),
#[cfg(feature = "pardiso")]
System::Pardiso(s) => s.factor_and_solve_values(values, rhs),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
const ENTRIES: [(usize, usize, f64); 7] = [
(0, 0, 4.0),
(0, 1, 1.0),
(1, 1, 5.0),
(2, 1, 2.0),
(1, 0, 1.0),
(2, 2, 3.0),
(1, 2, 1.0),
];
const RHS: [f64; 3] = [1.0, 2.0, 3.0];
fn adopted_order_solves_the_same_system(backend: LuBackend) {
let values: Vec<f64> = ENTRIES.iter().map(|e| e.2).collect();
let mut lu = SparseLu::new(backend);
assert!(lu.analyze(3, &ENTRIES));
let before = lu.factor_and_solve_values(&values, &RHS).unwrap();
let order = lu.adopt_storage_order().expect("KLU adopts its order");
assert_ne!(
order,
(0..ENTRIES.len()).collect::<Vec<_>>(),
"not a real permutation"
);
assert_eq!(lu.storage_order(), Some(&order[..]));
let reordered: Vec<f64> = order.iter().map(|&i| values[i]).collect();
let after = lu.factor_and_solve_values(&reordered, &RHS).unwrap();
assert_eq!(before, after);
lu.reset();
assert_eq!(lu.storage_order(), None);
}
#[test]
fn klu_native_adopts_its_storage_order() {
adopted_order_solves_the_same_system(LuBackend::KluNative);
}
#[cfg(feature = "klu")]
#[test]
fn klu_adopts_its_storage_order() {
adopted_order_solves_the_same_system(LuBackend::Klu);
}
#[test]
fn faer_keeps_the_callers_order() {
let mut lu = SparseLu::new(LuBackend::Faer);
assert!(lu.analyze(3, &ENTRIES));
assert_eq!(lu.adopt_storage_order(), None);
}
#[test]
fn duplicate_positions_keep_the_callers_order() {
let mut entries = ENTRIES.to_vec();
entries.push((0, 0, 1.0));
let mut lu = SparseLu::new(LuBackend::KluNative);
assert!(lu.analyze(3, &entries));
assert_eq!(lu.adopt_storage_order(), None);
}
}