module SAT.MiniSat.Formula where
import SAT.MiniSat.Variable
import SAT.MiniSat.Literals
import Control.Monad.Trans.State
import Control.Monad
import qualified Data.Map as Map
import Data.Map (Map)
import Data.Maybe
import Prelude hiding (all)
data Formula v =
Var v
| Yes
| No
| Not (Formula v)
| Formula v :&&: Formula v
| Formula v :||: Formula v
| Formula v :++: Formula v
| Formula v :->: Formula v
| Formula v :<->: Formula v
| All [Formula v]
| Some [Formula v]
| None [Formula v]
| ExactlyOne [Formula v]
| AtMostOne [Formula v]
| Let (Formula v) (Formula v -> Formula v)
| Bound Integer
infixr 6 :&&:
infixr 5 :||:
infixr 4 :++:
infixr 2 :->:
infix 1 :<->:
instance (Eq v) => Eq (Formula v) where
a == b = exformula 0 a == exformula 0 b
instance (Ord v) => Ord (Formula v) where
compare a b = compare (exformula 0 a) (exformula 0 b)
instance (Show v) => Show (Formula v) where
showsPrec d a = showsPrec d (exformula 0 a)
data ExFormula v =
ExVar v
| ExTrue
| ExFalse
| ExNot (ExFormula v)
| ExAnd (ExFormula v) (ExFormula v)
| ExOr (ExFormula v) (ExFormula v)
| ExXOr (ExFormula v) (ExFormula v)
| ExImp (ExFormula v) (ExFormula v)
| ExIff (ExFormula v) (ExFormula v)
| ExAll [ExFormula v]
| ExSome [ExFormula v]
| ExNone [ExFormula v]
| ExExactlyOne [ExFormula v]
| ExAtMostOne [ExFormula v]
| ExLet Integer (ExFormula v) (ExFormula v)
| ExBound Integer
deriving (Eq, Ord)
exformula :: Integer -> Formula v -> ExFormula v
exformula i (Var v) = ExVar v
exformula i Yes = ExTrue
exformula i No = ExFalse
exformula i (Not a) = ExNot (exformula i a)
exformula i (a :&&: b) = ExAnd (exformula i a) (exformula i b)
exformula i (a :||: b) = ExOr (exformula i a) (exformula i b)
exformula i (a :++: b) = ExXOr (exformula i a) (exformula i b)
exformula i (a :->: b) = ExImp (exformula i a) (exformula i b)
exformula i (a :<->: b) = ExIff (exformula i a) (exformula i b)
exformula i (All vs) = ExAll [exformula i v | v <- vs]
exformula i (Some vs) = ExSome [exformula i v | v <- vs]
exformula i (None vs) = ExNone [exformula i v | v <- vs]
exformula i (ExactlyOne vs) = ExExactlyOne [exformula i v | v <- vs]
exformula i (AtMostOne vs) = ExAtMostOne [exformula i v | v <- vs]
exformula i (Let a f) = ExLet i (exformula i a) (exformula (i+1) (f (Bound i)))
exformula i (Bound x)
| 0 <= x && x < i = ExBound x
| otherwise = error "MiniSat.Formula: 'Bound' is for internal use only"
variables :: [String]
variables = vs ++ [ v ++ show n | n <- [1..], v <- vs ]
where
vs = ["x", "y", "z", "u", "v", "w", "r", "s", "t"]
showBound :: Integer -> ShowS
showBound i = showString (variables !! (fromInteger i))
instance (Show v) => Show (ExFormula v) where
showsPrec d (ExVar v) = showParen (d > 10) $
showString "Var " . showsPrec d v
showsPrec d ExTrue = showString "Yes"
showsPrec d ExFalse = showString "No"
showsPrec d (ExNot a) = showParen (d > 10) $
showString "Not " . showsPrec 11 a
showsPrec d (ExAnd a b) = showParen (d > 6 || d == 4 || d == 5) $
showsPrec 6 a . showString " :&&: " . showsPrec 6 b
showsPrec d (ExOr a b) = showParen (d > 5 || d == 4) $
showsPrec 5 a . showString " :||: " . showsPrec 5 b
showsPrec d (ExXOr a b) = showParen (d > 4) $
showsPrec 4 a . showString " :++: " . showsPrec 4 b
showsPrec d (ExImp a b) = showParen (d > 2) $
showsPrec 3 a . showString " :->: " . showsPrec 2 b
showsPrec d (ExIff a b) = showParen (d > 1) $
showsPrec 3 a . showString " :<->: " . showsPrec 3 b
showsPrec d (ExAll as) = showParen (d > 10) $
showString "All " . showsPrec 11 as
showsPrec d (ExSome as) = showParen (d > 10) $
showString "Some " . showsPrec 11 as
showsPrec d (ExNone as) = showParen (d > 10) $
showString "None " . showsPrec 11 as
showsPrec d (ExExactlyOne as) = showParen (d > 10) $
showString "ExactlyOne " . showsPrec 11 as
showsPrec d (ExAtMostOne as) = showParen (d > 10) $
showString "AtMostOne " . showsPrec 11 as
showsPrec d (ExLet i a b) = showParen (d > 10) $
showString "Let " . showsPrec 11 a . showString " (\\"
. showBound i . showString " -> " . showsPrec 0 b . showString ")"
showsPrec d (ExBound i) = showBound i
let_list :: [Formula v] -> ([Formula v] -> Formula v) -> Formula v
let_list [] f = f []
let_list (a:as) f = Let a (\x -> let_list as (\xs -> f (x:xs)))
all :: [Formula v] -> Formula v
all vs = foldl (:&&:) Yes vs
some :: [Formula v] -> Formula v
some vs = foldl (:||:) No vs
none :: [Formula v] -> Formula v
none vs = Not (some vs)
atMostOne :: [Formula v] -> Formula v
atMostOne [] = Yes
atMostOne [a] = Yes
atMostOne (a1:a2:as) =
Let a1 (\x1 ->
Let a2 (\x2 ->
Not (x1 :&&: x2) :&&: atMostOne ((x1 :||: x2) : as)))
exactlyOne :: [Formula v] -> Formula v
exactlyOne as = let_list as (\xs -> some xs :&&: atMostOne xs)
data RFormula v =
RFVar v
| RFBound Integer
| RFNot (RFormula v)
| RFOr (RFormula v) (RFormula v)
| RFXOr (RFormula v) (RFormula v)
| RFLet Integer (RFormula v) (RFormula v)
deriving (Show)
rformula :: Integer -> Formula v -> Either Bool (RFormula v)
rformula i (Var v) = Right (RFVar v)
rformula i Yes = Left True
rformula i No = Left False
rformula i (Not a) =
case rformula i a of
Left x -> Left (not x)
Right a'' -> Right (RFNot a'')
rformula i (a :||: b) =
case (rformula i a, rformula i b) of
(Left True, b'') -> Left True
(Left False, b'') -> b''
(a'', Left True) -> Left True
(a'', Left False) -> a''
(Right a'', Right b'') -> Right (RFOr a'' b'')
rformula i (a :++: b) =
case (rformula i a, rformula i b) of
(Left x, Left y) -> Left (x /= y)
(Left True, Right b'') -> Right (RFNot b'')
(Left False, Right b'') -> Right b''
(Right a'', Left True) -> Right (RFNot a'')
(Right a'', Left False) -> Right a''
(Right a'', Right b'') -> Right (RFXOr a'' b'')
rformula i (a :&&: b) = rformula i (Not (Not a :||: Not b))
rformula i (a :->: b) = rformula i (Not a :||: b)
rformula i (a :<->: b) = rformula i (Not a :++: b)
rformula i (All vs) = rformula i (all vs)
rformula i (Some vs) = rformula i (some vs)
rformula i (None vs) = rformula i (none vs)
rformula i (AtMostOne vs) = rformula i (atMostOne vs)
rformula i (ExactlyOne vs) = rformula i (exactlyOne vs)
rformula i (Let a f) =
case rformula i a of
Left True -> rformula i (f Yes)
Left False -> rformula i (f No)
Right a'' ->
case rformula (i+1) (f (Bound i)) of
Left x -> Left x
Right b'' -> Right (RFLet i a'' b'')
rformula i (Bound x) = Right (RFBound x)
data DMFormula v =
DMPos v
| DMNeg v
| DMPosBound Integer
| DMNegBound Integer
| DMAnd (DMFormula v) (DMFormula v)
| DMOr (DMFormula v) (DMFormula v)
| DMXOr (DMFormula v) (DMFormula v)
| DMLet Integer (DMFormula v) (DMFormula v)
demorgan :: RFormula v -> DMFormula v
demorgan (RFVar v) = DMPos v
demorgan (RFBound x) = DMPosBound x
demorgan (RFNot a) = demorgan_neg a
demorgan (RFOr a b) = DMOr (demorgan a) (demorgan b)
demorgan (RFXOr a b) = DMXOr (demorgan a) (demorgan b)
demorgan (RFLet x a b) = DMLet x (demorgan a) (demorgan b)
demorgan_neg :: RFormula v -> DMFormula v
demorgan_neg (RFVar v) = DMNeg v
demorgan_neg (RFBound x) = DMNegBound x
demorgan_neg (RFNot a) = demorgan a
demorgan_neg (RFOr a b) = DMAnd (demorgan_neg a) (demorgan_neg b)
demorgan_neg (RFXOr a b) = DMXOr (demorgan_neg a) (demorgan b)
demorgan_neg (RFLet x a b) = DMLet x (demorgan a) (demorgan_neg b)
type ELit v = Lit (Either Integer v)
data Trans v a = Trans (Integer -> (a, Integer, [[ELit v]]))
instance Monad (Trans v) where
return a = Trans (\n -> (a, n, []))
(Trans f) >>= g = Trans (\n ->
let (a1, n1, l1) = f n in
let Trans h = g a1 in
let (a2, n2, l2) = h n1 in
(a2, n2, l1 ++ l2))
instance Applicative (Trans v) where
pure = return
(<*>) = ap
instance Functor (Trans v) where
fmap = liftM
runTrans :: Trans v a -> (a, [[ELit v]])
runTrans (Trans f) = (a, l)
where
(a, _, l) = f 0
fresh_lit :: Trans v (ELit v)
fresh_lit = Trans (\n -> (Pos (Left n), n+1, []))
add_cnf :: [[ELit v]] -> Trans v ()
add_cnf cs = Trans (\n -> ((), n, cs))
type Env v = Map Integer (ELit v)
trans_cnf :: Env v -> DMFormula v -> Trans v [[ELit v]]
trans_cnf env (DMPos v) = return [[Pos (Right v)]]
trans_cnf env (DMNeg v) = return [[Neg (Right v)]]
trans_cnf env (DMPosBound n) = return [[x]]
where
x = env Map.! n
trans_cnf env (DMNegBound n) = return [[neg x]]
where
x = env Map.! n
trans_cnf env (DMAnd a b) = do
a' <- trans_cnf env a
b' <- trans_cnf env b
return (a' ++ b')
trans_cnf env (DMOr a b) = do
a' <- trans_or env a
b' <- trans_or env b
return [a' ++ b']
trans_cnf env (DMXOr a b) = do
x <- trans_lit env a
y <- trans_lit env b
return [[x, y], [neg x, neg y]]
trans_cnf env (DMLet n a b) = do
do_let env n a b trans_cnf
trans_or :: Env v -> DMFormula v -> Trans v [ELit v]
trans_or env (DMXOr a b) = do
x <- trans_lit env (DMXOr a b)
return [x]
trans_or env (DMLet n a b) = do
do_let env n a b trans_or
trans_or env a = do
c <- trans_cnf env a
or_of_cnf c
trans_lit :: Env v -> DMFormula v -> Trans v (ELit v)
trans_lit env (DMXOr a b) = do
x <- trans_lit env a
y <- trans_lit env b
z <- lit_of_xor x y
return z
trans_lit env (DMLet n a b) = do
do_let env n a b trans_lit
trans_lit env a = do
c <- trans_cnf env a
lit_of_cnf c
do_let :: Env v -> Integer -> DMFormula v -> DMFormula v -> (Env v -> DMFormula v -> Trans v a) -> Trans v a
do_let env n a b cont = do
x <- trans_lit env a
let env' = Map.insert n x env
cont env' b
or_of_cnf :: [[ELit v]] -> Trans v [ELit v]
or_of_cnf [d] = return d
or_of_cnf ds = do
x <- lit_of_cnf ds
return [x]
lit_of_cnf :: [[ELit v]] -> Trans v (ELit v)
lit_of_cnf ds = do
xs <- sequence (map lit_of_or ds)
y <- lit_of_and xs
return y
lit_of_and :: [ELit v] -> Trans v (ELit v)
lit_of_and [l] = return l
lit_of_and cs = do
x <- fresh_lit
add_cnf [[neg x, c] | c <- cs ]
add_cnf [[x] ++ [neg c | c <- cs]]
return x
lit_of_or :: [ELit v] -> Trans v (ELit v)
lit_of_or [l] = return l
lit_of_or ds = do
x <- fresh_lit
add_cnf [ [x, neg d] | d <- ds ]
add_cnf [[neg x] ++ [d | d <- ds]]
return x
lit_of_xor :: ELit v -> ELit v -> Trans v (ELit v)
lit_of_xor x y = do
z <- fresh_lit
add_cnf [[z, x, neg y]]
add_cnf [[z, neg x, y]]
add_cnf [[neg z, x, y]]
add_cnf [[neg z, neg x, neg y]]
return z
cnf_of_dm :: DMFormula v -> [[ELit v]]
cnf_of_dm f = l ++ a
where
env = Map.empty
(a, l) = runTrans (trans_cnf env f)
cnf_of_formula :: Formula v -> [[ELit v]]
cnf_of_formula f =
case rformula 0 f of
Left True -> []
Left False -> [[]]
Right rf -> cnf_of_dm (demorgan rf)
satisfiable :: (Ord v) => Formula v -> Bool
satisfiable a = isJust (solve a)
solve :: (Ord v) => Formula v -> Maybe (Map v Bool)
solve a = listToMaybe (solve_all a)
solve_all :: (Ord v) => Formula v -> [Map v Bool]
solve_all a = res
where
a' = cnf_of_formula a
res' = cnf_solve_all a'
res = map restrict res'
restrict ans = Map.fromList [ (v, b) | (Right v, b) <- Map.toList ans ]