pub mod cholesky;
pub mod explicit_schur;
pub mod implicit_schur;
pub mod qr;
use crate::core::problem::VariableEnum;
use faer::{Mat, sparse::SparseColMat};
use std::{
collections::HashMap,
fmt,
fmt::{Display, Formatter},
};
use thiserror::Error;
use tracing::error;
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum LinearSolverType {
#[default]
SparseCholesky,
SparseQR,
SparseSchurComplement,
}
impl Display for LinearSolverType {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
LinearSolverType::SparseCholesky => write!(f, "Sparse Cholesky"),
LinearSolverType::SparseQR => write!(f, "Sparse QR"),
LinearSolverType::SparseSchurComplement => write!(f, "Sparse Schur Complement"),
}
}
}
#[derive(Debug, Clone, Error)]
pub enum LinAlgError {
#[error("Matrix factorization failed: {0}")]
FactorizationFailed(String),
#[error("Singular matrix detected: {0}")]
SingularMatrix(String),
#[error("Failed to create sparse matrix: {0}")]
SparseMatrixCreation(String),
#[error("Matrix conversion failed: {0}")]
MatrixConversion(String),
#[error("Invalid input: {0}")]
InvalidInput(String),
#[error("Invalid solver state: {0}")]
InvalidState(String),
}
impl LinAlgError {
#[must_use]
pub fn log(self) -> Self {
error!("{}", self);
self
}
#[must_use]
pub fn log_with_source<E: std::fmt::Debug>(self, source_error: E) -> Self {
error!("{} | Source: {:?}", self, source_error);
self
}
}
pub type LinAlgResult<T> = Result<T, LinAlgError>;
pub trait StructuredSparseLinearSolver {
fn initialize_structure(
&mut self,
variables: &HashMap<String, VariableEnum>,
variable_index_map: &HashMap<String, usize>,
) -> LinAlgResult<()>;
fn solve_normal_equation(
&mut self,
residuals: &Mat<f64>,
jacobians: &SparseColMat<usize, f64>,
) -> LinAlgResult<Mat<f64>>;
fn solve_augmented_equation(
&mut self,
residuals: &Mat<f64>,
jacobians: &SparseColMat<usize, f64>,
lambda: f64,
) -> LinAlgResult<Mat<f64>>;
fn get_hessian(&self) -> Option<&SparseColMat<usize, f64>>;
fn get_gradient(&self) -> Option<&Mat<f64>>;
}
pub trait SparseLinearSolver {
fn solve_normal_equation(
&mut self,
residuals: &Mat<f64>,
jacobians: &SparseColMat<usize, f64>,
) -> LinAlgResult<Mat<f64>>;
fn solve_augmented_equation(
&mut self,
residuals: &Mat<f64>,
jacobians: &SparseColMat<usize, f64>,
lambda: f64,
) -> LinAlgResult<Mat<f64>>;
fn get_hessian(&self) -> Option<&SparseColMat<usize, f64>>;
fn get_gradient(&self) -> Option<&Mat<f64>>;
fn compute_covariance_matrix(&mut self) -> Option<&Mat<f64>>;
fn get_covariance_matrix(&self) -> Option<&Mat<f64>>;
}
pub use cholesky::SparseCholeskySolver;
pub use explicit_schur::{
SchurBlockStructure, SchurOrdering, SchurPreconditioner, SchurSolverAdapter, SchurVariant,
SparseSchurComplementSolver,
};
pub use implicit_schur::IterativeSchurSolver;
pub use qr::SparseQRSolver;
pub fn extract_variable_covariances(
full_covariance: &Mat<f64>,
variables: &HashMap<String, VariableEnum>,
variable_index_map: &HashMap<String, usize>,
) -> HashMap<String, Mat<f64>> {
let mut result = HashMap::new();
for (var_name, var) in variables {
if let Some(&start_idx) = variable_index_map.get(var_name) {
let dim = var.get_size();
let mut var_cov = Mat::zeros(dim, dim);
for i in 0..dim {
for j in 0..dim {
var_cov[(i, j)] = full_covariance[(start_idx + i, start_idx + j)];
}
}
result.insert(var_name.clone(), var_cov);
}
}
result
}