module Proper.CNF(
CNF, SatisfyingAssignment,
cnf, mergeCNFFormulas,
naiveSAT, naiveSATBool) where
import Control.Monad
import Data.Generics.Aliases
import Data.List as L
import Data.Map as M
import Data.Set as S
import Proper.Clause
import Proper.Utils
type SatisfyingAssignment l = Map l Bool
type CNF c = Set (Clause c)
cnf :: (Ord c) => [Clause c] -> CNF c
cnf clauses = S.fromList clauses
mergeCNFFormulas :: (Ord c) => [CNF c] -> CNF c
mergeCNFFormulas formulas = S.foldl S.union S.empty (S.fromList formulas)
literals :: (Ord c) => CNF c -> Set c
literals formula = S.map atom $ S.foldl S.union S.empty (S.map (S.map literal) formula)
naiveSATBool :: (Ord c) => CNF c -> Bool
naiveSATBool formula = case naiveSAT formula of
Just asg -> True
Nothing -> False
naiveSAT :: (Ord c) => CNF c -> Maybe (SatisfyingAssignment c)
naiveSAT formula = nSat formula allLits
where
allLits = literals formula
nSat :: (Ord c) => CNF c -> Set c -> Maybe (SatisfyingAssignment c)
nSat formula lits = case S.member S.empty formula of
True -> Nothing
False -> case S.size formula of
0 -> Just M.empty
_ -> case S.size unitClauses of
0 -> pickLitAndSplit formula lits
_ -> unitSimplify unitClause formula lits
where
unitClauses = S.filter (\c -> S.size c == 1) formula
unitClause = S.findMin unitClauses
pickLitAndSplit :: (Ord c) => CNF c -> Set c -> Maybe (SatisfyingAssignment c)
pickLitAndSplit formula lits = orElse trueAsg falseAsg
where
nextLit = S.findMin lits
nextLits = S.delete nextLit lits
trueRes = nSat (S.insert (clause [lit nextLit]) formula) nextLits
falseRes = nSat (S.insert (clause [negation $ lit nextLit]) formula) nextLits
trueAsg = liftM (M.insert nextLit True) trueRes
falseAsg = liftM (M.insert nextLit False) falseRes
unitSimplify :: (Ord c) =>
Clause c ->
CNF c ->
Set c ->
Maybe (SatisfyingAssignment c)
unitSimplify unitClause formula lits = satResWithUnitAsg
where
simplifiedFormula = removeUnitClause formula unitClause
litToSimplify = S.findMin unitClause
tVal = assignTruthVal litToSimplify
satRes = nSat simplifiedFormula lits
satResWithUnitAsg = liftM (M.insert (atom litToSimplify) tVal) satRes
removeUnitClause :: (Ord c) => CNF c -> Clause c -> CNF c
removeUnitClause formula c = remainingClauses
where
elemC = S.findMin c
removeNegC = S.map (S.delete (negation elemC)) formula
remainingClauses = S.filter (\s -> (not $ S.member elemC s)) removeNegC