@@ -434,7 +434,7 @@ def pl_true(exp, model={}):
434
434
435
435
def to_cnf(s):
436
436
"""Convert a propositional logical sentence s to conjunctive normal form.
437
- That is, of the form ((A | ~B | ...) & (B | C | ...) & ...) [p. 215]
437
+ That is, to the form ((A | ~B | ...) & (B | C | ...) & ...) [p. 215]
438
438
>>> to_cnf("~(B|C)")
439
439
(~B & ~C)
440
440
>>> to_cnf("B <=> (P1|P2)")
@@ -443,6 +443,8 @@ def to_cnf(s):
443
443
((b | a | d) & (c | a | d))
444
444
>>> to_cnf("A & (B | (D & E))")
445
445
(A & (D | B) & (E | B))
446
+ >>> to_cnf("A | (B | (C | (D & E)))")
447
+ ((D | A | B | C) & (E | A | B | C))
446
448
"""
447
449
if isinstance(s, str): s = expr(s)
448
450
s = eliminate_implications(s) # Steps 1, 2 from p. 215
@@ -454,6 +456,8 @@ def eliminate_implications(s):
454
456
that is equivalent to s, but has only &, |, and ~ as logical operators.
455
457
>>> eliminate_implications(A >> (~B << C))
456
458
((~B | ~C) | ~A)
459
+ >>> eliminate_implications(A ^ B)
460
+ ((A & ~B) | (~A & B))
457
461
"""
458
462
if not s.args or is_symbol(s.op): return s ## (Atoms are unchanged.)
459
463
args = map(eliminate_implications, s.args)
@@ -464,7 +468,11 @@ def eliminate_implications(s):
464
468
return (a | ~b)
465
469
elif s.op == '<=>':
466
470
return (a | ~b) & (b | ~a)
471
+ elif s.op == '^':
472
+ assert len(args) == 2 ## TODO: relax this restriction
473
+ return (a & ~b) | (~a & b)
467
474
else:
475
+ assert s.op in ('&', '|', '~')
468
476
return Expr(s.op, *args)
469
477
470
478
def move_not_inwards(s):
@@ -522,14 +530,18 @@ def NaryExpr(op, *args):
522
530
nested instances of the same op up to the top level.
523
531
>>> NaryExpr('&', (A&B),(B|C),(B&C))
524
532
(A & B & (B | C) & B & C)
533
+ >>> NaryExpr('|', A|(B|(C|(A&B))))
534
+ (A | B | C | (A & B))
525
535
"""
526
536
arglist = []
527
- for arg in args:
528
- if arg.op == op: arglist.extend(arg.args)
529
- else: arglist.append(arg)
530
- if len(args) == 1:
531
- return args[0]
532
- elif len(args) == 0:
537
+ def collect(subargs):
538
+ for arg in subargs:
539
+ if arg.op == op: collect(arg.args)
540
+ else: arglist.append(arg)
541
+ collect(args)
542
+ if len(arglist) == 1:
543
+ return arglist[0]
544
+ elif len(arglist) == 0:
533
545
return _NaryExprTable[op]
534
546
else:
535
547
return Expr(op, *arglist)
0 commit comments