MIP-0.2.0.0: test/Test/MIP.hs
{-# OPTIONS_GHC -Wall -fno-warn-orphans #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
module Test.MIP (mipTestGroup) where
import Algebra.PartialOrd
#if !MIN_VERSION_lattices(2,0,0)
import Algebra.Lattice
#endif
import Data.Maybe
import qualified Data.Map as Map
import System.FilePath ((</>))
import System.IO (utf8)
import System.IO.Temp
import Test.QuickCheck.Instances.Text ()
import Test.Tasty
import Test.Tasty.HUnit
import Test.Tasty.QuickCheck
import Test.Tasty.TH
import Numeric.Optimization.MIP (meetStatus)
import qualified Numeric.Optimization.MIP as MIP
import qualified Numeric.Optimization.MIP.Solution.CBC as CBCSol
import qualified Numeric.Optimization.MIP.Solution.CPLEX as CPLEXSol
import qualified Numeric.Optimization.MIP.Solution.GLPK as GLPKSol
import qualified Numeric.Optimization.MIP.Solution.Gurobi as GurobiSol
import qualified Numeric.Optimization.MIP.Solution.HiGHS as HiGHSSol
import qualified Numeric.Optimization.MIP.Solution.Printemps as PrintempsSol
import qualified Numeric.Optimization.MIP.Solution.SCIP as SCIPSol
case_var_show :: Assertion
case_var_show = show (MIP.Var "x") @?= show ("x" :: String)
prop_var_name :: Property
prop_var_name =
forAll arbitrary $ \x -> do
MIP.varName (MIP.Var x) === x
prop_var_compare :: Property
prop_var_compare =
forAll arbitrary $ \(x1, x2) -> do
compare (MIP.Var x1) (MIP.Var x2) === compare x1 x2
prop_status_refl :: Property
prop_status_refl = forAll arbitrary $ \(x :: MIP.Status) -> do
x `leq` x
prop_status_trans :: Property
prop_status_trans =
forAll arbitrary $ \(x :: MIP.Status) ->
forAll (upper x) $ \y ->
forAll (upper y) $ \z ->
x `leq` z
where
-- upper :: (PartialOrd a, Enum a, Bounded a) => a -> Gen a
upper a = elements [b | b <- [minBound .. maxBound], a `leq` b]
prop_status_meet_idempotency :: Property
prop_status_meet_idempotency =
forAll arbitrary $ \(x :: MIP.Status) ->
x `meetStatus` x == x
prop_status_meet_comm :: Property
prop_status_meet_comm =
forAll arbitrary $ \(x :: MIP.Status) y ->
x `meetStatus` y == y `meetStatus` x
prop_status_meet_assoc :: Property
prop_status_meet_assoc =
forAll arbitrary $ \(x :: MIP.Status) y z ->
(x `meetStatus` y) `meetStatus` z == x `meetStatus` (y `meetStatus` z)
prop_status_meet_leq :: Property
prop_status_meet_leq =
forAll arbitrary $ \(x :: MIP.Status) y ->
#if MIN_VERSION_lattices(2,0,0)
(x == (x `meetStatus` y)) == x `leq` y
#else
x `meetLeq` y == x `leq` y
#endif
instance Arbitrary MIP.Status where
arbitrary = arbitraryBoundedEnum
case_eval_expr :: Assertion
case_eval_expr = do
MIP.eval MIP.def sol (MIP.varExpr "x" + 2 * MIP.varExpr "y" :: MIP.Expr Rational) @?= 8
where
sol :: Map.Map MIP.Var Rational
sol = Map.fromList [("x", 2), ("y", 3)]
case_eval_constraint :: Assertion
case_eval_constraint = do
let constr1 = MIP.varExpr "x" MIP..<=. MIP.constExpr (0 :: Double)
MIP.eval MIP.def (Map.singleton "x" (-1 :: Double)) constr1 @?= True
MIP.eval MIP.def (Map.singleton "x" (0 :: Double)) constr1 @?= True
MIP.eval MIP.def (Map.singleton "x" (1e-10 :: Double)) constr1 @?= True
MIP.eval MIP.def (Map.singleton "x" (1 :: Double)) constr1 @?= False
let constr2 :: MIP.Constraint Double
constr2 = MIP.def{ MIP.constrExpr = MIP.varExpr "x", MIP.constrLB = MIP.NegInf, MIP.constrUB = MIP.PosInf }
MIP.eval MIP.def (Map.singleton "x" (0 :: Double)) constr2 @?= True
MIP.eval MIP.def (Map.singleton "x" (1 :: Double)) constr2 @?= True
let constr3 :: MIP.Constraint Double
constr3 = MIP.def{ MIP.constrIndicator = Just ("y", 0), MIP.constrExpr = MIP.varExpr "x", MIP.constrLB = MIP.Finite 0, MIP.constrUB = MIP.Finite 1 }
MIP.eval MIP.def (Map.fromList [("x", 0 :: Double), ("y", 0)]) constr3 @?= True
MIP.eval MIP.def (Map.fromList [("x", 2 :: Double), ("y", 0)]) constr3 @?= False
MIP.eval MIP.def (Map.fromList [("x", 2 :: Double), ("y", 1)]) constr3 @?= True
case_eval_sos_constraint :: Assertion
case_eval_sos_constraint = do
let constr1 :: MIP.SOSConstraint Double
constr1 =
MIP.SOSConstraint
{ MIP.sosLabel = Nothing
, MIP.sosType = MIP.S1
, MIP.sosBody = [("x1", 1), ("x2", 2), ("x3", 3)]
}
MIP.eval MIP.def (Map.fromList [("x1", 0 :: Double), ("x2", 0), ("x3", 0)]) constr1 @?= True
MIP.eval MIP.def (Map.fromList [("x1", 1 :: Double), ("x2", 0), ("x3", 0)]) constr1 @?= True
MIP.eval MIP.def (Map.fromList [("x1", 1 :: Double), ("x2", 1), ("x3", 0)]) constr1 @?= False
let constr2 = constr1{ MIP.sosType = MIP.S2 }
MIP.eval MIP.def (Map.fromList [("x1", 0 :: Double), ("x2", 0), ("x3", 0)]) constr2 @?= True
MIP.eval MIP.def (Map.fromList [("x1", 1 :: Double), ("x2", 0), ("x3", 0)]) constr2 @?= True
MIP.eval MIP.def (Map.fromList [("x1", 1 :: Double), ("x2", 1), ("x3", 0)]) constr2 @?= True
MIP.eval MIP.def (Map.fromList [("x1", 0 :: Double), ("x2", 1), ("x3", 1)]) constr2 @?= True
MIP.eval MIP.def (Map.fromList [("x1", 1 :: Double), ("x2", 0), ("x3", 1)]) constr2 @?= False
MIP.eval MIP.def (Map.fromList [("x1", 1 :: Double), ("x2", 1), ("x3", 1)]) constr2 @?= False
case_eval_continuous_variable :: Assertion
case_eval_continuous_variable = do
MIP.eval MIP.def (Map.singleton "x" (0 :: Double)) prob @?= Nothing
MIP.eval MIP.def (Map.singleton "x" (1 - 1e-10 :: Double)) prob @?= Just 0
MIP.eval MIP.def (Map.singleton "x" (1.5 :: Double)) prob @?= Just 0
MIP.eval MIP.def (Map.singleton "x" (2 + 1e-10 :: Double)) prob @?= Just 0
MIP.eval MIP.def (Map.singleton "x" (3 :: Double)) prob @?= Nothing
where
prob :: MIP.Problem Double
prob =
MIP.def
{ MIP.varDomains = Map.fromList [("x", (MIP.ContinuousVariable, (MIP.Finite 1, MIP.Finite 2)))]
}
case_eval_semi_continuous_variable :: Assertion
case_eval_semi_continuous_variable = do
MIP.eval MIP.def (Map.singleton "x" (0 - 1e-10 :: Double)) prob @?= Just 0
MIP.eval MIP.def (Map.singleton "x" (0 + 1e-10 :: Double)) prob @?= Just 0
MIP.eval MIP.def (Map.singleton "x" (0.5 :: Double)) prob @?= Nothing
MIP.eval MIP.def (Map.singleton "x" (1 - 1e-10 :: Double)) prob @?= Just 0
MIP.eval MIP.def (Map.singleton "x" (1.5 :: Double)) prob @?= Just 0
MIP.eval MIP.def (Map.singleton "x" (2 + 1e-10 :: Double)) prob @?= Just 0
MIP.eval MIP.def (Map.singleton "x" (3 :: Double)) prob @?= Nothing
where
prob :: MIP.Problem Double
prob =
MIP.def
{ MIP.varDomains = Map.fromList [("x", (MIP.SemiContinuousVariable, (MIP.Finite 1, MIP.Finite 2)))]
}
case_eval_integer_variable :: Assertion
case_eval_integer_variable = do
MIP.eval MIP.def (Map.singleton "x" (0 - 1e-10 :: Double)) prob @?= Nothing
MIP.eval MIP.def (Map.singleton "x" (0 + 1e-10 :: Double)) prob @?= Nothing
MIP.eval MIP.def (Map.singleton "x" (0.5 :: Double)) prob @?= Nothing
MIP.eval MIP.def (Map.singleton "x" (1 - 1e-10 :: Double)) prob @?= Just 0
MIP.eval MIP.def (Map.singleton "x" (1.5 :: Double)) prob @?= Nothing
MIP.eval MIP.def (Map.singleton "x" (2 + 1e-10 :: Double)) prob @?= Just 0
MIP.eval MIP.def (Map.singleton "x" (3 :: Double)) prob @?= Nothing
where
prob :: MIP.Problem Double
prob =
MIP.def
{ MIP.varDomains = Map.fromList [("x", (MIP.IntegerVariable, (MIP.Finite 1, MIP.Finite 2)))]
}
case_eval_semi_integer_variable :: Assertion
case_eval_semi_integer_variable = do
MIP.eval MIP.def (Map.singleton "x" (0 - 1e-10 :: Double)) prob @?= Just 0
MIP.eval MIP.def (Map.singleton "x" (0 + 1e-10 :: Double)) prob @?= Just 0
MIP.eval MIP.def (Map.singleton "x" (0.5 :: Double)) prob @?= Nothing
MIP.eval MIP.def (Map.singleton "x" (1 - 1e-10 :: Double)) prob @?= Just 0
MIP.eval MIP.def (Map.singleton "x" (1.5 :: Double)) prob @?= Nothing
MIP.eval MIP.def (Map.singleton "x" (2 + 1e-10 :: Double)) prob @?= Just 0
MIP.eval MIP.def (Map.singleton "x" (3 :: Double)) prob @?= Nothing
where
prob :: MIP.Problem Double
prob =
MIP.def
{ MIP.varDomains = Map.fromList [("x", (MIP.SemiIntegerVariable, (MIP.Finite 1, MIP.Finite 2)))]
}
case_file_io_lp :: Assertion
case_file_io_lp = do
let opt = MIP.def{ MIP.optFileEncoding = Just utf8 }
prob <- MIP.readFile opt "samples/lp/test.lp"
withSystemTempDirectory "MIP" $ \dir -> do
MIP.writeFile opt (dir </> "test.lp") prob
prob2 <- MIP.readFile opt (dir </> "test.lp")
prob2 @?= prob
#ifdef WITH_ZLIB
MIP.writeFile opt (dir </> "test.lp.gz") prob
prob3 <- MIP.readFile opt (dir </> "test.lp.gz")
prob3 @?= prob
#endif
case_file_io_mps :: Assertion
case_file_io_mps = do
let opt = MIP.def{ MIP.optFileEncoding = Just utf8 }
prob <- MIP.readFile opt "samples/lp/test.lp"
withSystemTempDirectory "MIP" $ \dir -> do
MIP.writeFile opt (dir </> "test.mps") prob
prob2 <- MIP.readFile opt (dir </> "test.mps")
prob2 @?= prob
#ifdef WITH_ZLIB
MIP.writeFile opt (dir </> "test.mps.gz") prob
prob3 <- MIP.readFile opt (dir </> "test.mps.gz")
prob3 @?= prob
#endif
case_CBCSol :: Assertion
case_CBCSol = do
sol <- CBCSol.readFile "samples/lp/test-solution-cbc.txt"
sol @?=
MIP.Solution
{ MIP.solStatus = MIP.StatusOptimal
, MIP.solObjectiveValue = Just (-122.5)
, MIP.solVariables = Map.fromList [("x1", 40), ("x2", 10.5), ("x3", 19.5), ("x4", 3)]
}
case_CBCSol_infeasible :: Assertion
case_CBCSol_infeasible = do
sol <- CBCSol.readFile "samples/lp/test-solution-cbc-infeasible.txt"
sol @?=
MIP.Solution
{ MIP.solStatus = MIP.StatusInfeasible
, MIP.solObjectiveValue = Just 0.00000000
, MIP.solVariables = Map.fromList [("x", 0.11111111), ("y", 0), ("z", 0.33333333)]
}
case_CBCSol_unbounded :: Assertion
case_CBCSol_unbounded = do
sol <- CBCSol.readFile "samples/lp/test-solution-cbc-unbounded.txt"
sol @?=
MIP.Solution
{ MIP.solStatus = MIP.StatusInfeasibleOrUnbounded
, MIP.solObjectiveValue = Just 0.00000000
, MIP.solVariables = Map.fromList [("x", 0), ("y", 0)]
}
case_CPLEXSol :: Assertion
case_CPLEXSol = do
sol <- CPLEXSol.readFile "samples/lp/test-solution-cplex.sol"
sol @?=
MIP.Solution
{ MIP.solStatus = MIP.StatusOptimal
, MIP.solObjectiveValue = Just 122.5
, MIP.solVariables = Map.fromList [("x1", 40), ("x2", 10.5), ("x3", 19.5), ("x4", 3)]
}
case_CPLEXSol_unbounded :: Assertion
case_CPLEXSol_unbounded = do
sol <- CPLEXSol.readFile "samples/lp/test-solution-cplex-unbounded.sol"
sol @?=
MIP.Solution
{ MIP.solStatus = MIP.StatusUnbounded
, MIP.solObjectiveValue = Just 3.0
, MIP.solVariables = Map.fromList [("x", 1.0), ("y", 2.0)]
}
case_GLPKSol :: Assertion
case_GLPKSol = do
sol <- GLPKSol.readFile "samples/lp/test-solution-glpk.sol"
sol @?=
MIP.Solution
{ MIP.solStatus = MIP.StatusOptimal
, MIP.solObjectiveValue = Just 122.5
, MIP.solVariables = Map.fromList [("x1", 40), ("x2", 10.5), ("x3", 19.5), ("x4", 3)]
}
case_GLPKSol_long_var :: Assertion
case_GLPKSol_long_var = do
sol <- GLPKSol.readFile "samples/lp/test-solution-glpk-long.sol"
sol @?=
MIP.Solution
{ MIP.solStatus = MIP.StatusOptimal
, MIP.solObjectiveValue = Just 122.5
, MIP.solVariables = Map.fromList [("x1AAAAAAAAAAAAAAAAAAAAAAAAAAAA", 40), ("x2", 10.5), ("x3", 19.5), ("x4", 3)]
}
case_GurobiSol :: Assertion
case_GurobiSol = do
sol <- GurobiSol.readFile "samples/lp/test-solution-gurobi.sol"
isJust (GurobiSol.solObjectiveValue sol) @?= True
GurobiSol.parse (GurobiSol.render sol) @?= sol
case_HiGHSSol :: Assertion
case_HiGHSSol = do
sol <- HiGHSSol.readFile "samples/lp/test-solution-highs.sol"
sol @?=
MIP.Solution
{ MIP.solStatus = MIP.StatusOptimal
, MIP.solObjectiveValue = Just 122.5
, MIP.solVariables = Map.fromList [("x1", 40), ("x2", 10.5), ("x3", 19.5), ("x4", 3)]
}
case_HiGHSSol_infeasible :: Assertion
case_HiGHSSol_infeasible = do
sol <- HiGHSSol.readFile "samples/lp/test-solution-highs-infeasible.sol"
sol @?=
MIP.Solution
{ MIP.solStatus = MIP.StatusInfeasible
, MIP.solObjectiveValue = Nothing
, MIP.solVariables = Map.empty
}
case_HiGHSSol_unbounded :: Assertion
case_HiGHSSol_unbounded = do
sol <- HiGHSSol.readFile "samples/lp/test-solution-highs-unbounded.sol"
sol @?=
MIP.Solution
{ MIP.solStatus = MIP.StatusInfeasibleOrUnbounded
, MIP.solObjectiveValue = Nothing
, MIP.solVariables = Map.empty
}
case_PrintempsSol :: Assertion
case_PrintempsSol = do
sol <- PrintempsSol.readFile "samples/lp/test-solution-printemps.json"
sol @?=
MIP.Solution
{ MIP.solStatus = MIP.StatusFeasible
, MIP.solObjectiveValue = Just (-1.070000e+02)
, MIP.solVariables = Map.fromList [("x1", 29), ("x2", 7), ("x3", 22), ("x4", 2)]
}
case_PrintempsSol_infeasible :: Assertion
case_PrintempsSol_infeasible = do
sol <- PrintempsSol.readFile "samples/lp/test-solution-printemps-infeasible.json"
sol @?=
MIP.Solution
{ MIP.solStatus = MIP.StatusUnknown
, MIP.solObjectiveValue = Just 0
, MIP.solVariables = Map.fromList [("x", 0), ("y", 0)]
}
case_SCIPSol :: Assertion
case_SCIPSol = do
sol <- SCIPSol.readFile "samples/lp/test-solution-scip.sol"
sol @?=
MIP.Solution
{ MIP.solStatus = MIP.StatusOptimal
, MIP.solObjectiveValue = Just 122.5
, MIP.solVariables = Map.fromList [("x1", 40), ("x2", 10.5), ("x3", 19.5), ("x4", 3)]
}
mipTestGroup :: TestTree
mipTestGroup = $(testGroupGenerator)