ipopt-hs-0.4.2.0: Nlopt/Raw.chs
{-# LANGUAGE TypeFamilies,DeriveDataTypeable, RankNTypes, ConstraintKinds, FlexibleContexts #-}
module Nlopt.Raw where
import Ipopt.AnyRF
import Foreign.C.Types
import Foreign.Ptr
import Foreign.Marshal
import Foreign.Storable
import Data.Int
import C2HS
import qualified Data.Vector.Storable.Mutable as VM
import qualified Data.Vector as V
import qualified Data.Vector.Storable as VS
import qualified Data.Vector.Generic as VG
import Control.Exception
import Data.Typeable
import Numeric.AD (grad, jacobian, hessian)
import Control.Monad
#include "nlopt.h"
-- * converting haskell functions
{- $conventions
NLOpt has three different types for functions 'FunPtrFunc' 'FunPtrMFunc' and 'FunPtrPrecond'.
[@AD@] means derivatives are calculated, and the haskell function does no IO.
[@G@] mean you are providing the derivatives
[@M@] means m functions are calculated at a time
-}
-- | an exact hessian calculated with AD. See 'toPrecondG'
-- XXX BFGS approx could also be done...
toPrecondAD :: (forall a. AnyRFCxt a => V.Vector a -> a) -> Precond
toPrecondAD f n xs vs r _usrData =
toPrecondG (\x v -> return (hessian f x `mXv` v)) n xs vs r _usrData
-- | see <http://ab-initio.mit.edu/wiki/index.php/NLopt_Reference#Preconditioning_with_approximate_Hessians>
-- only applies to 'NLOPT_LD_CCSAQ'
toPrecondG :: (VG.Vector vec Double, v ~ vec Double)
=> (v -> v -> IO v) -- ^ given @x,v@ calculate @H(x)v@ where H the hessian
-> Precond
toPrecondG f n xs vs r _usrData = do
xs' <- ptrToV n xs
vs' <- ptrToV n vs
copyInto n r =<< f xs' vs'
toFuncM :: VG.Vector v Double => (v Double -> IO (v Double) ) -> MFunc
toFuncM f m r n xs _g _usrData = do
copyInto m r =<< f =<< ptrToV n xs
-- | @n@ and @m@ type variables indicate the vector size as
-- number of inputs and number of outputs respectively
toFuncMG :: (VG.Vector n Double, VG.Vector n (m Double), n ~ m)
=> (n Double -> IO (m Double) ) -- @f@
-> (n Double -> IO (n (m Double))) -- @grad f@
-> MFunc
toFuncMG f g m r n xs g' _usrData = do
xs' <- ptrToV n xs
copyInto m r =<< f xs'
-- probably should do this better...
copyInto (n*m) g' . VG.concat . VG.toList =<< g xs'
toFuncMAD :: (forall a. AnyRFCxt a => V.Vector a -> V.Vector a) -> MFunc
toFuncMAD f m r n xs g _usrData = do
xs' <- ptrToV n xs
copyInto m r (f xs')
-- grad[i*n + j] should be satisfied...
copyInto (n*m) g (V.concat (V.toList (jacobian f xs')))
toFunc :: (VG.Vector v Double) => (v Double -> IO Double) -> Func
toFunc f n xs _g _usrData = fmap CDouble $ f =<< ptrToV n xs
-- | where the gradient happens via AD
toFuncAD :: (forall a. AnyRFCxt a => V.Vector a -> a) -> Func
toFuncAD f n xs g _usrData = do
xs' <- ptrToV n xs
copyInto n g (grad f xs')
return (CDouble (f xs'))
toFuncG :: (VG.Vector v Double) => (v Double -> IO Double) -- ^ @f@
-> (v Double -> IO (v Double)) -- ^ @grad(f)@
-> Func
toFuncG f g n xs g' _usrData = do
xs' <- ptrToV n xs
copyInto n g' =<< g xs'
CDouble `fmap` f xs'
type family UnFunPtr a
type instance UnFunPtr (FunPtr a) = a
type Func = UnFunPtr FunPtrFunc
type MFunc = UnFunPtr FunPtrMFunc
type Precond = UnFunPtr FunPtrPrecond
type FunPtrFunc = {# type nlopt_func #}
type FunPtrMFunc = {# type nlopt_mfunc #}
type FunPtrPrecond = {# type nlopt_precond #}
{#pointer *nlopt_opt as NLOpt foreign newtype #}
{#enum nlopt_algorithm as ^ {} deriving (Show,Bounded,Ord,Eq) #}
-- | negative (above NLOPT_SUCCESS) values of these are thrown as exceptions. The positive ones are
-- return values.
{#enum nlopt_result as ^ {} deriving (Show,Typeable) #}
instance Exception NloptResult
checkEC :: CInt -> IO NloptResult
checkEC n | n `elem` [-1,-2,-3] = throwIO e
| otherwise = return e
where e = fromCInt n :: NloptResult
{#fun nlopt_srand as ^ { `Int' } -> `()' #}
{#fun nlopt_srand_time as ^ { } -> `()' #}
{#fun nlopt_version as ^ {
alloca- `Int' peekInt*,
alloca- `Int' peekInt*,
alloca- `Int' peekInt*} -> `()' #}
{#fun nlopt_create as ^ { toCInt `NloptAlgorithm', `Int' } -> `NLOpt' ptrToNLOpt * #}
{#fun nlopt_copy as ^ { withNLOpt_* `NLOpt' } -> `NLOpt' ptrToNLOpt * #}
-- | should not need to be called manually
{#fun nlopt_destroy as ^ { id `Ptr ()' } -> `()' #}
{#fun nlopt_optimize as ^ {
withNLOpt_* `NLOpt', vmUnsafeWith* `Vec', alloca- `Double' peekDouble* }
-> `NloptResult' checkEC* #}
{#fun nlopt_set_min_objective as ^ {
withNLOpt_* `NLOpt', withFunc* `Func', withNull- `()' }
-> `NloptResult' checkEC* #}
{#fun nlopt_set_max_objective as ^ {
withNLOpt_* `NLOpt', withFunc* `Func', withNull- `()' }
-> `NloptResult' checkEC* #}
{#fun nlopt_set_precond_min_objective as ^ {
withNLOpt_* `NLOpt',
withFunc* `Func',
withPrecond* `Precond',
withNull- `()' }
-> `NloptResult' checkEC* #}
{#fun nlopt_set_precond_max_objective as ^ {
withNLOpt_* `NLOpt',
withFunc* `Func',
withPrecond* `Precond',
withNull- `()' } -> `NloptResult' checkEC* #}
{#fun nlopt_get_algorithm as ^ { withNLOpt_* `NLOpt' } -> `NloptAlgorithm' fromCInt #}
{#fun nlopt_get_dimension as ^ { withNLOpt_* `NLOpt' } -> `Int' #}
-- * constraints
{#fun nlopt_set_lower_bounds as ^ { withNLOpt_* `NLOpt', vmUnsafeWith* `Vec' }
-> `NloptResult' checkEC* #}
{#fun nlopt_set_upper_bounds as ^ { withNLOpt_* `NLOpt', vmUnsafeWith* `Vec' }
-> `NloptResult' checkEC* #}
{#fun nlopt_get_lower_bounds as ^ { withNLOpt_* `NLOpt', vmUnsafeWith* `Vec' }
-> `NloptResult' checkEC* #}
{#fun nlopt_get_upper_bounds as ^ { withNLOpt_* `NLOpt', vmUnsafeWith* `Vec' }
-> `NloptResult' checkEC* #}
{#fun nlopt_set_lower_bounds1 as ^ { withNLOpt_* `NLOpt', `Double' }
-> `NloptResult' checkEC* #}
{#fun nlopt_set_upper_bounds1 as ^ { withNLOpt_* `NLOpt', `Double' }
-> `NloptResult' checkEC* #}
{#fun nlopt_remove_inequality_constraints as ^ { withNLOpt_* `NLOpt' }
-> `NloptResult' checkEC* #}
{#fun nlopt_add_inequality_constraint as ^
{ withNLOpt_* `NLOpt',
withFunc* `Func',
withNull- `()',
`Double' } -> `NloptResult' checkEC* #}
{#fun nlopt_add_precond_inequality_constraint as ^
{ withNLOpt_* `NLOpt',
withFunc* `Func',
withPrecond* `Precond',
withNull- `()',
`Double' } -> `NloptResult' checkEC* #}
{#fun nlopt_add_inequality_mconstraint as ^
{ withNLOpt_* `NLOpt',
`Int',
withMFunc* `MFunc',
withNull- `()',
vmUnsafeWith* `Vec' } -> `NloptResult' checkEC* #}
{#fun nlopt_remove_equality_constraints as ^ { withNLOpt_* `NLOpt' }
-> `NloptResult' checkEC* #}
{#fun nlopt_add_equality_constraint as ^
{ withNLOpt_* `NLOpt',
withFunc* `Func',
withNull- `()',
`Double' } -> `NloptResult' checkEC* #}
{#fun nlopt_add_precond_equality_constraint as ^
{ withNLOpt_* `NLOpt',
withFunc* `Func',
withPrecond* `Precond',
withNull- `()',
`Double' } -> `NloptResult' checkEC* #}
{#fun nlopt_add_equality_mconstraint as ^
{ withNLOpt_* `NLOpt',
`Int',
withMFunc* `MFunc',
withNull- `()',
vmUnsafeWith* `Vec' } -> `NloptResult' checkEC* #}
-- * stopping criteria
{#fun nlopt_set_stopval as ^ { withNLOpt_* `NLOpt', `Double' }
-> `NloptResult' checkEC* #}
{#fun nlopt_get_stopval as ^ { withNLOpt_* `NLOpt' } -> `Double' #}
{#fun nlopt_set_ftol_rel as ^ { withNLOpt_* `NLOpt', `Double' }
-> `NloptResult' checkEC* #}
{#fun nlopt_get_ftol_rel as ^ { withNLOpt_* `NLOpt' } -> `Double' #}
{#fun nlopt_set_ftol_abs as ^ { withNLOpt_* `NLOpt', `Double' }
-> `NloptResult' checkEC* #}
{#fun nlopt_get_ftol_abs as ^ { withNLOpt_* `NLOpt' } -> `Double' #}
{#fun nlopt_set_xtol_rel as ^ { withNLOpt_* `NLOpt', `Double' }
-> `NloptResult' checkEC* #}
{#fun nlopt_get_xtol_rel as ^ { withNLOpt_* `NLOpt' } -> `Double' #}
{#fun nlopt_set_xtol_abs1 as ^ { withNLOpt_* `NLOpt', `Double' }
-> `NloptResult' checkEC* #}
{#fun nlopt_set_xtol_abs as ^ { withNLOpt_* `NLOpt', vmUnsafeWith* `Vec' }
-> `NloptResult' checkEC* #}
{#fun nlopt_get_xtol_abs as ^ { withNLOpt_* `NLOpt', vmUnsafeWith* `Vec' }
-> `NloptResult' checkEC* #}
{#fun nlopt_set_maxeval as ^ { withNLOpt_* `NLOpt', `Int' }
-> `NloptResult' checkEC* #}
{#fun nlopt_get_maxeval as ^ { withNLOpt_* `NLOpt' } -> `Int' #}
{#fun nlopt_set_maxtime as ^ { withNLOpt_* `NLOpt', `Double' }
-> `NloptResult' checkEC* #}
{#fun nlopt_get_maxtime as ^ { withNLOpt_* `NLOpt' } -> `Double' #}
{#fun nlopt_force_stop as ^ { withNLOpt_* `NLOpt' } -> `NloptResult' checkEC* #}
{#fun nlopt_set_force_stop as ^ { withNLOpt_* `NLOpt', `Int' } -> `NloptResult' checkEC* #}
{#fun nlopt_get_force_stop as ^ { withNLOpt_* `NLOpt' } -> `Int' #}
-- * more algorithm-specific parameters
{#fun nlopt_set_local_optimizer as ^
{ withNLOpt_* `NLOpt', withNLOpt_* `NLOpt' } -> `NloptResult' checkEC* #}
{#fun nlopt_set_population as ^
{ withNLOpt_* `NLOpt', `Int' } -> `NloptResult' checkEC* #}
{#fun nlopt_get_population as ^ { withNLOpt_* `NLOpt' } -> `Int' #}
{#fun nlopt_set_vector_storage as ^
{ withNLOpt_* `NLOpt', `Int' } -> `NloptResult' checkEC* #}
{#fun nlopt_get_vector_storage as ^ { withNLOpt_* `NLOpt' } -> `Int' #}
{#fun nlopt_set_initial_step as ^ { withNLOpt_* `NLOpt',
vmUnsafeWith* `Vec' } -> `NloptResult' checkEC* #}
{#fun nlopt_get_initial_step as ^ { withNLOpt_* `NLOpt',
vmUnsafeWith* `Vec',
vmUnsafeWith* `Vec' } -> `NloptResult' checkEC* #}
{#fun nlopt_set_initial_step1 as ^ { withNLOpt_* `NLOpt',
`Double' } -> `NloptResult' checkEC* #}
-- * utils for ffi
-- $note
-- much is copied from Ipopt.Raw
withNLOpt_ x f = withNLOpt x (f . castPtr)
vmUnsafeWith :: VM.IOVector Double -> (Ptr CDouble -> IO b) -> IO b
vmUnsafeWith v f = VM.unsafeWith v (f . castPtr)
ptrToVS :: Integral n => n -> Ptr CDouble -> IO Vec
ptrToVS n p = do
fp <- newForeignPtr_ (castPtr p)
return (VM.unsafeFromForeignPtr0 fp (fromIntegral n))
ptrToV :: (VG.Vector v Double, Integral n) => n -> Ptr CDouble -> IO (v Double)
ptrToV n p = fmap V.convert $ VS.unsafeFreeze =<< ptrToVS n p
copyInto _ p _ | p == nullPtr = return ()
copyInto n dest src = do
to <- ptrToVS n dest
VM.copy to =<< VS.unsafeThaw (V.convert src)
type Vec = VM.IOVector Double
toCInt x = fromIntegral (fromEnum x)
fromCInt x = toEnum (fromIntegral x)
peekInt ptr = fmap fromIntegral (peek ptr)
ptrToNLOpt :: Ptr () -> IO NLOpt
ptrToNLOpt p = do
fp <- newForeignPtr nloptDestroyFP p
return (NLOpt (castForeignPtr fp))
foreign import ccall "wrapper" mkNloptFinalizer :: (Ptr () -> IO ()) -> IO (FunPtr (Ptr () -> IO ()))
foreign import ccall unsafe "& nlopt_destroy" nloptDestroyFP :: FunPtr (Ptr () -> IO ())
foreign import ccall "wrapper" mkFunc :: Func -> IO (FunPtr Func)
foreign import ccall "wrapper" mkPrecond :: Precond -> IO (FunPtr Precond)
foreign import ccall "wrapper" mkMFunc :: MFunc -> IO (FunPtr MFunc)
withFunc :: Func -> (FunPtr Func -> IO b) -> IO b
withFunc f g = do
f' <- mkFunc f
r <- g f'
-- freeHaskellFunPtr f'
return r
withPrecond :: Precond -> (FunPtr Precond -> IO b) -> IO b
withPrecond f g = do
f' <- mkPrecond f
r <- g f'
-- freeHaskellFunPtr f'
return r
withMFunc :: MFunc -> (FunPtr MFunc -> IO b) -> IO b
withMFunc f g = do
f' <- mkMFunc f
r <- g f'
-- freeHaskellFunPtr f'
return r
withNull f = f nullPtr
-- | c2hs generates CDouble peek a Double instead
peekDouble :: Ptr CDouble -> IO Double
peekDouble p = peek (castPtr p)
-- | naive matrix × vector
mXv :: Num a => V.Vector (V.Vector a) -> V.Vector a -> V.Vector a
mXv m v = V.map (V.sum . V.zipWith (*) v) m
-- * c2hs-generated