toysolver-0.6.0: test/Test/FiniteModelFinder.hs
{-# LANGUAGE TemplateHaskell, ScopedTypeVariables, OverloadedStrings #-}
module Test.FiniteModelFinder (fmfTestGroup) where
import Control.Monad
import Control.Monad.State
import Control.Monad.Trans
import Data.Interned (intern, unintern)
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Monoid
import Data.String
import Test.Tasty
import Test.Tasty.QuickCheck hiding ((.&&.), (.||.))
import Test.Tasty.HUnit
import Test.Tasty.TH
import qualified Test.QuickCheck.Monadic as QM
import qualified ToySolver.EUF.FiniteModelFinder as MF
type Sig = (Map MF.FSym Int, Map MF.PSym Int)
genTerm' :: Sig -> [MF.Var] -> StateT Int Gen MF.Term
genTerm' (fsyms, _) vs = m
where
m = do
budget <- get
f <- lift $ elements $ map Left vs ++ [Right (f, arity) | (f, arity) <- Map.toList fsyms, arity == 0 || budget >= arity+1]
modify (subtract 1)
case f of
Left v -> return (MF.TmVar v)
Right (f', arity) -> do
args <- replicateM arity m
return $ MF.TmApp f' args
genAtom' :: Sig -> [MF.Var] -> StateT Int Gen MF.Atom
genAtom' sig@(fsyms, psyms) vs = do
budget <- get
(p, arity) <- lift $ elements $ ("=",2) : [(p, arity) | (p, arity) <- Map.toList psyms, arity == 0 || budget >= arity+1]
modify (subtract 1)
args <- replicateM arity (genTerm' sig vs)
return $ MF.PApp p args
genLit' :: Sig -> [MF.Var] -> StateT Int Gen MF.Lit
genLit' sig vs = do
atom <- genAtom' sig vs
lift $ elements [MF.Pos atom, MF.Neg atom]
genClause' :: Sig -> [MF.Var] -> StateT Int Gen MF.Clause
genClause' sig vs = do
n <- lift $ choose (1,4)
replicateM n $ genLit' sig vs
genClause :: Sig -> [MF.Var] -> Gen MF.Clause
--genClause sig vs = sized (evalStateT (genClause' sig vs))
genClause sig vs = evalStateT (genClause' sig vs) 8
genSmallSig :: Gen Sig
genSmallSig = do
nFun <- choose (1::Int, 5)
nPred <- choose (0::Int, 3)
fsyms <- liftM Map.fromList $ forM [0..nFun-1] $ \i -> do
arity <- if i == 0 then return 0 else choose (0, 3)
return (intern ("f" <> fromString (show i)), arity)
psyms <- liftM Map.fromList $ forM [0..nPred-1] $ \i -> do
arity <- choose (0, 3)
return (intern ("p" <> fromString (show i)), arity)
return (fsyms, psyms)
prop_findModel_soundness = QM.monadicIO $ do
sig <- QM.pick genSmallSig
nv <- QM.pick $ choose (0::Int, 2)
let vs = [fromString ("v" ++ show i) | i <- [0..nv-1]]
nc <- QM.pick $ choose (0::Int, 3)
cs <- QM.pick $ replicateM nc $ genClause sig vs
size <- QM.pick $ choose (1::Int, 5)
ret <- QM.run $ MF.findModel size cs
case ret of
Nothing -> return ()
Just m -> QM.assert (MF.evalClausesU m cs)
case_example_1 = do
ret <- MF.findModel 2 cs
case ret of
Nothing -> assertFailure (show cs ++ " should be satisfiable")
Just m -> assertBool (show cs ++ " should be evaluated to true on " ++ unlines (MF.showModel m)) (MF.evalClausesU m cs)
where
cs = [[f b .=. c], [f c .=. a], [g a .=. h a a], [g b ./=. h c b]]
(.=.) x y = MF.Pos $ MF.PApp "=" [x, y]
(./=.) x y = MF.Neg $ MF.PApp "=" [x, y]
a = MF.TmApp "a" []
b = MF.TmApp "b" []
c = MF.TmApp "c" []
f x = MF.TmApp "f" [x]
g x = MF.TmApp "g" [x]
h x y = MF.TmApp "h" [x, y]
case_example_2 = do
ret <- MF.findModel 5 cs
case ret of
Nothing -> return ()
Just _ -> assertFailure (show cs ++ " should be unsatisfiable")
where
cs = [[f b .=. c], [f c .=. a], [g a .=. h a a], [g b ./=. h c b], [b .=. c]]
(.=.) x y = MF.Pos $ MF.PApp "=" [x, y]
(./=.) x y = MF.Neg $ MF.PApp "=" [x, y]
a = MF.TmApp "a" []
b = MF.TmApp "b" []
c = MF.TmApp "c" []
f x = MF.TmApp "f" [x]
g x = MF.TmApp "g" [x]
h x y = MF.TmApp "h" [x, y]
-- ---------------------------------------------------------------------
-- Test harness
fmfTestGroup :: TestTree
fmfTestGroup = $(testGroupGenerator)