1use std::{
2 ffi::{CStr, NulError},
3 fmt,
4};
5
6use singe_cuda::{data_type::DataType, error::Error as CudaError};
7#[cfg(feature = "mp")]
8use singe_nccl::error::Error as NcclError;
9use thiserror::Error;
10
11use crate::{
12 sys,
13 types::{OperationDescriptorAttribute, PlanAttribute, PlanPreferenceAttribute},
14};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
18#[non_exhaustive]
19pub enum Status {
20 Success,
22 NotInitialized,
24 AllocFailed,
26 InvalidValue,
28 ArchitectureMismatch,
30 MappingError,
32 ExecutionFailed,
35 InternalError,
37 NotSupported,
39 LicenseError,
42 CublasError,
44 CudaError,
46 InsufficientWorkspace,
48 InsufficientDriver,
50 IoError,
52 Unknown(u32),
54}
55
56impl Status {
57 pub fn description(self) -> String {
58 match sys::cutensorStatus_t::try_from(self.raw()) {
59 Ok(status) => cutensor_error_string(status),
60 Err(_) => String::from("unknown cutensor error"),
61 }
62 }
63
64 pub const fn raw(self) -> u32 {
65 match self {
66 Self::Success => sys::cutensorStatus_t::CUTENSOR_STATUS_SUCCESS as _,
67 Self::NotInitialized => sys::cutensorStatus_t::CUTENSOR_STATUS_NOT_INITIALIZED as _,
68 Self::AllocFailed => sys::cutensorStatus_t::CUTENSOR_STATUS_ALLOC_FAILED as _,
69 Self::InvalidValue => sys::cutensorStatus_t::CUTENSOR_STATUS_INVALID_VALUE as _,
70 Self::ArchitectureMismatch => sys::cutensorStatus_t::CUTENSOR_STATUS_ARCH_MISMATCH as _,
71 Self::MappingError => sys::cutensorStatus_t::CUTENSOR_STATUS_MAPPING_ERROR as _,
72 Self::ExecutionFailed => sys::cutensorStatus_t::CUTENSOR_STATUS_EXECUTION_FAILED as _,
73 Self::InternalError => sys::cutensorStatus_t::CUTENSOR_STATUS_INTERNAL_ERROR as _,
74 Self::NotSupported => sys::cutensorStatus_t::CUTENSOR_STATUS_NOT_SUPPORTED as _,
75 Self::LicenseError => sys::cutensorStatus_t::CUTENSOR_STATUS_LICENSE_ERROR as _,
76 Self::CublasError => sys::cutensorStatus_t::CUTENSOR_STATUS_CUBLAS_ERROR as _,
77 Self::CudaError => sys::cutensorStatus_t::CUTENSOR_STATUS_CUDA_ERROR as _,
78 Self::InsufficientWorkspace => {
79 sys::cutensorStatus_t::CUTENSOR_STATUS_INSUFFICIENT_WORKSPACE as _
80 }
81 Self::InsufficientDriver => {
82 sys::cutensorStatus_t::CUTENSOR_STATUS_INSUFFICIENT_DRIVER as _
83 }
84 Self::IoError => sys::cutensorStatus_t::CUTENSOR_STATUS_IO_ERROR as _,
85 Self::Unknown(code) => code,
86 }
87 }
88}
89
90impl TryFrom<u32> for Status {
91 type Error = u32;
92
93 fn try_from(code: u32) -> std::result::Result<Self, u32> {
94 match code {
95 code if code == sys::cutensorStatus_t::CUTENSOR_STATUS_SUCCESS as u32 => {
96 Ok(Self::Success)
97 }
98 code if code == sys::cutensorStatus_t::CUTENSOR_STATUS_NOT_INITIALIZED as u32 => {
99 Ok(Self::NotInitialized)
100 }
101 code if code == sys::cutensorStatus_t::CUTENSOR_STATUS_ALLOC_FAILED as u32 => {
102 Ok(Self::AllocFailed)
103 }
104 code if code == sys::cutensorStatus_t::CUTENSOR_STATUS_INVALID_VALUE as u32 => {
105 Ok(Self::InvalidValue)
106 }
107 code if code == sys::cutensorStatus_t::CUTENSOR_STATUS_ARCH_MISMATCH as u32 => {
108 Ok(Self::ArchitectureMismatch)
109 }
110 code if code == sys::cutensorStatus_t::CUTENSOR_STATUS_MAPPING_ERROR as u32 => {
111 Ok(Self::MappingError)
112 }
113 code if code == sys::cutensorStatus_t::CUTENSOR_STATUS_EXECUTION_FAILED as u32 => {
114 Ok(Self::ExecutionFailed)
115 }
116 code if code == sys::cutensorStatus_t::CUTENSOR_STATUS_INTERNAL_ERROR as u32 => {
117 Ok(Self::InternalError)
118 }
119 code if code == sys::cutensorStatus_t::CUTENSOR_STATUS_NOT_SUPPORTED as u32 => {
120 Ok(Self::NotSupported)
121 }
122 code if code == sys::cutensorStatus_t::CUTENSOR_STATUS_LICENSE_ERROR as u32 => {
123 Ok(Self::LicenseError)
124 }
125 code if code == sys::cutensorStatus_t::CUTENSOR_STATUS_CUBLAS_ERROR as u32 => {
126 Ok(Self::CublasError)
127 }
128 code if code == sys::cutensorStatus_t::CUTENSOR_STATUS_CUDA_ERROR as u32 => {
129 Ok(Self::CudaError)
130 }
131 code if code
132 == sys::cutensorStatus_t::CUTENSOR_STATUS_INSUFFICIENT_WORKSPACE as u32 =>
133 {
134 Ok(Self::InsufficientWorkspace)
135 }
136 code if code == sys::cutensorStatus_t::CUTENSOR_STATUS_INSUFFICIENT_DRIVER as u32 => {
137 Ok(Self::InsufficientDriver)
138 }
139 code if code == sys::cutensorStatus_t::CUTENSOR_STATUS_IO_ERROR as u32 => {
140 Ok(Self::IoError)
141 }
142 code => Err(code),
143 }
144 }
145}
146
147impl From<sys::cutensorStatus_t> for Status {
148 fn from(status: sys::cutensorStatus_t) -> Self {
149 Self::try_from(status as u32).unwrap_or_else(Self::Unknown)
150 }
151}
152
153impl TryFrom<Status> for sys::cutensorStatus_t {
154 type Error = Status;
155
156 fn try_from(status: Status) -> std::result::Result<Self, Status> {
157 match status {
158 Status::Success => Ok(Self::CUTENSOR_STATUS_SUCCESS),
159 Status::NotInitialized => Ok(Self::CUTENSOR_STATUS_NOT_INITIALIZED),
160 Status::AllocFailed => Ok(Self::CUTENSOR_STATUS_ALLOC_FAILED),
161 Status::InvalidValue => Ok(Self::CUTENSOR_STATUS_INVALID_VALUE),
162 Status::ArchitectureMismatch => Ok(Self::CUTENSOR_STATUS_ARCH_MISMATCH),
163 Status::MappingError => Ok(Self::CUTENSOR_STATUS_MAPPING_ERROR),
164 Status::ExecutionFailed => Ok(Self::CUTENSOR_STATUS_EXECUTION_FAILED),
165 Status::InternalError => Ok(Self::CUTENSOR_STATUS_INTERNAL_ERROR),
166 Status::NotSupported => Ok(Self::CUTENSOR_STATUS_NOT_SUPPORTED),
167 Status::LicenseError => Ok(Self::CUTENSOR_STATUS_LICENSE_ERROR),
168 Status::CublasError => Ok(Self::CUTENSOR_STATUS_CUBLAS_ERROR),
169 Status::CudaError => Ok(Self::CUTENSOR_STATUS_CUDA_ERROR),
170 Status::InsufficientWorkspace => Ok(Self::CUTENSOR_STATUS_INSUFFICIENT_WORKSPACE),
171 Status::InsufficientDriver => Ok(Self::CUTENSOR_STATUS_INSUFFICIENT_DRIVER),
172 Status::IoError => Ok(Self::CUTENSOR_STATUS_IO_ERROR),
173 Status::Unknown(_) => Err(status),
174 }
175 }
176}
177
178impl fmt::Display for Status {
179 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180 match self {
181 Self::Success => f.write_str("CUTENSOR_STATUS_SUCCESS"),
182 Self::NotInitialized => f.write_str("CUTENSOR_STATUS_NOT_INITIALIZED"),
183 Self::AllocFailed => f.write_str("CUTENSOR_STATUS_ALLOC_FAILED"),
184 Self::InvalidValue => f.write_str("CUTENSOR_STATUS_INVALID_VALUE"),
185 Self::ArchitectureMismatch => f.write_str("CUTENSOR_STATUS_ARCH_MISMATCH"),
186 Self::MappingError => f.write_str("CUTENSOR_STATUS_MAPPING_ERROR"),
187 Self::ExecutionFailed => f.write_str("CUTENSOR_STATUS_EXECUTION_FAILED"),
188 Self::InternalError => f.write_str("CUTENSOR_STATUS_INTERNAL_ERROR"),
189 Self::NotSupported => f.write_str("CUTENSOR_STATUS_NOT_SUPPORTED"),
190 Self::LicenseError => f.write_str("CUTENSOR_STATUS_LICENSE_ERROR"),
191 Self::CublasError => f.write_str("CUTENSOR_STATUS_CUBLAS_ERROR"),
192 Self::CudaError => f.write_str("CUTENSOR_STATUS_CUDA_ERROR"),
193 Self::InsufficientWorkspace => f.write_str("CUTENSOR_STATUS_INSUFFICIENT_WORKSPACE"),
194 Self::InsufficientDriver => f.write_str("CUTENSOR_STATUS_INSUFFICIENT_DRIVER"),
195 Self::IoError => f.write_str("CUTENSOR_STATUS_IO_ERROR"),
196 Self::Unknown(code) => write!(f, "UNKNOWN_CUTENSOR_STATUS({code})"),
197 }
198 }
199}
200
201fn cutensor_error_string(status: sys::cutensorStatus_t) -> String {
202 unsafe {
203 let ptr = sys::cutensorGetErrorString(status);
204 if ptr.is_null() {
205 String::from("unknown cutensor error")
206 } else {
207 CStr::from_ptr(ptr).to_string_lossy().into_owned()
208 }
209 }
210}
211
212#[derive(Error, Debug)]
213#[non_exhaustive]
214pub enum Error {
215 #[error("cuda error: {0}")]
216 Cuda(#[from] CudaError),
217
218 #[cfg(feature = "mp")]
219 #[error("nccl error: {0}")]
220 Nccl(#[from] NcclError),
221
222 #[error("cutensor error ({code}): {message}")]
223 Cutensor { code: Status, message: String },
224
225 #[error("string contains interior nul byte")]
226 InteriorNul,
227
228 #[error("unexpected null handle")]
229 NullHandle,
230
231 #[error("handle is shared and cannot be consumed")]
232 HandleShared,
233
234 #[error("invalid `{name}` version")]
235 InvalidVersion { name: &'static str },
236
237 #[error("`{name}` is out of range")]
238 OutOfRange { name: String },
239
240 #[error("`{name}` length mismatch (expected {expected}, got {actual})")]
241 LengthMismatch {
242 name: String,
243 expected: usize,
244 actual: usize,
245 },
246
247 #[error("tensor mode length mismatch for tensor rank {rank} (got {mode_length})")]
248 TensorModeMismatch { rank: u32, mode_length: usize },
249
250 #[error("tensor stride length mismatch for tensor rank {rank} (got {stride_length})")]
251 TensorStrideMismatch { rank: u32, stride_length: usize },
252
253 #[error("tensor descriptors must match for `{name}`")]
254 DescriptorMismatch { name: String },
255
256 #[error("tensor modes must match for `{name}`")]
257 ModeMismatch { name: String },
258
259 #[error("tensor data type mismatch (descriptor is {descriptor} and memory is {memory})")]
260 TensorMemoryDataTypeMismatch {
261 descriptor: DataType,
262 memory: DataType,
263 },
264
265 #[error("scalar data type mismatch (descriptor is {descriptor} and memory is {memory})")]
266 ScalarDataTypeMismatch {
267 descriptor: DataType,
268 memory: DataType,
269 },
270
271 #[error("unsupported scalar data type {data_type} for `{name}`")]
272 UnsupportedScalarDataType { name: String, data_type: DataType },
273
274 #[error("`{name}` pointer count mismatch (expected {expected}, got {actual})")]
275 PointerCountMismatch {
276 name: String,
277 expected: usize,
278 actual: usize,
279 },
280
281 #[error("insufficient workspace size: {actual} bytes provided (required {required} bytes)")]
282 InsufficientWorkspaceSize { required: u64, actual: u64 },
283
284 #[error("workspace size is not aligned to {required_alignment} bytes (size is {size} bytes)")]
285 WorkspaceMisaligned {
286 required_alignment: u32,
287 size: usize,
288 },
289
290 #[error("stream belongs to a different cuda context")]
291 StreamContextMismatch,
292
293 #[error("cutensor contexts must match for `{name}`")]
294 ContextMismatch { name: String },
295
296 #[error("plan operation kind mismatch (expected {expected}, got {actual})")]
297 PlanOperationMismatch { expected: String, actual: String },
298
299 #[error("cutensormg contexts must match for `{name}`")]
300 MgContextMismatch { name: String },
301
302 #[error("cutensormg host tensor memory is not supported by `{name}`")]
303 MgHostTensorMemoryUnsupported { name: String },
304
305 #[error("cutensormp contexts must match for `{name}`")]
306 MpContextMismatch { name: String },
307
308 #[error("invalid plan attribute size for {attr:?}: {actual} (expected {expected})")]
309 PlanInvalidAttributeSize {
310 attr: PlanAttribute,
311 expected: usize,
312 actual: usize,
313 },
314
315 #[error(
316 "invalid operation descriptor attribute size for {attr:?}: {actual} (expected {expected})"
317 )]
318 OperationDescriptorInvalidAttributeSize {
319 attr: OperationDescriptorAttribute,
320 expected: usize,
321 actual: usize,
322 },
323
324 #[error("invalid plan preference attribute size for {attr:?}: {actual} (expected {expected})")]
325 PlanPreferenceInvalidAttributeSize {
326 attr: PlanPreferenceAttribute,
327 expected: usize,
328 actual: usize,
329 },
330}
331
332pub type Result<T> = std::result::Result<T, Error>;
333
334impl From<sys::cutensorStatus_t> for Error {
335 fn from(status: sys::cutensorStatus_t) -> Self {
336 let code = Status::from(status);
337 Self::from(code)
338 }
339}
340
341impl From<Status> for Error {
342 fn from(code: Status) -> Self {
343 Self::Cutensor {
344 code,
345 message: code.description(),
346 }
347 }
348}
349
350impl From<NulError> for Error {
351 fn from(_: NulError) -> Self {
352 Self::InteriorNul
353 }
354}
355
356#[macro_export]
357macro_rules! try_ffi {
358 ($expr:expr) => {{
359 let status = { $expr };
360 if status != singe_cutensor_sys::cutensorStatus_t::CUTENSOR_STATUS_SUCCESS {
361 Err($crate::error::Error::from(status))
362 } else {
363 Ok(())
364 }
365 }};
366}