Skip to content

Commit cc74417

Browse files
authored
Update and rename dpll.py to davis–putnam–logemann–loveland.py
1 parent 3a0d65b commit cc74417

File tree

1 file changed

+54
-65
lines changed

1 file changed

+54
-65
lines changed

other/dpll.py renamed to other/davis–putnam–logemann–loveland.py

Lines changed: 54 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
#!/usr/bin/env python3
2+
13
"""
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 Conjunctive Normal Form SATisfiability(CNF-SAT) problem.
4+
Davis–Putnam–Logemann–Loveland (DPLL) algorithm is a complete, backtracking-based
5+
search algorithm for deciding the satisfiability of propositional logic formulae in
6+
conjunctive normal form, i.e, for solving the Conjunctive Normal Form SATisfiability
7+
(CNF-SAT) problem.
68
7-
For more information about the algorithm:
8-
https://en.wikipedia.org/wiki/DPLL_algorithm
9+
For more information about the algorithm: https://en.wikipedia.org/wiki/DPLL_algorithm
910
"""
1011

1112
import random
@@ -14,16 +15,12 @@
1415

1516
class Clause:
1617
"""
17-
A clause represented in CNF.
18+
A clause represented in Conjunctive Normal Form.
1819
A clause is a set of literals, either complemented or otherwise.
1920
For example:
2021
{A1, A2, A3'} is the clause (A1 v A2 v A3')
2122
{A5', A2', A1} is the clause (A5' v A2' v A1)
2223
23-
Create a set of literals and a clause with them
24-
>>> str(Clause(["A1", "A2'", "A3"]))
25-
"{A1 , A2' , A3}"
26-
2724
Create model
2825
>>> clause = Clause(["A1", "A2'", "A3"])
2926
>>> clause.evaluate({"A1": True})
@@ -36,32 +33,40 @@ def __init__(self, literals: List[int]) -> None:
3633
"""
3734
# Assign all literals to None initially
3835
self.literals = {literal: None for literal in literals}
39-
self.no_of_literals = len(self.literals)
4036

4137
def __str__(self) -> str:
4238
"""
43-
To print a clause as in CNF.
39+
To print a clause as in Conjunctive Normal Form.
40+
>>> str(Clause(["A1", "A2'", "A3"]))
41+
"{A1 , A2' , A3}"
4442
"""
4543
return "{" + " , ".join(self.literals) + "}"
4644

45+
def __len__(self) -> int:
46+
"""
47+
To print a clause as in Conjunctive Normal Form.
48+
>>> len(Clause([]))
49+
0
50+
>>> len(Clause(["A1", "A2'", "A3"]))
51+
3
52+
"""
53+
return len(self.literals)
54+
4755
def assign(self, model: Dict[str, bool]) -> None:
4856
"""
4957
Assign values to literals of the clause as given by model.
5058
"""
51-
i = 0
52-
while i < self.no_of_literals:
53-
symbol = list(self.literals.keys())[i][:2]
54-
if symbol in model.keys():
55-
val = model[symbol]
59+
for literal in self.literals:
60+
symbol = literal[:2]
61+
if symbol in model:
62+
value = model[symbol]
5663
else:
57-
i += 1
5864
continue
59-
if val is not None:
65+
if value is not None:
6066
# Complement assignment if literal is in complemented form
61-
if list(self.literals.keys())[i][-1] == "'":
62-
val = not val
63-
self.literals[list(self.literals.keys())[i]] = val
64-
i += 1
67+
if literal.endswith("'"):
68+
value = not value
69+
self.literals[literal] = value
6570

6671
def evaluate(self, model: Dict[str, bool]) -> bool:
6772
"""
@@ -73,48 +78,36 @@ def evaluate(self, model: Dict[str, bool]) -> bool:
7378
4. Compute disjunction of all values assigned in clause.
7479
"""
7580
for literal in self.literals:
76-
if len(literal) == 2:
77-
symbol = literal + "'"
78-
if symbol in self.literals:
79-
return True
80-
else:
81-
symbol = literal[:2]
82-
if symbol in self.literals:
83-
return True
81+
symbol = literal.rstrip("'") if literal.endswith("'") else literal + "'"
82+
if symbol in self.literals:
83+
return True
8484

8585
self.assign(model)
86-
result = False
87-
for j in self.literals.values():
88-
if j in (True, None):
89-
return j
90-
for j in self.literals.values():
91-
result = result or j
92-
return result
86+
for value in self.literals.values():
87+
if value in (True, None):
88+
return value
89+
return any(self.literals.values())
9390

9491

9592
class Formula:
9693
"""
97-
A formula represented in CNF.
94+
A formula represented in Conjunctive Normal Form.
9895
A formula is a set of clauses.
9996
For example,
10097
{{A1, A2, A3'}, {A5', A2', A1}} is ((A1 v A2 v A3') and (A5' v A2' v A1))
101-
102-
Create two clauses and a formula with them
103-
>>> formula = Formula([Clause(["A1", "A2'", "A3"]), Clause(["A5'", "A2'", "A1"])])
104-
>>> str(formula)
105-
"{{A1 , A2' , A3} , {A5' , A2' , A1}}"
10698
"""
10799

108100
def __init__(self, clauses: List[Clause]) -> None:
109101
"""
110102
Represent the number of clauses and the clauses themselves.
111103
"""
112-
self.clauses = [c for c in clauses]
113-
self.no_of_clauses = len(self.clauses)
104+
self.clauses = list(clauses)
114105

115106
def __str__(self) -> str:
116107
"""
117-
To print a formula as in CNF.
108+
To print a formula as in Conjunctive Normal Form.
109+
str(Formula([Clause(["A1", "A2'", "A3"]), Clause(["A5'", "A2'", "A1"])]))
110+
"{{A1 , A2' , A3} , {A5' , A2' , A1}}"
118111
"""
119112
return "{" + " , ".join(str(clause) for clause in self.clauses) + "}"
120113

@@ -146,16 +139,10 @@ def generate_formula() -> Formula:
146139
"""
147140
Randomly generate a formula.
148141
"""
149-
clauses = []
142+
clauses = set()
150143
no_of_clauses = random.randint(1, 10)
151-
i = 0
152-
while i < no_of_clauses:
153-
clause = generate_clause()
154-
if clause in clauses:
155-
i -= 1
156-
else:
157-
clauses.append(clause)
158-
i += 1
144+
while len(clauses) < no_of_clauses:
145+
clauses.add(generate_clause())
159146
return Formula(set(clauses))
160147

161148

@@ -264,7 +251,7 @@ def find_unit_clauses(
264251
"""
265252
unit_symbols = []
266253
for clause in clauses:
267-
if clause.no_of_literals == 1:
254+
if len(clause) == 1:
268255
unit_symbols.append(list(clause.literals.keys())[0])
269256
else:
270257
Fcount, Ncount = 0, 0
@@ -274,7 +261,7 @@ def find_unit_clauses(
274261
elif value is None:
275262
sym = literal
276263
Ncount += 1
277-
if Fcount == clause.no_of_literals - 1 and Ncount == 1:
264+
if Fcount == len(clause) - 1 and Ncount == 1:
278265
unit_symbols.append(sym)
279266
assignment = dict()
280267
for i in unit_symbols:
@@ -317,7 +304,11 @@ def dpll_algorithm(
317304
if check_clause_all_true:
318305
return True, model
319306

320-
pure_symbols, assignment = find_pure_symbols(clauses, symbols, model)
307+
try:
308+
pure_symbols, assignment = find_pure_symbols(clauses, symbols, model)
309+
except RecursionError:
310+
print("raises a RecursionError and is")
311+
return None, {}
321312
P = None
322313
if len(pure_symbols) > 0:
323314
P, value = pure_symbols[0], assignment[pure_symbols[0]]
@@ -355,14 +346,12 @@ def dpll_algorithm(
355346
doctest.testmod()
356347

357348
formula = generate_formula()
358-
print(f"The formula \n{formula}\n is", end=" ")
349+
print(f"The formula {formula} is", end=" ")
359350

360351
clauses, symbols = generate_parameters(formula)
361-
362352
solution, model = dpll_algorithm(clauses, symbols, {})
363353

364354
if solution:
365-
print(" satisfiable with the assignment: ")
366-
print(model)
355+
print(f"satisfiable with the assignment {model}.")
367356
else:
368-
print(" not satisfiable")
357+
print("not satisfiable.")

0 commit comments

Comments
 (0)