module Data.LinearProgram.GLPK.Internal (writeProblem, solveSimplex, mipSolve,
getObjVal, getRowPrim, getColPrim, mipObjVal, mipRowVal, mipColVal, getBadRay) where
import Control.Monad
import Foreign.Ptr
import Foreign.C
import Foreign.Marshal.Array
import Data.Bits
import Data.Map hiding (map)
import Data.LinearProgram.Common
import Data.LinearProgram.GLPK.Types
foreign import ccall unsafe "c_glp_minimize" glpMinimize :: Ptr GlpProb -> IO ()
foreign import ccall unsafe "c_glp_maximize" glpMaximize :: Ptr GlpProb -> IO ()
foreign import ccall unsafe "c_glp_add_rows" glpAddRows :: Ptr GlpProb -> CInt -> IO CInt
foreign import ccall unsafe "c_glp_add_cols" glpAddCols :: Ptr GlpProb -> CInt -> IO CInt
foreign import ccall unsafe "c_glp_set_row_bnds" glpSetRowBnds :: Ptr GlpProb -> CInt -> CInt -> CDouble -> CDouble -> IO ()
foreign import ccall unsafe "c_glp_set_col_bnds" glpSetColBnds :: Ptr GlpProb -> CInt -> CInt -> CDouble -> CDouble -> IO ()
foreign import ccall unsafe "c_glp_set_obj_coef" glpSetObjCoef :: Ptr GlpProb -> CInt -> CDouble -> IO ()
foreign import ccall unsafe "c_glp_set_mat_row" glpSetMatRow :: Ptr GlpProb -> CInt -> CInt -> Ptr CInt -> Ptr CDouble -> IO ()
foreign import ccall unsafe "c_glp_solve_simplex" glpSolveSimplex :: Ptr GlpProb -> CInt -> CInt -> CInt -> IO CInt
foreign import ccall unsafe "c_glp_get_obj_val" glpGetObjVal :: Ptr GlpProb -> IO CDouble
foreign import ccall unsafe "c_glp_get_row_prim" glpGetRowPrim :: Ptr GlpProb -> CInt -> IO CDouble
foreign import ccall unsafe "c_glp_get_col_prim" glpGetColPrim :: Ptr GlpProb -> CInt -> IO CDouble
foreign import ccall unsafe "c_glp_set_col_kind" glpSetColKind :: Ptr GlpProb -> CInt -> CInt -> IO ()
foreign import ccall unsafe "c_glp_mip_solve" glpMipSolve ::
Ptr GlpProb -> CInt -> CInt -> CInt -> CInt -> CInt -> CInt -> CInt -> CDouble -> CInt -> IO CInt
foreign import ccall unsafe "c_glp_mip_obj_val" glpMIPObjVal :: Ptr GlpProb -> IO CDouble
foreign import ccall unsafe "c_glp_mip_row_val" glpMIPRowVal :: Ptr GlpProb -> CInt -> IO CDouble
foreign import ccall unsafe "c_glp_mip_col_val" glpMIPColVal :: Ptr GlpProb -> CInt -> IO CDouble
foreign import ccall unsafe "c_glp_set_row_name" glpSetRowName :: Ptr GlpProb -> CInt -> CString -> IO ()
foreign import ccall unsafe "c_glp_get_bad_ray" glpGetBadRay :: Ptr GlpProb -> IO CInt
setObjectiveDirection :: Direction -> GLPK ()
setObjectiveDirection dir = GLP $ case dir of
Min -> glpMinimize
Max -> glpMaximize
getBadRay :: GLPK (Maybe Int)
getBadRay = liftM (\ x -> guard (x /= 0) >> return (fromIntegral x)) $ GLP glpGetBadRay
addRows :: Int -> GLPK Int
addRows n = GLP $ liftM fromIntegral . flip glpAddRows (fromIntegral n)
addCols :: Int -> GLPK Int
addCols n = GLP $ liftM fromIntegral . flip glpAddCols (fromIntegral n)
setRowBounds :: Real a => Int -> Bounds a -> GLPK ()
setRowBounds i bds = GLP $ \ lp -> onBounds (glpSetRowBnds lp (fromIntegral i)) bds
setColBounds :: Real a => Int -> Bounds a -> GLPK ()
setColBounds i bds = GLP $ \ lp -> onBounds (glpSetColBnds lp (fromIntegral i)) bds
onBounds :: Real a => (CInt -> CDouble -> CDouble -> x) -> Bounds a -> x
onBounds f bds = case bds of
Free -> f 1 0 0
LBound a -> f 2 (realToFrac a) 0
UBound a -> f 3 0 (realToFrac a)
Bound a b -> f 4 (realToFrac a) (realToFrac b)
Equ a -> f 5 (realToFrac a) 0
setObjCoef :: Real a => Int -> a -> GLPK ()
setObjCoef i v = GLP $ \ lp -> glpSetObjCoef lp (fromIntegral i) (realToFrac v)
setMatRow :: Real a => Int -> [(Int, a)] -> GLPK ()
setMatRow i row = GLP $ \ lp ->
allocaArray (len+1) $ \ (ixs :: Ptr CInt) -> allocaArray (len+1) $ \ (coeffs :: Ptr CDouble) -> do
pokeArray ixs (0:map (fromIntegral . fst) row)
pokeArray coeffs (0:map (realToFrac . snd) row)
glpSetMatRow lp (fromIntegral i) (fromIntegral len) ixs coeffs
where len = length row
solveSimplex :: MsgLev -> Int -> Bool -> GLPK ReturnCode
solveSimplex msglev tmLim presolve = GLP $ \ lp -> liftM (toEnum . fromIntegral) $ glpSolveSimplex lp
(getMsgLev msglev)
tmLim'
(if presolve then 1 else 0)
where tmLim' = fromIntegral (tmLim * 1000)
getMsgLev :: MsgLev -> CInt
getMsgLev = fromIntegral . fromEnum
getObjVal :: GLPK Double
getObjVal = liftM realToFrac $ GLP glpGetObjVal
getRowPrim :: Int -> GLPK Double
getRowPrim i = liftM realToFrac $ GLP (`glpGetRowPrim` fromIntegral i)
getColPrim :: Int -> GLPK Double
getColPrim i = liftM realToFrac $ GLP (`glpGetColPrim` fromIntegral i)
setColKind :: Int -> VarKind -> GLPK ()
setColKind i kind = GLP $ \ lp -> glpSetColKind lp (fromIntegral i) (fromIntegral $ 1 + fromEnum kind)
mipSolve :: MsgLev -> BranchingTechnique -> BacktrackTechnique -> Preprocessing -> Bool ->
[Cuts] -> Double -> Int -> Bool -> GLPK ReturnCode
mipSolve msglev brt btt pp fp cuts mipgap tmlim presol =
liftM (toEnum . fromIntegral) $ GLP $ \ lp -> glpMipSolve lp msglev'
brt' btt' pp' fp' tmlim' cuts' mipgap' presol'
where !msglev' = getMsgLev msglev
!brt' = 1 + fromIntegral (fromEnum brt)
!btt' = 1 + fromIntegral (fromEnum btt)
!pp' = fromIntegral (fromEnum pp)
!fp' = fromIntegral (fromEnum fp)
!cuts' = (if GMI `elem` cuts then 1 else 0) .|.
(if MIR `elem` cuts then 2 else 0) .|.
(if Cov `elem` cuts then 4 else 0) .|.
(if Clq `elem` cuts then 8 else 0)
!mipgap' = realToFrac mipgap
!tmlim' = fromIntegral (1000 * tmlim)
!presol' = fromIntegral (fromEnum presol)
mipObjVal :: GLPK Double
mipObjVal = liftM realToFrac $ GLP glpMIPObjVal
mipRowVal :: Int -> GLPK Double
mipRowVal i = liftM realToFrac $ GLP (`glpMIPRowVal` fromIntegral i)
mipColVal :: Int -> GLPK Double
mipColVal i = liftM realToFrac $ GLP (`glpMIPColVal` fromIntegral i)
setRowName :: Int -> String -> GLPK ()
setRowName i nam = GLP $ withCString nam . flip glpSetRowName (fromIntegral i)
writeProblem :: (Ord v, Real c) => LP v c -> GLPK (Map v Int)
writeProblem LP{..} = do
setObjectiveDirection direction
i0 <- addCols nVars
let allVars' = fmap (i0 +) allVars
sequence_ [setObjCoef i v | (i, v) <- elems $ intersectionWith (,) allVars' objective]
j0 <- addRows (length constraints)
sequence_ [do maybe (return ()) (setRowName j) lab
setMatRow j
[(i, v) | (i, v) <- elems (intersectionWith (,) allVars' f)]
setRowBounds j bnds
| (j, Constr lab f bnds) <- zip [j0..] constraints]
sequence_ [setColBounds i bnds |
(i, bnds) <- elems $ intersectionWith (,) allVars' varBounds]
sequence_ [setColBounds i Free | i <- elems $ difference allVars' varBounds]
sequence_ [setColKind i knd |
(i, knd) <- elems $ intersectionWith (,) allVars' varTypes]
return allVars'
where allVars0 = fmap (const ()) objective `union`
unions [fmap (const ()) f | Constr _ f _ <- constraints] `union`
fmap (const ()) varBounds `union` fmap (const ()) varTypes
(nVars, allVars) = mapAccum (\ n _ -> (n+1, n)) (0 :: Int) allVars0