test: add cardinality, xor, proof, and incremental tests
This commit is contained in:
1 parent
cca8377dfb
commit
14cd727d4a
1 file changed
+294
+294
@@ -162,6 +162,66 @@ tests =
|
|||||||
, check ("difference logic agrees with bounded brute force on "
|
, check ("difference logic agrees with bounded brute force on "
|
||||||
++ show (length randomDlProblems) ++ " random problems")
|
++ show (length randomDlProblems) ++ " random problems")
|
||||||
(all dlAgrees randomDlProblems)
|
(all dlAgrees randomDlProblems)
|
||||||
|
|
||||||
|
, check "at-most-k (sequential counter) encodes <= k exactly"
|
||||||
|
(all (\n -> all (cardCorrect atMostK (<=) n) [0 .. n]) [1 .. 4])
|
||||||
|
, check "at-least-k (sequential counter) encodes >= k exactly"
|
||||||
|
(all (\n -> all (cardCorrect atLeastK (>=) n) [0 .. n]) [1 .. 4])
|
||||||
|
, check "exactly-k (sequential counter) encodes == k exactly"
|
||||||
|
(all (\n -> all (cardCorrect exactlyK (==) n) [0 .. n]) [1 .. 4])
|
||||||
|
|
||||||
|
, check "vertex cover: triangle K3 has a cover of size 2 but not 1"
|
||||||
|
(vcSatValid 2 triangleK3 && solveCdcl (vertexCover 1 triangleK3) == CdclUnsat)
|
||||||
|
, check "vertex cover: odd cycle C5 needs 3, not 2"
|
||||||
|
(vcSatValid 3 cycleC5 && solveCdcl (vertexCover 2 cycleC5) == CdclUnsat)
|
||||||
|
, check "vertex cover: star K1,3 is covered by its center alone"
|
||||||
|
(vcSatValid 1 star13)
|
||||||
|
|
||||||
|
, check ("BVE is equisatisfiable and reconstructs models on "
|
||||||
|
++ show (length randomCnfs) ++ " random CNFs")
|
||||||
|
(all bveCorrect randomCnfs)
|
||||||
|
, check ("BVE reconstructs models for " ++ show (length satisfiableCnfs)
|
||||||
|
++ " constructed-satisfiable CNFs")
|
||||||
|
(all bveReconstructs satisfiableCnfs)
|
||||||
|
|
||||||
|
, check ("XOR Gaussian solve agrees with brute force on " ++ show (length xorSystems)
|
||||||
|
++ " random GF(2) systems")
|
||||||
|
(all xorGaussSound xorSystems)
|
||||||
|
, check "XOR Gaussian solve is equisatisfiable to the CNF expansion"
|
||||||
|
(all xorEquisat xorSystems)
|
||||||
|
, check "XOR empty parity-1 clause is UNSAT, parity-0 is SAT"
|
||||||
|
(gaussIsUnsat (xorClause [] True) && gaussIsSat (xorClause [] False))
|
||||||
|
|
||||||
|
, check "hamiltonian path agrees with brute-force permutation search"
|
||||||
|
(all hamPathOk hamGraphs)
|
||||||
|
, check "hamiltonian cycle agrees with brute-force permutation search"
|
||||||
|
(all hamCycleOk hamGraphs)
|
||||||
|
|
||||||
|
, check ("model enumeration counts match brute force on " ++ show (length randomCnfs)
|
||||||
|
++ " random CNFs")
|
||||||
|
(all enumerateOk randomCnfs)
|
||||||
|
, check "N-queens solution counts match the known sequence for N=1..7"
|
||||||
|
queensCountsOk
|
||||||
|
|
||||||
|
, check ("incremental SAT under assumptions agrees with brute force on "
|
||||||
|
++ show (length incAssumps) ++ " cases")
|
||||||
|
(all incrementalOk incAssumps)
|
||||||
|
, check "incremental final conflict clause is a real unsatisfiable core"
|
||||||
|
(all incrementalCoreReal incAssumps)
|
||||||
|
, check "batched incremental queries reuse the solver without changing verdicts"
|
||||||
|
incrementalBatchConsistent
|
||||||
|
|
||||||
|
, check ("DRAT/RUP proof or model is correct on " ++ show (length randomCnfs)
|
||||||
|
++ " random CNFs")
|
||||||
|
(all dratVerifies randomCnfs)
|
||||||
|
, check "DRAT proofs for pigeonhole UNSAT instances verify by RUP"
|
||||||
|
phpProofsVerify
|
||||||
|
, check "corrupting a pigeonhole proof makes the RUP checker reject it"
|
||||||
|
dratCorruptRejected
|
||||||
|
|
||||||
|
, check "heap returns variables in non-increasing activity order"
|
||||||
|
(all heapDescends heapActivityCases)
|
||||||
|
, check "heap increase-key promotes a bumped variable to the top" heapBumpOk
|
||||||
]
|
]
|
||||||
|
|
||||||
check :: String -> Bool -> IO Bool
|
check :: String -> Bool -> IO Bool
|
||||||
@@ -570,3 +630,237 @@ randDlProblem s0 =
|
|||||||
|
|
||||||
randomDlProblems :: [DlProblem]
|
randomDlProblems :: [DlProblem]
|
||||||
randomDlProblems = fst (genN 60 randDlProblem 20240828)
|
randomDlProblems = fst (genN 60 randDlProblem 20240828)
|
||||||
|
|
||||||
|
buildDoubleArray :: [Double] -> ST s (STUArray s Int Double)
|
||||||
|
buildDoubleArray xs = newListArray (0, length xs - 1) xs
|
||||||
|
|
||||||
|
zeroDoubleArray :: Int -> ST s (STUArray s Int Double)
|
||||||
|
zeroDoubleArray n = newArray (0, n - 1) 0
|
||||||
|
|
||||||
|
popAll :: VarHeap s -> ST s [Int]
|
||||||
|
popAll h = do
|
||||||
|
done <- heapIsEmpty h
|
||||||
|
if done
|
||||||
|
then return []
|
||||||
|
else do
|
||||||
|
v <- heapRemoveMax h
|
||||||
|
rest <- popAll h
|
||||||
|
return (v : rest)
|
||||||
|
|
||||||
|
nonIncreasing :: [Double] -> Bool
|
||||||
|
nonIncreasing xs = and (zipWith (>=) xs (drop 1 xs))
|
||||||
|
|
||||||
|
heapDescends :: [Double] -> Bool
|
||||||
|
heapDescends acts = runST $ do
|
||||||
|
let n = length acts
|
||||||
|
arr <- buildDoubleArray acts
|
||||||
|
h <- newHeap n arr
|
||||||
|
mapM_ (heapInsert h) [0 .. n - 1]
|
||||||
|
popped <- popAll h
|
||||||
|
let poppedActs = map (acts !!) popped
|
||||||
|
return (nonIncreasing poppedActs
|
||||||
|
&& sort poppedActs == sort acts
|
||||||
|
&& length popped == n)
|
||||||
|
|
||||||
|
heapActivityCases :: [[Double]]
|
||||||
|
heapActivityCases =
|
||||||
|
[ [3, 1, 4, 1, 5, 9, 2, 6]
|
||||||
|
, [1, 2, 3, 4, 5]
|
||||||
|
, [5, 4, 3, 2, 1]
|
||||||
|
, [2, 2, 2, 2]
|
||||||
|
, [7]
|
||||||
|
, [0.5, 10.5, 3.3, 3.3, 8.1, 0.0]
|
||||||
|
]
|
||||||
|
|
||||||
|
heapBumpOk :: Bool
|
||||||
|
heapBumpOk = runST $ do
|
||||||
|
arr <- zeroDoubleArray 5
|
||||||
|
h <- newHeap 5 arr
|
||||||
|
mapM_ (heapInsert h) [0 .. 4]
|
||||||
|
writeArray arr 3 100
|
||||||
|
heapIncrease h 3
|
||||||
|
top <- heapRemoveMax h
|
||||||
|
return (top == 3)
|
||||||
|
|
||||||
|
bitVectors :: Int -> [[Bool]]
|
||||||
|
bitVectors 0 = [[]]
|
||||||
|
bitVectors m = [ b : rest | b <- [False, True], rest <- bitVectors (m - 1) ]
|
||||||
|
|
||||||
|
cardCorrect :: (Int -> [Lit] -> Int -> ([Clause], Int)) -> (Int -> Int -> Bool) -> Int -> Int -> Bool
|
||||||
|
cardCorrect enc predicate n k = all check (bitVectors n)
|
||||||
|
where
|
||||||
|
lits = [ posLit (Var i) | i <- [1 .. n] ]
|
||||||
|
check bits =
|
||||||
|
let units = zipWith (\i b -> [ if b then posLit (Var i) else negLit (Var i) ]) [1 ..] bits
|
||||||
|
(cardClauses, _) = enc k lits (n + 1)
|
||||||
|
in isCdclSat (solveCdcl (CNF (cardClauses ++ units)))
|
||||||
|
== predicate (length (filter id bits)) k
|
||||||
|
|
||||||
|
vcSatValid :: Int -> Graph -> Bool
|
||||||
|
vcSatValid k g = case solveCdcl (vertexCover k g) of
|
||||||
|
CdclSat model -> validVertexCover k g model
|
||||||
|
CdclUnsat -> False
|
||||||
|
|
||||||
|
triangleK3 :: Graph
|
||||||
|
triangleK3 = Graph 3 [(1, 2), (2, 3), (1, 3)]
|
||||||
|
|
||||||
|
star13 :: Graph
|
||||||
|
star13 = Graph 4 [(1, 2), (1, 3), (1, 4)]
|
||||||
|
|
||||||
|
bveCorrect :: CNF -> Bool
|
||||||
|
bveCorrect cnf =
|
||||||
|
bruteForceSat cnf == bruteForceSat (bveCnf r)
|
||||||
|
&& case solveCdcl (bveCnf r) of
|
||||||
|
CdclSat m -> satisfies (completeModel cnf (reconstructBve r m)) cnf
|
||||||
|
CdclUnsat -> True
|
||||||
|
where r = bve cnf
|
||||||
|
|
||||||
|
bveReconstructs :: CNF -> Bool
|
||||||
|
bveReconstructs cnf = case solveCdcl (bveCnf r) of
|
||||||
|
CdclSat m -> satisfies (completeModel cnf (reconstructBve r m)) cnf
|
||||||
|
CdclUnsat -> not (bruteForceSat cnf)
|
||||||
|
where r = bve cnf
|
||||||
|
|
||||||
|
randXorClause :: Int -> Seed -> (XorClause, Seed)
|
||||||
|
randXorClause numVars s0 =
|
||||||
|
let (parity, s1) = randBool s0
|
||||||
|
pick v st = let (b, st') = randBool st in (if b then [Var v] else [], st')
|
||||||
|
go [] st acc = (acc, st)
|
||||||
|
go (v : vs) st acc = let (xs, st') = pick v st in go vs st' (acc ++ xs)
|
||||||
|
(vs, s2) = go [1 .. numVars] s1 []
|
||||||
|
in (xorClause vs parity, s2)
|
||||||
|
|
||||||
|
randXorSystem :: Seed -> ([XorClause], Seed)
|
||||||
|
randXorSystem s0 = let (nc, s1) = randInt 1 6 s0 in genN nc (randXorClause 5) s1
|
||||||
|
|
||||||
|
xorSystems :: [[XorClause]]
|
||||||
|
xorSystems = fst (genN 200 randXorSystem 777)
|
||||||
|
|
||||||
|
xorBruteSat :: [XorClause] -> Bool
|
||||||
|
xorBruteSat sys = any (`systemSatisfies` sys) (allAssignments vs)
|
||||||
|
where vs = nub [ Var v | c <- sys, v <- IntSet.toList (xorVars c) ]
|
||||||
|
|
||||||
|
gaussIsSat :: XorClause -> Bool
|
||||||
|
gaussIsSat c = case gaussSolve [c] of Just _ -> True; Nothing -> False
|
||||||
|
|
||||||
|
gaussIsUnsat :: XorClause -> Bool
|
||||||
|
gaussIsUnsat = not . gaussIsSat
|
||||||
|
|
||||||
|
xorGaussSound :: [XorClause] -> Bool
|
||||||
|
xorGaussSound sys = case gaussSolve sys of
|
||||||
|
Just m -> systemSatisfies m sys && xorBruteSat sys
|
||||||
|
Nothing -> not (xorBruteSat sys)
|
||||||
|
|
||||||
|
xorEquisat :: [XorClause] -> Bool
|
||||||
|
xorEquisat sys =
|
||||||
|
isGaussSat == isCdclSat (solveCdcl (systemToCnf sys))
|
||||||
|
where isGaussSat = case gaussSolve sys of Just _ -> True; Nothing -> False
|
||||||
|
|
||||||
|
hamGraphs :: [Graph]
|
||||||
|
hamGraphs =
|
||||||
|
[ Graph 4 [(1, 2), (2, 3), (3, 4)]
|
||||||
|
, Graph 4 [(1, 2), (2, 3), (3, 4), (4, 1)]
|
||||||
|
, Graph 5 [(1, 2), (2, 3), (3, 4), (4, 5), (5, 1)]
|
||||||
|
, Graph 4 [(1, 2), (1, 3), (1, 4)]
|
||||||
|
, Graph 5 [(1, 2), (2, 3), (3, 1), (3, 4), (4, 5)]
|
||||||
|
, Graph 6 [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 1), (1, 4)]
|
||||||
|
]
|
||||||
|
|
||||||
|
adjPairs :: Graph -> [(Int, Int)]
|
||||||
|
adjPairs (Graph _ edges) = concat [ [(u, w), (w, u)] | (u, w) <- edges ]
|
||||||
|
|
||||||
|
brutePathExists :: Graph -> Bool
|
||||||
|
brutePathExists g@(Graph n _) = any ok (permutations [1 .. n])
|
||||||
|
where
|
||||||
|
adj = adjPairs g
|
||||||
|
ok p = all (`elem` adj) (zip p (drop 1 p))
|
||||||
|
|
||||||
|
bruteCycleExists :: Graph -> Bool
|
||||||
|
bruteCycleExists g@(Graph n _) = any ok (permutations [1 .. n])
|
||||||
|
where
|
||||||
|
adj = adjPairs g
|
||||||
|
ok p = all (`elem` adj) (zip p (drop 1 p))
|
||||||
|
&& (n < 2 || (last p, head p) `elem` adj)
|
||||||
|
|
||||||
|
hamPathOk :: Graph -> Bool
|
||||||
|
hamPathOk g = case solveCdcl (hamiltonianPath g) of
|
||||||
|
CdclSat m -> validHamiltonianPath g m && brutePathExists g
|
||||||
|
CdclUnsat -> not (brutePathExists g)
|
||||||
|
|
||||||
|
hamCycleOk :: Graph -> Bool
|
||||||
|
hamCycleOk g = case solveCdcl (hamiltonianCycle g) of
|
||||||
|
CdclSat m -> validHamiltonianCycle g m && bruteCycleExists g
|
||||||
|
CdclUnsat -> not (bruteCycleExists g)
|
||||||
|
|
||||||
|
countBrute :: CNF -> Int
|
||||||
|
countBrute cnf = length (filter (`satisfies` cnf) (allAssignments (allVars cnf)))
|
||||||
|
|
||||||
|
enumerateOk :: CNF -> Bool
|
||||||
|
enumerateOk cnf = countModels (allVars cnf) cnf == countBrute cnf
|
||||||
|
|
||||||
|
queensCountsOk :: Bool
|
||||||
|
queensCountsOk = [ countQueensSolutions n | n <- [1 .. 7] ] == [1, 0, 0, 2, 10, 4, 40]
|
||||||
|
|
||||||
|
randAssumpSet :: Int -> Seed -> ([Lit], Seed)
|
||||||
|
randAssumpSet numVars s0 = let (k, s1) = randInt 0 3 s0 in genN k (randLit numVars) s1
|
||||||
|
|
||||||
|
incAssumps :: [(CNF, [Lit])]
|
||||||
|
incAssumps =
|
||||||
|
concat
|
||||||
|
[ let (sets, _) = genN 3 (randAssumpSet 5) (9000 + i)
|
||||||
|
in [ (cnf, a) | a <- sets ]
|
||||||
|
| (i, cnf) <- zip [0 ..] randomCnfs ]
|
||||||
|
|
||||||
|
bruteIncSat :: CNF -> [Lit] -> Bool
|
||||||
|
bruteIncSat cnf assumps = any ok (allAssignments vs)
|
||||||
|
where
|
||||||
|
vs = nub (allVars cnf ++ map litVar assumps)
|
||||||
|
ok m = satisfies m cnf && all (\l -> litValue m l == Just True) assumps
|
||||||
|
|
||||||
|
incrementalOk :: (CNF, [Lit]) -> Bool
|
||||||
|
incrementalOk (cnf, assumps) = case solveAssuming cnf assumps of
|
||||||
|
IncSat m -> satisfies m cnf
|
||||||
|
&& all (\l -> litValue m l == Just True) assumps
|
||||||
|
&& bruteIncSat cnf assumps
|
||||||
|
IncUnsat _core -> not (bruteIncSat cnf assumps)
|
||||||
|
|
||||||
|
incrementalCoreReal :: (CNF, [Lit]) -> Bool
|
||||||
|
incrementalCoreReal (cnf, assumps) = case solveAssuming cnf assumps of
|
||||||
|
IncSat _ -> True
|
||||||
|
IncUnsat core -> all (`elem` assumps) core && not (bruteIncSat cnf core)
|
||||||
|
|
||||||
|
incrementalBatchConsistent :: Bool
|
||||||
|
incrementalBatchConsistent =
|
||||||
|
all consistent (take 40 (groupQueries incAssumps))
|
||||||
|
where
|
||||||
|
groupQueries pairs = [ (cnf, [ a | (c, a) <- pairs, c == cnf ]) | cnf <- nub (map fst pairs) ]
|
||||||
|
consistent (cnf, queries) =
|
||||||
|
let batched = solveManyAssuming cnf queries
|
||||||
|
in length batched == length queries
|
||||||
|
&& map isIncSat batched == map (isIncSat . solveAssuming cnf) queries
|
||||||
|
&& and (zipWith (validResult cnf) batched queries)
|
||||||
|
isIncSat (IncSat _) = True
|
||||||
|
isIncSat (IncUnsat _) = False
|
||||||
|
validResult cnf (IncSat m) as =
|
||||||
|
satisfies m cnf && all (\l -> litValue m l == Just True) as
|
||||||
|
validResult cnf (IncUnsat core) as =
|
||||||
|
all (`elem` as) core && not (bruteIncSat cnf core)
|
||||||
|
|
||||||
|
dratVerifies :: CNF -> Bool
|
||||||
|
dratVerifies cnf = case solveProof cnf of
|
||||||
|
ProofSat m -> satisfies m cnf
|
||||||
|
ProofUnsat p -> not (bruteForceSat cnf) && verifyProof cnf p
|
||||||
|
|
||||||
|
phpProofsVerify :: Bool
|
||||||
|
phpProofsVerify = all ok [(3, 2), (4, 3), (5, 4)]
|
||||||
|
where
|
||||||
|
ok (h, p) = case solveProof (pigeonhole h p) of
|
||||||
|
ProofUnsat proof -> verifyProof (pigeonhole h p) proof
|
||||||
|
ProofSat _ -> False
|
||||||
|
|
||||||
|
dratCorruptRejected :: Bool
|
||||||
|
dratCorruptRejected = all rejected [pigeonhole 4 3, pigeonhole 5 4]
|
||||||
|
where
|
||||||
|
rejected cnf = case solveProof cnf of
|
||||||
|
ProofUnsat proof -> length proof >= 2 && not (verifyProof cnf (drop 1 proof))
|
||||||
|
ProofSat _ -> False
|
||||||
Reference in new issue
Block a user