forked from BR-/sat_solve
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathWalkSAT.py
More file actions
185 lines (155 loc) · 4.07 KB
/
WalkSAT.py
File metadata and controls
185 lines (155 loc) · 4.07 KB
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# test files
# there are some test files at the bottom of
# http://people.sc.fsu.edu/~jburkardt/data/cnf/cnf.html
# https://toughsat.appspot.com
import random
import timeit
class Boolean:
def __init__(self, variables, name, inverted=False):
self.variables = variables
self.name = name
self.inverted = inverted
def get(self):
return self.inverted != self.variables[self.name]
def toggle(self):
self.variables[self.name] = not self.variables[self.name]
def __str__(self):
if self.inverted:
return "!" + self.name
else:
return self.name
class CNFClause:
def __init__(self, vars):
self.vars = vars
def evaluate(self):
ret = False
for var in self.vars:
ret = ret or var.get()
return ret
def __str__(self):
return "+".join(str(var) for var in self.vars)
class CNF: #AND of ORs
def from_human(expr):
variables = {}
clauses = []
for clause in expr.split("*"):
cl = []
for var in clause.strip("()").split("+"):
if var[0] == "!":
cl.append(Boolean(variables, var[1:], True))
variables[var[1:]] = None
else:
cl.append(Boolean(variables, var))
variables[var] = None
clauses.append(CNFClause(cl))
return CNF(variables, clauses)
def from_dimacs(dimacs):
variables = {}
clauses = []
cl = []
for line in dimacs:
if line[0] == 'c':
continue
elif line[0] == 'p':
p, problem_type, vars, cls = line.split()
if problem_type != "cnf":
raise Exception("invalid file type")
for v in range(int(vars)):
variables[str(v+1)] = None
else:
for var in line.split():
var = int(var)
if var == 0:
clauses.append(CNFClause(cl))
cl = []
elif var < 0:
cl.append(Boolean(variables, str(-1 * var), True))
else:
cl.append(Boolean(variables, str(var)))
return CNF(variables, clauses)
def __init__(self, variables, clauses):
self.variables = variables
self.clauses = clauses
def evaluate(self):
ret = True
for clause in self.clauses:
ret = ret and clause.evaluate()
return ret
def satisfied(self, rly=True):
ret = []
for clause in self.clauses:
if rly == clause.evaluate():
ret.append(clause)
return ret
def random(self):
for var in self.variables:
self.variables[var] = not random.getrandbits(1)
def walksat(self, random_flip=0.2, gsat=False):
self.random()
reset_counter = 0
reset_best = 0
while not self.evaluate():
orig = self.satisfied()
if len(orig) > reset_best:
#print(len(orig))
reset_counter = 0
reset_best = len(orig)
else:
reset_counter += 1
if reset_counter > 500:
self.random()
reset_counter = 0
reset_best = 0
continue
clause = random.choice(self.satisfied(False))
if random.random() > random_flip:
best_var = []
best_sat = 0
for var in clause.vars:
var.toggle()
if gsat:
sat = sum(1 for o in self.clauses if o.evaluate())
else:
sat = sum(1 for o in orig if o.evaluate())
if sat > best_sat:
best_sat = sat
best_var = [var]
elif sat == best_sat:
best_var.append(var)
var.toggle()
random.choice(best_var).toggle()
else:
random.choice(clause.vars).toggle()
return True
def __str__(self):
return "(" + ")*(".join(str(cl) for cl in self.clauses) + ")"
cnf = CNF.from_human("(A+B)*(!B+C+!D)*(D+!E)")
print(cnf)
print(timeit.timeit(cnf.walksat, number=5))
print(cnf.variables)
# generate a harder test
vars = {}
for v in range(100):
vars[str(v)] = not random.getrandbits(1)
expr = []
for clause in range(1000):
cl = []
attempt = False
for _ in range(2):
v = random.choice(list(vars.keys()))
inv = not random.getrandbits(1)
cl.append(Boolean(vars, v, inv))
attempt = attempt or (inv != vars[v])
if attempt:
expr.append(cl)
string = "*".join("+".join(str(v) for v in cl) for cl in expr)
cnf = CNF.from_human(string)
#print(cnf)
print(len(cnf.clauses), "clauses")
print(timeit.timeit(cnf.walksat, number=5))
print(cnf.variables)
with open("A:\\sat.cnf", "r") as fh:
cnf = CNF.from_dimacs(fh)
print(len(cnf.clauses), "clauses")
print(timeit.timeit(cnf.walksat, number=5))
print(cnf.variables)