module ToySolver.SAT.TseitinEncoder
(
Encoder
, newEncoder
, setUsePB
, encSolver
, Formula (..)
, evalFormula
, addFormula
, encodeConj
, encodeDisj
, getDefinitions
) where
import Control.Monad
import Data.IORef
import Data.Map (Map)
import qualified Data.Map as Map
import Data.IntSet (IntSet)
import qualified Data.IntSet as IntSet
import qualified ToySolver.SAT as SAT
import qualified ToySolver.SAT.Types as SAT
data Formula
= Lit SAT.Lit
| And [Formula]
| Or [Formula]
| Not Formula
| Imply Formula Formula
| Equiv Formula Formula
deriving (Show, Eq, Ord)
evalFormula :: SAT.IModel m => m -> Formula -> Bool
evalFormula m = e
where
e (Lit l) = SAT.evalLit m l
e (And fs) = and (map e fs)
e (Or fs) = or (map e fs)
e (Not f) = not (e f)
e (Imply f1 f2) = not (e f1) || e f2
e (Equiv f1 f2) = e f1 == e f2
data Encoder =
Encoder
{ encSolver :: SAT.Solver
, encUsePB :: IORef Bool
, encConjTable :: !(IORef (Map SAT.LitSet SAT.Lit))
}
newEncoder :: SAT.Solver -> IO Encoder
newEncoder solver = do
usePBRef <- newIORef False
table <- newIORef Map.empty
return $
Encoder
{ encSolver = solver
, encUsePB = usePBRef
, encConjTable = table
}
setUsePB :: Encoder -> Bool -> IO ()
setUsePB encoder usePB = writeIORef (encUsePB encoder) usePB
addFormula :: Encoder -> Formula -> IO ()
addFormula encoder formula = do
let solver = encSolver encoder
case formula of
And xs -> mapM_ (addFormula encoder) xs
Equiv a b -> do
lit1 <- encodeToLit encoder a
lit2 <- encodeToLit encoder b
SAT.addClause solver [SAT.litNot lit1, lit2]
SAT.addClause solver [SAT.litNot lit2, lit1]
Not (Not a) -> addFormula encoder a
Not (Or xs) -> addFormula encoder (And (map Not xs))
Not (Imply a b) -> addFormula encoder (And [a, Not b])
Not (Equiv a b) -> do
lit1 <- encodeToLit encoder a
lit2 <- encodeToLit encoder b
SAT.addClause solver [lit1, lit2]
SAT.addClause solver [SAT.litNot lit1, SAT.litNot lit2]
_ -> do
c <- encodeToClause encoder formula
SAT.addClause solver c
encodeToClause :: Encoder -> Formula -> IO SAT.Clause
encodeToClause encoder formula =
case formula of
And [x] -> encodeToClause encoder x
Or xs -> do
cs <- mapM (encodeToClause encoder) xs
return $ concat cs
Not (Not x) -> encodeToClause encoder x
Not (And xs) -> do
encodeToClause encoder (Or (map Not xs))
Imply a b -> do
encodeToClause encoder (Or [Not a, b])
_ -> do
l <- encodeToLit encoder formula
return [l]
encodeToLit :: Encoder -> Formula -> IO SAT.Lit
encodeToLit encoder formula = do
case formula of
Lit l -> return l
And xs -> encodeConj encoder =<< mapM (encodeToLit encoder) xs
Or xs -> encodeDisj encoder =<< mapM (encodeToLit encoder) xs
Not x -> liftM SAT.litNot $ encodeToLit encoder x
Imply x y -> do
encodeToLit encoder (Or [Not x, y])
Equiv x y -> do
lit1 <- encodeToLit encoder x
lit2 <- encodeToLit encoder y
encodeToLit encoder $
And [ Imply (Lit lit1) (Lit lit2)
, Imply (Lit lit2) (Lit lit1)
]
encodeConj :: Encoder -> [SAT.Lit] -> IO SAT.Lit
encodeConj _ [l] = return l
encodeConj encoder ls = do
let ls2 = IntSet.fromList ls
table <- readIORef (encConjTable encoder)
case Map.lookup ls2 table of
Just l -> return l
Nothing -> do
let sat = encSolver encoder
v <- SAT.newVar sat
addIsConjOf encoder v ls
return v
encodeDisj :: Encoder -> [SAT.Lit] -> IO SAT.Lit
encodeDisj _ [l] = return l
encodeDisj encoder ls = do
l <- encodeConj encoder [SAT.litNot li | li <- ls]
return $ SAT.litNot l
addIsConjOf :: Encoder -> SAT.Lit -> [SAT.Lit] -> IO ()
addIsConjOf encoder l ls = do
let solver = encSolver encoder
usePB <- readIORef (encUsePB encoder)
if usePB
then do
let n = length ls
SAT.addPBAtLeast solver (( fromIntegral n, l) : [(1,li) | li <- ls]) 0
else do
forM_ ls $ \li -> do
SAT.addClause solver [SAT.litNot l, li]
SAT.addClause solver (l : map SAT.litNot ls)
modifyIORef (encConjTable encoder) (Map.insert (IntSet.fromList ls) l)
getDefinitions :: Encoder -> IO [(SAT.Lit, Formula)]
getDefinitions encoder = do
t <- readIORef (encConjTable encoder)
return $ [(l, And [Lit l1 | l1 <- IntSet.toList ls]) | (ls, l) <- Map.toList t]