-
Notifications
You must be signed in to change notification settings - Fork 0
/
sat-solver-python.py
118 lines (97 loc) · 3.23 KB
/
sat-solver-python.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# A simple SAT solver written in Python
from dataclasses import dataclass
from sys import argv
from io import TextIOWrapper
@dataclass
class Solver:
n_vars: int
clauses: list
assignment: list
def __init__(self, n_vars: int, clauses: list):
self.n_vars = n_vars
self.clauses = clauses
self.assignment = [0 for _ in range(0, n_vars + 1)]
def is_consistent(self) -> bool:
def is_consistent_clause(clause: list) -> bool:
for idx_lit in range(len(clause)):
lit = clause[idx_lit]
if lit > 0:
lit_var = lit
lit_neg = False
else:
lit_var = -lit
lit_neg = True
lit_satisfied: bool
if self.assignment[lit_var] == 0:
lit_satisfied = True
else:
lit_satisfied = (self.assignment[lit_var] > 0) ^ lit_neg
if lit_satisfied == True:
return True
# Note: empty clause (no literals) => trivially false
return False
for idx_cls in range(len(self.clauses)):
clause = self.clauses[idx_cls]
if not is_consistent_clause(clause):
return False
# Note: empty formula (no clauses) => trivially satisfied
return True
def solve(self, i: int = 1) -> bool:
if i > self.n_vars:
# all variables are assigned
return True
# print( "debug: var", i, "=> T" )
self.assignment[i] = 1
if self.is_consistent():
if self.solve( i + 1 ):
return True
# print( "debug: var", i, "=> F" )
self.assignment[i] = -1
if self.is_consistent():
if self.solve( i + 1 ):
return True
# print( "debug: reset var", i )
self.assignment[i] = 0 # reset assignment
return False
# returns (formula, # of vars, # of clauses)
def parse_formula( f: TextIOWrapper ) -> ([], int, int):
lines = f.read().split('\n')
# first line: header
header = lines[0].split(' ')
n_vars = int( header[2] )
n_clauses = int( header[3] )
# parse formula
formula = []
for idx in range(1, len(lines)):
line = lines[idx]
if len(line) == 0:
# skip empty lines
continue
# parse clause
str_lits = line.strip().split(' ')
clause = []
for i in range(len(str_lits)):
str_lit = str_lits[i]
lit = int(str_lit)
if lit == 0: # end of clause
break
clause.append( lit )
formula.append( clause )
return (formula, n_vars, n_clauses)
def main():
if len(argv) != 2:
raise "Missing filepath argument"
filepath = argv[1]
with open(filepath, "r") as f:
clauses, n_vars, n_clauses = parse_formula( f )
# solve
print( "# of vars (from input):", n_vars )
print( "# of clauses (from input):", n_clauses )
solver = Solver( n_vars, clauses )
result = solver.solve()
if result:
print( "SAT" )
else:
print( "UNSAT" )
if __name__ == "__main__":
main()