Truth tables in python using sympy
-
02-07-2021 - |
Question
I'm trying to create a program, that uses sympy to take a set of variables and evaluate a symbolic logic expression over the domain of those variables. The problem is that I cannot get python to evaluate the expression after it spits out the truth table.
Here's the code:
from sympy import *
from sympy.abc import p, q, r
def get_vars():
vars = []
print "Please enter the number of variables to use in the equation"
numVars = int(raw_input())
print "please enter each of the variables on a newline"
for i in xrange(numVars):
vars.append(raw_input())
return vars
def get_expr():
print "Please enter the expression to use"
return str(raw_input())
def convert_to_expr(inputStr):
return eval(inputStr)
def main():
vars = get_vars()
expr = get_expr()
print("recieved input: " + str(vars) + " expr " + str(expr))
print "Truth table for " + str(len(vars)) + "variable(s)"
for i in enumerate(truth_table(vars, expr)):
print i
def fixed_table(numvars):
"""
Generate true/false permutations for the given number of variables.
So if numvars=2
Returns (not necessarily in this order):
True, True
True, False
False, False
False, True
"""
if numvars is 1:
yield [True]
yield [False]
else:
for i in fixed_table(numvars-1):
yield i + [True]
yield i + [False]
def truth_table(vars, expr):
"""
Takes an array of variables, vars, and displays a truth table
for each possible value combination of vars.
"""
for cond in fixed_table(len(vars)):
values=dict(zip(vars,cond))
yield cond + [eval(expr)]
if __name__ == "__main__":
main()
If I do the following, here's the output:
Please enter the number of variables to use in the equation
3
please enter each of the variables on a newline
p
q
r
Please enter the expression to use
p&q&r
recieved input: ['p', 'q', 'r'] expr p&q&r
Truth table for 3variable(s)
(0, [True, True, True, And(p, q, r)])
(1, [True, True, False, And(p, q, r)])
(2, [True, False, True, And(p, q, r)])
(3, [True, False, False, And(p, q, r)])
(4, [False, True, True, And(p, q, r)])
(5, [False, True, False, And(p, q, r)])
(6, [False, False, True, And(p, q, r)])
(7, [False, False, False, And(p, q, r)])
If some software exists to perform this task, I'd really like to know about it :-)
Thanks in advance.
La solution
You're really close! Once you've got And(p, q, r)
and your truth tables, you can use the subs
method to push your values
dict into the expression: i.e.
yield cond + [eval(expr).subs(values)]
gives
p&q&r
recieved input: ['p', 'q', 'r'] expr p&q&r
Truth table for 3variable(s)
(0, [True, True, True, True])
(1, [True, True, False, False])
(2, [True, False, True, False])
(3, [True, False, False, False])
(4, [False, True, True, False])
(5, [False, True, False, False])
(6, [False, False, True, False])
(7, [False, False, False, False])
But I think there's a simpler way to do this. The sympify
function already works to generate expressions from strings:
In [7]: expr = sympify("x & y | z")
In [8]: expr
Out[8]: Or(z, And(x, y))
and we can get the variables too:
In [9]: expr.free_symbols
Out[9]: set([x, z, y])
plus itertools.product
can generate the values (and cartes
is an alias for it in sympy
):
In [12]: cartes([False, True], repeat=3)
Out[12]: <itertools.product at 0xa24889c>
In [13]: list(cartes([False, True], repeat=3))
Out[13]:
[(False, False, False),
(False, False, True),
(False, True, False),
(False, True, True),
(True, False, False),
(True, False, True),
(True, True, False),
(True, True, True)]
Combining these, which is basically just using sympify
to get the expression and avoid eval
, using the built-in Cartesian product, and adding .subs()
to use your values
dictionary, we get:
def explore():
expr_string = raw_input("Enter an expression: ")
expr = sympify(expr_string)
variables = sorted(expr.free_symbols)
for truth_values in cartes([False, True], repeat=len(variables)):
values = dict(zip(variables, truth_values))
print sorted(values.items()), expr.subs(values)
which gives
In [22]: explore()
Enter an expression: a & (b | c)
[(a, False), (b, False), (c, False)] False
[(a, False), (b, False), (c, True)] False
[(a, False), (b, True), (c, False)] False
[(a, False), (b, True), (c, True)] False
[(a, True), (b, False), (c, False)] False
[(a, True), (b, False), (c, True)] True
[(a, True), (b, True), (c, False)] True
[(a, True), (b, True), (c, True)] True
This is shorter than yours, but it uses exactly your approach.