feat(proof): add drat/rup proof logging and checking

This commit is contained in:
dualfroz committed 2026-08-27 22:40:00 +02:00
1 parent 8fea34a107
commit cf47589cf6
1 file changed
+166
+166
View File
@@ -0,0 +1,166 @@
{-# LANGUAGE FlexibleContexts #-}
module Sift.Proof.Drat
( ProofResult(..)
, solveProof
, verifyProof
, isRup
, renderProof
) where
import Control.Monad (forM_)
import Control.Monad.ST (ST, runST)
import Data.STRef
import Data.Array.ST (readArray)
import Data.List (nub)
import qualified Data.Map.Strict as Map
import Sift.Types
import qualified Sift.Cdcl.State as St
import Sift.Cdcl.Search
( propagate, analyze, pickBranchVar, maybeReduce, installLearnt, luby )
data ProofResult
= ProofSat Assignment
| ProofUnsat [Clause]
deriving (Eq, Show)
solveProof :: CNF -> ProofResult
solveProof (CNF cs) = runST $ do
let nv = maximum (0 : map varId (concatMap varsOfClause cs))
s <- St.newSolver nv
proofRef <- newSTRef []
forM_ cs (St.addClause s . map litToInt)
ok0 <- readSTRef (St.okFlag s)
if not ok0
then pure (ProofUnsat [[]])
else do
confl0 <- propagate s
if confl0 >= 0
then pure (ProofUnsat [[]])
else do
verdict <- runRestarts s proofRef 0
if verdict
then do
model <- extractAssignment s
pure (ProofSat model)
else do
recorded <- reverse <$> readSTRef proofRef
pure (ProofUnsat (map toClause recorded ++ [[]]))
toClause :: [Int] -> Clause
toClause = map (litFromInt . St.dimacsOfLit)
runRestarts :: St.Solver s -> STRef s [[Int]] -> Int -> ST s Bool
runRestarts s proofRef restartNo = do
let budget = luby restartNo * 100
outcome <- proofSearch s proofRef budget
case outcome of
Just verdict -> pure verdict
Nothing -> do
modifySTRef' (St.statRest s) (+ 1)
runRestarts s proofRef (restartNo + 1)
proofSearch :: St.Solver s -> STRef s [[Int]] -> Int -> ST s (Maybe Bool)
proofSearch s proofRef confBudget = loop 0
where
loop confSoFar = do
confl <- propagate s
if confl >= 0
then do
modifySTRef' (St.statConf s) (+ 1)
lvl <- St.currentLevel s
if lvl == 0
then pure (Just False)
else do
(learnt, btLevel, lbd) <- analyze s confl
modifySTRef' proofRef (learnt :)
St.cancelUntil s btLevel
installLearnt s learnt lbd
loop (confSoFar + 1)
else
if confSoFar >= confBudget
then do St.cancelUntil s 0; pure Nothing
else do
maybeReduce s
v <- pickBranchVar s
if v < 0
then pure (Just True)
else do
modifySTRef' (St.statDec s) (+ 1)
pol <- readArray (St.polarity s) v
let l = if pol then St.posLit v else St.negLit v
St.newDecisionLevel s
_ <- St.enqueue s l (-1)
loop confSoFar
extractAssignment :: St.Solver s -> ST s Assignment
extractAssignment s = do
pairs <- mapM (\v -> do
vv <- St.varValue s v
pure (Var (v + 1), vv /= -1)) [0 .. St.numVars s - 1]
pure (Map.fromList pairs)
verifyProof :: CNF -> [Clause] -> Bool
verifyProof (CNF orig) = go orig
where
go _ [] = False
go formula (c : rest)
| not (isRup formula c) = False
| null c = True
| otherwise = go (c : formula) rest
isRup :: [Clause] -> Clause -> Bool
isRup formula lemma =
propagatesToConflict formula (map neg lemma)
propagatesToConflict :: [Clause] -> [Lit] -> Bool
propagatesToConflict clauses0 assumed =
case assignAll Map.empty assumed of
Nothing -> True
Just a0 -> loop a0
where
clauses = normalizeClauses clauses0
assignAll a [] = Just a
assignAll a (l : ls) = case extend a l of
Nothing -> Nothing
Just a' -> assignAll a' ls
extend a l = case Map.lookup (litVar l) a of
Just b
| b == litSign l -> Just a
| otherwise -> Nothing
Nothing -> Just (Map.insert (litVar l) (litSign l) a)
loop a
| any (falsifiedBy a) clauses = True
| otherwise =
case pickUnit a clauses of
Nothing -> False
Just l -> case extend a l of
Nothing -> True
Just a' -> loop a'
normalizeClauses :: [Clause] -> [Clause]
normalizeClauses = concatMap norm
where
norm c =
let deduped = nub c
in if any (\l -> neg l `elem` deduped) deduped then [] else [deduped]
falsifiedBy :: Assignment -> Clause -> Bool
falsifiedBy a c = all (\l -> litValue a l == Just False) c
pickUnit :: Assignment -> [Clause] -> Maybe Lit
pickUnit a = firstJust . map (unitLiteral a)
where
firstJust [] = Nothing
firstJust (Just l : _) = Just l
firstJust (Nothing : xs) = firstJust xs
unitLiteral :: Assignment -> Clause -> Maybe Lit
unitLiteral a c =
case [ l | l <- c, litValue a l == Nothing ] of
[l] | not (any (\k -> litValue a k == Just True) c) -> Just l
_ -> Nothing
renderProof :: [Clause] -> String
renderProof = unlines . map renderClause
where
renderClause c = unwords (map (show . litToInt) c ++ ["0"])