I am programming a little calculator using ipython console and sympy.
The setup is
import sympy
x, y, z = symbols('x y z')
f = (x+2)**2
g = lambda y: y+3
It would be nice to see which symbols have already been defined in sympy.
How do I get a list of all sympy symbols? i.e. a list containing x, y, z and f but not g, as g is an ordinary python lambda.
I am looking for something like vars() or globals, but containing only sympy related definitions.
Would this work for you?
>>> b4 = set(dir()) # everything before SymPy work started
>>> ...
>>> from sympy import Basic
>>> [i for i in dir() if not i.startswith("_") and i not in b4 and
... isinstance(globals()[i], Basic)]
Basic could be replaced with Expr if you wanted only symbols that pointed to Expr
Related
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 line expr.free_symbols.pop() only gives one of the variables - it gives x. Is there a way to use free_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.
Let's create an expression with two symbols in it:
In [44]: x, y = symbols('x, y')
In [45]: expr = sin(y) + x**2
Suppose that we only have the expression as a local variable. We can find the symbols it contains using free_symbols. We can use that to build a dict that maps symbol names (as strings) to the symbols themselves:
In [46]: expr.free_symbols
Out[46]: {x, y}
In [47]: syms = {s.name: s for s in expr.free_symbols}
In [48]: syms
Out[48]: {'y': y, 'x': x}
This mapping is important because different symbols with the same names but different assumptions are not interchangeable. With that we can substitute the symbol whose name is 'x' like this:
In [49]: expr.subs(syms['x'], 2)
Out[49]: sin(y) + 4
x as a Symbol object is only declared in the function's body, so it's not accessible from the outside. Use 'x' as a string.
x = list(expr.free_symbols)[0]
print(expr.subs({x: 2}))
In Wolfram Research Mathematica it is possible to define a sustitution rule of the type
sustitution = g_[arg___] -> g[Sequence ## Reverse[{arg}]]
Then it is possible to apply it to different expressions involving functions with the following results:
f[x, y] /. sustitution >> f[y,x]
g[x1,x2,x3] >> g[x3,x2,x1]
h[1,z,w,t]/.sustitution >> h[t,w,z,1]
As it is possible to use a pattern with name, g_, for the name of the function and another pattern, arg___, for the arguments, the same sustitution rule is valid no matter the name of the function that appears in the expression.
Is it possible to use WildFunction symbols along with replace to obtain a similar efect with Sympy?
The arg__ is not necessary for a SymPy type arg-remapping function since the args can be retrieved from the function call itself:
>>> from sympy.abc import x,y,z
>>> from sympy import Function
>>> f = Function('f')
>>> g_ = lambda f: f.func(*list(reversed(f.args)))
>>> g_(f(x,y,z))
f(z, y, x)
Another way of changing all user-defined functions the same way within an expression is to use replace as follows:
>>> from sympy.abc import *
>>> from sympy import Function, AppliedUndef
>>> f,g,h,F=symbols('f,g,h,F',cls=Function)
>>> eq=f(x,y,z)+g(y,x,w)*h(1,u,t)**cos(F(x,y,1,w))
>>> eq.replace(
... lambda x: isinstance(x, AppliedUndef),
... lambda x: x.func(*list(reversed(x.args))))
f(z, y, x) + g(w, x, y)*h(t, u, 1)**cos(F(w, 1, y, x))
If you wanted to apply such a transformation to all functions then use Function instead of AppliedUndef.
I am wondering how to find the symbols connected with Functions in a sympy expression. I am aware of .free_symbols, .atoms(Function) as well as .atoms(AppliedUndef). Here is some code to show why none of these does what I need.
f1 = Function(r'f_1')
f2 = Function(r'f_2')
c1, x = symbols(r'c_1, x')
expr = c1+f1(x)+f2(x)
print(expr)
# c_1 + f_1(x) + f_2(x)
print(expr.free_symbols)
# {x, c_1}
print(expr.atoms(Function))
# {f_1(x), f_2(x)}
from sympy.core.function import AppliedUndef
print(expr.atoms(AppliedUndef))
# {f_1(x), f_2(x)}
(The comments are the outputs of each print line). So .free_symbols is nice, it gives me c_1 and x. However, it does not return the symbols associated with the functions f_1 and f_2. (First question: why? Are they not free somehow?). .atoms(Function) does not help either. It finds the functions, but does not return their associated symbols (e.g. f_1), it returns the whole function call (e.g. f_1(x)).
Main question: How do I find the symbols f_1 and f_2 in above expression?
Background: The reason I want this, is because I would like to lambdify in the following way
expr_num = lambdify([c1, f1, f2, x], expr)
but instead of giving the argument [c1, f1, f2, x] manually, I would like to find all necessary symbols in the expression.
The following obtains free symbols and names of AppliedUndef functions:
>>> s = f(x).free_symbols
>>> func = set([i.func for i in f(x).atoms(Function) if isinstance(i, AppliedUndef)])
>>> s | func
{x, f}
Based on the accepted solution by #smichr, here is a piece of code that can be directly appended to the code in the question (adds nothing interesting, just for convenience):
f1 = Function(r'f_1')
f2 = Function(r'f_2')
c1, x = symbols(r'c_1, x')
syms_and_funs = set(expr.free_symbols) | set([i.func for i in expr.atoms(Function) if isinstance(i, AppliedUndef)])
print(syms_and_funs)
# {x, f_2, f_1, c_1}
expr_num = lambdify(syms_and_funs, expr)
Consider these two equations:
x^y + xy
a^b + ab
I want to be able to recognize these as identical symbolic expressions. "Simplify" won't work. "Subs" (substituting a for x, etc.) won't always work either because the order of the variables may change, etc.
So, is there a way to get an abstract representation for a SymPy expression that is independent of the symbols used?
A representation that is not tied to the symbols used is a function. For example,
f1 = lambda x, y: (2*x+y)**2
defines a function that is not tied to x and y, they don't exist other than as placeholders inside of this function. (This is a Python function; one can also define a SymPy Function object but the distinction is not material here.)
If someone asked you whether (2*x+y)**2 is "identical" to a**2 + 4*b*(a+b), what would you do? The only way I know is to simplify both and try to match variables under all possible permutations. Which is what the following code does.
from sympy import *
from itertools import permutations
f1 = lambda x, y: (2*x+y)**2
f2 = lambda a, b: a**2 + 4*b*(a+b)
vars = symbols("v0:2") # auxiliary variables to plug in f1 and f2
identical = any([simplify(f1(*vars) - f2(*p)) == 0 for p in permutations(vars)])
Now identical is True, since the expressions are the same in the sense you described.
If you have expressions, rather than functions, to start with, then subs can be used instead:
x, y, a, b = symbols("x y a b")
expr1 = (2*x+y)**2
expr2 = a**2 + 4*b*(a+b)
vars = symbols("v0:2")
identical = any([simplify(expr1.subs(zip((x, y), vars)) - expr2.subs(zip((a, b), p))) for p in permutations(vars)])
Just to elaborate a bit:
I wasn't too happy with the permutation approach. So, I kept digging and I noticed that SymPy generates a tree graph to represent the symbolic expressions. The structure of the tree is the structure of the expression, and it is independent of the symbols. However, once you have the corresponding graphs, you will need to decide whether they are isomorphic (i.e. if the two expressions are identical), which is a very non-trivial problem.
How can I get sympy to simplify an expression like log(exp(exp(x))) to exp(x)? It seems to work on simpler expressions like exp(log(x)) => x. This is a minimal example showing what I've tried so far:
import sympy
from sympy import exp, log
x = sympy.symbols('x')
a = exp(log(x))
print a
# Gives `x` automatically, no call to simplify needed
b = log(exp(exp(x)))
print sympy.simplify(b), sympy.powsimp(b,deep=True)
# Gives `log(exp(exp(x)))` back, expected `exp(x)`
This is happening because of lack of information. I think you want to do this:
In [7]: x = Symbol('x', real=True)
In [8]: (log(exp(exp(x)))).simplify()
Out[8]: exp(x)