scirs2_sparse/
error.rs

1//! Error types for the SciRS2 sparse module
2//!
3//! This module provides error types for both sparse matrix and sparse array operations.
4
5use thiserror::Error;
6
7/// Sparse matrix/array error type
8#[derive(Error, Debug)]
9pub enum SparseError {
10    /// Computation error (generic error)
11    #[error("Computation error: {0}")]
12    ComputationError(String),
13
14    /// Dimension mismatch error
15    #[error("Dimension mismatch: expected {expected}, found {found}")]
16    DimensionMismatch { expected: usize, found: usize },
17
18    /// Index out of bounds error
19    #[error("Index {index:?} out of bounds for array with shape {shape:?}")]
20    IndexOutOfBounds {
21        index: (usize, usize),
22        shape: (usize, usize),
23    },
24
25    /// Invalid axis error
26    #[error("Invalid axis specified")]
27    InvalidAxis,
28
29    /// Invalid slice range error
30    #[error("Invalid slice range specified")]
31    InvalidSliceRange,
32
33    /// Inconsistent data error
34    #[error("Inconsistent data: {reason}")]
35    InconsistentData { reason: String },
36
37    /// Not implemented error
38    #[error("Feature not implemented: {0}")]
39    NotImplemented(String),
40
41    /// Singular matrix error
42    #[error("Singular matrix error: {0}")]
43    SingularMatrix(String),
44
45    /// Value error (invalid value)
46    #[error("Value error: {0}")]
47    ValueError(String),
48
49    /// Conversion error
50    #[error("Conversion error: {0}")]
51    ConversionError(String),
52
53    /// Operation not supported error
54    #[error("Operation not supported: {0}")]
55    OperationNotSupported(String),
56
57    /// Shape mismatch error
58    #[error("Shape mismatch: expected {expected:?}, found {found:?}")]
59    ShapeMismatch {
60        expected: (usize, usize),
61        found: (usize, usize),
62    },
63
64    /// Iterative solver failure error
65    #[error("Iterative solver failure: {0}")]
66    IterativeSolverFailure(String),
67
68    /// Convergence error for iterative algorithms
69    #[error("Convergence error: {0}")]
70    ConvergenceError(String),
71
72    /// Index cast overflow error
73    #[error("Index value {value} cannot be represented in the target type {target_type}")]
74    IndexCastOverflow {
75        value: usize,
76        target_type: &'static str,
77    },
78
79    /// Invalid format error
80    #[error("Invalid format: {0}")]
81    InvalidFormat(String),
82
83    /// I/O error
84    #[error("I/O error: {0}")]
85    IoError(#[from] std::io::Error),
86
87    /// GPU error
88    #[error("GPU error: {0}")]
89    GpuError(#[from] crate::gpu_ops::GpuError),
90
91    /// Compression error
92    #[error("Compression error: {0}")]
93    CompressionError(String),
94
95    /// I/O error with custom message
96    #[error("I/O error: {0}")]
97    Io(String),
98
99    /// Block not found error
100    #[error("Block not found: {0}")]
101    BlockNotFound(String),
102}
103
104/// Result type for sparse matrix/array operations
105pub type SparseResult<T> = Result<T, SparseError>;
106
107impl SparseError {
108    /// Get a user-friendly description of the error with possible solutions
109    pub fn help_message(&self) -> &'static str {
110        match self {
111            SparseError::DimensionMismatch { .. } => {
112                "Ensure that vectors and matrices have compatible dimensions for the operation. \
113                 For matrix-vector multiplication A*x, A must have the same number of columns as x has elements."
114            },
115            SparseError::IndexOutOfBounds { .. } => {
116                "Check that all row and column indices are within the matrix bounds. \
117                 Remember that indices are 0-based in Rust."
118            },
119            SparseError::ShapeMismatch { .. } => {
120                "Verify that matrix shapes are compatible for the operation. \
121                 For addition/subtraction, matrices must have identical shapes. \
122                 For multiplication A*B, A's column count must equal B's row count."
123            },
124            SparseError::SingularMatrix(_) => {
125                "The matrix is singular (non-invertible). This can happen when:\n\
126                 - The matrix has zero or very small diagonal elements\n\
127                 - Rows or columns are linearly dependent\n\
128                 - The condition number is too large\n\
129                 Consider using iterative methods or regularization."
130            },
131            SparseError::IterativeSolverFailure(_) => {
132                "The iterative solver failed to converge. Try:\n\
133                 - Increasing the maximum number of iterations\n\
134                 - Using a better preconditioner\n\
135                 - Reducing the convergence tolerance\n\
136                 - Using a different solver method"
137            },
138            SparseError::ConvergenceError(_) => {
139                "Algorithm failed to converge within the specified tolerance and iterations. \
140                 Consider relaxing the tolerance or increasing iteration limits."
141            },
142            SparseError::InconsistentData { .. } => {
143                "The sparse matrix data is inconsistent. Check that:\n\
144                 - indptr array has correct length (nrows + 1)\n\
145                 - indices and data arrays have the same length\n\
146                 - indptr values are non-decreasing\n\
147                 - All indices are within bounds"
148            },
149            SparseError::IndexCastOverflow { .. } => {
150                "Index values are too large for the target type. \
151                 Consider using a larger index type (e.g., usize instead of u32)."
152            },
153            SparseError::InvalidFormat(_) => {
154                "The sparse matrix format is invalid or unsupported for this operation. \
155                 Check that the matrix format matches the expected format for the operation."
156            }_ => "Refer to the documentation for more information about this error."
157        }
158    }
159
160    /// Get suggestions for how to fix this error
161    pub fn suggestions(&self) -> Vec<&'static str> {
162        match self {
163            SparseError::DimensionMismatch { .. } => vec![
164                "Check matrix and vector dimensions before operations",
165                "Use .shape() to inspect matrix dimensions",
166                "Consider transposing matrices if needed",
167            ],
168            SparseError::SingularMatrix(_) => vec![
169                "Check matrix condition number with condest()",
170                "Add regularization to improve conditioning",
171                "Use iterative methods like CG or BiCGSTAB",
172                "Try incomplete factorizations (ILU, IC)",
173            ],
174            SparseError::IterativeSolverFailure(_) => vec![
175                "Increase max_iter in solver options",
176                "Use preconditioning (Jacobi, SSOR, ILU)",
177                "Try a different solver (GMRES, BiCGSTAB)",
178                "Check matrix properties (symmetry, definiteness)",
179            ],
180            SparseError::InconsistentData { .. } => vec![
181                "Use from_triplets() for safer construction",
182                "Validate data with .check_format() if available",
183                "Ensure indices are sorted when required",
184            ],
185            SparseError::InvalidFormat(_) => vec![
186                "Convert matrix to the required format",
187                "Use .to_csr() or .to_csc() for format conversion",
188                "Check if the operation supports the current format",
189            ],
190            _ => vec!["Check the documentation for this operation"],
191        }
192    }
193
194    /// Create a dimension mismatch error with helpful context
195    pub fn dimension_mismatch_with_context(expected: usize, found: usize, operation: &str) -> Self {
196        SparseError::DimensionMismatch { expected, found }
197    }
198
199    /// Create a shape mismatch error with helpful context
200    pub fn shape_mismatch_with_context(
201        expected: (usize, usize),
202        found: (usize, usize),
203        _operation: &str,
204    ) -> Self {
205        SparseError::ShapeMismatch { expected, found }
206    }
207}
208
209impl From<String> for SparseError {
210    fn from(error: String) -> Self {
211        SparseError::ComputationError(error)
212    }
213}