1use std::{
2 ffi::{CStr, NulError},
3 fmt::{self, Display, Formatter},
4};
5
6use num_enum::{IntoPrimitive, TryFromPrimitive};
7use singe_core::impl_enum_conversion;
8use singe_cuda::{data_type::DataType, error::Error as CudaError};
9#[cfg(feature = "mp")]
10use singe_nccl::error::Error as NcclError;
11use thiserror::Error;
12
13use crate::{
14 sys,
15 types::{OperationDescriptorAttribute, PlanAttribute, PlanPreferenceAttribute},
16};
17
18#[derive(Error, Debug)]
19pub enum Error {
20 #[error("cuda error: {0}")]
21 Cuda(#[from] CudaError),
22
23 #[cfg(feature = "mp")]
24 #[error("nccl error: {0}")]
25 Nccl(#[from] NcclError),
26
27 #[error("cutensor error ({code}): {message}")]
28 Cutensor { code: Status, message: String },
29
30 #[error("string contains interior nul byte")]
31 InteriorNul,
32
33 #[error("unexpected null handle")]
34 NullHandle,
35
36 #[error("invalid {name} version")]
37 InvalidVersion { name: &'static str },
38
39 #[error("`{name}` is out of range")]
40 OutOfRange { name: String },
41
42 #[error("`{name}` length mismatch (expected {expected}, got {actual})")]
43 LengthMismatch {
44 name: String,
45 expected: usize,
46 actual: usize,
47 },
48
49 #[error("tensor mode length mismatch for tensor rank {rank} (got {mode_length})")]
50 TensorModeMismatch { rank: u32, mode_length: usize },
51
52 #[error("tensor stride length mismatch for tensor rank {rank} (got {stride_length})")]
53 TensorStrideMismatch { rank: u32, stride_length: usize },
54
55 #[error("tensor descriptors must match for `{name}`")]
56 DescriptorMismatch { name: String },
57
58 #[error("tensor modes must match for `{name}`")]
59 ModeMismatch { name: String },
60
61 #[error("tensor data type mismatch (descriptor is {descriptor} and memory is {memory})")]
62 TensorMemoryDataTypeMismatch {
63 descriptor: DataType,
64 memory: DataType,
65 },
66
67 #[error("scalar data type mismatch (descriptor is {descriptor} and memory is {memory})")]
68 ScalarDataTypeMismatch {
69 descriptor: DataType,
70 memory: DataType,
71 },
72
73 #[error("unsupported scalar data type {data_type} for `{name}`")]
74 UnsupportedScalarDataType { name: String, data_type: DataType },
75
76 #[error("{name} pointer count mismatch (expected {expected}, got {actual})")]
77 PointerCountMismatch {
78 name: String,
79 expected: usize,
80 actual: usize,
81 },
82
83 #[error("insufficient workspace size: {actual} bytes provided (required {required} bytes)")]
84 InsufficientWorkspaceSize { required: u64, actual: u64 },
85
86 #[error("workspace size is not aligned to {required_alignment} bytes (size is {size} bytes)")]
87 WorkspaceMisaligned {
88 required_alignment: u32,
89 size: usize,
90 },
91
92 #[error("stream belongs to a different cuda context")]
93 StreamContextMismatch,
94
95 #[error("plan operation kind mismatch (expected {expected}, got {actual})")]
96 PlanOperationMismatch { expected: String, actual: String },
97
98 #[error("cutensormg contexts must match for `{name}`")]
99 MgContextMismatch { name: String },
100
101 #[error("cutensormg host tensor memory is not supported by `{name}`")]
102 MgHostTensorMemoryUnsupported { name: String },
103
104 #[error("cutensormp contexts must match for `{name}`")]
105 MpContextMismatch { name: String },
106
107 #[error("invalid plan attribute size for {attr:?}: {actual} (expected {expected})")]
108 PlanInvalidAttributeSize {
109 attr: PlanAttribute,
110 expected: usize,
111 actual: usize,
112 },
113
114 #[error(
115 "invalid operation descriptor attribute size for {attr:?}: {actual} (expected {expected})"
116 )]
117 OperationDescriptorInvalidAttributeSize {
118 attr: OperationDescriptorAttribute,
119 expected: usize,
120 actual: usize,
121 },
122
123 #[error("invalid plan preference attribute size for {attr:?}: {actual} (expected {expected})")]
124 PlanPreferenceInvalidAttributeSize {
125 attr: PlanPreferenceAttribute,
126 expected: usize,
127 actual: usize,
128 },
129}
130
131pub type Result<T> = std::result::Result<T, Error>;
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive, IntoPrimitive)]
135#[repr(u32)]
136pub enum Status {
137 Success = sys::cutensorStatus_t::CUTENSOR_STATUS_SUCCESS as _,
139 NotInitialized = sys::cutensorStatus_t::CUTENSOR_STATUS_NOT_INITIALIZED as _,
141 AllocFailed = sys::cutensorStatus_t::CUTENSOR_STATUS_ALLOC_FAILED as _,
143 InvalidValue = sys::cutensorStatus_t::CUTENSOR_STATUS_INVALID_VALUE as _,
145 ArchMismatch = sys::cutensorStatus_t::CUTENSOR_STATUS_ARCH_MISMATCH as _,
147 MappingError = sys::cutensorStatus_t::CUTENSOR_STATUS_MAPPING_ERROR as _,
149 ExecutionFailed = sys::cutensorStatus_t::CUTENSOR_STATUS_EXECUTION_FAILED as _,
152 InternalError = sys::cutensorStatus_t::CUTENSOR_STATUS_INTERNAL_ERROR as _,
154 NotSupported = sys::cutensorStatus_t::CUTENSOR_STATUS_NOT_SUPPORTED as _,
156 LicenseError = sys::cutensorStatus_t::CUTENSOR_STATUS_LICENSE_ERROR as _,
159 CublasError = sys::cutensorStatus_t::CUTENSOR_STATUS_CUBLAS_ERROR as _,
161 CudaError = sys::cutensorStatus_t::CUTENSOR_STATUS_CUDA_ERROR as _,
163 InsufficientWorkspace = sys::cutensorStatus_t::CUTENSOR_STATUS_INSUFFICIENT_WORKSPACE as _,
165 InsufficientDriver = sys::cutensorStatus_t::CUTENSOR_STATUS_INSUFFICIENT_DRIVER as _,
167 IoError = sys::cutensorStatus_t::CUTENSOR_STATUS_IO_ERROR as _,
169}
170
171impl_enum_conversion!(sys::cutensorStatus_t, Status);
172
173impl Status {
174 pub fn description(self) -> String {
175 unsafe {
176 let ptr = sys::cutensorGetErrorString(self.into());
177 if ptr.is_null() {
178 String::from("unknown cutensor error")
179 } else {
180 CStr::from_ptr(ptr).to_string_lossy().into_owned()
181 }
182 }
183 }
184}
185
186impl Display for Status {
187 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
188 match self {
189 Self::Success => write!(f, "CUTENSOR_STATUS_SUCCESS"),
190 Self::NotInitialized => write!(f, "CUTENSOR_STATUS_NOT_INITIALIZED"),
191 Self::AllocFailed => write!(f, "CUTENSOR_STATUS_ALLOC_FAILED"),
192 Self::InvalidValue => write!(f, "CUTENSOR_STATUS_INVALID_VALUE"),
193 Self::ArchMismatch => write!(f, "CUTENSOR_STATUS_ARCH_MISMATCH"),
194 Self::MappingError => write!(f, "CUTENSOR_STATUS_MAPPING_ERROR"),
195 Self::ExecutionFailed => write!(f, "CUTENSOR_STATUS_EXECUTION_FAILED"),
196 Self::InternalError => write!(f, "CUTENSOR_STATUS_INTERNAL_ERROR"),
197 Self::NotSupported => write!(f, "CUTENSOR_STATUS_NOT_SUPPORTED"),
198 Self::LicenseError => write!(f, "CUTENSOR_STATUS_LICENSE_ERROR"),
199 Self::CublasError => write!(f, "CUTENSOR_STATUS_CUBLAS_ERROR"),
200 Self::CudaError => write!(f, "CUTENSOR_STATUS_CUDA_ERROR"),
201 Self::InsufficientWorkspace => {
202 write!(f, "CUTENSOR_STATUS_INSUFFICIENT_WORKSPACE")
203 }
204 Self::InsufficientDriver => write!(f, "CUTENSOR_STATUS_INSUFFICIENT_DRIVER"),
205 Self::IoError => write!(f, "CUTENSOR_STATUS_IO_ERROR"),
206 }
207 }
208}
209
210impl From<sys::cutensorStatus_t> for Error {
211 fn from(status: sys::cutensorStatus_t) -> Self {
212 debug_assert_ne!(status, sys::cutensorStatus_t::CUTENSOR_STATUS_SUCCESS);
213
214 let message = unsafe {
215 let c_ptr = sys::cutensorGetErrorString(status);
216 if c_ptr.is_null() {
217 String::from("unknown cutensor error")
218 } else {
219 CStr::from_ptr(c_ptr).to_string_lossy().into_owned()
220 }
221 };
222
223 Self::Cutensor {
224 code: status.into(),
225 message,
226 }
227 }
228}
229
230impl From<Status> for Error {
231 fn from(status: Status) -> Self {
232 sys::cutensorStatus_t::from(status).into()
233 }
234}
235
236impl From<NulError> for Error {
237 fn from(_: NulError) -> Self {
238 Self::InteriorNul
239 }
240}
241
242#[macro_export]
243macro_rules! try_ffi {
244 ($expr:expr) => {{
245 let status = { $expr };
246 if status != singe_cutensor_sys::cutensorStatus_t::CUTENSOR_STATUS_SUCCESS {
247 Err($crate::error::Error::from(status))
248 } else {
249 Ok(())
250 }
251 }};
252}