@@ -309,7 +309,7 @@ def parse_definite_clause(s):
309
309
310
310
## Useful constant Exprs used in examples and code:
311
311
TRUE , FALSE , ZERO , ONE , TWO = map (Expr , ['TRUE' , 'FALSE' , 0 , 1 , 2 ])
312
- A , B , C , F , G , P , Q , x , y , z = map (Expr , 'ABCFGPQxyz ' )
312
+ A , B , C , D , E , F , G , P , Q , x , y , z = map (Expr , 'ABCDEFGPQxyz ' )
313
313
314
314
#______________________________________________________________________________
315
315
@@ -683,33 +683,50 @@ def find_pure_symbol(symbols, clauses):
683
683
return None , None
684
684
685
685
def find_unit_clause (clauses , model ):
686
- """A unit clause has only 1 variable that is not bound in the model.
687
- >>> find_unit_clause([A|B|C, B|~C, A|~B], {A:True})
686
+ """Find a forced assignment if possible from a clause with only 1
687
+ variable not bound in the model.
688
+ >>> find_unit_clause([A|B|C, B|~C, ~A|~B], {A:True})
688
689
(B, False)
689
690
"""
690
691
for clause in clauses :
691
- num_not_in_model = 0
692
- for literal in disjuncts (clause ):
693
- sym = literal_symbol (literal )
694
- if sym not in model :
695
- num_not_in_model += 1
696
- P , value = sym , (literal .op != '~' )
697
- if num_not_in_model == 1 :
698
- return P , value
692
+ P , value = unit_clause_assign (clause , model )
693
+ if P : return P , value
699
694
return None , None
700
695
701
-
702
- def literal_symbol (literal ):
703
- """The symbol in this literal (without the negation).
704
- >>> literal_symbol(P)
705
- P
706
- >>> literal_symbol(~P)
707
- P
696
+ def unit_clause_assign (clause , model ):
697
+ """Return a single variable/value pair that makes clause true in
698
+ the model, if possible.
699
+ >>> unit_clause_assign(A|B|C, {A:True})
700
+ (None, None)
701
+ >>> unit_clause_assign(B|~C, {A:True})
702
+ (None, None)
703
+ >>> unit_clause_assign(~A|~B, {A:True})
704
+ (B, False)
705
+ """
706
+ P , value = None , None
707
+ for literal in disjuncts (clause ):
708
+ sym , positive = inspect_literal (literal )
709
+ if sym in model :
710
+ if model [sym ] == positive :
711
+ return None , None # clause already True
712
+ elif P :
713
+ return None , None # more than 1 unbound variable
714
+ else :
715
+ P , value = sym , positive
716
+ return P , value
717
+
718
+ def inspect_literal (literal ):
719
+ """The symbol in this literal, and the value it should take to
720
+ make the literal true.
721
+ >>> inspect_literal(P)
722
+ (P, True)
723
+ >>> inspect_literal(~P)
724
+ (P, False)
708
725
"""
709
726
if literal .op == '~' :
710
- return literal .args [0 ]
727
+ return literal .args [0 ], False
711
728
else :
712
- return literal
729
+ return literal , True
713
730
714
731
#______________________________________________________________________________
715
732
# Walk-SAT [Fig. 7.18]
@@ -1153,6 +1170,10 @@ class logicTest: """
1153
1170
>>> tt_true(A & B)
1154
1171
False
1155
1172
1173
+ ### An earlier version of the code failed on this:
1174
+ >>> dpll_satisfiable(A & ~B & C & (A | ~D) & (~E | ~D) & (C | ~D) & (~A | ~F) & (E | ~F) & (~D | ~F) & (B | ~C | D) & (A | ~E | F) & (~A | E | D))
1175
+ {B: False, C: True, A: True, F: False, D: True, E: False}
1176
+
1156
1177
### [Fig. 7.13]
1157
1178
>>> alpha = expr("~P12")
1158
1179
>>> to_cnf(Fig[7,13] & ~alpha)
0 commit comments