2 {-# OPTIONS_GHC -fglasgow-exts #-}
7 import Control.Monad.State
8 import Control.Monad.Trans
9 import Control.Monad.Error
12 import Text.ParserCombinators.Parsec hiding (State)
15 import qualified Data.Set as Set
17 import qualified Data.Map as Map
25 type StateWithFail s e a = ErrorT e (State s) a
27 evalSWF :: StateWithFail s e a -> s -> Either e a
28 evalSWF swf s = evalState (runErrorT swf) s
30 runSWF :: StateWithFail s e a -> s -> (Either e a, s)
31 runSWF swf s = runState (runErrorT swf) s
33 instance Error () where
37 instance Error Clause where
42 data CNF = And [Clause] deriving (Show, Eq)
43 data Clause = Or [Literal] deriving (Show, Eq)
44 type Literal = (Bool, Variable)
45 type Decision = (Bool, Variable)
46 type WatchList = Map Literal [Clause]
47 data Env = Env (Map Variable Bool) [Variable]
50 class WatchListC w where
52 appendWatches :: Literal -> [Clause] -> w -> w
53 appendWatch :: Literal -> Clause -> w -> w
54 clearWatch :: Literal -> w -> w
55 lookupWatch :: Decision -> w -> [Clause]
56 updateWatch :: Literal -> Clause -> Clause -> w -> w
59 empty :: [Variable] -> e
60 bindVar :: Variable -> Bool -> e -> e
61 lookupVar :: Variable -> e -> Maybe Bool
62 decide :: e -> Maybe Variable
66 instance EnvC Env where
67 empty = Env (Map.empty)
68 bindVar v b (Env a ua) = Env (Map.insert v b a) (delete v ua)
69 lookupVar v (Env a _) = Map.lookup v a
70 decide (Env _ uas) = listToMaybe uas
72 litValue :: EnvC a => a -> Literal -> Maybe Bool
73 litValue e (bar, v) = (/= bar) `fmap` lookupVar v e
77 instance WatchListC WatchList where
78 initWL (And cs) = foldl' addReps Map.empty cs
80 addReps :: WatchList -> Clause -> WatchList
81 addReps wl c@(Or (l1:l2:lits)) = appendWatch l1 c (appendWatch l2 c wl)
82 appendWatches lit cs wl = Map.insertWith (flip (++)) lit cs wl
83 appendWatch lit c wl = Map.insertWith ((:) . head) lit (return c) wl
84 clearWatch lit wl = Map.delete lit wl
85 lookupWatch d wl = maybe [] id $ Map.lookup d wl
86 updateWatch lit oldc c wl = wl'
88 oldcs = maybe [] id (Map.lookup lit wl)
89 cs = c : (delete oldc oldcs)
90 wl' = Map.insert lit cs (Map.delete lit wl)
94 dpll :: CNF -> [Variable] -> Maybe Env
95 dpll cnf vars = either (const Nothing) Just $ evalSWF (dpll' (empty vars)) (initWL cnf)
97 dpll' :: Env -> StateWithFail WatchList () Env
100 maybe (return e) (\v -> msum [tryDecision e (True, v), tryDecision e (False, v)]) $ trace "decide" (decide e)
102 tryDecision :: Env -> Decision -> StateWithFail WatchList () Env
103 tryDecision e (b, v) = do
105 --let (ee, wl') = bcp wl e (b, v) in put wl' >> either (const $ throwError ()) dpll' ee
106 let (ee, wl') = runState (bcpM e v b) wl in put wl' >> either (const $ throwError ()) dpll' ee
110 bcpM :: (EnvC e, WatchListC w) => e -> Variable -> Bool -> State w (Either Clause e)
114 let cs = trace "prop" (lookupWatch (b, v) wl) in
115 case runSWF (mapM (checkClauseM e' (b, v)) cs) (clearWatch (b, v) wl) of
116 (Left c, wl') -> do put $ addOldWatches (b, v) c cs wl'
118 (Right ws, wl') -> do put wl'
119 uncurry (flip (>>)) ((return *** put) (foldl' checkImpl (Right e', wl') ws))
122 checkImpl (Left c, wl'') _ = (Left c, wl'')
123 checkImpl ee Nothing = ee
124 checkImpl (Right e'', wl'') (Just (b'', v'')) = runState (bcpM e'' v'' (not b'')) wl''
126 addOldWatches lit c cs wl' = appendWatches lit (drop 1 $ dropWhile (/= c) cs) wl'
128 -- Left clause ==> Conflict
129 -- Right Nothing ==> Continue -- not unit
130 -- Right (Just lit) ==> Continue -- unit
131 checkClauseM :: (EnvC e, WatchListC w)
132 => e -> Decision -> Clause -> StateWithFail w Clause (Maybe Literal)
133 checkClauseM e d c = do
135 let (w1, w2, lits) = case c of
136 Or (lit1:lit2:lits) | d == lit1 -> (d, lit2, lits)
137 | otherwise -> (d, lit1, lits)
138 isSat = fromMaybe False $ litValue e w2
139 findLit = find (\lit -> litValue e lit /= Just False) lits
140 computeUnit w = maybe (return (Just w))
141 (\b -> if b then return Nothing else (trace "conflict" (throwError c))) $ litValue e w
142 swapWatches lit awl = awl''
144 c' = (Or (lit:w2:w1:delete lit lits))
145 awl' = updateWatch w2 c c' awl
146 awl'' = appendWatch lit c' awl'
149 then put (appendWatch d c wl) >> return Nothing
151 Just wi -> put (swapWatches wi wl) >> return Nothing
152 Nothing -> put (appendWatch d c wl) >> computeUnit w2
157 bcp :: (EnvC e, WatchListC w) => w -> e -> Decision -> (Either e e, w)
160 True -> (Left e', wl')
161 False -> foldl' checkImpls (Right e', wl') impls
165 cs = lookupWatch d wl
166 (wl', cs') = mapAccumL checkClause (clearWatch d wl) cs
168 conflict = Left False `elem` cs'
169 impls = [(not bar, v) | Right (bar, v) <- cs']
170 checkImpls ((Left e''), wl'') _ = ((Left e''), wl'')
171 checkImpls ((Right e''), wl'') ri = bcp wl'' e'' ri
173 checkClause :: WatchListC w => w -> Clause -> (w, Either Bool Literal)
176 then (appendWatch d c wl, Left True)
178 Just wi -> (adjWatches wi wl, Left True)
179 Nothing -> (appendWatch d c wl, newUnit w2)
181 (Or (lit1:lit2:lits)) = c
182 (w1, w2) = if lit1 == d then (lit1, lit2) else (lit2, lit1)
184 isSat = (maybe False id $ litValue e' w2)
185 newLit = find (\lit -> litValue e' lit /= Just False) lits
186 newUnit w = maybe (Right w) Left $ litValue e' w
187 newClause w oldw = (Or ((w:w2:delete w lits) ++ [oldw]))
188 adjWatches newLit awl = bothFixed
190 c' = newClause newLit w1
191 w2Fixed = updateWatch w2 c c' awl
192 bothFixed = appendWatch newLit c' w2Fixed
196 main = interact checkSat
198 checkSat :: String -> String
200 case parse parseDIMACS "" s of
201 Left err -> "parse error at " ++ show err
202 Right (cnf, v) -> maybe "UNSAT\n" (const "SAT\n") $ dpll cnf [1..v]
210 parseDIMACS :: Parser (CNF, Int)
211 parseDIMACS = do parseComments
212 (v, c) <- parseHeader
213 cnf <- liftM And parseClauses
216 parseComments :: Parser [String]
217 parseComments = many $ char 'c' >> manyTill anyChar newline
219 parseHeader :: Parser (Int, Int)
220 parseHeader = do string "p cnf"
224 clauses <- many1 digit
227 return (read vars, read clauses)
229 parseClauses :: Parser [Clause]
230 parseClauses = sepEndBy1 parseLiteral space >>= return . map Or . split
233 split xs = let (c, (z:xs')) = break (== (False, 0)) xs in c : split xs'
235 parseLiteral :: Parser Literal
236 parseLiteral = many space >> choice [char '-' >> parseInt True, parseInt False]
238 parseInt polar = many1 digit >>= return . (,) polar . read