| module SAT.CDCL (cdcl, SATResult(..)) where | |
| import SAT.CNF | |
| import SAT.UnitProp | |
| import qualified Data.Map.Strict as Map | |
| import Data.List (nub) | |
| data SATResult | |
| = SAT (Map.Map Int Bool) | |
| | UNSAT [Clause] | |
| deriving (Show) | |
| data DecisionLevel = DecisionLevel | |
| { dlVar :: Int | |
| , dlVal :: Bool | |
| , dlLevel :: Int | |
| , dlImplied :: [(Int, Bool)] | |
| } deriving (Show) | |
| cdcl :: CNF -> SATResult | |
| cdcl (CNF clauses numVars) = go Map.empty clauses [] 0 | |
| where | |
| go asgn cls trail level = | |
| case unitPropagate asgn cls of | |
| Conflict -> | |
| if level == 0 then UNSAT cls | |
| else | |
| let learned = analyzeConflict trail cls | |
| newLevel = backtrackLevel trail learned | |
| asgn' = backtrack asgn trail newLevel | |
| trail' = take newLevel trail | |
| in go asgn' (learned : cls) trail' newLevel | |
| Propagated asgn' cls' -> | |
| if null cls' then SAT asgn' | |
| else | |
| let var = chooseVar asgn' cls' | |
| level' = level + 1 | |
| asgn'' = Map.insert var True asgn' | |
| dl = DecisionLevel var True level' [] | |
| in go asgn'' cls' (trail ++ [dl]) level' | |
| analyzeConflict :: [DecisionLevel] -> [Clause] -> Clause | |
| analyzeConflict trail _ = | |
| case trail of | |
| [] -> [] | |
| dls -> let lastDl = last dls | |
| in [Neg (dlVar lastDl)] | |
| backtrackLevel :: [DecisionLevel] -> Clause -> Int | |
| backtrackLevel trail _ = max 0 (length trail - 1) | |
| backtrack :: Map.Map Int Bool -> [DecisionLevel] -> Int -> Map.Map Int Bool | |
| backtrack asgn trail level = | |
| let toRemove = drop level trail | |
| vars = map dlVar toRemove | |
| in foldr Map.delete asgn vars | |
| chooseVar :: Map.Map Int Bool -> [Clause] -> Int | |
| chooseVar asgn clauses = | |
| let allVars = nub $ map litVar $ concat clauses | |
| unassigned = filter (\v -> not (Map.member v asgn)) allVars | |
| in case unassigned of | |
| (v:_) -> v | |
| [] -> 0 | |