sympy: Using Wild symbols as input for function - python

Consider the following expression in python/sympy:
from sympy.abc import x, y
expression = 3*x**2*y**1 + x**2*y**3
Now I want to replace x**n*y**m by max(n,m) by using pattern matching:
from sympy import Wild
n = Wild('n')
m = Wild('m')
expression = expression.replace(x**n*y**m,max(n,m))
However, I get a TypeError:
Traceback (most recent call last):
File "wild.py", line 15, in <module>
expression = expression.replace(x**n*y**m,max(n,m))
File "/usr/lib/python3/dist-packages/sympy/core/relational.py", line 103, in __nonzero__
raise TypeError("cannot determine truth value of\n%s" % self)
TypeError: cannot determine truth value of
m_ > n_
The Problem apparently is that upon matching the expression sympy does not convert the value of the Wild symbol to the matched value before forwarding it to the max-function. Is there any way to make this work?
Please note that this a simple example of a more general problem I have, so a workaround that does not generalize well is not very helpful. I especially hope that there is a solution using expression matching.
Update: Following sandwich's suggestion and replacing max(n,m) by (m+n+abs(m-n))/2 works, however, the function I am using in my real program is much more complicated.
Concerning the problem mentioned by sandwich that replace performs substitutions starting at the bottom of the expression tree: if I use exact=True and define f = sympy.Function('f') then the following works (except that I will have to deal with some cases separately):
expression = expression.replace(x**n*y**m,f(n,m),exact=True)
It still, however, does not work for max(n,m).

max is the built in Python function, which tries to evaluate immediately, hence the error (it can't determine which of n and m are larger when they are symbolic). You probably want the SymPy Max function, which works symbolically. The my_func you defined in your answer is effectively a basic implementation of Max.
In [14]: expression = expression.replace(x**n*y**m, Max(n,m))
In [15]: expression
Out[15]: 4
It seems this doesn't work (the answer 4 is wrong). The problem is that it's matching x**2 as x**2*y**0. Since the mathematical value of the replacement function depends on the form of the expression, this is problematic, since SymPy is trying to be smart. You can actually make n and m not match 0 with Wild('n', exclude=[x, 0]), but then there is an issue that it doesn't match x**2*y as x**2*y**1.
So I recommend converting your expression to a polynomial and doing the replacement exactly. Hopefully this generalizes well to what you are actually doing
In [18]: Poly(expression, x, y)
Out[18]: Poly(x**2*y**3 + 3*x**2*y, x, y, domain='ZZ')
In [19]: Poly(expression, x, y).terms()
Out[19]: [((2, 3), 1), ((2, 1), 3)]
In [20]: sum(max(pows)*coeff for pows, coeff in Poly(expression, x, y).terms())
Out[20]: 9

Ok, I have found a solution, it is note very pretty though which might have to do with the fact that I know little about writing functions that interact nicely with sympy.
At first I import everything relevant:
import sympy
from sympy.abc import x, y, a, b,z
from sympy import Wild
from sympy import Function
Then I define my own function which will return the maximum, here returning max(x,y) could be replaced by a more complicated function:
class my_func(Function):
#classmethod
def eval(cls, x, y):
if x.is_Number and y.is_Number:
return max(x,y)
The expression I want to modify is:
3*x**2*y**1 + x**2*y**3
The I define the necessary wild symbols and a sympy function which is used temporarily to make replacements without evaluation:
n = Wild('n')
m = Wild('m')
k = Wild('k')
f = Function('f')
expression = expression.replace(x,f(1,0),exact=True)
expression = expression.replace(y,f(0,1),exact=True)
expression = expression.replace(f(1,0)**n,f(n,0),exact=True)
expression = expression.replace(f(0,1)**n,f(0,n),exact=True)
expression = expression.replace(k*f(0,m)*f(n,0),k*f(n,m),exact=True)
These replacements catch all the cases which come up in my problem. In the last step I replace f by my_func which does the evaluation.
expression = expression.replace(f(n,m),my_func(n,m))
Maybe somebody will find a nicer solution....

Related

I have a long expression which I want to simplify but assuming that an equation holds

In Mathematica, you can use the function FullSimplify[expression,assumptions] to simplify expressions using assumptions. For instance, if I do this:
FullSimplify[x^2-y^2,x^2-y^2==1],
then the result will be 1 because that's the 'simplest expression that is equivalent to the function I gave the software.
Now I need to do the same with Python, but I don't know how to do that. I have seen the documentation about the command sympy.refine on this page:
https://docs.sympy.org/latest/modules/assumptions/refine.html
However, I haven't been able to use equalities as assumptions. It doesn't seem possible to assume things like that with the Q function.
I have tried to do something like this:
import sympy as sp
x,y=sp.symbols('x y')
sp.refine(x**2-y**2,x==y)
However, this gives me the following error: ValueError: Inconsistent assumptions
Does someone have any ideas about this? Thank you.
Without some scope for what kind of expressions and assumptions you want to work with this is probably an unsolvable problem so I'll make some assumptions.
If you want to simplify a polynomial expression based on some other polynomial expression(s) being zero then you can do this in sympy using ratsimpmodprime:
In [1]: x, y = symbols('x, y')
In [2]: assumptions = [x**2 + y**2 - 1]
In [3]: expression = x**2 + y**2
In [4]: ratsimpmodprime(expression, assumptions)
Out[4]: 1
https://docs.sympy.org/latest/modules/simplify/simplify.html#ratsimpmodprime
I tried two approaches to the problem.
Assume that x - y = 0. This was the only way I could find to express the assumption that you want to make. Unfortunately, it doesn't seem to be smart enough to notice that this assumption allows it to substitute x for y.
print("refine:", sp.refine(x**2-y**2,Q.zero(x-y)))
This just returns the original expression.
Ask sympy to substitute the expression. This isn't as automatic - you're asking it to do the substitution instead of giving it the option of doing the substitution, but it does work for this toy example.
expr = (x**2-y**2)
print("substitution:", expr.subs(x**2-y**2, 1))
prints 1.

After substituting Functions in sympy, the evaluation fails to do straightforward derivatives

Last Day I have been bothering with this problem. At first I made a question but after comment of needing a simplified example, I deleted the question and I finally found the "source" of the problem.
I fail to evaluate expressions AFTER substituting Functions by expressions: Following example will show you what I mean:
xx = sy.Symbol('x',real=True)
yy = sy.Symbol('y',real=True)
FuncT = sy.Function('F')
TestExp= sy.cos(sy.diff(FuncT(xx,yy),xx)+xx+yy*xx+yy)
print(TestExp.subs({FuncT(xx,yy):xx})).subs({xx:1,yy:0.1})
which results
How can it replace dx/dx = 1 ?
Just doit:
>>> TestExp.subs({FuncT(xx,yy):xx}).subs({xx:1,yy:0.1}).doit()
-0.588501117255346
How to know to use doit?
When I print (not pprint) the expressions I see
cos(Subs(Derivative(x, x), x, 1) + 1.2)
I don't want Subs there but I don't know much about Subs so I
ask for help and read the following:
>>> help(Subs)
...
There's no automatic expansion - use the method .doit() to effect all
possible substitutions of the object and also of objects inside the
expression.
...

Parsing Symbolic Equations to Sympy functions

I am trying to pass image1 in my code when i copy and paste this as text it looks like this ∑(i=1,n)(a+m*x*(i)-y*(i))^2.
but it does not work.
following is my code which is working with a different syntax:
from sympy import symbols,sympify,solve,Eq,Symbol
from sympy import symbols,Eq,sympify,summation
expr = **('summation((m * x*(i) + a - y*(i))^2, (i, 0, n))')**
Eq1 = Eq(sympify(expr))
print(Eq1)
values = {2001,10,2,3,5}
arr_symbols = list(Eq1.free_symbols)
print(arr_symbols)
Method1(arr_symbols,values,expr)
def Method1(arr_symbols,Values,expr):
from sympy import symbols, Eq, solve, pprint, integrate, sympify
z = symbols('z')
Formula = Eq(sympify(expr),z)
print(Formula)
index = 0
for i in Values:
Formula = Formula.subs(arr_symbols[index],i)
index+=1
print(solve(Formula))
but what i want to do is to use ∑(i=1,n)(a+m*x*(i)-y*(i))^2 and ask sympy to convert it for me.
SymPy can represent this equation, but it can only parse Python. You might be able to write extensions to its parser to handle this sort of thing (see https://docs.sympy.org/latest/modules/parsing.html). It should be possible in principle, although it might not be straightforward. I would only recommend doing this the syntax of your expressions is already very close to Python. If it isn't (and it looks like it isn't), it would be better to a real parsing library like ANTLR to build up a grammar for your expressions. You can then use that to parse into SymPy (see for example how the sympy.parsing.latex module is written).
I don't know if there is pre-existing library in Python that handles your types of expressions. I'm not aware of any. At best you might be able to find a grammar that someone has already written, so you don't have to write it yourself.
If you are, given a, m and values for x and y and are trying to compute the sum of the squares of the residuals, then it would be better to write a function that does that or do something like this:
>>> x = [1, 2, 3]
>>> y = [4, 7, 11]
>>> a = 2
>>> m = 5
>>> sum((a + m*xi - yi)**2 for xi, yi in zip(x, y))
70
it cannot be done.
we have to pass latex code.

Replacing numbers with parameters in sympy

I have some SymPy expressions of the form: 2*x1**2+7*cos(8*x2)+2*Pi (mine are longer and more complicated, but this should be enough for my question). How can I turn all the numbers appearing in this expression into parameters, something like this: a*x1**2+b*cos(c*x2)+d. Basically, I have a program which is able to give me an approximate function able to fit some data, but the parameters are integers or some well known numbers, such as pi or e (this is the first expression I provided). Then, I want to take that expression and fine tune these numbers (using gradient descent), such that to obtain the actual parameters (one can assume that the functional form is right, just the parameters need to be adjusted). For example, in the end, the right equation could be: 2.87*x1**2+6.95*cos(8.05*x2)+6.27. Is there a way to do this? Thank you!
It's a little tricky because you say "all numbers" but you are ignoring exponents. In your example you are only replacing numerical factors in a term with new symbols. To do that (and to get you on your way with a possible solution) try using replace, telling it you are looking for a Mul and then telling it what you want to do with the Mul when you have it:
from sympy import *
from sympy.abc import x,y
eq=2*x**2+7*cos(8*y)+2*pi
def nfact2dum(m):
assert m.is_Mul
nonnum = sift(m.args, lambda i:i.is_number, binary=True)[1]
return Mul(*([Dummy()] + nonnum))
deq = eq.replace(
lambda x:x.is_Mul,
lambda x: nfact2dum(x))
print(
deq.subs(list(zip(deq.atoms(Dummy),numbered_symbols('c')))))
output: c0*x**2 + c2*cos(c1*y) + c3

Sympy - Comparing expressions

Is there a way to check if two expressions are mathematically equal? I expected
tg(x)cos(x) == sin(x) to output True, but it outputs False. Is there a way to make such comparisons with sympy? Another example is
(a+b)**2 == a**2 + 2*a*b + b**2 which surprisingly also outputs False.
I found some similar questions, but none covered this exact problem.
From the SymPy documentation
== represents exact structural equality testing. “Exact” here means that two expressions will compare equal with == only if they are exactly equal structurally. Here, (x+1)^2 and x^2+2x+1 are not the same symbolically. One is the power of an addition of two terms, and the other is the addition of three terms.
It turns out that when using SymPy as a library, having == test for exact symbolic equality is far more useful than having it represent symbolic equality, or having it test for mathematical equality. However, as a new user, you will probably care more about the latter two. We have already seen an alternative to representing equalities symbolically, Eq. To test if two things are equal, it is best to recall the basic fact that if a=b, then a−b=0. Thus, the best way to check if a=b is to take a−b and simplify it, and see if it goes to 0. We will learn later that the function to do this is called simplify. This method is not infallible—in fact, it can be theoretically proven that it is impossible to determine if two symbolic expressions are identically equal in general—but for most common expressions, it works quite well.
As a demo for your particular question, we can use the subtraction of equivalent expressions and compare to 0 like so
>>> from sympy import simplify
>>> from sympy.abc import x,y
>>> vers1 = (x+y)**2
>>> vers2 = x**2 + 2*x*y + y**2
>>> simplify(vers1-vers2) == 0
True
>>> simplify(vers1+vers2) == 0
False
Alternatively you can use the .equals method to compare expressions:
from sympy import *
x = symbols('x')
expr1 = tan(x) * cos(x)
expr2 = sin(x)
expr1.equals(expr2)
True
The solution with simplify was too slow for me (had to crosscheck multiple variables), so I wrote the following function, which does some simple checkes beforehand, to reduce computational time, to use simplify only in the last step.
import numpy as np
import sympy as sp
def check_equal(Expr1,Expr2):
if Expr1==None or Expr2==None:
return(False)
if Expr1.free_symbols!=Expr2.free_symbols:
return(False)
vars = Expr1.free_symbols
your_values=np.random.random(len(vars))
Expr1_num=Expr1
Expr2_num=Expr2
for symbol,number in zip(vars, your_values):
Expr1_num=Expr1_num.subs(symbol, sp.Float(number))
Expr2_num=Expr2_num.subs(symbol, sp.Float(number))
Expr1_num=float(Expr2_num)
Expr2_num=float(Expr2_num)
if not np.allclose(Expr1_num,Expr2_num):
return(False)
if (Expr1.equals(Expr2)):
return(True)
else:
return(False)
As previously stated, (expr1 - expr2).simplify() or expr1.equals(expr2) will sometimes fail to recognize equality for expressions that are complex to simplify. To deal with this, a numerical evaluation of the expressions with random numbers may constitute a relatively safe "brute force" test. I've adapted the excellent solution by #Okapi575 to:
Test the numerical equality N-times with different random numbers each time for a more confident diagnostic
Warn the user when a pair of expressions only passes the numeric test but not the symbolic equality test.
For example:
Hope it can prove useful:
import sympy as sp
import numpy as np
def check_equal(Expr1, Expr2, n=10, positive=False, strictly_positive=False):
# Determine over what range to generate random numbers
sample_min = -1
sample_max = 1
if positive:
sample_min = 0
sample_max = 1
if strictly_positive:
sample_min = 1
sample_max = 2
# Regroup all free symbols from both expressions
free_symbols = set(Expr1.free_symbols) | set(Expr2.free_symbols)
# Numeric (brute force) equality testing n-times
for i in range(n):
your_values=np.random.uniform(sample_min, sample_max, len(free_symbols))
Expr1_num=Expr1
Expr2_num=Expr2
for symbol,number in zip(free_symbols, your_values):
Expr1_num=Expr1_num.subs(symbol, sp.Float(number))
Expr2_num=Expr2_num.subs(symbol, sp.Float(number))
Expr1_num=float(Expr2_num)
Expr2_num=float(Expr2_num)
if not np.allclose(Expr1_num, Expr2_num):
print("Fails numerical test")
return(False)
# If all goes well so far, check symbolic equality
if (Expr1.equals(Expr2)):
return(True)
else:
print("Passes the numerical test but not the symbolic test")
# Still returns true though
return(True)
EDIT: code updated (1) to compare expressions with differing numbers of free symbols (for example, after symbols got cancelled out during a simplification), and (2) to allow for the specification of a positive or strictly positive random number range.
Check this out from the original sympy themselves.
https://github.com/sympy/sympy/wiki/Faq
Example of it working for me

Categories

Resources