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