1use tenferro_tensor::{DType, ErrorKind};
22
23#[cfg(any(feature = "cpu-blas", feature = "cuda"))]
25#[derive(Debug, thiserror::Error)]
26pub(crate) enum BackendError {
27 #[cfg(feature = "cuda")]
28 #[error("{library} call {call} returned status {status}")]
29 ProviderStatus {
30 library: &'static str,
31 call: &'static str,
32 status: i32,
33 },
34 #[cfg(feature = "cpu-blas")]
35 #[error("{library} routine {routine} returned an invalid workspace: {detail}")]
36 InvalidWorkspace {
37 library: &'static str,
38 routine: &'static str,
39 detail: String,
40 },
41}
42
43#[derive(Debug, thiserror::Error)]
55#[non_exhaustive]
56pub enum Error {
57 #[error("{op} did not converge")]
59 NonConvergence {
60 op: &'static str,
62 },
63 #[error("{op} encountered non-finite {role}")]
65 NonFinite {
66 op: &'static str,
68 role: &'static str,
70 },
71 #[error("{op} is singular")]
73 Singular {
74 op: &'static str,
76 },
77 #[error("{op} does not support dtype {dtype:?}")]
79 UnsupportedDType {
80 op: &'static str,
82 dtype: DType,
84 },
85}
86
87impl Error {
88 #[must_use]
102 pub fn kind(&self) -> ErrorKind {
103 match self {
104 Self::NonConvergence { .. } | Self::NonFinite { .. } | Self::Singular { .. } => {
105 ErrorKind::NumericalFailure
106 }
107 Self::UnsupportedDType { .. } => ErrorKind::Unsupported,
108 }
109 }
110}
111
112pub type Result<T> = std::result::Result<T, Error>;
123
124pub(crate) fn into_tensor_error(op: &'static str, source: Error) -> tenferro_tensor::Error {
126 tenferro_tensor::Error::extension(
127 op,
128 crate::extension::LINALG_EXTENSION_FAMILY_ID,
129 source.kind(),
130 source,
131 )
132}
133
134pub(crate) fn unsupported_dtype(op: &'static str, dtype: DType) -> tenferro_tensor::Error {
136 into_tensor_error(op, Error::UnsupportedDType { op, dtype })
137}
138
139#[cfg(feature = "cuda")]
141pub(crate) fn backend_status(
142 op: &'static str,
143 library: &'static str,
144 call: &'static str,
145 status: i32,
146) -> tenferro_tensor::Error {
147 tenferro_tensor::Error::backend_source(
148 op,
149 BackendError::ProviderStatus {
150 library,
151 call,
152 status,
153 },
154 )
155}
156
157#[cfg(feature = "cpu-blas")]
159pub(crate) fn invalid_workspace(
160 op: &'static str,
161 library: &'static str,
162 routine: &'static str,
163 detail: impl Into<String>,
164) -> tenferro_tensor::Error {
165 tenferro_tensor::Error::backend_source(
166 op,
167 BackendError::InvalidWorkspace {
168 library,
169 routine,
170 detail: detail.into(),
171 },
172 )
173}
174
175#[cfg(all(test, any(feature = "cpu-blas", feature = "cuda")))]
176mod tests {
177 use std::error::Error as _;
178
179 use super::*;
180
181 #[cfg(feature = "cuda")]
182 #[test]
183 fn provider_status_keeps_typed_backend_source() {
184 let error = backend_status("svd", "cuSOLVER", "cusolverDnSgesvd", 7);
185
186 assert_eq!(error.kind(), ErrorKind::BackendFailure);
187 assert!(matches!(
188 error.source().and_then(|source| source.downcast_ref()),
189 Some(BackendError::ProviderStatus {
190 library: "cuSOLVER",
191 call: "cusolverDnSgesvd",
192 status: 7,
193 })
194 ));
195 }
196
197 #[cfg(feature = "cpu-blas")]
198 #[test]
199 fn invalid_workspace_keeps_typed_backend_source() {
200 let error = invalid_workspace("eigh", "LAPACK", "dsyevd", "query was zero");
201
202 assert_eq!(error.kind(), ErrorKind::BackendFailure);
203 assert!(matches!(
204 error.source().and_then(|source| source.downcast_ref()),
205 Some(BackendError::InvalidWorkspace {
206 library: "LAPACK",
207 routine: "dsyevd",
208 detail,
209 }) if detail == "query was zero"
210 ));
211 }
212}