feat(cdcl): add cdcl search with 1-uip analysis and restarts
This commit is contained in:
1 parent
95b52dec31
commit
b5d1f4e2f3
1 file changed
+393
@@ -0,0 +1,393 @@
|
|||||||
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
|
module Sift.Cdcl.Search
|
||||||
|
( solveInternal
|
||||||
|
, luby
|
||||||
|
, propagate
|
||||||
|
, analyze
|
||||||
|
, analyzeFinal
|
||||||
|
, pickBranchVar
|
||||||
|
, maybeReduce
|
||||||
|
, installLearnt
|
||||||
|
, extractModel
|
||||||
|
, collectStats
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Control.Monad (when, forM_, foldM, filterM)
|
||||||
|
import Control.Monad.ST (ST)
|
||||||
|
import Data.STRef
|
||||||
|
import Data.Array.ST
|
||||||
|
import qualified Data.IntSet as IntSet
|
||||||
|
import Sift.Cdcl.State
|
||||||
|
import Sift.Cdcl.Heap
|
||||||
|
import Sift.Stats
|
||||||
|
|
||||||
|
luby :: Int -> Int
|
||||||
|
luby x0 = descend size0 seq0 x0
|
||||||
|
where
|
||||||
|
(size0, seq0) = ascend 1 0
|
||||||
|
ascend :: Int -> Int -> (Int, Int)
|
||||||
|
ascend size sq
|
||||||
|
| size < x0 + 1 = ascend (2 * size + 1) (sq + 1)
|
||||||
|
| otherwise = (size, sq)
|
||||||
|
descend :: Int -> Int -> Int -> Int
|
||||||
|
descend size sq x
|
||||||
|
| size - 1 == x = 2 ^ sq
|
||||||
|
| otherwise =
|
||||||
|
let size' = (size - 1) `div` 2
|
||||||
|
in descend size' (sq - 1) (x `mod` size')
|
||||||
|
|
||||||
|
propagate :: Solver s -> ST s Cid
|
||||||
|
propagate s = loop
|
||||||
|
where
|
||||||
|
loop = do
|
||||||
|
qh <- readSTRef (qhead s)
|
||||||
|
n <- readSTRef (trailN s)
|
||||||
|
if qh >= n
|
||||||
|
then pure (-1)
|
||||||
|
else do
|
||||||
|
p <- readArray (trail s) qh
|
||||||
|
writeSTRef (qhead s) (qh + 1)
|
||||||
|
modifySTRef' (statProp s) (+ 1)
|
||||||
|
confl <- propagateLit s p
|
||||||
|
if confl >= 0 then pure confl else loop
|
||||||
|
|
||||||
|
propagateLit :: Solver s -> Int -> ST s Cid
|
||||||
|
propagateLit s p = do
|
||||||
|
ws <- readArray (watches s) p
|
||||||
|
writeArray (watches s) p []
|
||||||
|
go ws []
|
||||||
|
where
|
||||||
|
falseLit = litNeg p
|
||||||
|
go [] kept = do
|
||||||
|
writeArray (watches s) p kept
|
||||||
|
pure (-1)
|
||||||
|
go (w@(Watcher cid blocker) : rest) kept = do
|
||||||
|
bv <- litValueInt s blocker
|
||||||
|
if bv == 1
|
||||||
|
then go rest (w : kept)
|
||||||
|
else do
|
||||||
|
cl <- getClause s cid
|
||||||
|
dead <- readSTRef (clsDead cl)
|
||||||
|
if dead
|
||||||
|
then go rest kept
|
||||||
|
else do
|
||||||
|
let lits = clsLits cl
|
||||||
|
sz = clsSize cl
|
||||||
|
l0 <- readArray lits 0
|
||||||
|
when (l0 == falseLit) $ do
|
||||||
|
l1 <- readArray lits 1
|
||||||
|
writeArray lits 0 l1
|
||||||
|
writeArray lits 1 l0
|
||||||
|
first <- readArray lits 0
|
||||||
|
fv <- litValueInt s first
|
||||||
|
if fv == 1
|
||||||
|
then go rest (Watcher cid first : kept)
|
||||||
|
else do
|
||||||
|
mNew <- findNewWatch s lits sz 2
|
||||||
|
case mNew of
|
||||||
|
Just k -> do
|
||||||
|
lk <- readArray lits k
|
||||||
|
writeArray lits 1 lk
|
||||||
|
writeArray lits k falseLit
|
||||||
|
modifyWatch s (litNeg lk) (Watcher cid first)
|
||||||
|
go rest kept
|
||||||
|
Nothing ->
|
||||||
|
if fv == -1
|
||||||
|
then do
|
||||||
|
writeArray (watches s) p (reverse kept ++ (w : rest))
|
||||||
|
pure cid
|
||||||
|
else do
|
||||||
|
_ <- enqueue s first cid
|
||||||
|
go rest (Watcher cid first : kept)
|
||||||
|
|
||||||
|
findNewWatch :: Solver s -> STUArray s Int Int -> Int -> Int -> ST s (Maybe Int)
|
||||||
|
findNewWatch s lits sz i
|
||||||
|
| i >= sz = pure Nothing
|
||||||
|
| otherwise = do
|
||||||
|
l <- readArray lits i
|
||||||
|
lv <- litValueInt s l
|
||||||
|
if lv /= -1 then pure (Just i) else findNewWatch s lits sz (i + 1)
|
||||||
|
|
||||||
|
analyze :: Solver s -> Cid -> ST s ([Int], Int, Int)
|
||||||
|
analyze s conflict0 = do
|
||||||
|
dl <- currentLevel s
|
||||||
|
othersRef <- newSTRef []
|
||||||
|
counterRef <- newSTRef (0 :: Int)
|
||||||
|
pathRef <- newSTRef conflict0
|
||||||
|
pivotRef <- newSTRef (-1 :: Int)
|
||||||
|
idxRef <- newSTRef =<< (subtract 1 <$> readSTRef (trailN s))
|
||||||
|
let step = do
|
||||||
|
cid <- readSTRef pathRef
|
||||||
|
cl <- getClause s cid
|
||||||
|
when (clsLearnt cl) $ claBumpActivity s cl
|
||||||
|
let lits = clsLits cl
|
||||||
|
sz = clsSize cl
|
||||||
|
pivot <- readSTRef pivotRef
|
||||||
|
forM_ [0 .. sz - 1] $ \j -> do
|
||||||
|
q <- readArray lits j
|
||||||
|
when (q /= pivot) $ do
|
||||||
|
let v = litVar q
|
||||||
|
sv <- readArray (seen s) v
|
||||||
|
lev <- readArray (varLevel s) v
|
||||||
|
when (not sv && lev > 0) $ do
|
||||||
|
varBumpActivity s v
|
||||||
|
writeArray (seen s) v True
|
||||||
|
if lev >= dl
|
||||||
|
then modifySTRef' counterRef (+ 1)
|
||||||
|
else modifySTRef' othersRef (q :)
|
||||||
|
let selectNext = do
|
||||||
|
i <- readSTRef idxRef
|
||||||
|
l <- readArray (trail s) i
|
||||||
|
sv <- readArray (seen s) (litVar l)
|
||||||
|
if sv then pure (i, l) else do writeSTRef idxRef (i - 1); selectNext
|
||||||
|
(i, l) <- selectNext
|
||||||
|
writeSTRef idxRef (i - 1)
|
||||||
|
let v = litVar l
|
||||||
|
writeArray (seen s) v False
|
||||||
|
writeSTRef pivotRef l
|
||||||
|
writeSTRef pathRef =<< readArray (varReason s) v
|
||||||
|
modifySTRef' counterRef (subtract 1)
|
||||||
|
c <- readSTRef counterRef
|
||||||
|
when (c > 0) step
|
||||||
|
step
|
||||||
|
pivot <- readSTRef pivotRef
|
||||||
|
let asserting = litNeg pivot
|
||||||
|
othersRaw <- readSTRef othersRef
|
||||||
|
minimized <- minimize s othersRaw
|
||||||
|
let learnt = orderLearnt asserting minimized
|
||||||
|
btLevel <- computeBtLevel s minimized
|
||||||
|
lbd <- computeLbd s learnt
|
||||||
|
forM_ (litVar asserting : map litVar othersRaw) $ \v -> writeArray (seen s) v False
|
||||||
|
pure (learnt, btLevel, lbd)
|
||||||
|
|
||||||
|
installLearnt :: Solver s -> [Int] -> Int -> ST s ()
|
||||||
|
installLearnt s learnt lbd = do
|
||||||
|
case learnt of
|
||||||
|
[u] -> do
|
||||||
|
_ <- enqueue s u (-1)
|
||||||
|
pure ()
|
||||||
|
_ -> do
|
||||||
|
cid <- newClause s learnt True
|
||||||
|
cl <- getClause s cid
|
||||||
|
writeSTRef (clsLbd cl) lbd
|
||||||
|
claBumpActivity s cl
|
||||||
|
attachClause s cid
|
||||||
|
l0 <- readArray (clsLits cl) 0
|
||||||
|
_ <- enqueue s l0 cid
|
||||||
|
pure ()
|
||||||
|
modifySTRef' (statLearn s) (+ 1)
|
||||||
|
modifySTRef' (varInc s) (/ varDecayConst)
|
||||||
|
modifySTRef' (claInc s) (/ claDecayConst)
|
||||||
|
|
||||||
|
analyzeFinal :: Solver s -> Int -> ST s [Int]
|
||||||
|
analyzeFinal s p = do
|
||||||
|
dl <- currentLevel s
|
||||||
|
if dl == 0
|
||||||
|
then pure [p]
|
||||||
|
else do
|
||||||
|
writeArray (seen s) (litVar p) True
|
||||||
|
lim <- readSTRef (trailLim s)
|
||||||
|
n <- readSTRef (trailN s)
|
||||||
|
let bottom = last lim
|
||||||
|
outRef <- newSTRef [p]
|
||||||
|
forM_ [n - 1, n - 2 .. bottom] $ \i -> do
|
||||||
|
l <- readArray (trail s) i
|
||||||
|
let x = litVar l
|
||||||
|
sv <- readArray (seen s) x
|
||||||
|
when sv $ do
|
||||||
|
r <- readArray (varReason s) x
|
||||||
|
if r < 0
|
||||||
|
then modifySTRef' outRef (litNeg l :)
|
||||||
|
else do
|
||||||
|
cl <- getClause s r
|
||||||
|
let lits = clsLits cl
|
||||||
|
sz = clsSize cl
|
||||||
|
forM_ [1 .. sz - 1] $ \j -> do
|
||||||
|
q <- readArray lits j
|
||||||
|
lev <- readArray (varLevel s) (litVar q)
|
||||||
|
when (lev > 0) $ writeArray (seen s) (litVar q) True
|
||||||
|
writeArray (seen s) x False
|
||||||
|
writeArray (seen s) (litVar p) False
|
||||||
|
readSTRef outRef
|
||||||
|
|
||||||
|
minimize :: Solver s -> [Int] -> ST s [Int]
|
||||||
|
minimize s others = filterM (fmap not . redundant s) others
|
||||||
|
|
||||||
|
redundant :: Solver s -> Int -> ST s Bool
|
||||||
|
redundant s l = do
|
||||||
|
r <- readArray (varReason s) (litVar l)
|
||||||
|
if r < 0
|
||||||
|
then pure False
|
||||||
|
else do
|
||||||
|
cl <- getClause s r
|
||||||
|
let lits = clsLits cl
|
||||||
|
sz = clsSize cl
|
||||||
|
let chk j
|
||||||
|
| j >= sz = pure True
|
||||||
|
| otherwise = do
|
||||||
|
q <- readArray lits j
|
||||||
|
let v = litVar q
|
||||||
|
sv <- readArray (seen s) v
|
||||||
|
lev <- readArray (varLevel s) v
|
||||||
|
if sv || lev == 0 then chk (j + 1) else pure False
|
||||||
|
chk 1
|
||||||
|
|
||||||
|
orderLearnt :: Int -> [Int] -> [Int]
|
||||||
|
orderLearnt asserting [] = [asserting]
|
||||||
|
orderLearnt asserting others = asserting : others
|
||||||
|
|
||||||
|
computeBtLevel :: Solver s -> [Int] -> ST s Int
|
||||||
|
computeBtLevel s others =
|
||||||
|
foldM (\acc l -> do
|
||||||
|
lev <- readArray (varLevel s) (litVar l)
|
||||||
|
pure (max acc lev)) 0 others
|
||||||
|
|
||||||
|
computeLbd :: Solver s -> [Int] -> ST s Int
|
||||||
|
computeLbd s learnt = do
|
||||||
|
levels <- foldM (\acc l -> do
|
||||||
|
lev <- readArray (varLevel s) (litVar l)
|
||||||
|
pure (IntSet.insert lev acc)) IntSet.empty learnt
|
||||||
|
pure (IntSet.size levels)
|
||||||
|
|
||||||
|
pickBranchVar :: Solver s -> ST s Int
|
||||||
|
pickBranchVar s = go
|
||||||
|
where
|
||||||
|
go = do
|
||||||
|
empty <- heapIsEmpty (heapH s)
|
||||||
|
if empty
|
||||||
|
then pure (-1)
|
||||||
|
else do
|
||||||
|
v <- heapRemoveMax (heapH s)
|
||||||
|
val <- varValue s v
|
||||||
|
if val == 0 then pure v else go
|
||||||
|
|
||||||
|
search :: Solver s -> Int -> ST s (Maybe Bool)
|
||||||
|
search s confBudget = loop 0
|
||||||
|
where
|
||||||
|
loop confSoFar = do
|
||||||
|
confl <- propagate s
|
||||||
|
if confl >= 0
|
||||||
|
then do
|
||||||
|
modifySTRef' (statConf s) (+ 1)
|
||||||
|
lvl <- currentLevel s
|
||||||
|
if lvl == 0
|
||||||
|
then do
|
||||||
|
writeSTRef (okFlag s) False
|
||||||
|
pure (Just False)
|
||||||
|
else do
|
||||||
|
(learnt, btLevel, lbd) <- analyze s confl
|
||||||
|
cancelUntil s btLevel
|
||||||
|
installLearnt s learnt lbd
|
||||||
|
loop (confSoFar + 1)
|
||||||
|
else do
|
||||||
|
if confSoFar >= confBudget
|
||||||
|
then do
|
||||||
|
cancelUntil s 0
|
||||||
|
pure Nothing
|
||||||
|
else do
|
||||||
|
maybeReduce s
|
||||||
|
v <- pickBranchVar s
|
||||||
|
if v < 0
|
||||||
|
then pure (Just True)
|
||||||
|
else do
|
||||||
|
modifySTRef' (statDec s) (+ 1)
|
||||||
|
pol <- readArray (polarity s) v
|
||||||
|
let l = if pol then posLit v else negLit v
|
||||||
|
newDecisionLevel s
|
||||||
|
_ <- enqueue s l (-1)
|
||||||
|
loop confSoFar
|
||||||
|
|
||||||
|
maybeReduce :: Solver s -> ST s ()
|
||||||
|
maybeReduce s = do
|
||||||
|
nl <- readSTRef (nLearnts s)
|
||||||
|
when (nl >= 2000) $ reduceDB s
|
||||||
|
|
||||||
|
reduceDB :: Solver s -> ST s ()
|
||||||
|
reduceDB s = do
|
||||||
|
cids <- readSTRef (learnts s)
|
||||||
|
alive <- filterM (\cid -> do cl <- getClause s cid; not <$> readSTRef (clsDead cl)) cids
|
||||||
|
scored <- mapM (\cid -> do
|
||||||
|
cl <- getClause s cid
|
||||||
|
a <- readSTRef (clsAct cl)
|
||||||
|
lbd <- readSTRef (clsLbd cl)
|
||||||
|
locked <- isLocked s cid cl
|
||||||
|
pure (cid, cl, a, lbd, locked)) alive
|
||||||
|
let removable = [ t | t@(_, _, _, lbd, locked) <- scored, not locked, lbd > 2 ]
|
||||||
|
keepFixed = [ (cid, cl) | (cid, cl, _, lbd, locked) <- scored, locked || lbd <= 2 ]
|
||||||
|
sortedByAct = sortByActAsc removable
|
||||||
|
half = length sortedByAct `div` 2
|
||||||
|
(toRemove, toKeepRest) = splitAt half sortedByAct
|
||||||
|
forM_ toRemove $ \(_, cl, _, _, _) -> writeSTRef (clsDead cl) True
|
||||||
|
let survivors = map (\(cid, cl) -> (cid, cl)) keepFixed
|
||||||
|
++ map (\(cid, cl, _, _, _) -> (cid, cl)) toKeepRest
|
||||||
|
writeSTRef (learnts s) (map fst survivors)
|
||||||
|
writeSTRef (nLearnts s) (length survivors)
|
||||||
|
modifySTRef' (statRemove s) (+ length toRemove)
|
||||||
|
|
||||||
|
sortByActAsc :: [(Cid, Clause s, Double, Int, Bool)] -> [(Cid, Clause s, Double, Int, Bool)]
|
||||||
|
sortByActAsc = sortByKey (\(_, _, a, _, _) -> a)
|
||||||
|
|
||||||
|
sortByKey :: Ord k => (a -> k) -> [a] -> [a]
|
||||||
|
sortByKey f = foldr insertBy []
|
||||||
|
where
|
||||||
|
insertBy x [] = [x]
|
||||||
|
insertBy x (y : ys)
|
||||||
|
| f x <= f y = x : y : ys
|
||||||
|
| otherwise = y : insertBy x ys
|
||||||
|
|
||||||
|
isLocked :: Solver s -> Cid -> Clause s -> ST s Bool
|
||||||
|
isLocked s cid cl = do
|
||||||
|
l0 <- readArray (clsLits cl) 0
|
||||||
|
let v = litVar l0
|
||||||
|
vv <- varValue s v
|
||||||
|
if vv == 0
|
||||||
|
then pure False
|
||||||
|
else do
|
||||||
|
r <- readArray (varReason s) v
|
||||||
|
pure (r == cid)
|
||||||
|
|
||||||
|
solveInternal :: Int -> [[Int]] -> ST s (Maybe [Bool], Stats)
|
||||||
|
solveInternal nv clausesIn = do
|
||||||
|
s <- newSolver nv
|
||||||
|
forM_ clausesIn (addClause s)
|
||||||
|
ok0 <- readSTRef (okFlag s)
|
||||||
|
result <-
|
||||||
|
if not ok0
|
||||||
|
then pure False
|
||||||
|
else do
|
||||||
|
confl0 <- propagate s
|
||||||
|
if confl0 >= 0
|
||||||
|
then do writeSTRef (okFlag s) False; pure False
|
||||||
|
else runRestarts s 0
|
||||||
|
model <-
|
||||||
|
if result
|
||||||
|
then Just <$> extractModel s
|
||||||
|
else pure Nothing
|
||||||
|
st <- collectStats s
|
||||||
|
pure (model, st)
|
||||||
|
|
||||||
|
runRestarts :: Solver s -> Int -> ST s Bool
|
||||||
|
runRestarts s restartNo = do
|
||||||
|
let budget = luby restartNo * 100
|
||||||
|
outcome <- search s budget
|
||||||
|
case outcome of
|
||||||
|
Just verdict -> pure verdict
|
||||||
|
Nothing -> do
|
||||||
|
modifySTRef' (statRest s) (+ 1)
|
||||||
|
runRestarts s (restartNo + 1)
|
||||||
|
|
||||||
|
extractModel :: Solver s -> ST s [Bool]
|
||||||
|
extractModel s =
|
||||||
|
mapM (\v -> do
|
||||||
|
vv <- varValue s v
|
||||||
|
pure (vv /= -1)) [0 .. numVars s - 1]
|
||||||
|
|
||||||
|
collectStats :: Solver s -> ST s Stats
|
||||||
|
collectStats s = do
|
||||||
|
d <- readSTRef (statDec s)
|
||||||
|
c <- readSTRef (statConf s)
|
||||||
|
p <- readSTRef (statProp s)
|
||||||
|
r <- readSTRef (statRest s)
|
||||||
|
l <- readSTRef (statLearn s)
|
||||||
|
rm <- readSTRef (statRemove s)
|
||||||
|
pure (Stats d c p r l rm)
|
||||||
Reference in new issue
Block a user