1use thiserror::Error;
6
7#[derive(Error, Debug)]
9pub enum SparseError {
10 #[error("Computation error: {0}")]
12 ComputationError(String),
13
14 #[error("Dimension mismatch: expected {expected}, found {found}")]
16 DimensionMismatch { expected: usize, found: usize },
17
18 #[error("Index {index:?} out of bounds for array with shape {shape:?}")]
20 IndexOutOfBounds {
21 index: (usize, usize),
22 shape: (usize, usize),
23 },
24
25 #[error("Invalid axis specified")]
27 InvalidAxis,
28
29 #[error("Invalid slice range specified")]
31 InvalidSliceRange,
32
33 #[error("Inconsistent data: {reason}")]
35 InconsistentData { reason: String },
36
37 #[error("Feature not implemented: {0}")]
39 NotImplemented(String),
40
41 #[error("Singular matrix error: {0}")]
43 SingularMatrix(String),
44
45 #[error("Value error: {0}")]
47 ValueError(String),
48
49 #[error("Conversion error: {0}")]
51 ConversionError(String),
52
53 #[error("Operation not supported: {0}")]
55 OperationNotSupported(String),
56
57 #[error("Shape mismatch: expected {expected:?}, found {found:?}")]
59 ShapeMismatch {
60 expected: (usize, usize),
61 found: (usize, usize),
62 },
63
64 #[error("Iterative solver failure: {0}")]
66 IterativeSolverFailure(String),
67
68 #[error("Convergence error: {0}")]
70 ConvergenceError(String),
71
72 #[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 #[error("Invalid format: {0}")]
81 InvalidFormat(String),
82
83 #[error("I/O error: {0}")]
85 IoError(#[from] std::io::Error),
86
87 #[error("GPU error: {0}")]
89 GpuError(#[from] crate::gpu_ops::GpuError),
90
91 #[error("Compression error: {0}")]
93 CompressionError(String),
94
95 #[error("I/O error: {0}")]
97 Io(String),
98
99 #[error("Block not found: {0}")]
101 BlockNotFound(String),
102}
103
104pub type SparseResult<T> = Result<T, SparseError>;
106
107impl SparseError {
108 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 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 pub fn dimension_mismatch_with_context(expected: usize, found: usize, operation: &str) -> Self {
196 SparseError::DimensionMismatch { expected, found }
197 }
198
199 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}