1
+ #!/usr/bin/env python3
2
+
1
3
"""
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.
6
8
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
9
10
"""
10
11
11
12
import random
14
15
15
16
class Clause :
16
17
"""
17
- A clause represented in CNF .
18
+ A clause represented in Conjunctive Normal Form .
18
19
A clause is a set of literals, either complemented or otherwise.
19
20
For example:
20
21
{A1, A2, A3'} is the clause (A1 v A2 v A3')
21
22
{A5', A2', A1} is the clause (A5' v A2' v A1)
22
23
23
- Create a set of literals and a clause with them
24
- >>> str(Clause(["A1", "A2'", "A3"]))
25
- "{A1 , A2' , A3}"
26
-
27
24
Create model
28
25
>>> clause = Clause(["A1", "A2'", "A3"])
29
26
>>> clause.evaluate({"A1": True})
@@ -36,32 +33,40 @@ def __init__(self, literals: List[int]) -> None:
36
33
"""
37
34
# Assign all literals to None initially
38
35
self .literals = {literal : None for literal in literals }
39
- self .no_of_literals = len (self .literals )
40
36
41
37
def __str__ (self ) -> str :
42
38
"""
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}"
44
42
"""
45
43
return "{" + " , " .join (self .literals ) + "}"
46
44
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
+
47
55
def assign (self , model : Dict [str , bool ]) -> None :
48
56
"""
49
57
Assign values to literals of the clause as given by model.
50
58
"""
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 ]
56
63
else :
57
- i += 1
58
64
continue
59
- if val is not None :
65
+ if value is not None :
60
66
# 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
65
70
66
71
def evaluate (self , model : Dict [str , bool ]) -> bool :
67
72
"""
@@ -73,48 +78,36 @@ def evaluate(self, model: Dict[str, bool]) -> bool:
73
78
4. Compute disjunction of all values assigned in clause.
74
79
"""
75
80
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
84
84
85
85
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 ())
93
90
94
91
95
92
class Formula :
96
93
"""
97
- A formula represented in CNF .
94
+ A formula represented in Conjunctive Normal Form .
98
95
A formula is a set of clauses.
99
96
For example,
100
97
{{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}}"
106
98
"""
107
99
108
100
def __init__ (self , clauses : List [Clause ]) -> None :
109
101
"""
110
102
Represent the number of clauses and the clauses themselves.
111
103
"""
112
- self .clauses = [c for c in clauses ]
113
- self .no_of_clauses = len (self .clauses )
104
+ self .clauses = list (clauses )
114
105
115
106
def __str__ (self ) -> str :
116
107
"""
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}}"
118
111
"""
119
112
return "{" + " , " .join (str (clause ) for clause in self .clauses ) + "}"
120
113
@@ -146,16 +139,10 @@ def generate_formula() -> Formula:
146
139
"""
147
140
Randomly generate a formula.
148
141
"""
149
- clauses = []
142
+ clauses = set ()
150
143
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 ())
159
146
return Formula (set (clauses ))
160
147
161
148
@@ -264,7 +251,7 @@ def find_unit_clauses(
264
251
"""
265
252
unit_symbols = []
266
253
for clause in clauses :
267
- if clause . no_of_literals == 1 :
254
+ if len ( clause ) == 1 :
268
255
unit_symbols .append (list (clause .literals .keys ())[0 ])
269
256
else :
270
257
Fcount , Ncount = 0 , 0
@@ -274,7 +261,7 @@ def find_unit_clauses(
274
261
elif value is None :
275
262
sym = literal
276
263
Ncount += 1
277
- if Fcount == clause . no_of_literals - 1 and Ncount == 1 :
264
+ if Fcount == len ( clause ) - 1 and Ncount == 1 :
278
265
unit_symbols .append (sym )
279
266
assignment = dict ()
280
267
for i in unit_symbols :
@@ -317,7 +304,11 @@ def dpll_algorithm(
317
304
if check_clause_all_true :
318
305
return True , model
319
306
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 , {}
321
312
P = None
322
313
if len (pure_symbols ) > 0 :
323
314
P , value = pure_symbols [0 ], assignment [pure_symbols [0 ]]
@@ -355,14 +346,12 @@ def dpll_algorithm(
355
346
doctest .testmod ()
356
347
357
348
formula = generate_formula ()
358
- print (f"The formula \n { formula } \n is" , end = " " )
349
+ print (f"The formula { formula } is" , end = " " )
359
350
360
351
clauses , symbols = generate_parameters (formula )
361
-
362
352
solution , model = dpll_algorithm (clauses , symbols , {})
363
353
364
354
if solution :
365
- print (" satisfiable with the assignment: " )
366
- print (model )
355
+ print (f"satisfiable with the assignment { model } ." )
367
356
else :
368
- print (" not satisfiable" )
357
+ print ("not satisfiable. " )
0 commit comments