use tenferro_tensor::{DType, ErrorKind, ShapeMismatch, ShapeVec, ValidationError, ValidationKind};
use crate::EINSUM_EXTENSION_FAMILY_ID;
#[derive(Debug, thiserror::Error)]
pub enum PlanningError {
#[error("invalid einsum planning configuration: {message}")]
InvalidConfiguration {
message: String,
},
#[error("einsum planning runtime state unavailable: {message}")]
RuntimeState {
message: String,
},
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("{op}: {source}")]
Validation {
op: &'static str,
#[source]
source: ValidationError,
},
#[error("invalid einsum subscripts: {message}")]
InvalidSubscripts {
message: String,
},
#[error("einsum planning failed: {source}")]
Planning {
#[source]
source: PlanningError,
},
#[error("einsum numerical failure: {message}")]
Numerical {
message: String,
},
#[error(transparent)]
Tensor(#[from] tenferro_tensor::Error),
#[error(transparent)]
Runtime(#[from] tenferro_runtime::Error),
}
impl Error {
pub fn validation(op: &'static str, source: ValidationError) -> Self {
Self::Validation { op, source }
}
pub fn invalid_argument(
op: &'static str,
argument: &'static str,
message: impl Into<String>,
) -> Self {
Self::validation(
op,
ValidationError::InvalidArgument {
argument,
message: message.into(),
},
)
}
pub fn shape_mismatch(
op: &'static str,
expected: impl Into<Vec<usize>>,
actual: impl Into<Vec<usize>>,
) -> Self {
Self::validation(
op,
ShapeMismatch::ExpectedActual {
expected: ShapeVec::from_vec(expected.into()),
actual: ShapeVec::from_vec(actual.into()),
}
.into(),
)
}
pub fn dtype_mismatch(op: &'static str, expected: DType, actual: DType) -> Self {
Self::Tensor(tenferro_tensor::Error::dtype_mismatch(op, expected, actual))
}
pub fn rank_mismatch(op: &'static str, expected: usize, actual: usize) -> Self {
Self::validation(op, ValidationError::RankMismatch { expected, actual })
}
pub fn invalid_subscripts(message: impl Into<String>) -> Self {
Self::InvalidSubscripts {
message: message.into(),
}
}
pub fn planning(message: impl Into<String>) -> Self {
Self::Planning {
source: PlanningError::InvalidConfiguration {
message: message.into(),
},
}
}
pub fn planning_runtime_state(message: impl Into<String>) -> Self {
Self::Planning {
source: PlanningError::RuntimeState {
message: message.into(),
},
}
}
pub fn numerical(message: impl Into<String>) -> Self {
Self::Numerical {
message: message.into(),
}
}
#[must_use]
pub fn kind(&self) -> ErrorKind {
match self {
Self::Validation { source, .. } => ErrorKind::Validation(source.kind()),
Self::InvalidSubscripts { .. } => {
ErrorKind::Validation(ValidationKind::InvalidArgument)
}
Self::Planning { source } => match source {
PlanningError::InvalidConfiguration { .. } => {
ErrorKind::Validation(ValidationKind::InvalidArgument)
}
PlanningError::RuntimeState { .. } => ErrorKind::RuntimeState,
},
Self::Numerical { .. } => ErrorKind::NumericalFailure,
Self::Tensor(error) => error.kind(),
Self::Runtime(error) => error.kind(),
}
}
#[must_use]
pub fn into_tensor_error(self, op: &'static str) -> tenferro_tensor::Error {
match self {
Self::Validation { op, source } => tenferro_tensor::Error::validation(op, source),
Self::Tensor(error) => error,
error => {
let kind = error.kind();
tenferro_tensor::Error::extension(op, EINSUM_EXTENSION_FAMILY_ID, kind, error)
}
}
}
}
pub type Result<T> = std::result::Result<T, Error>;