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.
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}))
I have defined the following function using lambda:
f = lambda x: x**2 + 3*x + 3
Is there any way that I use Python to find the value of x that minimizes the value f(x)?
I know that min() function is to find the minimum element in a list of values, but here, I am not given the list of values, rather I need to locate the value of x that minimizes f(x).
Thank you,
What you're trying to do can be done using python's symbolic package.
There are many other ways, but I suspect this will be the most useful to you.
>>> import sympy
>>> y = sympy.sympify("x**2 + 3*x + 3")
>>> sympy.solve( sympy.diff(y) ) # minimize by solving for the derivative
[-3/2]
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)
I have this expression −0.00117115487626846cos(ϕc)+0.00241553041801322 and it belongs to the type add.add from sympy, i have a list like this which depends on phic or phia, i want to test for several on which one depends. I have tried with in and args, but still not have the answer. Thanks
i hace tried args and in
return:
simplify(expand((u[0,2]*conjugate(u[0,2])).subs(constantes),complex=True))
S13=[]
for i in range(6):
S13.append(s13(U[i]))
I expect the output gives me if the dependence is on phia or phic or both
In general, the free symbols (those symbols on which an expression depends) are available through the free_symbols attribute.
So, you can do something like this:
from sympy import *
x,y = symbols('x y')
exp = x**2 + y**2 + x*y
s = exp.free_symbols
print(s) # {x,y}
Here's my problem (illustraded with an applicable example):
... some code
### x=0.5*t*(copysign(1, t - 0.5) + 1) + 0.1
### x=string value
X= Matrix(len(x),1,x)
>>>print X[0]
0.5*t*(copysign(1, t - 0.5) + 1) + 0.1
>>>print type(X[0])
<class 'sympy.core.add.Add'>
t1=linspace(0,1,2)
REF=[]
for i in range(len(t1)):
REF.append(M_ref[0].subs(t,t1[i]))
>>>print REF
0.100000000000000, 0.5*copysign(1, 0.5) + 0.6
So REF[0] is from the 'sympy.core.numbers.Float' class, but REF[1] is from the 'sympy.core.add.Add' class (as are the rest of list values when I expand the linspace). Therefore I can't use them in the rest of my code. I tried to use evalf but that didn't solve the problem.
I need the values in the REF list to be all floats (or integers).
Any help would be appreciated.
I think I understand what is happening.
You are converting your input from strings. SymPy doesn't have a function called copysign, and sympify doesn't use the math library, so it just creates Function('copysign'), which is an unevaluated object. If you want to evaluate it as the math copysign from the get-go, you can add {'copysign': math.copysign} as a second argument when you call sympify, like sympify('copysign(1, 2)', {'copysign': math.copysign}). If you want a symbolic version, you will need to create one, as it doesn't exist in SymPy yet. Something like
class copysign(Function):
nargs = 2
#classmethod
def eval(cls, x, y):
if x.is_number and y.is_number:
return math.copysign(x, y)
That will work symbolically, but as soon as both arguments are numeric, it will evaluate using the math copysign.
>>> x, y = symbols('x y')
>>> copysign(x, y)
copysign(x, y)
>>> copysign(1, -2)
-1.0
Really, a better way to do it would be to reimplement the copysign logic symbolically, so that it always returns a SymPy type, but I'll leave that to you as an exercise. You can look at how sign is implemented in SymPy to get an idea.