singe-cusolver 0.1.0-alpha.5

Safe Rust wrappers for the NVIDIA cuSOLVER dense and sparse solver library.
Documentation
use std::{
    ffi::NulError,
    fmt::{self, Display, Formatter},
};

use num_enum::{IntoPrimitive, TryFromPrimitive};
use singe_core::impl_enum_conversion;
use singe_cuda::error::Error as CudaError;
use singe_cusolver_sys as sys;
use thiserror::Error;

#[derive(Error, Debug)]
pub enum Error {
    #[error("cuda error: {0}")]
    Cuda(#[from] CudaError),

    #[error("cusolver error ({code}): {message}")]
    Cusolver { code: Status, message: String },

    #[error("string contains interior nul byte")]
    InteriorNul,

    #[error("unexpected null handle")]
    NullHandle,

    #[error("{name} is out of range")]
    OutOfRange { name: String },

    #[error("invalid matrix leading dimension")]
    InvalidLeadingDimension,

    #[error("invalid matrix shape")]
    InvalidMatrixShape,

    #[error("invalid vector shape")]
    InvalidVectorShape,

    #[error("invalid eigensolver range")]
    InvalidEigRange,

    #[error("invalid svd mode")]
    InvalidSvdMode,

    #[error("invalid precision configuration")]
    InvalidPrecisionConfiguration,

    #[error("insufficient workspace size: {actual} provided (required {required})")]
    InsufficientWorkspaceSize { required: usize, actual: usize },

    #[error("invalid residual history")]
    InvalidResidualHistory,

    #[error("stream belongs to a different cuda context")]
    StreamContextMismatch,
}

pub type Result<T> = std::result::Result<T, Error>;

impl From<sys::cusolverStatus_t> for Error {
    fn from(status: sys::cusolverStatus_t) -> Self {
        debug_assert_ne!(status, sys::cusolverStatus_t::CUSOLVER_STATUS_SUCCESS);

        let message = match status {
            sys::cusolverStatus_t::CUSOLVER_STATUS_NOT_INITIALIZED => "library not initialized",
            sys::cusolverStatus_t::CUSOLVER_STATUS_ALLOC_FAILED => "allocation failed",
            sys::cusolverStatus_t::CUSOLVER_STATUS_INVALID_VALUE => "invalid value",
            sys::cusolverStatus_t::CUSOLVER_STATUS_ARCH_MISMATCH => "architecture mismatch",
            sys::cusolverStatus_t::CUSOLVER_STATUS_MAPPING_ERROR => "mapping error",
            sys::cusolverStatus_t::CUSOLVER_STATUS_EXECUTION_FAILED => "execution failed",
            sys::cusolverStatus_t::CUSOLVER_STATUS_INTERNAL_ERROR => "internal error",
            sys::cusolverStatus_t::CUSOLVER_STATUS_MATRIX_TYPE_NOT_SUPPORTED => {
                "matrix type not supported"
            }
            sys::cusolverStatus_t::CUSOLVER_STATUS_NOT_SUPPORTED => "not supported",
            sys::cusolverStatus_t::CUSOLVER_STATUS_ZERO_PIVOT => "zero pivot",
            sys::cusolverStatus_t::CUSOLVER_STATUS_INVALID_LICENSE => "invalid license",
            sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_PARAMS_NOT_INITIALIZED => {
                "irs params not initialized"
            }
            sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_PARAMS_INVALID => "irs params invalid",
            sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_PARAMS_INVALID_PREC => {
                "irs params invalid precision"
            }
            sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_PARAMS_INVALID_REFINE => {
                "irs params invalid refinement"
            }
            sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_PARAMS_INVALID_MAXITER => {
                "irs params invalid maxiter"
            }
            sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_INTERNAL_ERROR => "irs internal error",
            sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_NOT_SUPPORTED => "irs not supported",
            sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_OUT_OF_RANGE => "irs out of range",
            sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_NRHS_NOT_SUPPORTED_FOR_REFINE_GMRES => {
                "irs nrhs not supported for refine gmres"
            }
            sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_INFOS_NOT_INITIALIZED => {
                "irs infos not initialized"
            }
            sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_INFOS_NOT_DESTROYED => {
                "irs infos not destroyed"
            }
            sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_MATRIX_SINGULAR => "irs matrix singular",
            sys::cusolverStatus_t::CUSOLVER_STATUS_INVALID_WORKSPACE => "invalid workspace",
            _ => "unknown cusolver error",
        };

        Self::Cusolver {
            code: status.into(),
            message: message.to_string(),
        }
    }
}

impl From<NulError> for Error {
    fn from(_: NulError) -> Self {
        Self::InteriorNul
    }
}

#[macro_export]
macro_rules! try_ffi {
    ($expr:expr) => {{
        let status = { $expr };
        if status != singe_cusolver_sys::cusolverStatus_t::CUSOLVER_STATUS_SUCCESS {
            Err($crate::error::Error::from(status))
        } else {
            Ok(())
        }
    }};
}

/// cuSOLVER status code returned by wrapper operations.
/// All cuSOLVER operations report one of these values.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum Status {
    /// The operation completed successfully.
    Success = sys::cusolverStatus_t::CUSOLVER_STATUS_SUCCESS as _,
    /// The cuSOLVER library was not initialized.
    NotInitialized = sys::cusolverStatus_t::CUSOLVER_STATUS_NOT_INITIALIZED as _,
    /// Resource allocation failed.
    AllocFailed = sys::cusolverStatus_t::CUSOLVER_STATUS_ALLOC_FAILED as _,
    /// An invalid value was provided.
    InvalidValue = sys::cusolverStatus_t::CUSOLVER_STATUS_INVALID_VALUE as _,
    /// The current device architecture is not supported.
    ArchMismatch = sys::cusolverStatus_t::CUSOLVER_STATUS_ARCH_MISMATCH as _,
    /// Memory or resource mapping failed.
    MappingError = sys::cusolverStatus_t::CUSOLVER_STATUS_MAPPING_ERROR as _,
    /// Kernel execution failed.
    ExecutionFailed = sys::cusolverStatus_t::CUSOLVER_STATUS_EXECUTION_FAILED as _,
    /// An internal cuSOLVER error occurred.
    InternalError = sys::cusolverStatus_t::CUSOLVER_STATUS_INTERNAL_ERROR as _,
    /// The matrix type is not supported by the operation.
    MatrixTypeNotSupported = sys::cusolverStatus_t::CUSOLVER_STATUS_MATRIX_TYPE_NOT_SUPPORTED as _,
    /// The requested operation is not supported.
    NotSupported = sys::cusolverStatus_t::CUSOLVER_STATUS_NOT_SUPPORTED as _,
    /// A zero pivot was encountered.
    ZeroPivot = sys::cusolverStatus_t::CUSOLVER_STATUS_ZERO_PIVOT as _,
    /// The cuSOLVER license is invalid.
    InvalidLicense = sys::cusolverStatus_t::CUSOLVER_STATUS_INVALID_LICENSE as _,
    /// IRS parameters were not initialized.
    IrsParamsNotInitialized =
        sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_PARAMS_NOT_INITIALIZED as _,
    /// IRS parameters are invalid.
    IrsParamsInvalid = sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_PARAMS_INVALID as _,
    /// IRS precision parameters are invalid.
    IrsParamsInvalidPrec = sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_PARAMS_INVALID_PREC as _,
    /// IRS refinement parameters are invalid.
    IrsParamsInvalidRefine = sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_PARAMS_INVALID_REFINE as _,
    /// IRS max-iteration parameters are invalid.
    IrsParamsInvalidMaxIter =
        sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_PARAMS_INVALID_MAXITER as _,
    /// An internal IRS error occurred.
    IrsInternalError = sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_INTERNAL_ERROR as _,
    /// The requested IRS configuration is not supported.
    IrsNotSupported = sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_NOT_SUPPORTED as _,
    /// IRS configuration values are out of range.
    IrsOutOfRange = sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_OUT_OF_RANGE as _,
    /// GMRES refinement does not support the requested number of right-hand sides.
    IrsNrhsNotSupportedForRefineGmres =
        sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_NRHS_NOT_SUPPORTED_FOR_REFINE_GMRES as _,
    /// IRS info structures were not initialized.
    IrsInfosNotInitialized = sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_INFOS_NOT_INITIALIZED as _,
    /// IRS info structures were not destroyed before reuse.
    IrsInfosNotDestroyed = sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_INFOS_NOT_DESTROYED as _,
    /// The IRS system matrix is singular.
    IrsMatrixSingular = sys::cusolverStatus_t::CUSOLVER_STATUS_IRS_MATRIX_SINGULAR as _,
    /// The provided workspace is invalid.
    InvalidWorkspace = sys::cusolverStatus_t::CUSOLVER_STATUS_INVALID_WORKSPACE as _,
}

impl_enum_conversion!(sys::cusolverStatus_t, Status);

impl Status {
    pub const fn description(self) -> &'static str {
        match self {
            Self::Success => "success",
            Self::NotInitialized => "library not initialized",
            Self::AllocFailed => "allocation failed",
            Self::InvalidValue => "invalid value",
            Self::ArchMismatch => "architecture mismatch",
            Self::MappingError => "mapping error",
            Self::ExecutionFailed => "execution failed",
            Self::InternalError => "internal error",
            Self::MatrixTypeNotSupported => "matrix type not supported",
            Self::NotSupported => "not supported",
            Self::ZeroPivot => "zero pivot",
            Self::InvalidLicense => "invalid license",
            Self::IrsParamsNotInitialized => "irs params not initialized",
            Self::IrsParamsInvalid => "irs params invalid",
            Self::IrsParamsInvalidPrec => "irs params invalid precision",
            Self::IrsParamsInvalidRefine => "irs params invalid refinement",
            Self::IrsParamsInvalidMaxIter => "irs params invalid maxiter",
            Self::IrsInternalError => "irs internal error",
            Self::IrsNotSupported => "irs not supported",
            Self::IrsOutOfRange => "irs out of range",
            Self::IrsNrhsNotSupportedForRefineGmres => "irs nrhs not supported for refine gmres",
            Self::IrsInfosNotInitialized => "irs infos not initialized",
            Self::IrsInfosNotDestroyed => "irs infos not destroyed",
            Self::IrsMatrixSingular => "irs matrix singular",
            Self::InvalidWorkspace => "invalid workspace",
        }
    }
}

impl Display for Status {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            Self::Success => write!(f, "CUSOLVER_STATUS_SUCCESS"),
            Self::NotInitialized => write!(f, "CUSOLVER_STATUS_NOT_INITIALIZED"),
            Self::AllocFailed => write!(f, "CUSOLVER_STATUS_ALLOC_FAILED"),
            Self::InvalidValue => write!(f, "CUSOLVER_STATUS_INVALID_VALUE"),
            Self::ArchMismatch => write!(f, "CUSOLVER_STATUS_ARCH_MISMATCH"),
            Self::MappingError => write!(f, "CUSOLVER_STATUS_MAPPING_ERROR"),
            Self::ExecutionFailed => write!(f, "CUSOLVER_STATUS_EXECUTION_FAILED"),
            Self::InternalError => write!(f, "CUSOLVER_STATUS_INTERNAL_ERROR"),
            Self::MatrixTypeNotSupported => {
                write!(f, "CUSOLVER_STATUS_MATRIX_TYPE_NOT_SUPPORTED")
            }
            Self::NotSupported => write!(f, "CUSOLVER_STATUS_NOT_SUPPORTED"),
            Self::ZeroPivot => write!(f, "CUSOLVER_STATUS_ZERO_PIVOT"),
            Self::InvalidLicense => write!(f, "CUSOLVER_STATUS_INVALID_LICENSE"),
            Self::IrsParamsNotInitialized => {
                write!(f, "CUSOLVER_STATUS_IRS_PARAMS_NOT_INITIALIZED")
            }
            Self::IrsParamsInvalid => write!(f, "CUSOLVER_STATUS_IRS_PARAMS_INVALID"),
            Self::IrsParamsInvalidPrec => write!(f, "CUSOLVER_STATUS_IRS_PARAMS_INVALID_PREC"),
            Self::IrsParamsInvalidRefine => {
                write!(f, "CUSOLVER_STATUS_IRS_PARAMS_INVALID_REFINE")
            }
            Self::IrsParamsInvalidMaxIter => {
                write!(f, "CUSOLVER_STATUS_IRS_PARAMS_INVALID_MAXITER")
            }
            Self::IrsInternalError => write!(f, "CUSOLVER_STATUS_IRS_INTERNAL_ERROR"),
            Self::IrsNotSupported => write!(f, "CUSOLVER_STATUS_IRS_NOT_SUPPORTED"),
            Self::IrsOutOfRange => write!(f, "CUSOLVER_STATUS_IRS_OUT_OF_RANGE"),
            Self::IrsNrhsNotSupportedForRefineGmres => {
                write!(f, "CUSOLVER_STATUS_IRS_NRHS_NOT_SUPPORTED_FOR_REFINE_GMRES")
            }
            Self::IrsInfosNotInitialized => {
                write!(f, "CUSOLVER_STATUS_IRS_INFOS_NOT_INITIALIZED")
            }
            Self::IrsInfosNotDestroyed => write!(f, "CUSOLVER_STATUS_IRS_INFOS_NOT_DESTROYED"),
            Self::IrsMatrixSingular => write!(f, "CUSOLVER_STATUS_IRS_MATRIX_SINGULAR"),
            Self::InvalidWorkspace => write!(f, "CUSOLVER_STATUS_INVALID_WORKSPACE"),
        }
    }
}