1use oxicuda_blas::BlasError;
8use oxicuda_driver::CudaError;
9use oxicuda_ptx::PtxGenError;
10use thiserror::Error;
11
12#[derive(Debug, Error)]
18pub enum SolverError {
19 #[error("CUDA driver error: {0}")]
21 Cuda(#[from] CudaError),
22
23 #[error("BLAS error: {0}")]
25 Blas(#[from] BlasError),
26
27 #[error("PTX generation error: {0}")]
29 PtxGeneration(#[from] PtxGenError),
30
31 #[error("singular matrix detected")]
33 SingularMatrix,
34
35 #[error("matrix is not positive definite")]
37 NotPositiveDefinite,
38
39 #[error("dimension mismatch: {0}")]
41 DimensionMismatch(String),
42
43 #[error("convergence failure after {iterations} iterations (residual = {residual:.6e})")]
45 ConvergenceFailure {
46 iterations: u32,
48 residual: f64,
50 },
51
52 #[error("workspace of at least {0} bytes required")]
54 WorkspaceRequired(usize),
55
56 #[error("internal solver error: {0}")]
58 InternalError(String),
59}
60
61pub type SolverResult<T> = Result<T, SolverError>;
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 #[test]
69 fn display_singular_matrix() {
70 let err = SolverError::SingularMatrix;
71 assert!(err.to_string().contains("singular"));
72 }
73
74 #[test]
75 fn display_convergence_failure() {
76 let err = SolverError::ConvergenceFailure {
77 iterations: 100,
78 residual: 1e-3,
79 };
80 let msg = err.to_string();
81 assert!(msg.contains("100"));
82 assert!(msg.contains("1.0"));
83 }
84
85 #[test]
86 fn from_cuda_error() {
87 let cuda_err = CudaError::NotInitialized;
88 let solver_err: SolverError = cuda_err.into();
89 assert!(matches!(solver_err, SolverError::Cuda(_)));
90 }
91
92 #[test]
93 fn from_blas_error() {
94 let blas_err = BlasError::InvalidDimension("test".into());
95 let solver_err: SolverError = blas_err.into();
96 assert!(matches!(solver_err, SolverError::Blas(_)));
97 }
98
99 #[test]
100 fn display_workspace_required() {
101 let err = SolverError::WorkspaceRequired(4096);
102 assert!(err.to_string().contains("4096"));
103 }
104}