-
Notifications
You must be signed in to change notification settings - Fork 0
/
sat.py
159 lines (141 loc) · 5.12 KB
/
sat.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
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
# Scott Ratchford 2023.04.29
import math
from nonogram import *
class Clause:
def __init__(self, terms):
self.terms = terms
self.variables = []
self.dupes = 0
for var in self.terms:
if abs(var) not in self.variables:
self.variables.append(abs(var))
self.variables.sort() # sort variables in ascending order
else:
self.dupes += 1
def evaluate(self, values):
varIndicies = [abs(x) - 1 for x in self.terms]
attemptValues = []
for i in range(len(values)): # reduce the values list to only the values that correspond to the variables in this clause
if(i in varIndicies):
attemptValues.append(values[i])
for i, term in enumerate(self.terms):
if((values[abs(term)-1] and term > 0) or (not values[abs(term)-1] and self.terms[i] < 0)):
return True
return False
def print_with_letters(self):
s = ""
for index, term in enumerate(self.terms):
if(term > 0):
s += str(chr(term+64))
else:
s += "~" + str(chr(abs(term)+64))
if(index < len(self.terms) - 1):
s += " v "
return s
def __str__(self):
s = ""
for index, term in enumerate(self.terms):
if(term > 0):
s += str(term)
else:
s += "~" + str(abs(term))
if(index < len(self.terms) - 1):
s += " v "
return s
def __len__(self):
return len(self.terms)
class SAT:
def __init__(self):
self.clauses = []
self.variables = []
def add_clause(self, clause):
self.clauses.append(clause)
for var in clause.terms:
if abs(var) not in self.variables:
self.variables.append(abs(var))
self.variables.sort() # sort variables in ascending order
def evaluate(self, values):
for clause in self.clauses:
if(not clause.evaluate(values)):
return False
return True
def print_with_letters(self):
s = ""
for index, clause in enumerate(self.clauses):
s += "(" + str(clause.print_with_letters()) + ")"
if(index < len(self.clauses) - 1):
s += " ^ "
if(len(s) > 0): return s
else: return "(empty SAT)"
def __str__(self):
s = ""
for index, clause in enumerate(self.clauses):
s += "(" + str(clause) + ")"
if(index < len(self.clauses) - 1):
s += " ^ "
return s
def __len__(self):
return len(self.clauses)
def sat_from_file(filename) -> SAT:
try:
file = open(filename, "r")
lines = file.readlines()
file.close()
except:
raise Exception("Error reading file " + filename)
sat = SAT()
for line in lines[1:-1]: # skip first and last lines
terms = [int(x) for x in line.split()]
sat.add_clause(Clause(terms))
return sat
def brute_force_sat(sat: SAT) -> list:
for i in range(int(math.pow(2, len(sat.variables)))):
values = [bool(int(x)) for x in bin(i)[2:].zfill(len(sat.variables))]
if(sat.evaluate(values)):
return values
return [] # if no combination of values satisfies the SAT
def return_all_sat_solutions(sat: SAT) -> list:
solutions = []
for i in range(int(math.pow(2, len(sat.variables)))):
values = [bool(int(x)) for x in bin(i)[2:].zfill(len(sat.variables))]
if(sat.evaluate(values)):
solutions.append(values)
return solutions
def sat_to_file(sat: SAT, filename: str) -> bool:
try:
file = open(filename, "w")
file.write(str(len(sat.variables)) + "\n")
for clause in sat.clauses:
for index, term in enumerate(clause.terms):
file.write(str(term))
if(index < len(clause.terms) - 1):
file.write(" ") # space between terms, no trailing space
file.write("\n")
file.write("$")
file.close()
return True
except:
return False
if __name__ == "__main__":
# args:
# 1: filename
if(len(sys.argv) > 1):
filename = sys.argv[1]
try:
sat = sat_from_file(filename)
except:
raise Exception("Error reading file")
else:
raise Exception("No filename argument provided")
print("SAT: " + str(sat))
print("SAT as letters: " + str(sat.print_with_letters()))
# print("Variables (" + str(len(sat.variables)) + "): " + str(sat.variables))
satisfiable = len(brute_force_sat(sat)) > 0
if(satisfiable):
print("Satisfiable")
all_solutions = return_all_sat_solutions(sat)
print("All " + str(len(all_solutions)) + " solutions:")
for solution in all_solutions:
print(solution)
else:
print("Not satisfiable")