If I define a symbolic expression inside a function, how do I evaluate that expression using .subs()
outside that function. The following code demonstrates the issue:
import sympy as sp
def myFun():
x = sp.symbols("x", positive = True)
y = 3*x
return y
expr = myFun()
print(expr.subs({x:10}))
Running the code above gives NameError: name 'x' is not defined
.
How can I perform this evaluation? Two things I could think of is declaring x
to be a global variable using global x
inside myFun()
. However, I want to avoid this method. Another way is to lambdify
the expression in myFun()
before returning the expression. But I would prefer not to use this method because in the real problem I am working with, I have multiple variables in my expression and would like to substitute only some of the variables at one stage, and the remaining variables at a later stage. Would you adopt one of the two approaches I mentioned or is it possible to evaluate the expression using .subs()
using some other approach?
@cards answer is not going to work, the reason is that you have defined x
to be positive, instead when calling print(expr.subs({'x':10}))
the string 'x'
will generate a generic symbol, without assumptions.
You either create your symbols outside of the function, like this:
import sympy as sp
x = sp.symbols("x", positive = True)
def myFun():
y = 3*x
return y
expr = myFun()
print(expr.subs({x:10}))
# out: 30
Or you can retrieve the symbols that make up a symbolic expression with the free_symbol
attribute, like this:
import sympy as sp
def myFun():
x = sp.symbols("x", positive = True)
y = 3*x
return y
expr = myFun()
x = expr.free_symbols.pop()
print(expr.subs({x:10}))
# out: 30
EDIT (to accommodate comment):
I was just wondering but what if the expression had three variables, say
5*y + 3*x + 7*z
? I tried the code you provided. The lineexpr.free_symbols.pop()
only gives one of the variables - it givesx
. Is there a way to usefree_symbols
to get all three variables?
free_symbols
returns a set with all variables. If the expression is expr = 5*y + 3*x + 7*z
, then expr.free_symbols
returns {x, y, z}
.
pop()
is a method of Python's set
: it returns the first element of a set.
To get all the variables of your expression you could try: x, y, z = expr.free_symbols
, or x, y, z = list(expr.free_symbols)
. However, this creates the following problem: execute print(x, y, z)
and you'll get something similar to: y z x
. In other words, the symbols returned by expr.free_symbols
are unordered. This is the default Python behavior.
Can we get ordered symbols? Yes, we can sort them alphabetically with : x, y, z = sorted(expr.free_symbols, key=str)
. If we now execute print(x, y, z)
we will get x y z
.