1use tenferro_tensor::{DType, ErrorKind};
22
23#[cfg(any(feature = "blas", feature = "cuda", feature = "native"))]
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(any(feature = "blas", feature = "native"))]
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(any(feature = "blas", feature = "native"))]
162pub(crate) fn invalid_workspace(
163 op: &'static str,
164 library: &'static str,
165 routine: &'static str,
166 detail: impl Into<String>,
167) -> tenferro_tensor::Error {
168 tenferro_tensor::Error::backend_source(
169 op,
170 BackendError::InvalidWorkspace {
171 library,
172 routine,
173 detail: detail.into(),
174 },
175 )
176}
177
178#[cfg(all(test, any(feature = "blas", feature = "cuda", feature = "native")))]
179mod tests {
180 use std::error::Error as _;
181
182 use super::*;
183
184 #[cfg(feature = "cuda")]
185 #[test]
186 fn provider_status_keeps_typed_backend_source() {
187 let error = backend_status("svd", "cuSOLVER", "cusolverDnSgesvd", 7);
188
189 assert_eq!(error.kind(), ErrorKind::BackendFailure);
190 assert!(matches!(
191 error.source().and_then(|source| source.downcast_ref()),
192 Some(BackendError::ProviderStatus {
193 library: "cuSOLVER",
194 call: "cusolverDnSgesvd",
195 status: 7,
196 })
197 ));
198 }
199
200 #[cfg(any(feature = "blas", feature = "native"))]
201 #[test]
202 fn invalid_workspace_keeps_typed_backend_source() {
203 let error = invalid_workspace("eigh", "LAPACK", "dsyevd", "query was zero");
204
205 assert_eq!(error.kind(), ErrorKind::BackendFailure);
206 assert!(matches!(
207 error.source().and_then(|source| source.downcast_ref()),
208 Some(BackendError::InvalidWorkspace {
209 library: "LAPACK",
210 routine: "dsyevd",
211 detail,
212 }) if detail == "query was zero"
213 ));
214 }
215}