Collecting like term of an expression in Sympy - python

I am currently dealing with functions of more than one variable and need to collect like terms in an attempt to simplify an expression.
Say the expression is written as follows:
x = sympy.Symbol('x')
y = sympy.Symbol('y')
k = sympy.Symbol('k')
a = sympy.Symbol('a')
z = k*(y**2*(a + x) + (a + x)**3/3) - k((2*k*y*(a + x)*(n - 1)*(-k*(y**2*(-a + x) + (-a + x)**3/3) + k*(y**2*(a + x) + (a + x)**3/3)) + y)**2*(-a + k*(n - 1)*(y**2 + (a + x)**2)*(-k*(y**2*(-a + x)))))
zEx = z.expand()
print type(z)
print type(zEx)
EDIT: Formatting to add clarity and changed the expression z to make the problem easier to understand.
Say z contains so many terms, that sifting through them by eye. and selecting the appropriate terms, would take an unsatisfactory amount of time.
I want to collect all of the terms which are ONLY a multiple of a**1. I do not care for quadratic or higher powers of a, and I do not care for terms which do not contain a.
The type of z and zEx return the following:
print type(z)
print type(zEx)
>>>
<class 'sympy.core.add.Add'>
<class 'sympy.core.mul.Mul'>
Does anyone know how I can collect the terms which are a multiple of a , not a^0 or a^2?
tl'dr
Where z(x,y) with constants a and k described by z and zEx and their type(): How can one remove all non-a terms from z AND remove all quadratic or higher terms of a from the expression? Such that what is left is only the terms which contain a unity power of a.

In addition to the other answers given, you can also use collect as a dictionary.
print(collect(zEx,a,evaluate=False)[a])
yields the expression
k*x**2 + k*y**2

In the end it is just an one-liner. #asmeurer brought me on the right track (check the comments below this post). Here is the code; explanations can be found below:
from sympy import *
from sympy.parsing.sympy_parser import parse_expr
import sys
x, y, k, a = symbols('x y k a')
# modified string: I added a few terms
z = x*(k*a**9) + (k**1)*x**2 - k*a**8 + y*x*(k**2) + y*(x**2)*k**3 + x*(k*a**1) - k*a**3 + y*a**5
zmod = Add(*[argi for argi in z.args if argi.has(a)])
Then zmod is
a**9*k*x - a**8*k + a**5*y - a**3*k + a*k*x
So let's look at this more carefully:
z.args
is just a collection of all individual terms in your expression (please note, that also the sign is parsed which makes things easier):
(k*x**2, a**5*y, -a**3*k, -a**8*k, a*k*x, a**9*k*x, k**2*x*y, k**3*x**2*y)
In the list comprehension you then select all the terms that contain an a using the function has. All these terms can then be glued back together using Add which gives you the desired output.
EDIT
The above returns all all the expressions that contain an a. If you only want to filter out the expressions that contain a with unity power, you can use collect and Mul:
from sympy import *
from sympy.parsing.sympy_parser import parse_expr
import sys
x, y, k, a = symbols('x y k a')
z2 = x**2*(k*a**1) + (k**1)*x**2 - k*a**8 + y*x*(k**2) + y*(x**2)*k**3 + x*k*a - k*a**3 + y*a**1
zc = collect(z2, a, evaluate=False)
zmod2 = Mul(zc[a], a)
then zmod2 is
a*(k*x**2 + k*x + y)
and zmod2.expand()
a*k*x**2 + a*k*x + a*y
which is correct.
With the updated z you provide I run:
z3 = k*(y**2*(a + x) + (a + x)**3/3) - k((2*k*y*(a + x)*(n - 1)*(-k*(y**2*(-a + x) + (-a + x)**3/3) + k*(y**2*(a + x) + (a + x)**3/3)) + y)**2*(-a + k*(n - 1)*(y**2 + (a + x)**2)*(-k*(y**2*(-a + x)))))
zc3 = collect(z3.expand(), a, evaluate=False)
zmod3 = Mul(zc3[a], a)
and then obtain for zmod3.expand():
a*k*x**2 + a*k*y**2
Is this the result you were looking for?
PS: Thanks to #asmeurer for all these helpful comments!

To iterate over the terms of an expression use expr.args.
I'm unclear what a is supposed to be, but the collect function may do what you want.

Related

How can i change a variable name in a sympy expression

I am working on a project where I need to change all variables that are named 'a' to a new variable ai, where i is the order of the variable a in the expression. For instance if we use the expression: 1 + x + a + a ** 2, the output should be: 1 + x + a0 + a1 ** 2. Here is a code that I've written to solve this but it doesn't work, the expression remains unchanged.
import sympy.parsing.sympy_parser as sp1
import sympy as sp
I=sp1.parse_expr('1 + x + a + a**2', evaluate=False)
a,x=sp.symbols('a x')
def pre(expr):
i=0
for arg in sp.postorder_traversal(expr):
if arg==a:
tmp=sp.symbols('a'+str(i))
arg=tmp
print(arg)
i=i+1
pre(I)
print(I)
One way to achieve that is:
from sympy import Pow, Mul, Symbol, degree
def change_symbol(expr, a):
"""expr: the expression to modify
a: the symbol to look for and substitute
"""
# define a wild symbol to look for Symbols, Multiplications and Powers
# containing the specified symbol
w = Wild("w", properties=[
lambda t: isinstance(t, (Pow, Mul, Symbol)) and ((a in t.args) or (t == a))
])
# find the terms that satisfy the above criteria
terms = list(expr.find(w))
terms.sort(key=lambda t: degree(t), reverse=True)
# loop over those terms and performs the substitution with new symbols
name = a.name
for t in terms:
o = degree(t)
s = Symbol(name + "%s" % (o - 1))
expr = expr.subs(t, s**o)
return expr
change_symbol(I, a)
# out: a0 + a1**2 + x + 1
Your code did not work because you never changed the expression. When you say arg = tmp that assigns a value of tmp to arg but this does not update expr. #Davide_sd shows a way to recreate an expression with pieces that have been modified. You can also let replace do the traversal and let it replace a as it encounters it.
suffix = [0] #mutable suffix
def na():
rv = Symbol('a%s'%suffix[0])
suffix[0]+=1 # modify for next time
return rv
>>> a,x=var('a x')
>>> (1 + x + 2*a + a**2).replace(lambda x: x==a, lambda x: na())
a0**2 + 2*a1 + x + 1
Note that you said "order in expression" and coded as though you meant "order encountered" but in the polynomial sense, "higher order" terms will not necessarily appear later in the ordered terms. Note that a**2 appears before 2*a and that is why the replace gave it a value of a0:
>>> (1 + x + 2*a + a**2).args
(1, x, a**2, 2*a)

Collect sinusoids of same frequency

I have an expression of many sinusoids. Some frequencies are repeated, so I would like to collect the coefficients for identical frequencies. Seems like either collect() or factor() should so the trick but they only work on simple expressions and fail when many variables are involved. Is there a better way to collect terms? Is there a way to help guide factor() to what variable to operate on like exists for collect()? I am trying to manually simplify the equation into a particular form and I think cse() goes too far in reworking terms. Examples below:
w, x, y, z = symbols('w x y z')
factor(z*sin(2*pi*x)+y*sin(2*pi*x), deep=True)
(y + z)⋅sin(2⋅π⋅x) #WORKS
factor(z*sin(2*pi*x*w)+y*sin(2*pi*x*w), deep=True)
(y + z)⋅sin(2⋅π⋅w⋅x) #WORKS
factor(z*sin(2*pi*x)+y*sin(2*pi*x)+z*sin(w)+2*y*sin(w), deep=True)
2⋅y⋅sin(w) + y⋅sin(2⋅π⋅x) + z⋅sin(w) + z⋅sin(2⋅π⋅x) # FAILS – expected Expected (2y + z)*sin(w) + (y+z)⋅sin(2⋅π⋅x)
collect(z*sin(2*pi*x)+y*sin(2*pi*x), x)
y⋅sin(2⋅π⋅x) + z⋅sin(2⋅π⋅x) # FAILS expected (y+z)⋅sin(2⋅π⋅x)
collect(z*sin(2*pi*x)+y*sin(2*pi*x)+z*sin(w)+2*y*sin(w), x)
2⋅y⋅sin(w) + y⋅sin(2⋅π⋅x) + z⋅sin(w) + z⋅sin(2⋅π⋅x) #FAILS Expected (2y + z)*sin(w) + (y+z)⋅sin(2⋅π⋅x)
It is:
>>> from sympy import *
>>> w, x, y, z = symbols('w x y z')
>>> ex= z*sin(2*pi*x)+y*sin(2*pi*x)+z*sin(w)+2*y*sin(w)
>>> ex
2*y*sin(w) + y*sin(2*pi*x) + z*sin(w) + z*sin(2*pi*x)
>>> collect(ex, [sin(w), sin(2*pi*x)])
(y + z)*sin(2*pi*x) + (2*y + z)*sin(w)
Reference: https://docs.sympy.org/latest/modules/simplify/simplify.html?highlight=collect

Collect and substitute terms in very long and nested expressions with sympy

Short version:I want to collect and substitute some terms that I can clearly read in the expression but are not picked by sympy subs function.
I've done the symbolic computation in python, but in the end I will have to make these computation in C#. For this purpose, I'm trying to do some substitutions and partial numerical evaluations that I will hardcode in C#
As example this is one of the expressions (simple, I have to do this job on expressions ten times longer and with more levels of parenthesis):
from sympy import symbols
x,y,rho_0,v = symbols('x y rho_0 v')
expr = 4*x*(x**2 + y**2)*(7*(-1 + 2*(x**2 + y**2)/rho_0**2)**2 + 8 - 14*(x**2 + y**2)/rho_0**2)/rho_0**4 + (x**2 + y**2)**2*(56*x*(-1 + 2*(x**2 + y**2)/rho_0**2)/rho_0**2 - 28*x/rho_0**2)/rho_0**4
I don't know how to display equations in a better format here, sorry.
But the point is that I can clearly see that I can collect and substitute (x**2 + y**2)/rho_0**2 with very small manipulations
Using expr.subs((x**2 + y**2)/rho_0**2, v) has not given any result. I started using sympy last week so I don't know much yet, I think should try to navigate the expression from the innermost level of parenthesis, factorize and try to substitute, but I don't have any clue on how to do it.
subs has a hard time when a target contains an Add and is multiplied by a Rational. Targeting the Add first and continuing from there brings more success:
>>> expr
4*x*(x**2 + y**2)*(7*(-1 + (2*x**2 + 2*y**2)/rho_0**2)**2 + 8 - (14*x**2 +
14*y**2)/rho_0**2)/rho_0**4 + (x**2 + y**2)**2*(56*x*(-1 + (2*x**2 +
2*y**2)/rho_0**2)/rho_0**2 - 28*x/rho_0**2)/rho_0**4
Get the Rational separated from the Add
>>> factor_terms(expr)
4*x*(x**2 + y**2)*(7*(-1 + 2*(x**2 + y**2)/rho_0**2)**2 + 8 + 7*(-3 + 4*(x**2 +
y**2)/rho_0**2)*(x**2 + y**2)/rho_0**2 - 14*(x**2 + y**2)/rho_0**2)/rho_0**4
Do subs in two steps: make Add a Symbol and then Add/Pow the Symbol
>>> _.subs(x**2+y**2, v).subs(v/rho_0**2, v)
4*v*x*(7*v*(4*v - 3) - 14*v + 7*(2*v - 1)**2 + 8)/rho_0**2
Simplify if desired
>>> _.simplify()
4*v*x*(56*v**2 - 63*v + 15)/rho_0**2

i want to solve the equations: x**2*y**2 + x**2 -10*x*y + 4*y**2 + 9.0=0,Is there any way to get the real solutions?

I am trying to use python to solve the equations: x**2*y**2 + x**2 -10*x*y + 4*y**2 + 9.0=0, due to the equations equal to (x*y-3)**2+(x-2*y)**2=0 ,so hoping to get the real solution: x = 2*sqrt(3.0/2),y = sqrt(3.0/2)&& x = -2*sqrt(3.0/2),y = -sqrt(3.0/2) Is there any way to get this solutions?
from sympy import *
x = symbols("x")
y = symbols("y")
expression = x**2*y**2 + x**2 - 10*x*y + 4*y**2 + 9
solve(expression,(x,y))
above code only get the solution: [((5*y + I*(-2*y**2 + 3))/(y**2 + 1), y),
((5*y + I*(2*y**2 - 3))/(y**2 + 1), y)],thanks for your help and advice
It looks like what you are trying to do is find where both of the terms of the expression (x*y-3)**2+(x-2*y)**2 are simultaneously zero. Instead of expanding that, ask solve for that answer:
>>> eq = (x*y-3)**2+(x-2*y)**2
>>> terms = eq.args
>>> solve(terms, x, y)
[(-sqrt(6), -sqrt(6)/2), (sqrt(6), sqrt(6)/2)]
You can use solveset_real (although the exact equation may not have any real solutions)

How to edit the order of sympy latex print? [duplicate]

I have the following code:
from sympy import *
init_printing()
x,y = symbols('x y')
u = Function('u')(x,y)
ux,uy,uxx,uxy,uyy = symbols("u_x u_y u_xx u_xy u_yy")
mainEvaluation = uxx - 2*sin(x)*uxy - (cos(x) ** 2) * uyy - 2*ux + (2 - cos(x) + 2*sin(x) )*uy
And when the output of print(mainExpression) is
-2*u_x + u_xx - 2*u_xy*sin(x) + u_y*(2*sin(x) - cos(x) + 2) - u_yy*cos(x)**2
The problem is: I want the original order of variables.
u_xx - 2*u_xy*sin(x) - u_yy*cos(x)**2 - 2*u_x + u_y*(2*sin(x) - cos(x) + 2)
All this is done in IPython notebook.
Is there any way to keep order?
Sadly, SymPy does not keep track of the input order (see the other question I linked in a comment on the question). You can define your own ordering function that orders expressions however you want, but there's no way to order things exactly as they were input, since that information isn't saved.
If you know what your arguments/terms are then you can manually create the Add with evaluate=False to keep them in order and print them with a printer initialized to not change the order:
x,y = symbols('x y')
u = Function('u')(x,y)
ux,uy,uxx,uxy,uyy = symbols("u_x u_y u_xx u_xy u_yy")
args = uxx , -2*sin(x)*uxy, -cos(x)**2*uyy, -2*ux, +(2-cos(x)+2*sin(x))*uy
expr = Add(*args, evaluate=False)
from sympy.printing.str import StrPrinter # or LatexPrinter from .latex)
StrPrinter(dict(order='none'))._print_Add(expr)
This outputs
u_xx - 2*u_xy*sin(x) - u_yy*cos(x)**2 - 2*u_x + u_y*(2 - cos(x) + 2*sin(x))
try read this http://docs.sympy.org/0.7.2/modules/utilities/misc.html, may be could help you
Note:
The key returned is useful for getting items into a canonical order that will be the same across platforms. It is not directly useful for sorting lists of expressions:
>>> a, b = x, 1/x
Since a has only 1 term, its value of sort_key is unaffected by order:
>>> a.sort_key() == a.sort_key('rev-lex')
True
If a and b are combined then the key will differ because there are terms that can be ordered:
>>> eq = a + b
>>> eq.sort_key() == eq.sort_key('rev-lex')
False
>>> eq.as_ordered_terms()
[x, 1/x]
>>> eq.as_ordered_terms('rev-lex')
[1/x, x]
But since the keys for each of these terms are independent of order‘s value, they don’t sort differently when they appear separately in a list:
>>> sorted(eq.args, key=default_sort_key)
[1/x, x]
>>> sorted(eq.args, key=lambda i: default_sort_key(i, order='rev-lex'))
[1/x, x]
The order of terms obtained when using these keys is the order that would be obtained if those terms were factors in a product.
For the teaching purpose, I also don't want to simpilify or change the order of terms too early.
I'm using pytexit with jupyter notebook: https://pytexit.readthedocs.io/en/latest/
from pytexit import py2tex
def showeq(str):
py2tex(str, print_formula=False);
eq1 = "angle = acos((side_A**2 + side_B**2 - side_C**2)/(2*side_A*side_B))"
show(eq1)
side_A = 14.8
side_B = 16.3
side_C = 13.2
exec(eq1)

Categories

Resources