Skip to main content

singe_cutensor/
error.rs

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/// cuTENSOR status returned by library calls.
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
18#[non_exhaustive]
19pub enum Status {
20    /// The operation completed successfully.
21    Success,
22    /// The opaque data structure was not initialized.
23    NotInitialized,
24    /// Resource allocation failed inside the cuTENSOR library.
25    AllocFailed,
26    /// An unsupported value or parameter was passed to the operation.
27    InvalidValue,
28    /// The device is not ready, or the target architecture is not supported.
29    ArchitectureMismatch,
30    /// An access to GPU memory space failed, which is usually caused by a failure to bind a texture.
31    MappingError,
32    /// The GPU program failed to execute.
33    /// A kernel launch failure on the GPU is a common cause.
34    ExecutionFailed,
35    /// An internal cuTENSOR error occurred.
36    InternalError,
37    /// The requested operation is not supported.
38    NotSupported,
39    /// The requested operation requires a license, and an error was detected
40    /// when checking the current licensing.
41    LicenseError,
42    /// A call to cuBLAS did not succeed.
43    CublasError,
44    /// An unknown CUDA error occurred.
45    CudaError,
46    /// The provided workspace was insufficient.
47    InsufficientWorkspace,
48    /// Indicates that the driver version is insufficient.
49    InsufficientDriver,
50    /// Indicates an error related to file I/O.
51    IoError,
52    /// A status code not known to this version of Singe.
53    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}