casadi-bindings-3.4.5.0: src/Casadi/Function.hs
{-# OPTIONS_GHC -Wall -fno-cse #-}
module Casadi.Function
( C.Function
, externalFunction, externalFunction'
, generateCode, generateCode'
, callDM, callDM'
, callV, callV'
) where
import Control.Monad ( when, zipWithM, zipWithM_ )
import Data.Map ( Map )
import qualified Data.Map as M
import qualified Data.Set as S
import Data.Vector ( Vector )
import qualified Data.Vector as V
import qualified Data.Vector.Mutable as VM
import qualified Data.Traversable as T
import Foreign.C.Types ( CDouble, CInt(..) )
import Foreign.ForeignPtr ( withForeignPtr )
import Foreign.Ptr ( Ptr, nullPtr )
import Foreign.Marshal.Alloc ( free )
import Foreign.Marshal.Array ( mallocArray )
import Foreign.Marshal.Utils ( new )
import Foreign.Storable ( peekElemOff, pokeElemOff, peek )
import qualified Casadi.Core.Classes.CodeGenerator as C
import Casadi.Core.Classes.DM ( DM )
import qualified Casadi.Core.Classes.Function as C
import Casadi.Core.Data ( Function(..), Function' )
import qualified Casadi.Core.Tools as C
import Casadi.Internal.FormatException ( formatException )
import Casadi.Internal.MarshalTypes ( StdString )
import Casadi.Internal.WrapReturn ( wrapReturn )
import Casadi.GenericType ( GenericType, GType, fromGType )
generateCode :: C.Function -> String -> Map String GType -> IO String
generateCode f n opts0 = do
opts <- T.mapM fromGType opts0 :: IO (Map String GenericType)
C.function_generate__3 f n opts
generateCode' :: C.Function -> String -> Map String GType -> IO String
generateCode' f name opts0 = do
opts <- T.mapM fromGType opts0 :: IO (Map String GenericType)
cg <- C.codeGenerator__1 name opts
C.codeGenerator_add__0 cg f
C.codeGenerator_generate__0 cg
externalFunction :: String -> Map String GType -> IO C.Function
externalFunction name opts0 = do
opts <- T.mapM fromGType opts0 :: IO (Map String GenericType)
C.external__5 name opts
externalFunction' :: String -> String -> Map String GType -> IO C.Function
externalFunction' name binName opts0 = do
opts <- T.mapM fromGType opts0 :: IO (Map String GenericType)
C.external__3 name binName opts
callDM :: C.Function -> Vector DM -> IO (Vector DM)
callDM f ins = C.function_call__15 f ins
callDM' :: C.Function -> Map String DM -> IO (Map String DM)
callDM' f ins = C.function_call__6 f ins
foreign import ccall safe "void hs_call_casadi_function_with_pointers" c_callV
:: Ptr (Ptr StdString) -> Ptr Function'
-> Ptr (Ptr CDouble) -> CInt
-> Ptr (Ptr CDouble) -> CInt
-> IO ()
callV :: C.Function -> Vector (Vector Double) -> IO (Vector (Vector Double))
callV f@(Function f') args = do
-- check number of inputs
nIn <- C.function_n_in f
when (nIn /= V.length args) $
error $ "callV: Function has " ++ show nIn ++ " inputs but you provided " ++ show (V.length args)
-- check the size of each input
inputSizes <- mapM (C.function_nnz_in__1 f) (take nIn [0..])
let inputSizes' :: Vector Int
inputSizes' = fmap V.length args
checkInputSize k inputSize
| inputSizes' V.! k == inputSize = return ()
| otherwise = error $ "Function input " ++ show k ++ " has " ++ show inputSize
++ " nonzeros but you provided " ++ show (inputSizes' V.! k)
zipWithM_ checkInputSize [0..] inputSizes
-- allocate the input buffer
argBuffer <- mallocArray nIn :: IO (Ptr (Ptr CDouble))
-- allocate and assign each input array
let allocAndAssignArg k inputSize = do
-- allocate input array
argp <- mallocArray inputSize :: IO (Ptr CDouble)
-- copy input to array
let arg = args V.! k :: Vector Double
mapM_ (\j -> pokeElemOff argp j (realToFrac (arg V.! j))) (take inputSize [0..])
-- assign input array to input buffer
pokeElemOff argBuffer k argp
zipWithM_ allocAndAssignArg (take nIn [0..]) inputSizes
-- allocate output buffer
nOut <- C.function_n_out f
resBuffer <- mallocArray nOut :: IO (Ptr (Ptr CDouble))
-- allocate memory for each output
outputSizes <- mapM (C.function_nnz_out__1 f) (take nOut [0..])
let allocRes k outputSize = do
-- allocate output array
resp <- mallocArray outputSize :: IO (Ptr CDouble)
-- assign output array to output buffer
pokeElemOff resBuffer k resp
zipWithM_ allocRes [0..] outputSizes
-- call the function
errStrPtrP <- new nullPtr
withForeignPtr f' $ \ff -> c_callV errStrPtrP ff argBuffer (fromIntegral nIn) resBuffer (fromIntegral nOut)
errStrPtr <- peek errStrPtrP
free errStrPtrP
if errStrPtr /= nullPtr
then wrapReturn errStrPtr >>= (error . formatException)
else do
-- deallocate the input vectors
let freeInput k = peekElemOff argBuffer k >>= free
mapM_ freeInput (take nIn [0..])
-- deallocate the input buffer
free argBuffer
-- copy the outputs and free the output arrays
let copyAndFreeOutput k outputSize = do
resp <- peekElemOff resBuffer k
res <- VM.new outputSize :: IO (VM.IOVector Double)
let copyOutput j = peekElemOff resp j >>= (VM.write res j . realToFrac)
mapM_ copyOutput (take outputSize [0..])
-- free the output array
free resp
-- return the vector
V.unsafeFreeze res
ret <- zipWithM copyAndFreeOutput [0..] outputSizes
-- free the output buffer
free resBuffer
return (V.fromList ret)
callV' :: C.Function -> M.Map String (Vector Double) -> IO (M.Map String (Vector Double))
callV' f inputMap = do
funName <- C.function_name f
inputNames <- C.function_name_in__1 f
outputNames <- C.function_name_out__1 f
let inputNamesSet = S.fromList (V.toList inputNames)
inputMapKeysSet = M.keysSet inputMap
missingInputs = S.difference inputNamesSet inputMapKeysSet
extraInputs = S.difference inputMapKeysSet inputNamesSet
describeBadInputs =
error $ "callV': " ++ funName ++ ":\n" ++
"expected inputs: " ++ show (V.toList inputNames) ++
(if null missingInputs then "" else "\nmissing inputs: " ++ show missingInputs) ++
(if null extraInputs then "" else "\nextra inputs: " ++ show extraInputs)
when (M.size inputMap /= V.length inputNames) describeBadInputs
let inputVec :: Vector (Vector Double)
inputVec = fmap lookupInput inputNames
where
lookupInput inputName = case M.lookup inputName inputMap of
Nothing -> describeBadInputs
Just r -> r
outputVec <- callV f inputVec
when (V.length outputVec /= V.length outputNames) $
error $ "callV': " ++ funName ++ ": something really weird happened, length of outputs "
++ show (V.length outputVec) ++ " /= length of names " ++ show (V.length outputNames)
return $ M.fromList $ zip (V.toList outputNames) (V.toList outputVec)