{-# OPTIONS_GHC -Wall #-}
{-# OPTIONS_HADDOCK show-extensions #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}
module Numeric.Optimization.MIP.Solution.SCIP
( Solution (..)
, parse
, readFile
) where
import Prelude hiding (readFile, writeFile)
#if !MIN_VERSION_base(4,8,0)
import Control.Applicative
#endif
import Control.Monad.Except
import Data.Interned (intern)
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Scientific (Scientific)
import qualified Data.Text as T
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.IO as TLIO
import Numeric.Optimization.MIP (Solution)
import qualified Numeric.Optimization.MIP as MIP
parse :: TL.Text -> MIP.Solution Scientific
parse t =
case parse' $ TL.lines t of
Left e -> error e
Right x -> x
parse' :: [TL.Text] -> Either String (MIP.Solution Scientific)
parse' (t1:t2:ts) = do
status <-
case TL.stripPrefix "solution status:" t1 of
Nothing -> throwError "first line must start with \"solution status:\""
Just s -> return $ Map.findWithDefault MIP.StatusUnknown (TL.toStrict $ TL.strip s) statusTable
if t2 == "no solution available" then do
return $
MIP.Solution
{ MIP.solStatus = status
, MIP.solObjectiveValue = Nothing
, MIP.solVariables = Map.empty
}
else do
obj <-
case TL.stripPrefix "objective value:" t2 of
Nothing -> throwError "second line must start with \"objective value:\""
Just s -> return $ read $ TL.unpack $ TL.strip s
let f :: [(MIP.Var, Scientific)] -> TL.Text -> Either String [(MIP.Var, Scientific)]
f vs t =
case TL.words t of
(w1:w2:_) -> return $ (intern (TL.toStrict w1), read (TL.unpack w2)) : vs
[] -> return $ vs
_ -> throwError ("Numeric.Optimization.MIP.Solution.SCIP: invalid line " ++ show t)
vs <- foldM f [] ts
return $
MIP.Solution
{ MIP.solStatus = status
, MIP.solObjectiveValue = Just obj
, MIP.solVariables = Map.fromList vs
}
parse' _ = throwError "must have >=2 lines"
statusTable :: Map T.Text MIP.Status
statusTable = Map.fromList
[ ("user interrupt", MIP.StatusUnknown)
, ("node limit reached", MIP.StatusUnknown)
, ("total node limit reached", MIP.StatusUnknown)
, ("stall node limit reached", MIP.StatusUnknown)
, ("time limit reached", MIP.StatusUnknown)
, ("memory limit reached", MIP.StatusUnknown)
, ("gap limit reached", MIP.StatusUnknown)
, ("solution limit reached", MIP.StatusUnknown)
, ("solution improvement limit reached", MIP.StatusUnknown)
, ("optimal solution found", MIP.StatusOptimal)
, ("infeasible", MIP.StatusInfeasible)
, ("unbounded", MIP.StatusUnbounded)
, ("infeasible or unbounded", MIP.StatusInfeasibleOrUnbounded)
]
readFile :: FilePath -> IO (MIP.Solution Scientific)
readFile fname = parse <$> TLIO.readFile fname