Skip to content

Commit 17e7019

Browse files
committed
Formatting
1 parent be92721 commit 17e7019

File tree

1 file changed

+88
-67
lines changed

1 file changed

+88
-67
lines changed

other/dpll.py

Lines changed: 88 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
"""
2-
Davis–Putnam–Logemann–Loveland (DPLL) algorithm is a complete, backtracking-based search algorithm
3-
for deciding the satisfiability of propositional logic formulae in conjunctive normal form, i.e. for solving the CNF-SAT problem.
2+
Davis–Putnam–Logemann–Loveland (DPLL) algorithm is a complete,
3+
backtracking-based search algorithm for deciding the satisfiability of
4+
propositional logic formulae in conjunctive normal form,
5+
i.e, for solving the CNF-SAT problem.
46
5-
For more information about the algorithm: https://en.wikipedia.org/wiki/DPLL_algorithm
7+
For more information about the algorithm:
8+
https://en.wikipedia.org/wiki/DPLL_algorithm
69
"""
710

811
import random
912

1013

11-
class Clause():
14+
class Clause:
1215
"""
1316
A clause represented in CNF.
1417
A clause is a set of literals, either complemented or otherwise.
@@ -17,7 +20,7 @@ class Clause():
1720
{A5', A2', A1} is the clause (A5' v A2' v A1)
1821
1922
Create a set of literals and a clause with them
20-
>>> literals = ["A1", "A2'", "A3"]
23+
>>> literals = ["A1", "A2'", "A3"]
2124
>>> clause = Clause(literals)
2225
>>> print(clause)
2326
{ A1 , A2' , A3 }
@@ -32,23 +35,24 @@ def __init__(self, literals):
3235
"""
3336
Represent the literals and an assignment in a clause."
3437
"""
35-
self.literals = {literal : None for literal in literals} # Assign all literals to None initially
38+
# Assign all literals to None initially
39+
self.literals = {literal: None for literal in literals}
3640
self.no_of_literals = len(self.literals)
37-
41+
3842
def __str__(self):
3943
"""
40-
To print a clause as in CNF.
41-
Variable clause holds the string representation.
44+
To print a clause as in CNF.
45+
Variable clause holds the string representation.
4246
"""
4347
clause = "{ "
4448
for i in range(0, self.no_of_literals):
4549
clause += str(list(self.literals.keys())[i]) + " "
4650
if i != self.no_of_literals - 1:
4751
clause += ", "
4852
clause += "}"
49-
53+
5054
return clause
51-
55+
5256
def assign(self, model):
5357
"""
5458
Assign values to literals of the clause as given by model.
@@ -59,51 +63,53 @@ def assign(self, model):
5963
if symbol in model.keys():
6064
val = model[symbol]
6165
else:
62-
i += 1
63-
continue
64-
if val != None:
66+
i += 1
67+
continue
68+
if val is not None:
69+
# Complement assignment if literal is in complemented form
6570
if list(self.literals.keys())[i][-1] == "'":
66-
val = not val #Complement assignment if literal is in complemented form
71+
val = not val
6772
self.literals[list(self.literals.keys())[i]] = val
6873
i += 1
69-
74+
7075
def evaluate(self, model):
7176
"""
72-
Evaluates the clause till the extent possible with the current assignments in model.
77+
Evaluates the clause with the assignments in model.
7378
This has the following steps:
7479
1. Return True if both a literal and its complement exist in the clause.
7580
2. Return True if a single literal has the assignment True.
7681
3. Return None(unable to complete evaluation) if a literal has no assignment.
7782
4. Compute disjunction of all values assigned in clause.
7883
"""
79-
for l in list(self.literals.keys()):
80-
if len(l) == 2:
81-
symbol = l + "'"
84+
for literal in list(self.literals.keys()):
85+
if len(literal) == 2:
86+
symbol = literal + "'"
8287
if symbol in list(self.literals.keys()):
8388
return True
8489
else:
85-
symbol = l[:2]
90+
symbol = literal[:2]
8691
if symbol in list(self.literals.keys()):
87-
return True
92+
return True
8893

8994
self.assign(model)
9095
result = False
9196
for j in self.literals.values():
92-
if j == True:
97+
if j is True:
9398
return True
94-
elif j == None:
99+
elif j is None:
95100
return None
96101
for j in self.literals.values():
97102
result = result or j
98103
return result
99104

100-
class Formula():
105+
106+
class Formula:
101107
"""
102108
A formula represented in CNF.
103109
A formula is a set of clauses.
104110
For example,
105-
{{A1, A2, A3'}, {A5', A2', A1}} is the formula ((A1 v A2 v A3') and (A5' v A2' v A1))
106-
111+
{{A1, A2, A3'}, {A5', A2', A1}} is ((A1 v A2 v A3') and (A5' v A2' v A1))
112+
107113
Create two clauses and a formula with them
108114
>>> c1 = Clause(["A1", "A2'", "A3"])
109115
>>> c2 = Clause(["A5'", "A2'", "A1"])
@@ -112,27 +118,29 @@ class Formula():
112118
>>> print(f)
113119
{ { A1 , A2' , A3 } , { A5' , A2' , A1 } }
114120
"""
121+
115122
def __init__(self, clauses):
116123
"""
117124
Represent the number of clauses and the clauses themselves.
118125
"""
119126
self.clauses = [c for c in clauses]
120127
self.no_of_clauses = len(self.clauses)
121-
128+
122129
def __str__(self):
123130
"""
124-
To print a formula as in CNF.
125-
Variable formula holds the string representation.
131+
To print a formula as in CNF.
132+
Variable formula holds the string representation.
126133
"""
127134
formula = "{ "
128135
for i in range(0, self.no_of_clauses):
129-
formula += (str(self.clauses[i]) + " ")
136+
formula += str(self.clauses[i]) + " "
130137
if i != self.no_of_clauses - 1:
131138
formula += ", "
132139
formula += "}"
133-
140+
134141
return formula
135142

143+
136144
def generate_clause():
137145
"""
138146
Randomly generate a clause.
@@ -144,18 +152,19 @@ def generate_clause():
144152
i = 0
145153
while i < no_of_literals:
146154
var_no = random.randint(1, 5)
147-
var_name = base_var+str(var_no)
155+
var_name = base_var + str(var_no)
148156
var_complement = random.randint(0, 1)
149157
if var_complement == 1:
150158
var_name += "'"
151159
if var_name in literals:
152160
i -= 1
153161
else:
154162
literals.append(var_name)
155-
i+=1
163+
i += 1
156164
clause = Clause(literals)
157165
return clause
158166

167+
159168
def generate_formula():
160169
"""
161170
Randomly generate a formula.
@@ -173,6 +182,7 @@ def generate_formula():
173182
formula = Formula(set(clauses))
174183
return formula
175184

185+
176186
def generate_parameters(formula):
177187
"""
178188
Return the clauses and symbols from a formula.
@@ -198,26 +208,30 @@ def generate_parameters(formula):
198208
for literal in clause.literals.keys():
199209
symbol = literal[:2]
200210
if symbol not in symbols_set:
201-
symbols_set.append(symbol)
211+
symbols_set.append(symbol)
202212
return clauses, symbols_set
203213

214+
204215
def find_pure_symbols(clauses, symbols, model):
205216
"""
206-
Return pure symbols and their assignments to satisfy the clause, if figurable.
207-
Pure symbols are symbols in a formula that exist only in one form, either complemented or otherwise.
217+
Return pure symbols and their values to satisfy clause.
218+
Pure symbols are symbols in a formula that exist only
219+
in one form, either complemented or otherwise.
208220
For example,
209-
{ { A4 , A3 , A5' , A1 , A3' } , { A4 } , { A3 } } has the pure symbols A4, A5' and A1.
221+
{ { A4 , A3 , A5' , A1 , A3' } , { A4 } , { A3 } } has
222+
pure symbols A4, A5' and A1.
210223
This has the following steps:
211-
1. Ignore clauses that have already evaluated to be True.
224+
1. Ignore clauses that have already evaluated to be True.
212225
2. Find symbols that occur only in one form in the rest of the clauses.
213-
3. Assign value True or False depending on whether the symbols occurs in normal or complemented form respectively.
214-
226+
3. Assign value True or False depending on whether the symbols occurs
227+
in normal or complemented form respectively.
228+
215229
>>> c1 = Clause(["A1", "A2'", "A3"])
216230
>>> c2 = Clause(["A5'", "A2'", "A1"])
217231
218232
>>> f = Formula([c1, c2])
219233
>>> c, s = generate_parameters(f)
220-
234+
221235
>>> model = {}
222236
>>> p, v = find_pure_symbols(c, s, model)
223237
>>> print(p, v)
@@ -226,38 +240,42 @@ def find_pure_symbols(clauses, symbols, model):
226240
pure_symbols = []
227241
assignment = dict()
228242
literals = []
229-
243+
230244
for clause in clauses:
231-
if clause.evaluate(model) == True:
245+
if clause.evaluate(model) is True:
232246
continue
233-
for l in clause.literals.keys():
234-
literals.append(l)
247+
for literal in clause.literals.keys():
248+
literals.append(literal)
235249

236250
for s in symbols:
237-
sym = (s + "'")
238-
if (s in literals and sym not in literals) or (s not in literals and sym in literals):
251+
sym = s + "'"
252+
if (s in literals and sym not in literals) or (
253+
s not in literals and sym in literals
254+
):
239255
pure_symbols.append(s)
240256
for p in pure_symbols:
241257
assignment[p] = None
242258
for s in pure_symbols:
243-
sym = (s + "'")
259+
sym = s + "'"
244260
if s in literals:
245261
assignment[s] = True
246262
elif sym in literals:
247263
assignment[s] = False
248264
return pure_symbols, assignment
249265

266+
250267
def find_unit_clauses(clauses, model):
251268
"""
252-
Returns the unit symbols and their assignments to satisfy the clause, if figurable.
269+
Returns the unit symbols and their values to satisfy clause.
253270
Unit symbols are symbols in a formula that are:
254-
- Either the only symbol in a clause
255-
- Or all other literals in that clause have been assigned False
271+
- Either the only symbol in a clause
272+
- Or all other literals in that clause have been assigned False
256273
This has the following steps:
257274
1. Find symbols that are the only occurences in a clause.
258-
2. Find symbols in a clause where all other literals are assigned to be False.
259-
3. Assign True or False depending on whether the symbols occurs in normal or complemented form respectively.
260-
275+
2. Find symbols in a clause where all other literals are assigned False.
276+
3. Assign True or False depending on whether the symbols occurs in
277+
normal or complemented form respectively.
278+
261279
>>> c1 = Clause(["A4", "A3", "A5'", "A1", "A3'"])
262280
>>> c2 = Clause(["A4"])
263281
>>> c3 = Clause(["A3"])
@@ -277,11 +295,11 @@ def find_unit_clauses(clauses, model):
277295
unit_symbols.append(list(clause.literals.keys())[0])
278296
else:
279297
Fcount, Ncount = 0, 0
280-
for l,v in clause.literals.items():
281-
if v == False:
298+
for literal, value in clause.literals.items():
299+
if value is False:
282300
Fcount += 1
283-
elif v == None:
284-
sym = l
301+
elif value is None:
302+
sym = literal
285303
Ncount += 1
286304
if Fcount == clause.no_of_literals - 1 and Ncount == 1:
287305
unit_symbols.append(sym)
@@ -296,6 +314,7 @@ def find_unit_clauses(clauses, model):
296314

297315
return unit_symbols, assignment
298316

317+
299318
def dpll_algorithm(clauses, symbols, model):
300319
"""
301320
Returns the model if the formula is satisfiable, else None
@@ -321,35 +340,35 @@ def dpll_algorithm(clauses, symbols, model):
321340
check_clause_all_true = True
322341
for clause in clauses:
323342
clause_check = clause.evaluate(model)
324-
if clause_check == False:
343+
if clause_check is False:
325344
return False, None
326-
elif clause_check == None:
345+
elif clause_check is None:
327346
check_clause_all_true = False
328347
continue
329348

330349
if check_clause_all_true:
331350
return True, model
332-
351+
333352
pure_symbols, assignment = find_pure_symbols(clauses, symbols, model)
334353
P = None
335354
if len(pure_symbols) > 0:
336355
P, value = pure_symbols[0], assignment[pure_symbols[0]]
337-
356+
338357
if P:
339358
tmp_model = model
340359
tmp_model[P] = value
341360
tmp_symbols = [i for i in symbols]
342361
if P in tmp_symbols:
343362
tmp_symbols.remove(P)
344363
return dpll_algorithm(clauses, tmp_symbols, tmp_model)
345-
346-
unit_symbols, assignment = find_unit_clauses(clauses, model)
364+
365+
unit_symbols, assignment = find_unit_clauses(clauses, model)
347366
P = None
348367
if len(unit_symbols) > 0:
349368
P, value = unit_symbols[0], assignment[unit_symbols[0]]
350369
if P:
351370
tmp_model = model
352-
tmp_model[P]=value
371+
tmp_model[P] = value
353372
tmp_symbols = [i for i in symbols]
354373
if P in tmp_symbols:
355374
tmp_symbols.remove(P)
@@ -361,12 +380,14 @@ def dpll_algorithm(clauses, symbols, model):
361380

362381
return dpll_algorithm(clauses, rest, tmp1) or dpll_algorithm(clauses, rest, tmp2)
363382

383+
364384
if __name__ == "__main__":
365385
import doctest
386+
366387
doctest.testmod()
367388

368389
formula = generate_formula()
369-
print(f'The formula {formula} is', end = " ")
390+
print(f"The formula {formula} is", end=" ")
370391

371392
clauses, symbols = generate_parameters(formula)
372393

0 commit comments

Comments
 (0)