convex_math/
error.rs

1//! Error types for mathematical operations.
2
3use thiserror::Error;
4
5/// A specialized Result type for mathematical operations.
6pub type MathResult<T> = Result<T, MathError>;
7
8/// Errors that can occur during mathematical operations.
9#[derive(Error, Debug, Clone)]
10pub enum MathError {
11    /// Root-finding algorithm failed to converge.
12    #[error("Convergence failed after {iterations} iterations (residual: {residual:.2e})")]
13    ConvergenceFailed {
14        /// Number of iterations attempted.
15        iterations: u32,
16        /// Final residual value.
17        residual: f64,
18    },
19
20    /// Invalid bracket for root-finding.
21    #[error("Invalid bracket: f({a}) = {fa:.2e} and f({b}) = {fb:.2e} have same sign")]
22    InvalidBracket {
23        /// Lower bound of bracket.
24        a: f64,
25        /// Upper bound of bracket.
26        b: f64,
27        /// Function value at a.
28        fa: f64,
29        /// Function value at b.
30        fb: f64,
31    },
32
33    /// Division by zero or near-zero value.
34    #[error("Division by zero or near-zero value: {value:.2e}")]
35    DivisionByZero {
36        /// The near-zero value.
37        value: f64,
38    },
39
40    /// Matrix is singular (not invertible).
41    #[error("Singular matrix: cannot invert")]
42    SingularMatrix,
43
44    /// Matrix dimensions are incompatible.
45    #[error("Incompatible matrix dimensions: ({rows1}x{cols1}) and ({rows2}x{cols2})")]
46    DimensionMismatch {
47        /// Rows in first matrix.
48        rows1: usize,
49        /// Columns in first matrix.
50        cols1: usize,
51        /// Rows in second matrix.
52        rows2: usize,
53        /// Columns in second matrix.
54        cols2: usize,
55    },
56
57    /// Interpolation point is outside the valid range.
58    #[error("Extrapolation not allowed: {x} is outside [{min}, {max}]")]
59    ExtrapolationNotAllowed {
60        /// The query point.
61        x: f64,
62        /// Minimum valid value.
63        min: f64,
64        /// Maximum valid value.
65        max: f64,
66    },
67
68    /// Insufficient data points for operation.
69    #[error("Insufficient data: need at least {required}, got {actual}")]
70    InsufficientData {
71        /// Minimum required points.
72        required: usize,
73        /// Actual number of points.
74        actual: usize,
75    },
76
77    /// Invalid input parameter.
78    #[error("Invalid input: {reason}")]
79    InvalidInput {
80        /// Description of the invalid input.
81        reason: String,
82    },
83
84    /// Numerical overflow.
85    #[error("Numerical overflow in {operation}")]
86    Overflow {
87        /// The operation that caused overflow.
88        operation: String,
89    },
90
91    /// Numerical underflow.
92    #[error("Numerical underflow in {operation}")]
93    Underflow {
94        /// The operation that caused underflow.
95        operation: String,
96    },
97}
98
99impl MathError {
100    /// Creates a convergence failed error.
101    #[must_use]
102    pub fn convergence_failed(iterations: u32, residual: f64) -> Self {
103        Self::ConvergenceFailed {
104            iterations,
105            residual,
106        }
107    }
108
109    /// Creates an invalid input error.
110    #[must_use]
111    pub fn invalid_input(reason: impl Into<String>) -> Self {
112        Self::InvalidInput {
113            reason: reason.into(),
114        }
115    }
116
117    /// Creates an insufficient data error.
118    #[must_use]
119    pub fn insufficient_data(required: usize, actual: usize) -> Self {
120        Self::InsufficientData { required, actual }
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127
128    #[test]
129    fn test_error_display() {
130        let err = MathError::convergence_failed(100, 1e-6);
131        assert!(err.to_string().contains("100 iterations"));
132    }
133}