Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/logic/logic.py
8817 views
1
r"""
2
Manipulation of symbolic logic expressions.
3
4
An expression is created from a string that consists of the
5
operators !, &, |, ->, <->, which correspond to the logical
6
functions not, and, or, if then, if and only if, respectively.
7
Variable names must start with a letter and contain only
8
alpha-numerics and the underscore character.
9
10
AUTHORS:
11
12
- Chris Gorecki (2007): initial version
13
14
- William Stein (2007-08-31): integration into Sage 2.8.4
15
16
- Paul Scurek (2013-08-03): updated docstring formatting
17
"""
18
#*****************************************************************************
19
# Copyright (C) 2007 Chris Gorecki <[email protected]>
20
# Copyright (C) 2007 William Stein <[email protected]>
21
# Copyright (C) 2013 Paul Scurek <[email protected]>
22
#
23
# Distributed under the terms of the GNU General Public License (GPL)
24
# as published by the Free Software Foundation; either version 2 of
25
# the License, or (at your option) any later version.
26
# http://www.gnu.org/licenses/
27
#*****************************************************************************
28
29
import string
30
31
# constants
32
tok_list = ['OPAREN', 'CPAREN', 'AND', 'OR', 'NOT', 'IFTHEN', 'IFF']
33
bin_list = ['AND', 'OR', 'IFTHEN', 'IFF']
34
operators = '()&|!<->'
35
# variables
36
vars = {}
37
vars_order = []
38
39
class SymbolicLogic:
40
"""
41
EXAMPLES:
42
43
This example illustrates how to create a boolean formula and print its table.
44
45
::
46
47
sage: log = SymbolicLogic()
48
sage: s = log.statement("a&b|!(c|a)")
49
sage: t = log.truthtable(s)
50
sage: log.print_table(t)
51
a | b | c | value |
52
--------------------------------
53
False | False | False | True |
54
False | False | True | False |
55
False | True | False | True |
56
False | True | True | False |
57
True | False | False | False |
58
True | False | True | False |
59
True | True | False | True |
60
True | True | True | True |
61
"""
62
def statement(self, s):
63
r"""
64
Return a token list to be used by other functions in the class
65
66
INPUT:
67
68
- ``self`` -- calling object
69
70
- ``s`` -- a string containing the logic expression to be manipulated
71
72
- ``global vars`` -- a dictionary with variable names as keys and the
73
variables' current boolean values as dictionary values
74
75
- ``global vars_order`` -- a list of the variables in the order that they
76
are found
77
78
OUTPUT:
79
80
A list of length three containing the following in this order:
81
82
1. a list of tokens
83
2. a dictionary of variable/value pairs
84
3. a list of the variables in the order they were found
85
86
EXAMPLES:
87
88
This example illustrates the creation of a statement.
89
90
::
91
92
sage: log = SymbolicLogic()
93
sage: s = log.statement("a&b|!(c|a)")
94
95
::
96
97
sage: s2 = log.statement("!((!(a&b)))")
98
99
It is an error to use invalid variable names.
100
101
::
102
103
sage: s = log.statement("3fe & @q")
104
Invalid variable name: 3fe
105
Invalid variable name: @q
106
107
It is also an error to use invalid syntax.
108
109
::
110
111
sage: s = log.statement("a&&b")
112
Malformed Statement
113
sage: s = log.statement("a&((b)")
114
Malformed Statement
115
116
"""
117
global vars, vars_order
118
toks, vars, vars_order = ['OPAREN'], {}, []
119
tokenize(s, toks)
120
statement = [toks, vars, vars_order]
121
try: #verify the syntax
122
eval(toks)
123
except(KeyError, RuntimeError):
124
print 'Malformed Statement'
125
return []
126
return statement
127
128
def truthtable(self, statement, start=0, end=-1):
129
r"""
130
Return a truth table.
131
132
INPUT:
133
134
- ``self`` -- calling object
135
136
- ``statement`` -- a list. It contains the tokens and the two global
137
variables vars and vars_order
138
139
- ``start`` -- (default : 0) an integer. This represents the row of the
140
truth table from which to start.
141
142
- ``end`` -- (default : -1) an integer. This represents the last row of the
143
truth table to be created
144
145
OUTPUT:
146
147
The truth table as a 2d array with the creating formula tacked to the front
148
149
EXAMPLES:
150
151
This example illustrates the creation of a statement.
152
153
::
154
155
sage: log = SymbolicLogic()
156
sage: s = log.statement("a&b|!(c|a)")
157
sage: t = log.truthtable(s) #creates the whole truth table
158
159
We can now create truthtable of rows 1 to 5
160
161
::
162
163
sage: s2 = log.truthtable(s, 1, 5); s2
164
[[['OPAREN', 'a', 'AND', 'b', 'OR', 'NOT', 'OPAREN', 'c', 'OR', 'a', 'CPAREN', 'CPAREN'], {'a': 'False', 'c': 'True', 'b': 'False'}, ['a', 'b', 'c']], ['False', 'False', 'True', 'False'], ['False', 'True', 'False', 'True'], ['False', 'True', 'True', 'True'], ['True', 'False', 'False', 'False']]
165
166
.. NOTE::
167
168
When sent with no start or end parameters this is an
169
exponential time function requiring O(2**n) time, where
170
n is the number of variables in the logic expression
171
"""
172
global vars, vars_order
173
toks, vars, vars_order = statement
174
if end == -1:
175
end = 2 ** len(vars)
176
table = [statement]
177
keys = vars_order
178
keys.reverse()
179
for i in range(start,end):
180
j = 0
181
row = []
182
for key in keys:
183
bit = get_bit(i, j)
184
vars[key] = bit
185
j += 1
186
row.insert(0, bit)
187
row.append(eval(toks))
188
table.append(row)
189
return table
190
191
def print_table(self, table):
192
r"""
193
Return a truthtable corresponding to the given statement.
194
195
INPUT:
196
197
- ``self`` -- calling object
198
199
- ``table`` -- object created by truthtable() method. It contains
200
the variable values and the evaluation of the statement
201
202
OUTPUT:
203
204
A formatted version of the truth table
205
206
EXAMPLES:
207
208
This example illustrates the creation of a statement and its truth table.
209
210
::
211
212
sage: log = SymbolicLogic()
213
sage: s = log.statement("a&b|!(c|a)")
214
sage: t = log.truthtable(s) #creates the whole truth table
215
sage: log.print_table(t)
216
a | b | c | value |
217
--------------------------------
218
False | False | False | True |
219
False | False | True | False |
220
False | True | False | True |
221
False | True | True | False |
222
True | False | False | False |
223
True | False | True | False |
224
True | True | False | True |
225
True | True | True | True |
226
227
We can also print a shortened table.
228
229
::
230
231
sage: t = log.truthtable(s, 1, 5)
232
sage: log.print_table(t)
233
a | b | c | value | value |
234
----------------------------------------
235
False | False | False | True | True |
236
False | False | True | False | False |
237
False | False | True | True | False |
238
False | True | False | False | True |
239
240
"""
241
statement = table[0]
242
del table[0]
243
vars_order = statement[2]
244
vars_len = []
245
line = s = ""
246
vars_order.reverse()
247
vars_order.append('value')
248
for var in vars_order:
249
vars_len.append(len(var))
250
s = var + ' '
251
while len(s) < len('False '):
252
s += ' '
253
s += '| '
254
line += s
255
print line
256
print len(line) * '-'
257
for row in table:
258
line = s = ""
259
i = 0
260
for e in row:
261
if e == 'True':
262
j = 2
263
else:
264
j = 1
265
s = e + ' ' * j
266
if i < len(vars_len):
267
while len(s) <= vars_len[i]:
268
s += ' '
269
s += '| '
270
line += s
271
i += 1
272
print line
273
print
274
275
#TODO: implement the combine function which returns
276
# two statements or'd together
277
def combine(self, statement1, statement2):
278
x = 0
279
280
#TODO: implement the simplify function which calls
281
#a c++ implementation of the ESPRESSO algorithm
282
#to simplify the truthtable: probably Minilog
283
def simplify(self, table):
284
x = 0
285
286
#TODO: implement a prove function which test to
287
#see if the statement is a tautology or contradiction
288
#by calling a c++ library TBD
289
def prove(self, statement):
290
x = 0
291
292
def get_bit(x, c):
293
r"""
294
Determine if bit c of the number x is 1.
295
296
INPUT:
297
298
- ``x`` -- an integer. This is the number from which to take the bit.
299
300
- ``c`` -- an integer. This is the bit number to be taken.
301
302
OUTPUT:
303
304
A boolean value to be determined as follows:
305
306
True - if bit c of x is 1
307
308
False - if bit c of x is not 1
309
310
.. NOTE::
311
312
This function is for internal use by the SymbolicLogic class.
313
"""
314
bits = []
315
while x > 0:
316
if x % 2 == 0:
317
b = 'False'
318
else:
319
b = 'True'
320
x /= 2
321
bits.append(b)
322
if c > len(bits) - 1:
323
return 'False'
324
else:
325
return bits[c]
326
327
def eval(toks):
328
r"""
329
Evaluate the expression contained in toks.
330
331
INPUT:
332
333
- ``toks`` -- a list of tokens. This represents a boolean expression.
334
335
OUTPUT:
336
337
A boolean value to be determined as follows:
338
339
True - if expression evaluates to True
340
341
False - if expression evaluates to False
342
343
.. NOTE::
344
345
This function is for internal use by the SymbolicLogic class. The
346
evaluations rely on setting the values of the variables in the global
347
dictionary vars.
348
"""
349
stack = []
350
for tok in toks:
351
stack.append(tok)
352
if tok == 'CPAREN':
353
lrtoks = []
354
while tok != 'OPAREN':
355
tok = stack.pop()
356
lrtoks.insert(0, tok)
357
stack.append(eval_ltor_toks(lrtoks[1:-1]))
358
if len(stack) > 1:
359
raise RuntimeError
360
return stack[0]
361
362
def eval_ltor_toks(lrtoks):
363
r"""
364
Evaluates the expression contained in lrtoks.
365
366
INPUT:
367
368
- ``lrtoks`` -- a list of tokens. This represents a part of a boolean
369
formula that contains no inner parentheses.
370
371
OUTPUT:
372
373
A boolean value to be determined as follows:
374
375
True - if expression evaluates to True
376
377
False - if expression evaluates to False
378
379
.. NOTE::
380
381
This function is for internal use by the SymbolicLogic class. The
382
evaluations rely on setting the values of the variables in the global
383
dictionary vars.
384
"""
385
reduce_monos(lrtoks) # monotonic ! operators go first
386
reduce_bins(lrtoks) # then the binary operators
387
if len(lrtoks) > 1:
388
raise RuntimeError
389
return lrtoks[0]
390
391
def reduce_bins(lrtoks):
392
r"""
393
Evaluate lrtoks to a single boolean value.
394
395
INPUT:
396
397
- ``lrtoks`` -- a list of tokens. This represents a part of a boolean
398
formula that contains no inner parentheses or monotonic operators.
399
400
OUTPUT:
401
402
None. The pointer to lrtoks is now a list containing True or False
403
404
.. NOTE::
405
406
This function is for internal use by the SymbolicLogic class.
407
"""
408
i = 0
409
while i < len(lrtoks):
410
if lrtoks[i] in bin_list:
411
args = [lrtoks[i - 1], lrtoks[i], lrtoks[i + 1]]
412
lrtoks[i - 1] = eval_bin_op(args)
413
del lrtoks[i]
414
del lrtoks[i]
415
reduce_bins(lrtoks)
416
i += 1
417
418
def reduce_monos(lrtoks):
419
r"""
420
Replace monotonic operator/variable pairs with a boolean value.
421
422
INPUT:
423
424
- ``lrtoks`` -- a list of tokens. This represents a part of a boolean
425
expression that contains now inner parentheses.
426
427
OUTPUT:
428
429
None. The pointer to lrtoks is now a list containing monotonic operators.
430
431
.. NOTE::
432
This function is for internal use by the SymbolicLogic class.
433
"""
434
i = 0
435
while i < len(lrtoks):
436
if lrtoks[i] == 'NOT':
437
args = [lrtoks[i], lrtoks[i + 1]]
438
lrtoks[i] = eval_mon_op(args)
439
del lrtoks[i + 1]
440
i += 1
441
442
def eval_mon_op(args):
443
r"""
444
Return a boolean value based on the truth table of the operator in args.
445
446
INPUT:
447
448
- ``args`` -- a list of length 2. This contains the token 'NOT' and
449
then a variable name.
450
451
OUTPUT:
452
453
A boolean value to be determined as follows:
454
455
True - if the variable in args is False
456
457
False - if the variable in args is True
458
459
.. NOTE::
460
461
This function is for internal use by the SymbolicLogic class.
462
"""
463
if args[1] != 'True' and args[1] != 'False':
464
val = vars[args[1]]
465
else:
466
val = args[1]
467
468
if val == 'True':
469
return 'False'
470
else:
471
return 'True'
472
473
def eval_bin_op(args):
474
r"""
475
Return a boolean value based on the truth table of the operator in args.
476
477
INPUT:
478
479
- ``args`` -- a list of length 3. This contains a variable name,
480
then a binary operator, and then a variable name, in that order.
481
482
OUTPUT:
483
484
A boolean value. This is the evaluation of the operator based on the
485
truth values of the variables.
486
487
.. NOTE::
488
489
This function is for internal use by the SymbolicLogic class.
490
"""
491
if args[0] == 'False':
492
lval = 'False'
493
elif args[0] == 'True':
494
lval = 'True'
495
else:
496
lval = vars[args[0]]
497
498
if args[2] == 'False':
499
rval = 'False'
500
elif args[2] == 'True':
501
rval = 'True'
502
else:
503
rval = vars[args[2]]
504
505
if args[1] == 'AND':
506
return eval_and_op(lval, rval)
507
elif args[1] == 'OR':
508
return eval_or_op(lval, rval)
509
elif args[1] == 'IFTHEN':
510
return eval_ifthen_op(lval, rval)
511
elif args[1] == 'IFF':
512
return eval_iff_op(lval, rval)
513
514
def eval_and_op(lval, rval):
515
r"""
516
Apply the 'and' operator to lval and rval.
517
518
INPUT:
519
520
- ``lval`` -- a string. This represents the value of the variable
521
appearing to the left of the 'and' operator.
522
523
- ``rval`` -- a string. This represents the value of the variable
524
appearing to the right of the 'and' operator.
525
526
527
OUTPUT:
528
529
The result of applying 'and' to lval and rval as a string.
530
531
.. NOTE::
532
533
This function is for internal use by the SymbolicLogic class.
534
"""
535
if lval == 'False' and rval == 'False':
536
return 'False'
537
elif lval == 'False' and rval == 'True':
538
return 'False'
539
elif lval == 'True' and rval == 'False':
540
return 'False'
541
elif lval == 'True' and rval == 'True':
542
return 'True'
543
544
def eval_or_op(lval, rval):
545
r"""
546
Apply the 'or' operator to lval and rval
547
548
INPUT:
549
550
- ``lval`` -- a string. This represents the value of the variable
551
appearing to the left of the 'or' operator.
552
553
- ``rval`` -- a string. This represents the value of the variable
554
appearing to the right of the 'or' operator.
555
556
OUTPUT:
557
558
A string representing the result of applying 'or' to lval and rval
559
560
.. NOTE::
561
562
This function is for internal use by the SymbolicLogic class.
563
"""
564
if lval == 'False' and rval == 'False':
565
return 'False'
566
elif lval == 'False' and rval == 'True':
567
return 'True'
568
elif lval == 'True' and rval == 'False':
569
return 'True'
570
elif lval == 'True' and rval == 'True':
571
return 'True'
572
573
def eval_ifthen_op(lval, rval):
574
r"""
575
Apply the 'if then' operator to lval and rval
576
577
INPUT:
578
579
- ``lval`` -- a string. This represents the value of the variable
580
appearing to the left of the 'if then' operator.
581
582
- ``rval`` -- a string. This represents the value of the variable
583
appearing to the right of the 'if then' operator.
584
585
OUTPUT:
586
587
A string representing the result of applying 'if then' to lval and rval.
588
589
.. NOTE::
590
591
This function is for internal use by the SymbolicLogic class.
592
"""
593
if lval == 'False' and rval == 'False':
594
return 'True'
595
elif lval == 'False' and rval == 'True':
596
return 'True'
597
elif lval == 'True' and rval == 'False':
598
return 'False'
599
elif lval == 'True' and rval == 'True':
600
return 'True'
601
602
def eval_iff_op(lval, rval):
603
r"""
604
Apply the 'if and only if' operator to lval and rval.
605
606
INPUT:
607
608
- ``lval`` -- a string. This represents the value of the variable
609
appearing to the left of the 'if and only if' operator.
610
611
- ``rval`` -- a string. This represents the value of the variable
612
appearing to the right of the 'if and only if' operator.
613
614
OUTPUT:
615
616
A string representing the result of applying 'if and only if' to lval and rval
617
618
.. NOTE::
619
620
This function is for internal use by the SymbolicLogic class.
621
"""
622
if lval == 'False' and rval == 'False':
623
return 'True'
624
elif lval == 'False' and rval == 'True':
625
return 'False'
626
elif lval == 'True' and rval == 'False':
627
return 'False'
628
elif lval == 'True' and rval == 'True':
629
return 'True'
630
631
def tokenize(s, toks):
632
r"""
633
Tokenize s and place the tokens of s in toks.
634
635
INPUT:
636
637
- ``s`` -- a string. This contains a boolean expression.
638
639
- ``toks`` -- a list. This will be populated with the tokens
640
of s.
641
642
OUTPUT:
643
644
None. The tokens of s are placed in toks.
645
646
.. NOTE::
647
648
This function is for internal use by the SymbolicLogic class.
649
"""
650
i = 0
651
while i < len(s):
652
tok = ""
653
skip = valid = 1
654
if s[i] == '(':
655
tok = tok_list[0]
656
elif s[i] == ')':
657
tok = tok_list[1]
658
elif s[i] == '&':
659
tok = tok_list[2]
660
elif s[i] == '|':
661
tok = tok_list[3]
662
elif s[i] == '!':
663
tok = tok_list[4]
664
elif s[i:i + 2] == '->':
665
tok = tok_list[5]
666
skip = 2
667
elif s[i:i + 3] == '<->':
668
tok = tok_list[6]
669
skip = 3
670
671
if len(tok) > 0:
672
toks.append(tok)
673
i += skip
674
continue
675
else:
676
# token is a variable name
677
if(s[i] == ' '):
678
i += 1
679
continue
680
681
while i < len(s) and s[i] not in operators and s[i] != ' ':
682
tok += s[i]
683
i += 1
684
685
if len(tok) > 0:
686
if tok[0] not in string.letters:
687
valid = 0
688
for c in tok:
689
if c not in string.letters and c not in string.digits and c != '_':
690
valid = 0
691
692
if valid == 1:
693
toks.append(tok)
694
vars[tok] = 'False'
695
if tok not in vars_order:
696
vars_order.append(tok)
697
else:
698
print 'Invalid variable name: ', tok
699
toks = []
700
701
toks.append('CPAREN')
702
703