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} is singular")]
65 Singular {
66 op: &'static str,
68 },
69 #[error("{op} does not support dtype {dtype:?}")]
71 UnsupportedDType {
72 op: &'static str,
74 dtype: DType,
76 },
77}
78
79impl Error {
80 #[must_use]
94 pub fn kind(&self) -> ErrorKind {
95 match self {
96 Self::NonConvergence { .. } | Self::Singular { .. } => ErrorKind::NumericalFailure,
97 Self::UnsupportedDType { .. } => ErrorKind::Unsupported,
98 }
99 }
100}
101
102pub type Result<T> = std::result::Result<T, Error>;
113
114pub(crate) fn into_tensor_error(op: &'static str, source: Error) -> tenferro_tensor::Error {
116 tenferro_tensor::Error::extension(
117 op,
118 crate::extension::LINALG_EXTENSION_FAMILY_ID,
119 source.kind(),
120 source,
121 )
122}
123
124pub(crate) fn unsupported_dtype(op: &'static str, dtype: DType) -> tenferro_tensor::Error {
126 into_tensor_error(op, Error::UnsupportedDType { op, dtype })
127}
128
129#[cfg(feature = "cuda")]
131pub(crate) fn backend_status(
132 op: &'static str,
133 library: &'static str,
134 call: &'static str,
135 status: i32,
136) -> tenferro_tensor::Error {
137 tenferro_tensor::Error::backend_source(
138 op,
139 BackendError::ProviderStatus {
140 library,
141 call,
142 status,
143 },
144 )
145}
146
147#[cfg(feature = "cpu-blas")]
149pub(crate) fn invalid_workspace(
150 op: &'static str,
151 library: &'static str,
152 routine: &'static str,
153 detail: impl Into<String>,
154) -> tenferro_tensor::Error {
155 tenferro_tensor::Error::backend_source(
156 op,
157 BackendError::InvalidWorkspace {
158 library,
159 routine,
160 detail: detail.into(),
161 },
162 )
163}
164
165#[cfg(all(test, any(feature = "cpu-blas", feature = "cuda")))]
166mod tests {
167 use std::error::Error as _;
168
169 use super::*;
170
171 #[cfg(feature = "cuda")]
172 #[test]
173 fn provider_status_keeps_typed_backend_source() {
174 let error = backend_status("svd", "cuSOLVER", "cusolverDnSgesvd", 7);
175
176 assert_eq!(error.kind(), ErrorKind::BackendFailure);
177 assert!(matches!(
178 error.source().and_then(|source| source.downcast_ref()),
179 Some(BackendError::ProviderStatus {
180 library: "cuSOLVER",
181 call: "cusolverDnSgesvd",
182 status: 7,
183 })
184 ));
185 }
186
187 #[cfg(feature = "cpu-blas")]
188 #[test]
189 fn invalid_workspace_keeps_typed_backend_source() {
190 let error = invalid_workspace("eigh", "LAPACK", "dsyevd", "query was zero");
191
192 assert_eq!(error.kind(), ErrorKind::BackendFailure);
193 assert!(matches!(
194 error.source().and_then(|source| source.downcast_ref()),
195 Some(BackendError::InvalidWorkspace {
196 library: "LAPACK",
197 routine: "dsyevd",
198 detail,
199 }) if detail == "query was zero"
200 ));
201 }
202}