Skip to main content

numra_core/
error.rs

1//! Error types for Numra.
2//!
3//! This module defines the error types used throughout the Numra library.
4//!
5//! Author: Moussa Leblouba
6//! Date: 9 February 2026
7//! Modified: 2 May 2026
8
9use core::fmt;
10
11/// Result type alias for Numra operations.
12pub type NumraResult<T> = Result<T, NumraError>;
13
14/// Errors that can occur in Numra operations.
15#[derive(Clone, Debug, PartialEq)]
16pub enum NumraError {
17    /// Linear algebra error
18    Linalg(LinalgError),
19    /// Convergence failure
20    Convergence(ConvergenceError),
21    /// Invalid input
22    InvalidInput(String),
23    /// Step size became too small
24    StepSizeTooSmall { h: f64, h_min: f64 },
25    /// Maximum iterations exceeded
26    MaxIterations { iterations: usize, max: usize },
27    /// Integration reached a singularity or stiffness
28    Stiffness { t: f64, message: String },
29    /// Event caused termination
30    EventTermination { t: f64, event_index: usize },
31    /// Optimization error
32    Optimization(OptimizationError),
33}
34
35impl fmt::Display for NumraError {
36    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37        match self {
38            NumraError::Linalg(e) => write!(f, "Linear algebra error: {}", e),
39            NumraError::Convergence(e) => write!(f, "Convergence error: {}", e),
40            NumraError::InvalidInput(msg) => write!(f, "Invalid input: {}", msg),
41            NumraError::StepSizeTooSmall { h, h_min } => {
42                write!(f, "Step size {} below minimum {}", h, h_min)
43            }
44            NumraError::MaxIterations { iterations, max } => {
45                write!(f, "Maximum iterations ({}) exceeded at {}", max, iterations)
46            }
47            NumraError::Stiffness { t, message } => {
48                write!(f, "Stiffness detected at t={}: {}", t, message)
49            }
50            NumraError::EventTermination { t, event_index } => {
51                write!(f, "Event {} terminated integration at t={}", event_index, t)
52            }
53            NumraError::Optimization(e) => write!(f, "Optimization error: {}", e),
54        }
55    }
56}
57
58#[cfg(feature = "std")]
59impl std::error::Error for NumraError {}
60
61/// Linear algebra specific errors.
62#[derive(Clone, Debug, PartialEq)]
63pub enum LinalgError {
64    /// Matrix is singular
65    Singular { step: usize },
66    /// Dimensions mismatch
67    DimensionMismatch {
68        expected: (usize, usize),
69        actual: (usize, usize),
70    },
71    /// Matrix is not square
72    NotSquare { nrows: usize, ncols: usize },
73    /// Matrix is not positive definite
74    NotPositiveDefinite,
75    /// Iterative solver did not converge
76    IterativeNotConverged { iterations: usize, residual: f64 },
77    /// Eigenvalue decomposition failed
78    EigenDecompositionFailed,
79    /// SVD computation failed
80    SvdFailed,
81}
82
83impl fmt::Display for LinalgError {
84    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85        match self {
86            LinalgError::Singular { step } => {
87                write!(f, "Matrix is singular (detected at step {})", step)
88            }
89            LinalgError::DimensionMismatch { expected, actual } => {
90                write!(
91                    f,
92                    "Dimension mismatch: expected {:?}, got {:?}",
93                    expected, actual
94                )
95            }
96            LinalgError::NotSquare { nrows, ncols } => {
97                write!(f, "Matrix is not square: {}x{}", nrows, ncols)
98            }
99            LinalgError::NotPositiveDefinite => {
100                write!(f, "Matrix is not positive definite")
101            }
102            LinalgError::IterativeNotConverged {
103                iterations,
104                residual,
105            } => {
106                write!(
107                    f,
108                    "Iterative solver did not converge after {} iterations (residual: {})",
109                    iterations, residual
110                )
111            }
112            LinalgError::EigenDecompositionFailed => {
113                write!(f, "Eigenvalue decomposition failed")
114            }
115            LinalgError::SvdFailed => {
116                write!(f, "SVD computation failed")
117            }
118        }
119    }
120}
121
122impl From<LinalgError> for NumraError {
123    fn from(e: LinalgError) -> Self {
124        NumraError::Linalg(e)
125    }
126}
127
128/// Convergence-related errors.
129#[derive(Clone, Debug, PartialEq)]
130pub enum ConvergenceError {
131    /// Newton iteration did not converge
132    Newton { iterations: usize, residual: f64 },
133    /// Fixed point iteration did not converge
134    FixedPoint { iterations: usize, delta: f64 },
135}
136
137impl fmt::Display for ConvergenceError {
138    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139        match self {
140            ConvergenceError::Newton {
141                iterations,
142                residual,
143            } => {
144                write!(
145                    f,
146                    "Newton iteration did not converge after {} iterations (residual: {})",
147                    iterations, residual
148                )
149            }
150            ConvergenceError::FixedPoint { iterations, delta } => {
151                write!(
152                    f,
153                    "Fixed point iteration did not converge after {} iterations (delta: {})",
154                    iterations, delta
155                )
156            }
157        }
158    }
159}
160
161impl From<ConvergenceError> for NumraError {
162    fn from(e: ConvergenceError) -> Self {
163        NumraError::Convergence(e)
164    }
165}
166
167/// Optimization-related errors.
168#[derive(Clone, Debug, PartialEq)]
169pub enum OptimizationError {
170    /// Line search failed to find sufficient decrease
171    LineSearchFailed { iterations: usize, step_size: f64 },
172    /// Search direction is not a descent direction
173    NotDescentDirection { directional_derivative: f64 },
174    /// Optimization did not converge
175    NotConverged {
176        iterations: usize,
177        gradient_norm: f64,
178    },
179    /// Function evaluation returned NaN or infinity
180    InvalidFunctionValue { value: f64 },
181}
182
183impl fmt::Display for OptimizationError {
184    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185        match self {
186            OptimizationError::LineSearchFailed {
187                iterations,
188                step_size,
189            } => {
190                write!(
191                    f,
192                    "Line search failed after {} iterations (step size: {})",
193                    iterations, step_size
194                )
195            }
196            OptimizationError::NotDescentDirection {
197                directional_derivative,
198            } => {
199                write!(
200                    f,
201                    "Search direction is not a descent direction (directional derivative: {})",
202                    directional_derivative
203                )
204            }
205            OptimizationError::NotConverged {
206                iterations,
207                gradient_norm,
208            } => {
209                write!(
210                    f,
211                    "Optimization did not converge after {} iterations (gradient norm: {})",
212                    iterations, gradient_norm
213                )
214            }
215            OptimizationError::InvalidFunctionValue { value } => {
216                write!(f, "Function evaluation returned invalid value: {}", value)
217            }
218        }
219    }
220}
221
222impl From<OptimizationError> for NumraError {
223    fn from(e: OptimizationError) -> Self {
224        NumraError::Optimization(e)
225    }
226}