use tenferro_tensor::{DType, ErrorKind};
#[cfg(any(feature = "cpu-blas", feature = "cuda"))]
#[derive(Debug, thiserror::Error)]
pub(crate) enum BackendError {
#[cfg(feature = "cuda")]
#[error("{library} call {call} returned status {status}")]
ProviderStatus {
library: &'static str,
call: &'static str,
status: i32,
},
#[cfg(feature = "cpu-blas")]
#[error("{library} routine {routine} returned an invalid workspace: {detail}")]
InvalidWorkspace {
library: &'static str,
routine: &'static str,
detail: String,
},
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("{op} did not converge")]
NonConvergence {
op: &'static str,
},
#[error("{op} is singular")]
Singular {
op: &'static str,
},
#[error("{op} does not support dtype {dtype:?}")]
UnsupportedDType {
op: &'static str,
dtype: DType,
},
}
impl Error {
#[must_use]
pub fn kind(&self) -> ErrorKind {
match self {
Self::NonConvergence { .. } | Self::Singular { .. } => ErrorKind::NumericalFailure,
Self::UnsupportedDType { .. } => ErrorKind::Unsupported,
}
}
}
pub type Result<T> = std::result::Result<T, Error>;
pub(crate) fn into_tensor_error(op: &'static str, source: Error) -> tenferro_tensor::Error {
tenferro_tensor::Error::extension(
op,
crate::extension::LINALG_EXTENSION_FAMILY_ID,
source.kind(),
source,
)
}
pub(crate) fn unsupported_dtype(op: &'static str, dtype: DType) -> tenferro_tensor::Error {
into_tensor_error(op, Error::UnsupportedDType { op, dtype })
}
#[cfg(feature = "cuda")]
pub(crate) fn backend_status(
op: &'static str,
library: &'static str,
call: &'static str,
status: i32,
) -> tenferro_tensor::Error {
tenferro_tensor::Error::backend_source(
op,
BackendError::ProviderStatus {
library,
call,
status,
},
)
}
#[cfg(feature = "cpu-blas")]
pub(crate) fn invalid_workspace(
op: &'static str,
library: &'static str,
routine: &'static str,
detail: impl Into<String>,
) -> tenferro_tensor::Error {
tenferro_tensor::Error::backend_source(
op,
BackendError::InvalidWorkspace {
library,
routine,
detail: detail.into(),
},
)
}
#[cfg(all(test, any(feature = "cpu-blas", feature = "cuda")))]
mod tests {
use std::error::Error as _;
use super::*;
#[cfg(feature = "cuda")]
#[test]
fn provider_status_keeps_typed_backend_source() {
let error = backend_status("svd", "cuSOLVER", "cusolverDnSgesvd", 7);
assert_eq!(error.kind(), ErrorKind::BackendFailure);
assert!(matches!(
error.source().and_then(|source| source.downcast_ref()),
Some(BackendError::ProviderStatus {
library: "cuSOLVER",
call: "cusolverDnSgesvd",
status: 7,
})
));
}
#[cfg(feature = "cpu-blas")]
#[test]
fn invalid_workspace_keeps_typed_backend_source() {
let error = invalid_workspace("eigh", "LAPACK", "dsyevd", "query was zero");
assert_eq!(error.kind(), ErrorKind::BackendFailure);
assert!(matches!(
error.source().and_then(|source| source.downcast_ref()),
Some(BackendError::InvalidWorkspace {
library: "LAPACK",
routine: "dsyevd",
detail,
}) if detail == "query was zero"
));
}
}