packages feed

fei-base-1.0.0: c-apis/MXNet/Base/Raw/Common.chs

module MXNet.Base.Raw.Common where

import RIO
import qualified RIO.Text as T
import Data.Tuple.Ops (Unconsable, uncons)
import Foreign.Marshal (alloca, peekArray, withArray)
import Foreign.Storable (Storable(..))
import Foreign.C.Types
import Foreign.C.String (CString, peekCString, withCString)
import Foreign.Ptr
import GHC.Generics (Generic)
import GHC.Stack

type MX_UINT  = C2HSImp.CUInt
type MX_CCHAR = C2HSImp.CChar

{# typedef char CChar #}
{# default in  `Text' [char *] withCStringT* #}
{# default out `Text' [char *] peekCStringT* #}

data MXNetError = MXNetError String Text
    deriving Typeable
instance Exception MXNetError

instance Show MXNetError where
    show (MXNetError tb msg) = tb ++ "\n\nAn error occurred in MXNet.\n" ++ T.unpack msg

newtype WrapText = WrapText {unWrapText :: Text}
deriving instance Generic WrapText
deriving instance Generic C2HSImp.CInt
deriving instance Generic C2HSImp.CUInt

checked :: (HasCallStack, Unconsable t CInt r) => IO t -> IO r
checked call = do
    (res, ret) <- uncons <$> call
    if res < 0
      then do err <- mxGetLastError
              let tb = prettyCallStack callStack
              throwIO $ MXNetError tb err
      else return ret

peekCStringT :: CString -> IO Text
peekCStringT = fmap T.pack . peekCString

peekCStringPtrT :: Ptr CString -> IO Text
peekCStringPtrT ptr = do
    cstr <- peek ptr
    if | cstr == nullPtr -> return ""
       | otherwise -> peekCStringT cstr

peekCStringArrayT :: Int -> Ptr CString -> IO [Text]
peekCStringArrayT cnt ptr = peekArray cnt ptr >>= mapM peekCStringT

withCStringT :: Text -> (CString -> IO a) -> IO a
withCStringT str = withCString (T.unpack str)

-- TODO: Does it worth of any opt for the withCStringArrayT?
-- withCStringArrayT :: [Text] -> (Ptr CString -> IO a) -> IO a
-- withCStringArrayT arr act = do
--     let nul = T.singleton '\NUL'
--         whole = T.concat $ map (`T.append` nul) arr
--     T.encodeUtf8 whole

withCStringArrayT :: [Text] -> (Ptr CString -> IO a) -> IO a
withCStringArrayT strs act = go strs []
  where
    go [] all = withArray (reverse all) act
    go (s:ss) all = withCStringT s (go ss . (:all))

#include <mxnet/c_api.h>
#include <nnvm/c_api.h>

{#
fun MXGetVersion as mxGetVersion_
    {
        alloca- `CInt' peek*
    } -> `CInt'
#}

mxGetVersion :: IO Int
mxGetVersion = fromIntegral <$> checked mxGetVersion_

{#
fun MXListAllOpNames as mxListAllOpNames_
    {
        alloca- `MX_UINT' peek*,
        alloca- `Ptr (Ptr MX_CCHAR)' peek*
    } -> `CInt'
#}

mxListAllOpNames :: IO [Text]
mxListAllOpNames = do
    (cnt, ptr) <- checked mxListAllOpNames_
    peekCStringArrayT (fromIntegral cnt :: Int) ptr

{#
fun MXGetLastError as mxGetLastError
    {
    } -> `Text' peekCStringT*
#}

{#
pointer AtomicSymbolCreator newtype
#}

deriving instance Storable AtomicSymbolCreator

fromOpHandle :: OpHandle -> AtomicSymbolCreator
fromOpHandle (OpHandle ptr) = AtomicSymbolCreator (C2HSImp.castPtr ptr)

{#
fun MXSymbolListAtomicSymbolCreators as mxSymbolListAtomicSymbolCreators_
    {
        alloca- `MX_UINT' peek*,
        alloca- `Ptr AtomicSymbolCreator' peek*
    } -> `CInt'
#}

mxSymbolListAtomicSymbolCreators :: IO [AtomicSymbolCreator]
mxSymbolListAtomicSymbolCreators = do
    (cnt, ptr) <- checked $ mxSymbolListAtomicSymbolCreators_
    peekArray (fromIntegral cnt) ptr

mxSymbolGetAtomicSymbolCreatorAt :: Int -> IO AtomicSymbolCreator
mxSymbolGetAtomicSymbolCreatorAt idx = do
    (cnt, ptr) <- checked $ mxSymbolListAtomicSymbolCreators_
    peekElemOff ptr idx

{#
fun MXSymbolGetAtomicSymbolName as mxSymbolGetAtomicSymbolName_
    {
        `AtomicSymbolCreator',
        alloca- `Text' peekCStringPtrT*
    } -> `CInt'
#}

mxSymbolGetAtomicSymbolName :: AtomicSymbolCreator -> IO Text
mxSymbolGetAtomicSymbolName = fmap unWrapText . checked . fmap (second WrapText) . mxSymbolGetAtomicSymbolName_

{#
fun MXSymbolGetAtomicSymbolInfo as mxSymbolGetAtomicSymbolInfo_
    {
        `AtomicSymbolCreator',
        alloca- `Text' peekCStringPtrT*,
        alloca- `Text' peekCStringPtrT*,
        alloca- `MX_UINT' peek*,
        alloca- `Ptr CString' peek*,
        alloca- `Ptr CString' peek*,
        alloca- `Ptr CString' peek*,
        alloca- `Text' peekCStringPtrT*,
        alloca- `Text' peekCStringPtrT*
    } -> `CInt'
#}

mxSymbolGetAtomicSymbolInfo :: AtomicSymbolCreator
                            -> IO (Text,
                                   Text,
                                   [Text],
                                   [Text],
                                   [Text],
                                   Text,
                                   Text)
mxSymbolGetAtomicSymbolInfo creator = do
    (name, desc, argcnt, argname, argtype, argdesc, key_var_num_args, rettyp) <- checked $ mxSymbolGetAtomicSymbolInfo_ creator
    let n = fromIntegral argcnt
    argname <- peekCStringArrayT n argname
    argtype <- peekCStringArrayT n argtype
    argdesc <- peekCStringArrayT n argdesc
    return (name, desc, argname, argtype, argdesc, key_var_num_args, rettyp)

{#
fun MXNotifyShutdown as mxNotifyShutdown_
    {
    } -> `CInt'
#}

mxNotifyShutdown :: IO ()
mxNotifyShutdown = checked mxNotifyShutdown_


{#
fun MXRandomSeed as mxRandomSeed_
    {
        `CInt'
    } -> `CInt'
#}

mxRandomSeed :: Int -> IO ()
mxRandomSeed seed = checked $ mxRandomSeed_ (fromIntegral seed)

---------------------------------------------------
type NN_UINT  = C2HSImp.CUInt

{# pointer OpHandle newtype #}
deriving instance Storable OpHandle
deriving instance Generic  OpHandle

{# pointer GraphHandle newtype #}
deriving instance Storable GraphHandle
deriving instance Generic  GraphHandle

{#
fun NNListAllOpNames as nnListAllOpNames_
    {
        alloca- `NN_UINT' peek*,
        alloca- `Ptr (Ptr MX_CCHAR)' peek*
    } -> `CInt'
#}

nnListAllOpNames :: IO [Text]
nnListAllOpNames = do
    (cnt, ptr) <- checked nnListAllOpNames_
    peekCStringArrayT (fromIntegral cnt) ptr

{#
fun NNListUniqueOps as nnListUniqueOps_
    {
        alloca- `NN_UINT' peek*,
        alloca- `Ptr OpHandle' peek*
    } -> `CInt'
#}

nnListUniqueOps :: IO [OpHandle]
nnListUniqueOps = do
    (cnt, ptr) <- checked (nnListUniqueOps_ :: IO (CInt, NN_UINT, Ptr OpHandle))
    peekArray (fromIntegral cnt :: Int) (ptr :: Ptr OpHandle)

{#
fun NNGetOpHandle as nnGetOpHandle_
    {
        withCStringT* `Text',
        alloca- `OpHandle' peek*
    } -> `CInt'
#}

nnGetOpHandle :: Text -> IO OpHandle
nnGetOpHandle = checked . nnGetOpHandle_

{#
fun NNGetOpInfo as nnGetOpInfo_
    {
        `OpHandle',
        alloca- `Text' peekCStringPtrT*,
        alloca- `Text' peekCStringPtrT*,
        alloca- `NN_UINT' peek*,
        alloca- `Ptr CString' peek*,
        alloca- `Ptr CString' peek*,
        alloca- `Ptr CString' peek*,
        alloca- `Text' peekCStringPtrT*
    } -> `CInt'
#}

nnGetOpInfo :: OpHandle -> IO (Text, Text, [Text], [Text], [Text], Text)
nnGetOpInfo op = do
    (name, desc, num_args, ptr_arg_names, ptr_arg_types, ptr_arg_descs, ret_type) <- checked $ nnGetOpInfo_ op
    let num_args_ = fromIntegral num_args
    arg_names <- peekCStringArrayT num_args_ ptr_arg_names
    arg_types <- peekCStringArrayT num_args_ ptr_arg_types
    arg_descs <- peekCStringArrayT num_args_ ptr_arg_descs
    return (name, desc, arg_names, arg_types, arg_descs, ret_type)

{#
fun NNGetLastError as nnGetLastError
    {
    } -> `Text' peekCStringT*
#}

{#
fun MXStorageEmptyCache as mxStorageEmptyCache_
    {
        `CInt',
        `CInt'
    } -> `CInt'
#}

mxStorageEmptyCache :: Int -> Int -> IO ()
mxStorageEmptyCache devtype devid = checked $ mxStorageEmptyCache_ (fromIntegral devtype) (fromIntegral devid)