Skip to main content

rusolver/
error.rs

1// SPDX-License-Identifier: Apache-2.0
2use std::{
3    error::Error, fmt
4};
5/// Absolute/relative cutoff: `max(absolute, relative * scale)`.
6/// Both may be zero for exact-zero checks. Negative/nonfinite inputs are rejected.
7#[derive(Clone, Copy, Debug, PartialEq)]
8pub struct Tolerance {
9    pub absolute: f64, pub relative: f64
10}
11impl Default for Tolerance {
12    fn default() -> Self {
13        Self {
14            absolute: 0.0, relative: 1e-12
15        }
16    }
17}
18impl Tolerance {
19    pub const EXACT: Self = Self {
20        absolute: 0.0, relative: 0.0
21    };
22    pub fn validate(self) -> Result<(), SolverError> {
23        if !self.absolute.is_finite() || !self.relative.is_finite()
24        || self.absolute < 0.0 || self.relative < 0.0 {
25            Err(SolverError::InvalidOption("tolerance must be finite and nonnegative"))
26        } else {
27            Ok(())
28        }
29    }
30    pub(crate) fn threshold(self, scale: f64) -> Result<f64, SolverError> {
31        self.validate()?;
32        let value = self.absolute.max(self.relative * scale);
33        if !scale.is_finite() || scale < 0.0 || !value.is_finite() {
34            Err(SolverError::Arithmetic("tolerance scale overflow"))
35        } else {
36            Ok(value)
37        }
38    }
39}
40#[derive(Clone, Debug, PartialEq)]
41pub enum SolverError {
42    Shape(&'static str),
43    WorkspaceLimit { required: usize, limit: usize },
44    Communication(String),
45    SizeOverflow,
46    Allocation,
47    NonFinite {
48        index: usize
49    },
50    InvalidOption(&'static str),
51    Singular {
52        index: usize, pivot: f64, threshold: f64
53    },
54    NotSymmetric {
55        row: usize, column: usize
56    },
57    NotPositiveDefinite {
58        index: usize, pivot: f64
59    },
60    RankDeficient {
61        rank: usize, columns: usize
62    },
63    Arithmetic(&'static str),
64    Breakdown(&'static str),
65    NonConvergence {
66        iterations: usize, residual: f64
67    },
68    Operator(&'static str),
69}
70impl fmt::Display for SolverError {
71    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72        match self {
73            Self::WorkspaceLimit { required, limit } => write!(f, "workspace fill {required} exceeds limit {limit}"),
74            Self::Communication(s) => write!(f, "solver communication error: {s}"),
75            Self::Shape(s) => write!(f, "invalid matrix/vector shape: {s}"),
76            Self::SizeOverflow => write!(f, "matrix size or address range overflows usize"),
77            Self::Allocation => write!(f, "numerical workspace allocation failed"),
78            Self::NonFinite {
79                index
80            } => write!(f, "nonfinite input at logical index {index}"),
81            Self::InvalidOption(s) => write!(f, "invalid solver option: {s}"),
82            Self::Singular {
83                index, pivot, threshold
84            } => write!(f, "singular/numerically singular pivot {index}: |{pivot}| <= {threshold}"),
85            Self::NotSymmetric {
86                row, column
87            } => write!(f, "matrix differs across ({row},{column}) and its transpose"),
88            Self::NotPositiveDefinite {
89                index, pivot
90            } => write!(f, "nonpositive Cholesky pivot {index}: {pivot}"),
91            Self::RankDeficient {
92                rank, columns
93            } => write!(f, "numerical column rank {rank} < {columns}; this QR path requires full column rank; use Svd for a minimum-norm solve"),
94            Self::Arithmetic(s) => write!(f, "nonfinite/unsupported numerical intermediate: {s}"),
95            Self::Breakdown(s) => write!(f, "iterative solver breakdown: {s}"),
96            Self::NonConvergence {
97                iterations, residual
98            } => write!(f, "not converged after {iterations} iterations; residual {residual}"),
99            Self::Operator(s) => write!(f, "linear operator error: {s}"),
100        }
101    }
102}
103impl Error for SolverError {
104}