Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/logic/boolformula.py
8817 views
1
r"""
2
Module that creates boolean formulas as instances of the BooleanFormula class.
3
4
Formulas consist of the operators ``&``, ``|``, ``~``, ``^``, ``->``, ``<->``,
5
corresponding to ``and``, ``or``, ``not``, ``xor``, ``if...then``, ``if and
6
only if``. Operators can be applied to variables that consist of a leading
7
letter and trailing underscores and alphanumerics. Parentheses may be used
8
to explicitly show order of operation.
9
10
EXAMPLES:
11
12
Create boolean formulas and combine them with ifthen() method::
13
14
sage: import sage.logic.propcalc as propcalc
15
sage: f = propcalc.formula("a&((b|c)^a->c)<->b")
16
sage: g = propcalc.formula("boolean<->algebra")
17
sage: (f&~g).ifthen(f)
18
((a&((b|c)^a->c)<->b)&(~(boolean<->algebra)))->(a&((b|c)^a->c)<->b)
19
20
We can create a truth table from a formula::
21
22
sage: f.truthtable()
23
a b c value
24
False False False True
25
False False True True
26
False True False False
27
False True True False
28
True False False True
29
True False True False
30
True True False True
31
True True True True
32
sage: f.truthtable(end=3)
33
a b c value
34
False False False True
35
False False True True
36
False True False False
37
sage: f.truthtable(start=4)
38
a b c value
39
True False False True
40
True False True False
41
True True False True
42
True True True True
43
sage: propcalc.formula("a").truthtable()
44
a value
45
False False
46
True True
47
48
Now we can evaluate the formula for a given set of inputs::
49
50
sage: f.evaluate({'a':True, 'b':False, 'c':True})
51
False
52
sage: f.evaluate({'a':False, 'b':False, 'c':True})
53
True
54
55
And we can convert a boolean formula to conjunctive normal form::
56
57
sage: f.convert_cnf_table()
58
sage: f
59
(a|~b|c)&(a|~b|~c)&(~a|b|~c)
60
sage: f.convert_cnf_recur()
61
sage: f
62
(a|~b|c)&(a|~b|~c)&(~a|b|~c)
63
64
Or determine if an expression is satisfiable, a contradiction, or a tautology::
65
66
sage: f = propcalc.formula("a|b")
67
sage: f.is_satisfiable()
68
True
69
sage: f = f & ~f
70
sage: f.is_satisfiable()
71
False
72
sage: f.is_contradiction()
73
True
74
sage: f = f | ~f
75
sage: f.is_tautology()
76
True
77
78
The equality operator compares semantic equivalence::
79
80
sage: f = propcalc.formula("(a|b)&c")
81
sage: g = propcalc.formula("c&(b|a)")
82
sage: f == g
83
True
84
sage: g = propcalc.formula("a|b&c")
85
sage: f == g
86
False
87
88
It is an error to create a formula with bad syntax::
89
90
sage: propcalc.formula("")
91
Traceback (most recent call last):
92
...
93
SyntaxError: malformed statement
94
sage: propcalc.formula("a&b~(c|(d)")
95
Traceback (most recent call last):
96
...
97
SyntaxError: malformed statement
98
sage: propcalc.formula("a&&b")
99
Traceback (most recent call last):
100
...
101
SyntaxError: malformed statement
102
sage: propcalc.formula("a&b a")
103
Traceback (most recent call last):
104
...
105
SyntaxError: malformed statement
106
107
It is also an error to not abide by the naming conventions::
108
109
sage: propcalc.formula("~a&9b")
110
Traceback (most recent call last):
111
...
112
NameError: invalid variable name 9b: identifiers must begin with a letter and contain only alphanumerics and underscores
113
114
AUTHORS:
115
116
- Chris Gorecki (2006): initial version
117
118
- Paul Scurek (2013-08-03): added polish_notation, full_tree,
119
updated docstring formatting
120
"""
121
#*****************************************************************************
122
# Copyright (C) 2006 William Stein <wstein.gmail.com>
123
# Copyright (C) 2006 Chris Gorecki <[email protected]>
124
# Copyright (C) 2013 Paul Scurek <[email protected]>
125
#
126
# Distributed under the terms of the GNU General Public License (GPL)
127
# as published by the Free Software Foundation; either version 2 of
128
# the License, or (at your option) any later version.
129
# http://www.gnu.org/licenses/
130
#*****************************************************************************
131
132
import booleval
133
import logictable
134
import logicparser
135
# import boolopt
136
from types import TupleType, ListType
137
from sage.misc.flatten import flatten
138
139
latex_operators = [('&', '\\wedge '),
140
('|', '\\vee '),
141
('~', '\\neg '),
142
('^', '\\oplus '),
143
('<->', '\\leftrightarrow '),
144
('->', '\\rightarrow ')]
145
146
147
class BooleanFormula:
148
__expression = ""
149
__tree = []
150
__vars_order = []
151
152
def __init__(self, exp, tree, vo):
153
r"""
154
Initialize the data fields
155
156
INPUT:
157
158
- ``self`` -- calling object
159
160
- ``exp`` -- a string. This contains the boolean expression
161
to be manipulated
162
163
- ``tree`` -- a list. This contains the parse tree of the expression.
164
165
- ``vo`` -- a list. This contains the variables in the expression, in the
166
order that they appear. Each variable only occurs once in the list.
167
168
OUTPUT:
169
170
None
171
172
EXAMPLES:
173
174
This example illustrates the creation of a statement.
175
176
::
177
178
sage: import sage.logic.propcalc as propcalc
179
sage: s = propcalc.formula("a&b|~(c|a)")
180
sage: s
181
a&b|~(c|a)
182
"""
183
self.__expression = exp.replace(' ', '')
184
self.__tree = tree
185
self.__vars_order = vo
186
187
def __repr__(self):
188
r"""
189
Return a string representation of this statement.
190
191
INPUT:
192
193
- ``self`` -- calling object
194
195
OUTPUT:
196
197
A string representation of calling statement
198
199
EXAMPLES:
200
201
This example illustrates how a statement is represented with __repr__.
202
203
::
204
205
sage: import sage.logic.propcalc as propcalc
206
sage: propcalc.formula("man->monkey&human")
207
man->monkey&human
208
"""
209
return self.__expression
210
211
def _latex_(self):
212
r"""
213
Return a LaTeX representation of this statement.
214
215
INPUT:
216
217
- ``self`` -- calling object
218
219
OUTPUT:
220
221
A string containing the latex code for the statement
222
223
EXAMPLES:
224
225
This example shows how to get the latex code for a boolean formula.
226
227
::
228
229
sage: import sage.logic.propcalc as propcalc
230
sage: s = propcalc.formula("man->monkey&human")
231
sage: latex(s)
232
man\rightarrow monkey\wedge human
233
234
::
235
236
sage: f = propcalc.formula("a & ((~b | c) ^ a -> c) <-> ~b")
237
sage: latex(f)
238
a\wedge ((\neg b\vee c)\oplus a\rightarrow c)\leftrightarrow \neg b
239
"""
240
latex_expression = self.__expression
241
for old, new in latex_operators:
242
latex_expression = latex_expression.replace(old, new)
243
return latex_expression
244
245
def polish_notation(self):
246
r"""
247
Convert the calling boolean formula into polish notation
248
249
INPUT:
250
251
- ``self`` -- calling object
252
253
OUTPUT:
254
255
A string representation of the formula in polish notation.
256
257
EXAMPLES:
258
259
This example illustrates converting a formula to polish notation.
260
261
::
262
263
sage: import sage.logic.propcalc as propcalc
264
sage: f = propcalc.formula("~~a|(c->b)")
265
sage: f.polish_notation()
266
'|~~a->cb'
267
268
::
269
270
sage: g = propcalc.formula("(a|~b)->c")
271
sage: g.polish_notation()
272
'->|a~bc'
273
274
AUTHORS:
275
276
- Paul Scurek (2013-08-03)
277
"""
278
return ''.join(flatten(logicparser.polish_parse(repr(self))))
279
280
def tree(self):
281
r"""
282
Return the parse tree of this boolean expression.
283
284
INPUT:
285
286
- ``self`` -- calling object
287
288
OUTPUT:
289
290
The parse tree as a nested list
291
292
EXAMPLES:
293
294
This example illustrates how to find the parse tree of a boolean formula.
295
296
::
297
298
sage: import sage.logic.propcalc as propcalc
299
sage: s = propcalc.formula("man -> monkey & human")
300
sage: s.tree()
301
['->', 'man', ['&', 'monkey', 'human']]
302
303
::
304
305
sage: f = propcalc.formula("a & ((~b | c) ^ a -> c) <-> ~b")
306
sage: f.tree()
307
['<->',
308
['&', 'a', ['->', ['^', ['|', ['~', 'b', None], 'c'], 'a'], 'c']],
309
['~', 'b', None]]
310
311
.. NOTE::
312
313
This function is used by other functions in the logic module
314
that perform semantic operations on a boolean formula.
315
"""
316
return self.__tree
317
318
def full_tree(self):
319
r"""
320
Return a full syntax parse tree of the calling formula.
321
322
INPUT:
323
324
- ``self`` -- calling object. This is a boolean formula.
325
326
OUTPUT:
327
328
The full syntax parse tree as a nested list
329
330
EXAMPLES:
331
332
This example shows how to find the full syntax parse tree of a formula.
333
334
::
335
336
sage: import sage.logic.propcalc as propcalc
337
sage: s = propcalc.formula("a->(b&c)")
338
sage: s.full_tree()
339
['->', 'a', ['&', 'b', 'c']]
340
341
::
342
343
sage: t = propcalc.formula("a & ((~b | c) ^ a -> c) <-> ~b")
344
sage: t.full_tree()
345
['<->', ['&', 'a', ['->', ['^', ['|', ['~', 'b'], 'c'], 'a'], 'c']], ['~', 'b']]
346
347
::
348
349
sage: f = propcalc.formula("~~(a&~b)")
350
sage: f.full_tree()
351
['~', ['~', ['&', 'a', ['~', 'b']]]]
352
353
.. NOTE::
354
355
This function is used by other functions in the logic module
356
that perform syntactic operations on a boolean formula.
357
358
AUTHORS:
359
360
- Paul Scurek (2013-08-03)
361
"""
362
return logicparser.polish_parse(repr(self))
363
364
def __or__(self, other):
365
r"""
366
Overload the | operator to 'or' two statements together.
367
368
INPUT:
369
370
- ``self`` -- calling object. This is the statement on
371
the left side of the operator.
372
373
- ``other`` -- a boolean formula. This is the statement
374
on the right side of the operator.
375
376
OUTPUT:
377
378
A boolean formula of the following form:
379
380
``self`` | ``other``
381
382
EXAMPLES:
383
384
This example illustrates combining two formulas with '|'.
385
386
::
387
388
sage: import sage.logic.propcalc as propcalc
389
sage: s = propcalc.formula("a&b")
390
sage: f = propcalc.formula("c^d")
391
sage: s | f
392
(a&b)|(c^d)
393
"""
394
return self.add_statement(other, '|')
395
396
def __and__(self, other):
397
r"""
398
Overload the & operator to 'and' two statements together.
399
400
INPUT:
401
402
- ``self`` -- calling object. This is the formula on the
403
left side of the operator.
404
405
- ``other`` -- a boolean formula. This is the formula on
406
the right side of the operator.
407
408
OUTPUT:
409
410
A boolean formula of the following form:
411
412
``self`` & ``other``
413
414
EXAMPLES:
415
416
This example shows how to combine two formulas with '&'.
417
418
::
419
420
sage: import sage.logic.propcalc as propcalc
421
sage: s = propcalc.formula("a&b")
422
sage: f = propcalc.formula("c^d")
423
sage: s & f
424
(a&b)&(c^d)
425
"""
426
return self.add_statement(other, '&')
427
428
def __xor__(self, other):
429
r"""
430
Overload the ^ operator to xor two statements together.
431
432
INPUT:
433
434
- ``self`` -- calling object. This is the formula on the
435
left side of the operator.
436
437
- ``other`` -- a boolean formula. This is the formula on
438
the right side of the operator.
439
440
OUTPUT:
441
442
A boolean formula of the following form:
443
444
``self`` ^ ``other``
445
446
EXAMPLES:
447
448
This example illustrates how to combine two formulas with '^'.
449
450
::
451
452
sage: import sage.logic.propcalc as propcalc
453
sage: s = propcalc.formula("a&b")
454
sage: f = propcalc.formula("c^d")
455
sage: s ^ f
456
(a&b)^(c^d)
457
"""
458
return self.add_statement(other, '^')
459
460
def __pow__(self, other):
461
r"""
462
Overload the ^ operator to xor two statements together.
463
464
INPUT:
465
466
- ``self`` -- calling object. This is the formula on the
467
left side of the operator.
468
469
- ``other`` -- a boolean formula. This is the formula on
470
the right side of the operator.
471
472
OUTPUT:
473
474
A boolean formula of the following form:
475
476
``self`` ^ ``other``
477
478
EXAMPLES:
479
480
This example shows how to combine two formulas with '^'.
481
482
::
483
484
sage: import sage.logic.propcalc as propcalc
485
sage: s = propcalc.formula("a&b")
486
sage: f = propcalc.formula("c^d")
487
sage: s ^ f
488
(a&b)^(c^d)
489
490
.. TODO::
491
492
This function seems to be identical to __xor__.
493
Thus, this function should be replaced with __xor__ everywhere
494
that it appears in the logic module. Then it can be deleted
495
altogether.
496
"""
497
return self.add_statement(other, '^')
498
499
def __invert__(self):
500
r"""
501
Overload the ~ operator to not a statement.
502
503
INPUT:
504
505
- ``self`` -- calling object. This is the formula on the
506
right side of the operator.
507
508
OUTPUT:
509
510
A boolean formula of the following form:
511
512
~``self``
513
514
EXAMPLES:
515
516
This example shows how to negate a boolean formula.
517
518
::
519
520
sage: import sage.logic.propcalc as propcalc
521
sage: s = propcalc.formula("a&b")
522
sage: ~s
523
~(a&b)
524
"""
525
exp = '~(' + self.__expression + ')'
526
parse_tree, vars_order = logicparser.parse(exp)
527
return BooleanFormula(exp, parse_tree, vars_order)
528
529
def ifthen(self, other):
530
r"""
531
Combine two formulas with the -> operator.
532
533
INPUT:
534
535
- ``self`` -- calling object. This is the formula on
536
the left side of the operator.
537
538
- ``other`` -- a boolean formula. This is the formula
539
on the right side of the operator.
540
541
OUTPUT:
542
543
A boolean formula of the following form:
544
545
``self`` -> ``other``
546
547
EXAMPLES:
548
549
This example illustrates how to combine two formulas with '->'.
550
551
::
552
553
sage: import sage.logic.propcalc as propcalc
554
sage: s = propcalc.formula("a&b")
555
sage: f = propcalc.formula("c^d")
556
sage: s.ifthen(f)
557
(a&b)->(c^d)
558
"""
559
return self.add_statement(other, '->')
560
561
def iff(self, other):
562
r"""
563
Combine two formulas with the <-> operator.
564
565
INPUT:
566
567
- ``self`` -- calling object. This is the formula
568
on the left side of the operator.
569
570
- ``other`` -- a boolean formula. This is the formula
571
on the right side of the operator.
572
573
OUTPUT:
574
575
A boolean formula of the following form:
576
577
``self`` <-> ``other``
578
579
EXAMPLES:
580
581
This example illustrates how to combine two formulas with '<->'.
582
583
::
584
585
sage: import sage.logic.propcalc as propcalc
586
sage: s = propcalc.formula("a&b")
587
sage: f = propcalc.formula("c^d")
588
sage: s.iff(f)
589
(a&b)<->(c^d)
590
"""
591
return self.add_statement(other, '<->')
592
593
def __eq__(self, other):
594
r"""
595
Overload the == operator to deterine logical equivalence.
596
597
INPUT:
598
599
- ``self`` -- calling object. This is the formula on
600
the left side of the comparator.
601
602
- ``other`` -- a boolean formula. This is the formula
603
on the right side of the comparator.
604
605
OUTPUT:
606
607
A boolean value to be determined as follows:
608
609
True - if ``self`` and ``other`` are logically equivalent
610
611
False - if ``self`` and ``other`` are not logically equivalent
612
613
EXAMPLES:
614
615
This example shows how to determine logical equivalence.
616
617
::
618
619
sage: import sage.logic.propcalc as propcalc
620
sage: f = propcalc.formula("(a|b)&c")
621
sage: g = propcalc.formula("c&(b|a)")
622
sage: f == g
623
True
624
625
::
626
627
sage: g = propcalc.formula("a|b&c")
628
sage: f == g
629
False
630
"""
631
return self.equivalent(other)
632
633
def truthtable(self, start=0, end=-1):
634
r"""
635
Return a truth table for the calling formula.
636
637
INPUT:
638
639
- ``self`` -- calling object
640
641
- ``start`` -- (default: 0) an integer. This is the first
642
row of the truth table to be created.
643
644
- ``end`` -- (default: -1) an integer. This is the laste
645
row of the truth table to be created.
646
647
OUTPUT:
648
649
The truth table as a 2-D array
650
651
EXAMPLES:
652
653
This example illustrates the creation of a truth table.
654
655
::
656
657
sage: import sage.logic.propcalc as propcalc
658
sage: s = propcalc.formula("a&b|~(c|a)")
659
sage: s.truthtable()
660
a b c value
661
False False False True
662
False False True False
663
False True False True
664
False True True False
665
True False False False
666
True False True False
667
True True False True
668
True True True True
669
670
We can now create a truthtable of rows 1 to 4, inclusive.
671
672
::
673
674
sage: s.truthtable(1, 5)
675
a b c value
676
False False True False
677
False True False True
678
False True True False
679
True False False False
680
681
.. NOTE::
682
683
Each row of the table corresponds to a binary number, with
684
each variable associated to a column of the number, and taking on
685
a true value if that column has a value of 1. Please see the
686
logictable module for details. The function returns a table that
687
start inclusive and end exclusive so truthtable(0, 2) will include
688
row 0, but not row 2.
689
690
When sent with no start or end parameters, this is an
691
exponential time function requiring O(2**n) time, where
692
n is the number of variables in the expression.
693
"""
694
max = 2 ** len(self.__vars_order)
695
if end < 0:
696
end = max
697
if end > max:
698
end = max
699
if start < 0:
700
start = 0
701
if start > max:
702
start = max
703
keys, table = [], []
704
vars = {}
705
for var in self.__vars_order:
706
vars[var] = False
707
keys.insert(0, var)
708
keys = list(keys)
709
for i in range(start, end):
710
j = 0
711
row = []
712
for key in keys:
713
bit = self.get_bit(i, j)
714
vars[key] = bit
715
j += 1
716
row.insert(0, bit)
717
row.append(booleval.eval_formula(self.__tree, vars))
718
table.append(row)
719
keys.reverse()
720
table = logictable.Truthtable(table, keys)
721
return table
722
723
def evaluate(self, var_values):
724
r"""
725
Evaluate a formula for the given input values.
726
727
INPUT:
728
729
- ``self`` -- calling object
730
731
- ``var_values`` -- a dictionary. This contains the
732
pairs of variables and their boolean values.
733
734
OUTPUT:
735
736
The result of the evaluation as a boolean.
737
738
EXAMPLES:
739
740
This example illustrates the evaluation of a boolean formula.
741
742
::
743
744
sage: import sage.logic.propcalc as propcalc
745
sage: f = propcalc.formula("a&b|c")
746
sage: f.evaluate({'a':False, 'b':False, 'c':True})
747
True
748
749
::
750
751
sage: f.evaluate({'a':True, 'b':False, 'c':False})
752
False
753
"""
754
return booleval.eval_formula(self.__tree, var_values)
755
756
def is_satisfiable(self):
757
r"""
758
Determine if the formula is True for some assignment of values.
759
760
INPUT:
761
762
- ``self`` -- calling object
763
764
OUTPUT:
765
766
A boolean value to be determined as follows:
767
768
True - if there is an assignment of values that makes the formula True
769
770
False - if the formula cannot be made True by any assignment of values
771
772
EXAMPLES:
773
774
This example illustrates how to check a formula for satisfiability.
775
776
::
777
778
sage: import sage.logic.propcalc as propcalc
779
sage: f = propcalc.formula("a|b")
780
sage: f.is_satisfiable()
781
True
782
783
::
784
785
sage: g = f & (~f)
786
sage: g.is_satisfiable()
787
False
788
"""
789
table = self.truthtable().get_table_list()
790
for row in table[1:]:
791
if row[-1] is True:
792
return True
793
return False
794
795
def is_tautology(self):
796
r"""
797
Determine if the formula is always True.
798
799
INPUT:
800
801
- ``self`` -- calling object
802
803
OUTPUT:
804
805
A boolean value to be determined as follows:
806
807
True - if the formula is a tautology
808
809
False - if the formula is not a tautology
810
811
EXAMPLES:
812
813
This example illustrates how to check if a formula is a tautology.
814
815
::
816
817
sage: import sage.logic.propcalc as propcalc
818
sage: f = propcalc.formula("a|~a")
819
sage: f.is_tautology()
820
True
821
822
::
823
824
sage: f = propcalc.formula("a&~a")
825
sage: f.is_tautology()
826
False
827
828
::
829
830
sage: f = propcalc.formula("a&b")
831
sage: f.is_tautology()
832
False
833
834
"""
835
return not (~self).is_satisfiable()
836
837
def is_contradiction(self):
838
r"""
839
Determine if the formula is always False.
840
841
INPUT:
842
843
- ``self`` -- calling object
844
845
OUTPUT:
846
847
A boolean value to be determined as follows:
848
849
True - if the formula is a contradiction
850
851
False - if the formula is not a contradiction
852
853
EXAMPLES:
854
855
This example illustrates how to check if a formula is a contradiction.
856
857
::
858
859
sage: import sage.logic.propcalc as propcalc
860
sage: f = propcalc.formula("a&~a")
861
sage: f.is_contradiction()
862
True
863
864
::
865
866
sage: f = propcalc.formula("a|~a")
867
sage: f.is_contradiction()
868
False
869
870
::
871
872
sage: f = propcalc.formula("a|b")
873
sage: f.is_contradiction()
874
False
875
"""
876
return not self.is_satisfiable()
877
878
def equivalent(self, other):
879
r"""
880
Determine if two formulas are semantically equivalent.
881
882
INPUT:
883
884
- ``self`` -- calling object
885
886
- ``other`` -- instance of BooleanFormula class.
887
888
OUTPUT:
889
890
A boolean value to be determined as follows:
891
892
True - if the two formulas are logically equivalent
893
894
False - if the two formulas are not logically equivalent
895
896
EXAMPLES:
897
898
This example shows how to check for logical equivalence.
899
900
::
901
902
sage: import sage.logic.propcalc as propcalc
903
sage: f = propcalc.formula("(a|b)&c")
904
sage: g = propcalc.formula("c&(a|b)")
905
sage: f.equivalent(g)
906
True
907
908
::
909
910
sage: g = propcalc.formula("a|b&c")
911
sage: f.equivalent(g)
912
False
913
"""
914
return self.iff(other).is_tautology()
915
916
def convert_cnf_table(self):
917
r"""
918
Convert boolean formula to conjunctive normal form.
919
920
INPUT:
921
922
- ``self`` -- calling object
923
924
OUTPUT:
925
926
An instance of :class:`BooleanFormula` in conjunctive normal form
927
928
EXAMPLES:
929
930
This example illustrates how to convert a formula to cnf.
931
932
::
933
934
sage: import sage.logic.propcalc as propcalc
935
sage: s = propcalc.formula("a ^ b <-> c")
936
sage: s.convert_cnf()
937
sage: s
938
(a|b|~c)&(a|~b|c)&(~a|b|c)&(~a|~b|~c)
939
940
We now show that :meth:`convert_cnf` and :meth:`convert_cnf_table` are aliases.
941
942
::
943
944
sage: t = propcalc.formula("a ^ b <-> c")
945
sage: t.convert_cnf_table(); t
946
(a|b|~c)&(a|~b|c)&(~a|b|c)&(~a|~b|~c)
947
sage: t == s
948
True
949
950
.. NOTE::
951
952
This method creates the cnf parse tree by examining the logic
953
table of the formula. Creating the table requires `O(2^n)` time
954
where `n` is the number of variables in the formula.
955
"""
956
str = ''
957
t = self.truthtable()
958
table = t.get_table_list()
959
vars = table[0]
960
for row in table[1:]:
961
if row[-1] is False:
962
str += '('
963
for i in range(len(row) - 1):
964
if row[i] is True:
965
str += '~'
966
str += vars[i]
967
str += '|'
968
str = str[:-1] + ')&'
969
self.__expression = str[:-1]
970
# in case of tautology
971
if len(self.__expression) == 0:
972
self.__expression = '(' + self.__vars_order[0] + '|~' + self.__vars_order[0] + ')'
973
self.__tree, self.__vars_order = logicparser.parse(self.__expression)
974
975
convert_cnf = convert_cnf_table
976
977
def convert_cnf_recur(self):
978
r"""
979
Convert boolean formula to conjunctive normal form.
980
981
INPUT:
982
983
- ``self`` -- calling object
984
985
OUTPUT:
986
987
An instance of :class:`BooleanFormula` in conjunctive normal form
988
989
EXAMPLES:
990
991
This example hows how to convert a formula to conjunctive normal form.
992
993
::
994
995
sage: import sage.logic.propcalc as propcalc
996
sage: s = propcalc.formula("a^b<->c")
997
sage: s.convert_cnf_recur()
998
sage: s
999
(~a|a|c)&(~b|a|c)&(~a|b|c)&(~b|b|c)&(~c|a|b)&(~c|~a|~b)
1000
1001
.. NOTE::
1002
1003
This function works by applying a set of rules that are
1004
guaranteed to convert the formula. Worst case the converted
1005
expression has an O(2^n) increase in size (and time as well), but if
1006
the formula is already in CNF (or close to) it is only O(n).
1007
1008
This function can require an exponential blow up in space from the
1009
original expression. This in turn can require large amounts of time.
1010
Unless a formula is already in (or close to) being in cnf convert_cnf()
1011
is typically preferred, but results can vary.
1012
"""
1013
self.__tree = logicparser.apply_func(self.__tree, self.reduce_op)
1014
self.__tree = logicparser.apply_func(self.__tree, self.dist_not)
1015
self.__tree = logicparser.apply_func(self.__tree, self.dist_ors)
1016
self.convert_expression()
1017
1018
def satformat(self):
1019
r"""
1020
Return the satformat representation of a boolean formula.
1021
1022
INPUT:
1023
1024
- ``self`` -- calling object
1025
1026
OUTPUT:
1027
1028
The satformat of the formula as a string
1029
1030
EXAMPLES:
1031
1032
This example illustrates how to find the satformat of a formula.
1033
1034
::
1035
1036
sage: import sage.logic.propcalc as propcalc
1037
sage: f = propcalc.formula("a&((b|c)^a->c)<->b")
1038
sage: f.convert_cnf()
1039
sage: f
1040
(a|~b|c)&(a|~b|~c)&(~a|b|~c)
1041
sage: f.satformat()
1042
'p cnf 3 0\n1 -2 3 0 1 -2 -3 \n0 -1 2 -3'
1043
1044
.. NOTE::
1045
1046
See www.cs.ubc.ca/~hoos/SATLIB/Benchmarks/SAT/satformat.ps for a
1047
description of satformat.
1048
1049
If the instance of boolean formula has not been converted to
1050
CNF form by a call to convert_cnf() or convert_cnf_recur()
1051
satformat() will call convert_cnf(). Please see the notes for
1052
convert_cnf() and convert_cnf_recur() for performance issues.
1053
"""
1054
self.convert_cnf_table()
1055
s = ''
1056
vars_num = {}
1057
i = 0
1058
clauses = 0
1059
for e in self.__vars_order:
1060
vars_num[e] = str(i + 1)
1061
i += 1
1062
i = 0
1063
w = 1
1064
while i < len(self.__expression):
1065
c = self.__expression[i]
1066
if c == ')':
1067
clauses += 1
1068
if c in '()|':
1069
i += 1
1070
continue
1071
if c == '~':
1072
s += '-'
1073
elif c == '&':
1074
s += '0 '
1075
else:
1076
varname = ''
1077
while i < self.__expression[i] not in '|) ':
1078
varname += self.__expression[i]
1079
i += 1
1080
s += vars_num[varname] + ' '
1081
if len(s) >= (w * 15) and s[-1] != '-':
1082
s += '\n'
1083
w += 1
1084
i += 1
1085
s = 'p cnf ' + str(len(self.__vars_order)) + ' ' + str(clauses) + '\n' + s
1086
return s[:-1]
1087
1088
# def simplify(self):
1089
# r"""
1090
# This function uses the propcalc package to simplify an expression to
1091
# its minimal form.
1092
#
1093
# INPUT:
1094
# self -- the calling object.
1095
#
1096
# OUTPUT:
1097
# A simplified expression.
1098
#
1099
# EXAMPLES:
1100
# sage: import sage.logic.propcalc as propcalc
1101
# sage: f = propcalc.formula("a&((b|c)^a->c)<->b")
1102
# sage: f.truthtable()
1103
# a b c value
1104
# False False False True
1105
# False False True True
1106
# False True False False
1107
# False True True False
1108
# True False False True
1109
# True False True False
1110
# True True False True
1111
# True True True True
1112
# sage: f.simplify()
1113
# (~a&~b)|(a&~b&~c)|(a&b)
1114
# sage: f.truthtable()
1115
# a b c value
1116
# False False False True
1117
# False False True True
1118
# False True False False
1119
# False True True False
1120
# True False False True
1121
# True False True False
1122
# True True False True
1123
# True True True True
1124
#
1125
# NOTES:
1126
# If the instance of boolean formula has not been converted to
1127
# cnf form by a call to convert_cnf() or convert_cnf_recur()
1128
# satformat() will call convert_cnf(). Please see the notes for
1129
# convert_cnf() and convert_cnf_recur() for performance issues.
1130
# """
1131
# exp = ''
1132
# self.__tree = logicparser.apply_func(self.__tree, self.reduce_op)
1133
# plf = logicparser.apply_func(self.__tree, self.convert_opt)
1134
# wff = boolopt.PLFtoWFF()(plf) # convert to positive-normal form
1135
# wtd = boolopt.WFFtoDNF()
1136
# dnf = wtd(wff)
1137
# dnf = wtd.clean(dnf)
1138
# if(dnf == [] or dnf == [[]]):
1139
# exp = self.__vars_order[0] + '&~' + self.__vars_order[0] + ' '
1140
# opt = boolopt.optimize(dnf)
1141
# if(exp == '' and (opt == [] or opt == [[]])):
1142
# exp = self.__vars_order[0] + '|~' + self.__vars_order[0] + ' '
1143
# if(exp == ''):
1144
# for con in opt:
1145
# s = '('
1146
# for prop in con:
1147
# if(prop[0] == 'notprop'):
1148
# s += '~'
1149
# s += prop[1] + '&'
1150
# exp += s[:-1] + ')|'
1151
# self.__expression = exp[:-1]
1152
# self.__tree, self.__vars_order = logicparser.parse(self.__expression)
1153
# return BooleanFormula(self.__expression, self.__tree, self.__vars_order)
1154
1155
def convert_opt(self, tree):
1156
r"""
1157
Convert a parse tree to the tuple form used by bool_opt.
1158
1159
INPUT:
1160
1161
- ``self`` -- calling object
1162
1163
- ``tree`` -- a list. This is a branch of a
1164
parse tree and can only contain the '&', '|'
1165
and '~' operators along with variables.
1166
1167
OUTPUT:
1168
1169
A 3-tuple
1170
1171
EXAMPLES:
1172
1173
This example illustrates the conversion of a formula into its
1174
corresponding tuple.
1175
1176
::
1177
1178
sage: import sage.logic.propcalc as propcalc, sage.logic.logicparser as logicparser
1179
sage: s = propcalc.formula("a&(b|~c)")
1180
sage: tree = ['&', 'a', ['|', 'b', ['~', 'c', None]]]
1181
sage: logicparser.apply_func(tree, s.convert_opt)
1182
('and', ('prop', 'a'), ('or', ('prop', 'b'), ('not', ('prop', 'c'))))
1183
1184
.. NOTE::
1185
1186
This function only works on one branch of the parse tree. To
1187
apply the function to every branch of a parse tree, pass the
1188
function as an argument in :func:`apply_func` in logicparser.py.
1189
"""
1190
if type(tree[1]) is not TupleType and not (tree[1] is None):
1191
lval = ('prop', tree[1])
1192
else:
1193
lval = tree[1]
1194
if type(tree[2]) is not TupleType and not(tree[2] is None):
1195
rval = ('prop', tree[2])
1196
else:
1197
rval = tree[2]
1198
if tree[0] == '~':
1199
return ('not', lval)
1200
if tree[0] == '&':
1201
op = 'and'
1202
if tree[0] == '|':
1203
op = 'or'
1204
return (op, lval, rval)
1205
1206
def add_statement(self, other, op):
1207
r"""
1208
Combine two formulas with the given operator.
1209
1210
INPUT:
1211
1212
- ``self`` -- calling object. This is the formula on
1213
the left side of the operator.
1214
1215
- ``other`` -- instance of BooleanFormula class. This
1216
is the formula on the right of the operator.
1217
1218
- ``op`` -- a string. This is the operator used to
1219
combine the two formulas.
1220
1221
OUTPUT:
1222
1223
The result as an instance of :class:`BooleanFormula`
1224
1225
EXAMPLES:
1226
1227
This example shows how to create a new formula from two others.
1228
1229
::
1230
1231
sage: import sage.logic.propcalc as propcalc
1232
sage: s = propcalc.formula("a&b")
1233
sage: f = propcalc.formula("c^d")
1234
sage: s.add_statement(f, '|')
1235
(a&b)|(c^d)
1236
1237
::
1238
1239
sage: s.add_statement(f, '->')
1240
(a&b)->(c^d)
1241
"""
1242
exp = '(' + self.__expression + ')' + op + '(' + other.__expression + ')'
1243
parse_tree, vars_order = logicparser.parse(exp)
1244
return BooleanFormula(exp, parse_tree, vars_order)
1245
1246
def get_bit(self, x, c):
1247
r"""
1248
Determine if bit c of the number x is 1.
1249
1250
INPUT:
1251
1252
- ``self`` -- calling object
1253
1254
- ``x`` -- an integer. This is the number from
1255
which to take the bit.
1256
1257
- ``c`` -- an integer. This is the but number to
1258
be taken, where 0 is the low order bit.
1259
1260
OUTPUT:
1261
1262
A boolean to be determined as follows:
1263
1264
True - if bit c of x is 1
1265
1266
False - if bit c of x is not 1
1267
1268
EXAMPLES:
1269
1270
This example illustrates the use of :meth:`get_bit`.
1271
1272
::
1273
1274
sage: import sage.logic.propcalc as propcalc
1275
sage: s = propcalc.formula("a&b")
1276
sage: s.get_bit(2, 1)
1277
True
1278
sage: s.get_bit(8, 0)
1279
False
1280
1281
It is not an error to have a bit out of range.
1282
1283
::
1284
1285
sage: s.get_bit(64, 7)
1286
False
1287
1288
Nor is it an error to use a negative number.
1289
1290
::
1291
1292
sage: s.get_bit(-1, 3)
1293
False
1294
sage: s.get_bit(64, -1)
1295
True
1296
sage: s.get_bit(64, -2)
1297
False
1298
1299
.. NOTE::
1300
1301
The 0 bit is the low order bit. Errors should be handled
1302
gracefully by a return of false, and negative numbers x
1303
always return false while a negative c will index from the
1304
high order bit.
1305
"""
1306
bits = []
1307
while x > 0:
1308
if x % 2 == 0:
1309
b = False
1310
else:
1311
b = True
1312
x = int(x / 2)
1313
bits.append(b)
1314
if c > len(bits) - 1:
1315
return False
1316
else:
1317
return bits[c]
1318
1319
def reduce_op(self, tree):
1320
r"""
1321
Convert if-and-only-if, if-then, and xor operations to operations
1322
only involving and/or operations.
1323
1324
INPUT:
1325
1326
- ``self`` -- calling object
1327
1328
- ``tree`` -- a list. This represents a branch
1329
of a parse tree.
1330
1331
OUTPUT:
1332
1333
A new list with no ^, ->, or <-> as first element of list.
1334
1335
EXAMPLES:
1336
1337
This example illustrates the use of :meth:`reduce_op` with :func:`apply_func`.
1338
1339
::
1340
1341
sage: import sage.logic.propcalc as propcalc, sage.logic.logicparser as logicparser
1342
sage: s = propcalc.formula("a->b^c")
1343
sage: tree = ['->', 'a', ['^', 'b', 'c']]
1344
sage: logicparser.apply_func(tree, s.reduce_op)
1345
['|', ['~', 'a', None], ['&', ['|', 'b', 'c'], ['~', ['&', 'b', 'c'], None]]]
1346
1347
.. NOTE::
1348
1349
This function only operates on a single branch of a parse tree.
1350
To apply the function to an entire parse tree, pass the function
1351
as an argument to :func:`apply_func` in logicparser.py.
1352
"""
1353
if tree[0] == '<->':
1354
# parse tree for (~tree[1]|tree[2])&(~tree[2]|tree[1])
1355
new_tree = ['&', ['|', ['~', tree[1], None], tree[2]],
1356
['|', ['~', tree[2], None], tree[1]]]
1357
elif tree[0] == '^':
1358
# parse tree for (tree[1]|tree[2])&~(tree[1]&tree[2])
1359
new_tree = ['&', ['|', tree[1], tree[2]],
1360
['~', ['&', tree[1], tree[2]], None]]
1361
elif tree[0] == '->':
1362
# parse tree for ~tree[1]|tree[2]
1363
new_tree = ['|', ['~', tree[1], None], tree[2]]
1364
else:
1365
new_tree = tree
1366
return new_tree
1367
1368
def dist_not(self, tree):
1369
r"""
1370
Distribute ~ operators over & and | operators.
1371
1372
INPUT:
1373
1374
- ``self`` calling object
1375
1376
- ``tree`` a list. This represents a branch
1377
of a parse tree.
1378
1379
OUTPUT:
1380
1381
A new list
1382
1383
EXAMPLES:
1384
1385
This example illustrates the distribution of '~' over '&'.
1386
1387
::
1388
1389
sage: import sage.logic.propcalc as propcalc, sage.logic.logicparser as logicparser
1390
sage: s = propcalc.formula("~(a&b)")
1391
sage: tree = ['~', ['&', 'a', 'b'], None]
1392
sage: logicparser.apply_func(tree, s.dist_not) #long time
1393
['|', ['~', 'a', None], ['~', 'b', None]]
1394
1395
.. NOTE::
1396
1397
This function only operates on a single branch of a parse tree.
1398
To apply the function to an entire parse tree, pass the function
1399
as an argument to :func:`apply_func` in logicparser.py.
1400
"""
1401
if tree[0] == '~' and type(tree[1]) is ListType:
1402
op = tree[1][0]
1403
if op != '~':
1404
if op == '&':
1405
op = '|'
1406
else:
1407
op = '&'
1408
new_tree = [op, ['~', tree[1][1], None], ['~', tree[1][2], None]]
1409
return logicparser.apply_func(new_tree, self.dist_not)
1410
else:
1411
# cancel double negative
1412
return tree[1][1]
1413
else:
1414
return tree
1415
1416
def dist_ors(self, tree):
1417
r"""
1418
Distribute | over &.
1419
1420
INPUT:
1421
1422
- ``self`` -- calling object
1423
1424
- ``tree`` -- a list. This represents a branch of
1425
a parse tree.
1426
1427
OUTPUT:
1428
1429
A new list
1430
1431
EXAMPLES:
1432
1433
This example illustrates the distribution of '|' over '&'.
1434
1435
::
1436
1437
sage: import sage.logic.propcalc as propcalc, sage.logic.logicparser as logicparser
1438
sage: s = propcalc.formula("(a&b)|(a&c)")
1439
sage: tree = ['|', ['&', 'a', 'b'], ['&', 'a', 'c']]
1440
sage: logicparser.apply_func(tree, s.dist_ors) #long time
1441
['&', ['&', ['|', 'a', 'a'], ['|', 'b', 'a']], ['&', ['|', 'a', 'c'], ['|', 'b', 'c']]]
1442
1443
.. NOTE::
1444
1445
This function only operates on a single branch of a parse tree.
1446
To apply the function to an entire parse tree, pass the function
1447
as an argument to :func:`apply_func` in logicparser.py.
1448
"""
1449
if tree[0] == '|' and type(tree[2]) is ListType and tree[2][0] == '&':
1450
new_tree = ['&', ['|', tree[1], tree[2][1]],
1451
['|', tree[1], tree[2][2]]]
1452
return logicparser.apply_func(new_tree, self.dist_ors)
1453
if tree[0] == '|' and type(tree[1]) is ListType and tree[1][0] == '&':
1454
new_tree = ['&', ['|', tree[1][1], tree[2]],
1455
['|', tree[1][2], tree[2]]]
1456
return logicparser.apply_func(new_tree, self.dist_ors)
1457
return tree
1458
1459
def to_infix(self, tree):
1460
r"""
1461
Convert a parse tree from prefix to infix form.
1462
1463
INPUT:
1464
1465
- ``self`` -- calling object
1466
1467
- ``tree`` -- a list. This represents a branch
1468
of a parse tree.
1469
1470
OUTPUT:
1471
1472
A new list
1473
1474
EXAMPLES:
1475
1476
This example shows how to convert a parse tree from prefix to infix form.
1477
1478
::
1479
1480
sage: import sage.logic.propcalc as propcalc, sage.logic.logicparser as logicparser
1481
sage: s = propcalc.formula("(a&b)|(a&c)")
1482
sage: tree = ['|', ['&', 'a', 'b'], ['&', 'a', 'c']]
1483
sage: logicparser.apply_func(tree, s.to_infix)
1484
[['a', '&', 'b'], '|', ['a', '&', 'c']]
1485
1486
.. NOTE::
1487
1488
This function only operates on a single branch of a parse tree.
1489
To apply the function to an entire parse tree, pass the function
1490
as an argument to :func:`apply_func` in logicparser.py.
1491
"""
1492
if tree[0] != '~':
1493
return [tree[1], tree[0], tree[2]]
1494
return tree
1495
1496
def convert_expression(self):
1497
r"""
1498
Convert the string representation of a formula to conjunctive normal form.
1499
1500
INPUT:
1501
1502
- ``self`` -- calling object
1503
1504
OUTPUT:
1505
1506
None
1507
1508
EXAMPLES:
1509
1510
We show how the converted formula is printed in conjunctive normal form.
1511
1512
::
1513
1514
sage: import sage.logic.propcalc as propcalc
1515
sage: s = propcalc.formula("a^b<->c")
1516
sage: s.convert_cnf_recur(); s #long time
1517
(~a|a|c)&(~b|a|c)&(~a|b|c)&(~b|b|c)&(~c|a|b)&(~c|~a|~b)
1518
"""
1519
ttree = self.__tree[:]
1520
ttree = logicparser.apply_func(ttree, self.to_infix)
1521
self.__expression = ''
1522
str_tree = str(ttree)
1523
open_flag = False
1524
i = 0
1525
for c in str_tree:
1526
if i < len(str_tree) - 1:
1527
op = self.get_next_op(str_tree[i:])
1528
if op == '|' and not open_flag:
1529
self.__expression += '('
1530
open_flag = True
1531
if i < len(str_tree) - 2 and str_tree[i + 1] == '&' and open_flag:
1532
open_flag = False
1533
self.__expression += ')'
1534
if str_tree[i:i + 4] == 'None':
1535
i += 4
1536
if i < len(str_tree) and str_tree[i] not in ' \',[]':
1537
self.__expression += str_tree[i]
1538
i += 1
1539
if open_flag is True:
1540
self.__expression += ')'
1541
1542
def get_next_op(self, str):
1543
r"""
1544
Return the next operator in a string.
1545
1546
INPUT:
1547
1548
- ``self`` -- calling object
1549
1550
- ``str`` -- a string. This contains a logical
1551
expression.
1552
1553
OUTPUT:
1554
1555
The next operator as a string
1556
1557
EXAMPLES:
1558
1559
This example illustrates how to find the next operator in a formula.
1560
1561
::
1562
1563
sage: import sage.logic.propcalc as propcalc
1564
sage: s = propcalc.formula("f&p")
1565
sage: s.get_next_op("abra|cadabra")
1566
'|'
1567
1568
.. NOTE::
1569
1570
The parameter ``str`` is not necessarily the string
1571
representation of the calling object.
1572
"""
1573
i = 0
1574
while i < len(str) - 1 and str[i] != '&' and str[i] != '|':
1575
i += 1
1576
return str[i]
1577
1578