toysolver-0.4.0: test/Test/CongruenceClosure.hs
{-# LANGUAGE TemplateHaskell #-}
{-# OPTIONS_GHC -Wall #-}
module Test.CongruenceClosure (ccTestGroup) where
import Control.Monad
import Control.Monad.State
import Data.Array
import Data.Graph
import qualified Data.Tree as Tree
import Data.IntSet (IntSet)
import qualified Data.IntSet as IntSet
import Data.Set (Set)
import qualified Data.Set as Set
import Test.Tasty
import Test.Tasty.QuickCheck
import Test.Tasty.HUnit
import Test.Tasty.TH
import qualified Test.QuickCheck.Monadic as QM
import ToySolver.EUF.CongruenceClosure
import qualified ToySolver.EUF.EUFSolver as EUF
------------------------------------------------------------------------
-- Test cases
case_Example_1 :: Assertion
case_Example_1 = do
solver <- newSolver
a <- newFSym solver
b <- newFSym solver
c <- newFSym solver
d <- newFSym solver
merge solver (TApp a []) (TApp c [])
ret <- areCongruent solver (TApp a [TApp b []]) (TApp c [TApp d []])
ret @?= False
merge solver (TApp b []) (TApp d [])
ret <- areCongruent solver (TApp a [TApp b []]) (TApp c [TApp d []])
ret @?= True
case_Example_1_FlatTerm :: Assertion
case_Example_1_FlatTerm = do
solver <- newSolver
a <- newFSym solver
b <- newFSym solver
c <- newFSym solver
d <- newFSym solver
mergeFlatTerm solver (FTConst a) c
ret <- areCongruentFlatTerm solver (FTApp a b) (FTApp c d)
ret @?= False
mergeFlatTerm solver (FTConst b) d
ret <- areCongruentFlatTerm solver (FTApp a b) (FTApp c d)
ret @?= True
case_Example_2 :: Assertion
case_Example_2 = do
solver <- newSolver
a <- newConst solver
b <- newConst solver
c <- newConst solver
f <- newFun solver
g <- newFun solver
h <- newFun solver
merge solver (f b) c
merge solver (f c) a
merge solver (g a) (h a a)
ret <- areCongruent solver (g b) (h c b)
ret @?= False
merge solver b c
ret <- areCongruent solver (g b) (h c b)
ret @?= True
case_NO2007_Example_11 :: Assertion
case_NO2007_Example_11 = do
solver <- newSolver
replicateM_ 15 $ newFSym solver
let xs = [(1,8),(7,2),(3,13),(7,1),(6,7),(6,7),(9,5),(9,3),(14,11),(10,4),(12,9),(4,11),(10,7)]
forM_ (zip [0..] xs) $ \(i,(a,b)) -> mergeFlatTerm' solver (FTConst a) b (Just i)
m <- explainConst solver 1 4
fmap (Set.fromList . map (xs!!) . IntSet.toList) m @?= Just (Set.fromList [(7,1), (10,4), (10,7)])
-- f(g,h)=d, c=d, f(g,d)=a, e=c, e=b, b=h
case_NO2007_Example_16 :: Assertion
case_NO2007_Example_16 = do
solver <- newSolver
a <- newFSym solver
b <- newFSym solver
c <- newFSym solver
d <- newFSym solver
e <- newFSym solver
g <- newFSym solver
h <- newFSym solver
mergeFlatTerm' solver (FTApp g h) d (Just 0)
mergeFlatTerm' solver (FTConst c) d (Just 1)
mergeFlatTerm' solver (FTApp g d) a (Just 2)
mergeFlatTerm' solver (FTConst e) c (Just 3)
mergeFlatTerm' solver (FTConst e) b (Just 4)
mergeFlatTerm' solver (FTConst b) h (Just 5)
m <- explainConst solver a b
m @?= Just (IntSet.fromList [1,3,4,5,0,2])
-- d = c = e = b = h
-- a = f(g,d) = f(g,h) = d = c = e = b
case_backtracking_1 :: Assertion
case_backtracking_1 = do
solver <- newSolver
a1 <- newFSym solver
a2 <- newFSym solver
b1 <- newFSym solver
b2 <- newFSym solver
mergeFlatTerm solver (FTConst a1) b1
pushBacktrackPoint solver
mergeFlatTerm solver (FTConst a2) b2
ret <- areCongruentFlatTerm solver (FTApp a1 a2) (FTApp b1 b2)
ret @?= True
popBacktrackPoint solver
ret <- areCongruentFlatTerm solver (FTConst a2) (FTConst b2)
ret @?= False
ret <- areCongruentFlatTerm solver (FTApp a1 a2) (FTApp b1 b2)
ret @?= False
pushBacktrackPoint solver
ret <- areCongruentFlatTerm solver (FTConst a2) (FTConst b2)
ret @?= False
ret <- areCongruentFlatTerm solver (FTApp a1 a2) (FTApp b1 b2)
ret @?= False
popBacktrackPoint solver
case_backtracking_preserve_definition :: Assertion
case_backtracking_preserve_definition = do
solver <- newSolver
a1 <- newFSym solver
a2 <- newFSym solver
b1 <- newFSym solver
b2 <- newFSym solver
pushBacktrackPoint solver
a <- flatTermToFSym solver (FTApp a1 a2)
b <- flatTermToFSym solver (FTApp b1 b2)
popBacktrackPoint solver
c <- newFSym solver
mergeFlatTerm solver (FTApp a1 a2) c
mergeFlatTerm solver (FTApp b1 b2) c
ret <- areCongruentFlatTerm solver (FTConst a) (FTConst b)
ret @?= True
prop_components :: Property
prop_components = QM.monadicIO $ do
nv <- QM.pick $ choose (1, 10)
ne <- QM.pick $ choose (1, 100)
edges <- QM.pick $ replicateM ne $ do
s <- choose (0,nv-1)
t <- choose (0,nv-1)
return (s,t)
let g = buildG (0,nv-1) edges
repr = array (0,nv-1) [(c, Tree.rootLabel comp) | comp <- components g, c <- Tree.flatten comp]
solver <- QM.run $ newSolver
QM.run $ do
replicateM_ nv $ newFSym solver
forM_ edges $ \(s,t) -> mergeFlatTerm solver (FTConst s) t
forM_ [0..(nv-1)] $ \c ->
forM_ [0..(nv-1)] $ \d -> do
b <- QM.run $ areCongruentFlatTerm solver (FTConst c) (FTConst d)
QM.assert $ b == (repr ! c == repr ! d)
case_fsymToTerm_termToSym :: Assertion
case_fsymToTerm_termToSym = do
solver <- newSolver
f <- liftM (\f x y -> TApp f [x,y]) $ newFSym solver
g <- liftM (\f x -> TApp f [x]) $ newFSym solver
a <- newConst solver
let t = f (g a) (g (g a))
c <- termToFSym solver t
t2 <- fsymToTerm solver c
t2 @?= t
case_getModel_test1 :: Assertion
case_getModel_test1 = do
solver <- newSolver
a <- newConst solver
b <- newConst solver
c <- newConst solver
d <- newConst solver
f <- newFun solver
g <- newFun solver
h <- newFun solver
merge solver (f b) c
merge solver (f c) a
merge solver (g a) (h a a)
m1 <- getModel solver
(eval m1 (f b) == eval m1 c) @?= True
(eval m1 (f c) == eval m1 a) @?= True
(eval m1 (g a) == eval m1 (h a a)) @?= True
(eval m1 (f b) == eval m1 (f c)) @?= False
merge solver b c
m2 <- getModel solver
(eval m2 (f b) == eval m2 c) @?= True
(eval m2 (f c) == eval m2 a) @?= True
(eval m2 (g a) == eval m2 (h a a)) @?= True
(eval m2 (f b) == eval m2 (f c)) @?= True
(eval m2 (g b) == eval m2 (g c)) @?= True
case_EUF_getModel_test1 :: Assertion
case_EUF_getModel_test1 = do
solver <- EUF.newSolver
a <- EUF.newConst solver -- 0
b <- EUF.newConst solver -- 1
c <- EUF.newConst solver -- 2
f <- EUF.newFun solver -- 3
g <- EUF.newFun solver -- 4
h <- EUF.newFun solver -- 5
EUF.assertEqual solver (f b) c
EUF.assertEqual solver (f c) a
EUF.assertEqual solver (g a) (h a a)
True <- EUF.check solver
m1 <- EUF.getModel solver
(eval m1 (g b) == eval m1 (h c b)) @?= True
EUF.assertNotEqual solver (g b) (h c b)
True <- EUF.check solver
m2 <- EUF.getModel solver
(eval m2 (g b) == eval m2 (h c b)) @?= False
prop_getModel_eval_1 :: Property
prop_getModel_eval_1 = QM.monadicIO $ do
solver <- QM.run newSolver
a <- QM.run $ newConst solver
b <- QM.run $ newConst solver
c <- QM.run $ newConst solver
f <- QM.run $ newFun solver
g <- QM.run $ newFun solver
h <- QM.run $ newFun solver
let genExpr :: Gen Term
genExpr = evalStateT genExpr' 10
genExpr' :: StateT Int Gen Term
genExpr' = do
budget <- get
modify (subtract 1)
join $ lift $ elements $ concat $
[ map return [a,b,c]
, [ liftM f genExpr' | budget >= 2 ]
, [ liftM g genExpr' | budget >= 2 ]
, [ liftM2 h genExpr' genExpr' | budget >= 3 ]
]
es <- QM.pick $ do
n <- choose (0, 20)
replicateM n $ do
lhs <- genExpr
rhs <- genExpr
return (lhs,rhs)
join $ QM.run $ do
forM_ es $ \(lhs,rhs) ->
merge solver lhs rhs
m <- getModel solver
return $
forM_ es $ \(lhs,rhs) -> do
QM.assert (eval m lhs == eval m rhs)
prop_getModel_eval_2 :: Property
prop_getModel_eval_2 = QM.monadicIO $ do
solver <- QM.run newSolver
a <- QM.run $ newConst solver
b <- QM.run $ newConst solver
c <- QM.run $ newConst solver
f <- QM.run $ newFun solver
g <- QM.run $ newFun solver
h <- QM.run $ newFun solver
let genExpr :: Gen Term
genExpr = evalStateT genExpr' 10
genExpr' :: StateT Int Gen Term
genExpr' = do
budget <- get
modify (subtract 1)
join $ lift $ elements $ concat $
[ map return [a,b,c]
, [ liftM f genExpr' | budget >= 2 ]
, [ liftM g genExpr' | budget >= 2 ]
, [ liftM2 h genExpr' genExpr' | budget >= 3 ]
]
es <- QM.pick $ do
n <- choose (0, 20)
replicateM n $ do
lhs <- genExpr
rhs <- genExpr
return (lhs,rhs)
(lhs,rhs) <- QM.pick $ do
lhs <- genExpr
rhs <- genExpr
return (lhs,rhs)
join $ QM.run $ do
forM_ es $ \(lhs,rhs) -> do
merge solver lhs rhs
b <- areCongruent solver lhs rhs
if b then do
m <- getModel solver
return $ QM.assert (eval m lhs == eval m rhs)
else
return $ return ()
------------------------------------------------------------------------
-- Test harness
ccTestGroup :: TestTree
ccTestGroup = $(testGroupGenerator)