feat(preproc): add clause simplification pipeline
This commit is contained in:
1 parent
89a0105839
commit
fa34e71864
1 file changed
+120
@@ -0,0 +1,120 @@
|
||||
module Sift.Preprocess
|
||||
( PreResult(..)
|
||||
, unitPropagate
|
||||
, pureLiteralElim
|
||||
, subsume
|
||||
, selfSubsume
|
||||
, preprocess
|
||||
, reconstruct
|
||||
) where
|
||||
|
||||
import Sift.Types
|
||||
import Data.List (foldl')
|
||||
import Data.Set (Set)
|
||||
import qualified Data.Set as Set
|
||||
import qualified Data.Map.Strict as Map
|
||||
|
||||
data PreResult
|
||||
= Unsatisfiable
|
||||
| Simplified [Lit] CNF
|
||||
deriving (Eq, Show)
|
||||
|
||||
toSets :: CNF -> [Set Lit]
|
||||
toSets (CNF cs) = map Set.fromList cs
|
||||
|
||||
fromSets :: [Set Lit] -> CNF
|
||||
fromSets = CNF . map Set.toList
|
||||
|
||||
unitPropagate :: CNF -> Maybe ([Lit], CNF)
|
||||
unitPropagate cnf = go [] (toSets cnf)
|
||||
where
|
||||
go assigned clauses
|
||||
| any Set.null clauses = Nothing
|
||||
| otherwise =
|
||||
case [ Set.findMin c | c <- clauses, Set.size c == 1 ] of
|
||||
[] -> Just (reverse assigned, fromSets clauses)
|
||||
(l : _) ->
|
||||
let clauses' = propagateOne l clauses
|
||||
in go (l : assigned) clauses'
|
||||
|
||||
propagateOne :: Lit -> [Set Lit] -> [Set Lit]
|
||||
propagateOne l = foldr step []
|
||||
where
|
||||
step c acc
|
||||
| l `Set.member` c = acc
|
||||
| neg l `Set.member` c = Set.delete (neg l) c : acc
|
||||
| otherwise = c : acc
|
||||
|
||||
pureLiteralElim :: CNF -> ([Lit], CNF)
|
||||
pureLiteralElim cnf = go [] (toSets cnf)
|
||||
where
|
||||
go assigned clauses =
|
||||
case pureLiterals clauses of
|
||||
[] -> (reverse assigned, fromSets clauses)
|
||||
pures ->
|
||||
let clauses' = foldl' (flip removeSatisfied) clauses pures
|
||||
in go (reverse pures ++ assigned) clauses'
|
||||
|
||||
pureLiterals :: [Set Lit] -> [Lit]
|
||||
pureLiterals clauses =
|
||||
[ l
|
||||
| l <- Set.toList allLits
|
||||
, not (neg l `Set.member` allLits)
|
||||
]
|
||||
where
|
||||
allLits = Set.unions clauses
|
||||
|
||||
removeSatisfied :: Lit -> [Set Lit] -> [Set Lit]
|
||||
removeSatisfied l = filter (not . Set.member l)
|
||||
|
||||
subsume :: CNF -> CNF
|
||||
subsume cnf = fromSets (dropSubsumed (dedupe (toSets cnf)))
|
||||
|
||||
dedupe :: [Set Lit] -> [Set Lit]
|
||||
dedupe = Set.toList . Set.fromList
|
||||
|
||||
dropSubsumed :: [Set Lit] -> [Set Lit]
|
||||
dropSubsumed clauses =
|
||||
[ c | c <- clauses, not (any (subsumedBy c) clauses) ]
|
||||
where
|
||||
subsumedBy c d = d /= c && d `Set.isSubsetOf` c
|
||||
|
||||
selfSubsume :: CNF -> CNF
|
||||
selfSubsume cnf = fromSets (fixSelf (toSets cnf))
|
||||
|
||||
fixSelf :: [Set Lit] -> [Set Lit]
|
||||
fixSelf clauses =
|
||||
let stepped = map (strengthen clauses) clauses
|
||||
in if stepped == clauses then clauses else fixSelf stepped
|
||||
|
||||
strengthen :: [Set Lit] -> Set Lit -> Set Lit
|
||||
strengthen clauses c = foldl' tryLit c (Set.toList c)
|
||||
where
|
||||
tryLit acc l
|
||||
| any (resolvesAway l acc) clauses = Set.delete l acc
|
||||
| otherwise = acc
|
||||
resolvesAway l acc d =
|
||||
neg l `Set.member` d
|
||||
&& Set.delete (neg l) d `Set.isSubsetOf` Set.delete l acc
|
||||
|
||||
preprocess :: CNF -> PreResult
|
||||
preprocess cnf = loop [] cnf
|
||||
where
|
||||
loop fixed current =
|
||||
case unitPropagate current of
|
||||
Nothing -> Unsatisfiable
|
||||
Just (units, afterUnits) ->
|
||||
let (pures, afterPures) = pureLiteralElim afterUnits
|
||||
afterSub = subsume afterPures
|
||||
afterSelf = subsume (selfSubsume afterSub)
|
||||
fixed' = fixed ++ units ++ pures
|
||||
in if clauseSetEq afterSelf current && null units && null pures
|
||||
then Simplified fixed' afterSelf
|
||||
else loop fixed' afterSelf
|
||||
|
||||
clauseSetEq :: CNF -> CNF -> Bool
|
||||
clauseSetEq a b = Set.fromList (toSets a) == Set.fromList (toSets b)
|
||||
|
||||
reconstruct :: [Lit] -> Assignment -> Assignment
|
||||
reconstruct fixed asn =
|
||||
foldl' (\m l -> Map.insert (litVar l) (litSign l) m) asn fixed
|
||||
Reference in new issue
Block a user