1use std::{ffi::NulError, fmt};
2
3use singe_cuda::error::Error as CudaError;
4use singe_cusolver_sys as sys;
5use thiserror::Error;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8#[non_exhaustive]
9pub enum Status {
10 Success,
12 NotInitialized,
14 AllocFailed,
16 InvalidValue,
18 ArchitectureMismatch,
20 MappingError,
22 ExecutionFailed,
24 InternalError,
26 MatrixTypeNotSupported,
28 NotSupported,
30 ZeroPivot,
32 InvalidLicense,
34 IrsParamsNotInitialized,
36 IrsParamsInvalid,
38 IrsParamsInvalidPrec,
40 IrsParamsInvalidRefine,
42 IrsParamsInvalidMaxIter,
44 IrsInternalError,
46 IrsNotSupported,
48 IrsOutOfRange,
50 IrsNrhsNotSupportedForRefineGmres,
52 IrsInfosNotInitialized,
54 IrsInfosNotDestroyed,
56 IrsMatrixSingular,
58 InvalidWorkspace,
60 Unknown(u32),
62}
63
64impl Status {
65 pub const fn description(self) -> &'static str {
66 match self {
67 Self::Success => "success",
68 Self::NotInitialized => "library not initialized",
69 Self::AllocFailed => "allocation failed",
70 Self::InvalidValue => "invalid value",
71 Self::ArchitectureMismatch => "architecture mismatch",
72 Self::MappingError => "mapping error",
73 Self::ExecutionFailed => "execution failed",
74 Self::InternalError => "internal error",
75 Self::MatrixTypeNotSupported => "matrix type not supported",
76 Self::NotSupported => "not supported",
77 Self::ZeroPivot => "zero pivot",
78 Self::InvalidLicense => "invalid license",
79 Self::IrsParamsNotInitialized => "irs params not initialized",
80 Self::IrsParamsInvalid => "irs params invalid",
81 Self::IrsParamsInvalidPrec => "irs params invalid precision",
82 Self::IrsParamsInvalidRefine => "irs params invalid refinement",
83 Self::IrsParamsInvalidMaxIter => "irs params invalid maxiter",
84 Self::IrsInternalError => "irs internal error",
85 Self::IrsNotSupported => "irs not supported",
86 Self::IrsOutOfRange => "irs out of range",
87 Self::IrsNrhsNotSupportedForRefineGmres => "irs nrhs not supported for refine gmres",
88 Self::IrsInfosNotInitialized => "irs infos not initialized",
89 Self::IrsInfosNotDestroyed => "irs infos not destroyed",
90 Self::IrsMatrixSingular => "irs matrix singular",
91 Self::InvalidWorkspace => "invalid workspace",
92 Self::Unknown(_) => "unknown cusolver error",
93 }
94 }
95
96 pub const fn raw(self) -> u32 {
97 match self {
98 Self::Success => sys::cusolverStatus_t::CUSOLVER_STATUS_SUCCESS as _,
99 Self::NotInitialized => sys::cusolverStatus_t::CUSOLVER_STATUS_NOT_INITIALIZED as _,
100 Self::AllocFailed => sys::cusolverStatus_t::CUSOLVER_STATUS_ALLOC_FAILED as _,
101 Self::InvalidValue => sys::cusolverStatus_t::CUSOLVER_STATUS_INVALID_VALUE as _,
102 Self::ArchitectureMismatch => sys::cusolverStatus_t::CUSOLVER_STATUS_ARCH_MISMATCH as _,
103 Self::MappingError => sys::cusolverStatus_t::CUSOLVER_STATUS_MAPPING_ERROR as _,
104 Self::ExecutionFailed => sys::cusolverStatus_t::CUSOLVER_STATUS_EXECUTION_FAILED as _,
105 Self::InternalError => sys::cusolverStatus_t::CUSOLVER_STATUS_INTERNAL_ERROR as _,
106 Self::MatrixTypeNotSupported => {
107 sys::cusolverStatus_t::CUSOLVER_STATUS_MATRIX_TYPE_NOT_SUPPORTED as _
108 }
109 Self::NotSupported => sys::cusolverStatus_t::CUSOLVER_STATUS_NOT_SUPPORTED as _,
110 Self::ZeroPivot => sys::cusolverStatus_t::CUSOLVER_STATUS_ZERO_PIVOT as _,
111 Self::InvalidLicense => sys::cusolverStatus_t::CUSOLVER_STATUS_INVALID_LICENSE as _,
112 Self::IrsParamsNotInitialized => {
113 sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_PARAMS_NOT_INITIALIZED as _
114 }
115 Self::IrsParamsInvalid => {
116 sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_PARAMS_INVALID as _
117 }
118 Self::IrsParamsInvalidPrec => {
119 sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_PARAMS_INVALID_PREC as _
120 }
121 Self::IrsParamsInvalidRefine => {
122 sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_PARAMS_INVALID_REFINE as _
123 }
124 Self::IrsParamsInvalidMaxIter => {
125 sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_PARAMS_INVALID_MAXITER as _
126 }
127 Self::IrsInternalError => {
128 sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_INTERNAL_ERROR as _
129 }
130 Self::IrsNotSupported => sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_NOT_SUPPORTED as _,
131 Self::IrsOutOfRange => sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_OUT_OF_RANGE as _,
132 Self::IrsNrhsNotSupportedForRefineGmres => {
133 sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_NRHS_NOT_SUPPORTED_FOR_REFINE_GMRES as _
134 }
135 Self::IrsInfosNotInitialized => {
136 sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_INFOS_NOT_INITIALIZED as _
137 }
138 Self::IrsInfosNotDestroyed => {
139 sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_INFOS_NOT_DESTROYED as _
140 }
141 Self::IrsMatrixSingular => {
142 sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_MATRIX_SINGULAR as _
143 }
144 Self::InvalidWorkspace => sys::cusolverStatus_t::CUSOLVER_STATUS_INVALID_WORKSPACE as _,
145 Self::Unknown(code) => code,
146 }
147 }
148}
149
150impl TryFrom<u32> for Status {
151 type Error = u32;
152
153 fn try_from(code: u32) -> std::result::Result<Self, Self::Error> {
154 match code {
155 code if code == sys::cusolverStatus_t::CUSOLVER_STATUS_SUCCESS as u32 => {
156 Ok(Self::Success)
157 }
158 code if code == sys::cusolverStatus_t::CUSOLVER_STATUS_NOT_INITIALIZED as u32 => {
159 Ok(Self::NotInitialized)
160 }
161 code if code == sys::cusolverStatus_t::CUSOLVER_STATUS_ALLOC_FAILED as u32 => {
162 Ok(Self::AllocFailed)
163 }
164 code if code == sys::cusolverStatus_t::CUSOLVER_STATUS_INVALID_VALUE as u32 => {
165 Ok(Self::InvalidValue)
166 }
167 code if code == sys::cusolverStatus_t::CUSOLVER_STATUS_ARCH_MISMATCH as u32 => {
168 Ok(Self::ArchitectureMismatch)
169 }
170 code if code == sys::cusolverStatus_t::CUSOLVER_STATUS_MAPPING_ERROR as u32 => {
171 Ok(Self::MappingError)
172 }
173 code if code == sys::cusolverStatus_t::CUSOLVER_STATUS_EXECUTION_FAILED as u32 => {
174 Ok(Self::ExecutionFailed)
175 }
176 code if code == sys::cusolverStatus_t::CUSOLVER_STATUS_INTERNAL_ERROR as u32 => {
177 Ok(Self::InternalError)
178 }
179 code if code
180 == sys::cusolverStatus_t::CUSOLVER_STATUS_MATRIX_TYPE_NOT_SUPPORTED as u32 =>
181 {
182 Ok(Self::MatrixTypeNotSupported)
183 }
184 code if code == sys::cusolverStatus_t::CUSOLVER_STATUS_NOT_SUPPORTED as u32 => {
185 Ok(Self::NotSupported)
186 }
187 code if code == sys::cusolverStatus_t::CUSOLVER_STATUS_ZERO_PIVOT as u32 => {
188 Ok(Self::ZeroPivot)
189 }
190 code if code == sys::cusolverStatus_t::CUSOLVER_STATUS_INVALID_LICENSE as u32 => {
191 Ok(Self::InvalidLicense)
192 }
193 code if code
194 == sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_PARAMS_NOT_INITIALIZED as u32 =>
195 {
196 Ok(Self::IrsParamsNotInitialized)
197 }
198 code if code == sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_PARAMS_INVALID as u32 => {
199 Ok(Self::IrsParamsInvalid)
200 }
201 code if code
202 == sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_PARAMS_INVALID_PREC as u32 =>
203 {
204 Ok(Self::IrsParamsInvalidPrec)
205 }
206 code if code
207 == sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_PARAMS_INVALID_REFINE as u32 =>
208 {
209 Ok(Self::IrsParamsInvalidRefine)
210 }
211 code if code
212 == sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_PARAMS_INVALID_MAXITER as u32 =>
213 {
214 Ok(Self::IrsParamsInvalidMaxIter)
215 }
216 code if code == sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_INTERNAL_ERROR as u32 => {
217 Ok(Self::IrsInternalError)
218 }
219 code if code == sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_NOT_SUPPORTED as u32 => {
220 Ok(Self::IrsNotSupported)
221 }
222 code if code == sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_OUT_OF_RANGE as u32 => {
223 Ok(Self::IrsOutOfRange)
224 }
225 code if code
226 == sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_NRHS_NOT_SUPPORTED_FOR_REFINE_GMRES
227 as u32 =>
228 {
229 Ok(Self::IrsNrhsNotSupportedForRefineGmres)
230 }
231 code if code
232 == sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_INFOS_NOT_INITIALIZED as u32 =>
233 {
234 Ok(Self::IrsInfosNotInitialized)
235 }
236 code if code
237 == sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_INFOS_NOT_DESTROYED as u32 =>
238 {
239 Ok(Self::IrsInfosNotDestroyed)
240 }
241 code if code == sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_MATRIX_SINGULAR as u32 => {
242 Ok(Self::IrsMatrixSingular)
243 }
244 code if code == sys::cusolverStatus_t::CUSOLVER_STATUS_INVALID_WORKSPACE as u32 => {
245 Ok(Self::InvalidWorkspace)
246 }
247 code => Err(code),
248 }
249 }
250}
251
252impl From<sys::cusolverStatus_t> for Status {
253 fn from(status: sys::cusolverStatus_t) -> Self {
254 Self::try_from(status as u32).unwrap_or_else(Self::Unknown)
255 }
256}
257
258impl TryFrom<Status> for sys::cusolverStatus_t {
259 type Error = Status;
260
261 fn try_from(status: Status) -> std::result::Result<Self, Self::Error> {
262 match status {
263 Status::Success => Ok(Self::CUSOLVER_STATUS_SUCCESS),
264 Status::NotInitialized => Ok(Self::CUSOLVER_STATUS_NOT_INITIALIZED),
265 Status::AllocFailed => Ok(Self::CUSOLVER_STATUS_ALLOC_FAILED),
266 Status::InvalidValue => Ok(Self::CUSOLVER_STATUS_INVALID_VALUE),
267 Status::ArchitectureMismatch => Ok(Self::CUSOLVER_STATUS_ARCH_MISMATCH),
268 Status::MappingError => Ok(Self::CUSOLVER_STATUS_MAPPING_ERROR),
269 Status::ExecutionFailed => Ok(Self::CUSOLVER_STATUS_EXECUTION_FAILED),
270 Status::InternalError => Ok(Self::CUSOLVER_STATUS_INTERNAL_ERROR),
271 Status::MatrixTypeNotSupported => Ok(Self::CUSOLVER_STATUS_MATRIX_TYPE_NOT_SUPPORTED),
272 Status::NotSupported => Ok(Self::CUSOLVER_STATUS_NOT_SUPPORTED),
273 Status::ZeroPivot => Ok(Self::CUSOLVER_STATUS_ZERO_PIVOT),
274 Status::InvalidLicense => Ok(Self::CUSOLVER_STATUS_INVALID_LICENSE),
275 Status::IrsParamsNotInitialized => Ok(Self::CUSOLVER_STATUS_IRS_PARAMS_NOT_INITIALIZED),
276 Status::IrsParamsInvalid => Ok(Self::CUSOLVER_STATUS_IRS_PARAMS_INVALID),
277 Status::IrsParamsInvalidPrec => Ok(Self::CUSOLVER_STATUS_IRS_PARAMS_INVALID_PREC),
278 Status::IrsParamsInvalidRefine => Ok(Self::CUSOLVER_STATUS_IRS_PARAMS_INVALID_REFINE),
279 Status::IrsParamsInvalidMaxIter => Ok(Self::CUSOLVER_STATUS_IRS_PARAMS_INVALID_MAXITER),
280 Status::IrsInternalError => Ok(Self::CUSOLVER_STATUS_IRS_INTERNAL_ERROR),
281 Status::IrsNotSupported => Ok(Self::CUSOLVER_STATUS_IRS_NOT_SUPPORTED),
282 Status::IrsOutOfRange => Ok(Self::CUSOLVER_STATUS_IRS_OUT_OF_RANGE),
283 Status::IrsNrhsNotSupportedForRefineGmres => {
284 Ok(Self::CUSOLVER_STATUS_IRS_NRHS_NOT_SUPPORTED_FOR_REFINE_GMRES)
285 }
286 Status::IrsInfosNotInitialized => Ok(Self::CUSOLVER_STATUS_IRS_INFOS_NOT_INITIALIZED),
287 Status::IrsInfosNotDestroyed => Ok(Self::CUSOLVER_STATUS_IRS_INFOS_NOT_DESTROYED),
288 Status::IrsMatrixSingular => Ok(Self::CUSOLVER_STATUS_IRS_MATRIX_SINGULAR),
289 Status::InvalidWorkspace => Ok(Self::CUSOLVER_STATUS_INVALID_WORKSPACE),
290 Status::Unknown(_) => Err(status),
291 }
292 }
293}
294
295impl fmt::Display for Status {
296 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
297 match self {
298 Self::Success => f.write_str("CUSOLVER_STATUS_SUCCESS"),
299 Self::NotInitialized => f.write_str("CUSOLVER_STATUS_NOT_INITIALIZED"),
300 Self::AllocFailed => f.write_str("CUSOLVER_STATUS_ALLOC_FAILED"),
301 Self::InvalidValue => f.write_str("CUSOLVER_STATUS_INVALID_VALUE"),
302 Self::ArchitectureMismatch => f.write_str("CUSOLVER_STATUS_ARCH_MISMATCH"),
303 Self::MappingError => f.write_str("CUSOLVER_STATUS_MAPPING_ERROR"),
304 Self::ExecutionFailed => f.write_str("CUSOLVER_STATUS_EXECUTION_FAILED"),
305 Self::InternalError => f.write_str("CUSOLVER_STATUS_INTERNAL_ERROR"),
306 Self::MatrixTypeNotSupported => {
307 f.write_str("CUSOLVER_STATUS_MATRIX_TYPE_NOT_SUPPORTED")
308 }
309 Self::NotSupported => f.write_str("CUSOLVER_STATUS_NOT_SUPPORTED"),
310 Self::ZeroPivot => f.write_str("CUSOLVER_STATUS_ZERO_PIVOT"),
311 Self::InvalidLicense => f.write_str("CUSOLVER_STATUS_INVALID_LICENSE"),
312 Self::IrsParamsNotInitialized => {
313 f.write_str("CUSOLVER_STATUS_IRS_PARAMS_NOT_INITIALIZED")
314 }
315 Self::IrsParamsInvalid => f.write_str("CUSOLVER_STATUS_IRS_PARAMS_INVALID"),
316 Self::IrsParamsInvalidPrec => f.write_str("CUSOLVER_STATUS_IRS_PARAMS_INVALID_PREC"),
317 Self::IrsParamsInvalidRefine => {
318 f.write_str("CUSOLVER_STATUS_IRS_PARAMS_INVALID_REFINE")
319 }
320 Self::IrsParamsInvalidMaxIter => {
321 f.write_str("CUSOLVER_STATUS_IRS_PARAMS_INVALID_MAXITER")
322 }
323 Self::IrsInternalError => f.write_str("CUSOLVER_STATUS_IRS_INTERNAL_ERROR"),
324 Self::IrsNotSupported => f.write_str("CUSOLVER_STATUS_IRS_NOT_SUPPORTED"),
325 Self::IrsOutOfRange => f.write_str("CUSOLVER_STATUS_IRS_OUT_OF_RANGE"),
326 Self::IrsNrhsNotSupportedForRefineGmres => {
327 f.write_str("CUSOLVER_STATUS_IRS_NRHS_NOT_SUPPORTED_FOR_REFINE_GMRES")
328 }
329 Self::IrsInfosNotInitialized => {
330 f.write_str("CUSOLVER_STATUS_IRS_INFOS_NOT_INITIALIZED")
331 }
332 Self::IrsInfosNotDestroyed => f.write_str("CUSOLVER_STATUS_IRS_INFOS_NOT_DESTROYED"),
333 Self::IrsMatrixSingular => f.write_str("CUSOLVER_STATUS_IRS_MATRIX_SINGULAR"),
334 Self::InvalidWorkspace => f.write_str("CUSOLVER_STATUS_INVALID_WORKSPACE"),
335 Self::Unknown(code) => write!(f, "UNKNOWN_CUSOLVER_STATUS({code})"),
336 }
337 }
338}
339
340#[derive(Error, Debug)]
341#[non_exhaustive]
342pub enum Error {
343 #[error("cuda error: {0}")]
344 Cuda(#[from] CudaError),
345
346 #[error("cusolver error ({code}): {message}")]
347 Cusolver { code: Status, message: String },
348
349 #[error("string contains interior nul byte")]
350 InteriorNul,
351
352 #[error("unexpected null handle")]
353 NullHandle,
354
355 #[error("`{name}` is out of range")]
356 OutOfRange { name: String },
357
358 #[error("invalid matrix leading dimension")]
359 InvalidLeadingDimension,
360
361 #[error("invalid matrix shape")]
362 InvalidMatrixShape,
363
364 #[error("invalid vector shape")]
365 InvalidVectorShape,
366
367 #[error("invalid eigensolver range")]
368 InvalidEigenRange,
369
370 #[error("invalid svd mode")]
371 InvalidSvdMode,
372
373 #[error("invalid precision configuration")]
374 InvalidPrecisionConfiguration,
375
376 #[error("insufficient workspace size: {actual} provided (required {required})")]
377 InsufficientWorkspaceSize { required: usize, actual: usize },
378
379 #[error("invalid residual history")]
380 InvalidResidualHistory,
381
382 #[error("stream belongs to a different cuda context")]
383 StreamContextMismatch,
384}
385
386pub type Result<T> = std::result::Result<T, Error>;
387
388impl From<sys::cusolverStatus_t> for Error {
389 fn from(code: sys::cusolverStatus_t) -> Self {
390 let status = Status::from(code);
391 Self::from(status)
392 }
393}
394
395impl From<Status> for Error {
396 fn from(code: Status) -> Self {
397 Self::Cusolver {
398 code,
399 message: code.description().to_string(),
400 }
401 }
402}
403
404impl From<NulError> for Error {
405 fn from(_: NulError) -> Self {
406 Self::InteriorNul
407 }
408}
409
410#[macro_export]
411macro_rules! try_ffi {
412 ($expr:expr) => {{
413 let status = { $expr };
414 if status != singe_cusolver_sys::cusolverStatus_t::CUSOLVER_STATUS_SUCCESS {
415 Err($crate::error::Error::from(status))
416 } else {
417 Ok(())
418 }
419 }};
420}