Skip to main content

singe_cutensor/
error.rs

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/// cuTENSOR status returned by library calls.
134#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive, IntoPrimitive)]
135#[repr(u32)]
136pub enum Status {
137    /// The operation completed successfully.
138    Success = sys::cutensorStatus_t::CUTENSOR_STATUS_SUCCESS as _,
139    /// The opaque data structure was not initialized.
140    NotInitialized = sys::cutensorStatus_t::CUTENSOR_STATUS_NOT_INITIALIZED as _,
141    /// Resource allocation failed inside the cuTENSOR library.
142    AllocFailed = sys::cutensorStatus_t::CUTENSOR_STATUS_ALLOC_FAILED as _,
143    /// An unsupported value or parameter was passed to the operation.
144    InvalidValue = sys::cutensorStatus_t::CUTENSOR_STATUS_INVALID_VALUE as _,
145    /// The device is not ready, or the target architecture is not supported.
146    ArchMismatch = sys::cutensorStatus_t::CUTENSOR_STATUS_ARCH_MISMATCH as _,
147    /// An access to GPU memory space failed, which is usually caused by a failure to bind a texture.
148    MappingError = sys::cutensorStatus_t::CUTENSOR_STATUS_MAPPING_ERROR as _,
149    /// The GPU program failed to execute.
150    /// A kernel launch failure on the GPU is a common cause.
151    ExecutionFailed = sys::cutensorStatus_t::CUTENSOR_STATUS_EXECUTION_FAILED as _,
152    /// An internal cuTENSOR error occurred.
153    InternalError = sys::cutensorStatus_t::CUTENSOR_STATUS_INTERNAL_ERROR as _,
154    /// The requested operation is not supported.
155    NotSupported = sys::cutensorStatus_t::CUTENSOR_STATUS_NOT_SUPPORTED as _,
156    /// The requested operation requires a license, and an error was detected
157    /// when checking the current licensing.
158    LicenseError = sys::cutensorStatus_t::CUTENSOR_STATUS_LICENSE_ERROR as _,
159    /// A call to cuBLAS did not succeed.
160    CublasError = sys::cutensorStatus_t::CUTENSOR_STATUS_CUBLAS_ERROR as _,
161    /// An unknown CUDA error occurred.
162    CudaError = sys::cutensorStatus_t::CUTENSOR_STATUS_CUDA_ERROR as _,
163    /// The provided workspace was insufficient.
164    InsufficientWorkspace = sys::cutensorStatus_t::CUTENSOR_STATUS_INSUFFICIENT_WORKSPACE as _,
165    /// Indicates that the driver version is insufficient.
166    InsufficientDriver = sys::cutensorStatus_t::CUTENSOR_STATUS_INSUFFICIENT_DRIVER as _,
167    /// Indicates an error related to file I/O.
168    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}