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

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

Related

Filtering out/saving a term from an equation by using its factor (in multiplication)

I have the following equation/term:
1 + (a2*b2*c1 - a2*b2 + a2*c2 + 2)/λ + (a2*c2 + 1)/λ**2
I would like to save: (a2*c2 + 1) as a
and (a2*b2*c1 - a2*b2 + a2*c2 + 2) as b.
However, a and b vary each time. Hence, I need an algorithm which can separate a and b from the above equation using the "indicators" λ**2 and λ respectively. My idea is something along the lines: if multiplication contains λ**2 or λ then ...

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)

Sympy outputs a derivative with log(e)

I'm using Sympy to calculate derivatives and some other things. I tried to calculate the derivative of "e**x + x + 1", and it returns e**x*log(e) + 1 as the result, but as far as I know the correct result should be e**x + 1. What's going on here?
Full code:
from sympy import *
from sympy.parsing.sympy_parser import parse_expr
x = symbols("x")
_fOfX = "e**x + x + 1"
sympyFunction = parse_expr(_fOfX)
dSeconda = diff(sympyFunction,x,1)
print(dSeconda)
The answer correctly includes log(e) because you never specified what "e" is. It's just a letter like "a" or "b".
The Euler number 2.71828... is represented as E in SymPy. But usually, writing exp(x) is preferable because the notation is unambiguous, and also because SymPy is going to return exp(x) anyway. Examples:
>>> fx = E**x + x + 1
>>> diff(fx, x, 1)
exp(x) + 1
or with exp notation:
>>> fx = exp(x) + x + 1
>>> diff(fx, x, 1)
exp(x) + 1
Avoid creating expressions by parsing strings, unless you really need to and know why you need it.

sympy: simplify a larger expression with Binomial formula and quadratic complement

I get some eigenvalues of the matrix
import sys
import mpmath
from sympy import *
X,Y,Z = symbols("X,Y,Z")
Rxy,Rxz, Ry,Ryx,Ryz, Rz,Rzy,Rzz = symbols("Rxy,Rxz, Ry,Ryx,Ryz, Rz,Rzy,Rzz")
J = Matrix([
[ -1, 0, 0],
[ 0, -Ry*Y, Ry*Rzy*Y],
[Rxz*Rz*Z, Ryz*Rz*Z, -Rz*Z]])
which are the following:
{-Ry*Y/2 - Rz*Z/2 + sqrt(Ry**2*Y**2 + 4*Ry*Ryz*Rz*Rzy*Y*Z - 2*Ry*Rz*Y*Z + Rz**2*Z**2)/2: 1,
-Ry*Y/2 - Rz*Z/2 - sqrt(Ry**2*Y**2 + 4*Ry*Ryz*Rz*Rzy*Y*Z - 2*Ry*Rz*Y*Z + Rz**2*Z**2)/2: 1,
-1: 1}
lets just look at eigenvalue one:
In [25]: J.eigenvals().keys()[0]
Out[25]: -Ry*Y/2 - Rz*Z/2 + sqrt(Ry**2*Y**2 + 4*Ry*Ryz*Rz*Rzy*Y*Z - 2*Ry*Rz*Y*Z + Rz**2*Z**2)/2
I want to simplify this term the following: factor out 1/2 and (this is important) the radicant.
I can transform the radicant as following by adding the quadratic complement
Ry**2*Y**2 + 4*Ry*Ryz*Rz*Rzy*Y*Z - 2*Ry*Rz*Y*Z + Rz**2*Z**2 | + 4*Ry*Rz*Y*Z -4*Ry*Rz*Y*Z
which leads to
Ry**2*Y**2 + Rz**2*Z**2 + 2*Ry*Rz*Y*Z - 4*Ry*Rz*Y*Z + 4*Ry*Ryz*Rz*Rzy*Y*Z
which can be factorized to
(Ry*Y + Rz*Z)**2 - 4*Ry*Rz*Y*Z*(1 - Ryz*Rzy)
with these evaluations the complete eigenvalue should look like this
-1/2*(Ry*Y + Rz*Z - sqrt((Ry*Y + Rz*Z)**2 - 4*Ry*Rz*Y*Z*(1 - Ryz*Rzy)))
This calculation is really important for me because I have to evaluate if the eigenvalue is <0. Which is much easier in the last form.
Let me show you what I did until now.
In [24]: J.eigenvals().keys()[0]
Out[24]: -Ry*Y/2 - Rz*Z/2 + sqrt(Ry**2*Y**2 + 4*Ry*Ryz*Rz*Rzy*Y*Z - 2*Ry*Rz*Y*Z + Rz**2*Z**2)/2
In [25]: J.eigenvals().keys()[0].factor()
Out[25]: -(Ry*Y + Rz*Z - sqrt(Ry**2*Y**2 + 4*Ry*Ryz*Rz*Rzy*Y*Z - 2*Ry*Rz*Y*Z + Rz**2*Z**2))/2
In [26]: J.eigenvals().keys()[0].simplify()
Out[26]: -Ry*Y/2 - Rz*Z/2 + sqrt(Ry**2*Y**2 + 4*Ry*Ryz*Rz*Rzy*Y*Z - 2*Ry*Rz*Y*Z + Rz**2*Z**2)/2
So .simplify() doesnt changes the result at all.
.factor() just factors out the -1/2.
If I remember correct, I can pass an argument to .factor() like Y or Z, which variable should be factorized. But I get a lot of slightly different eigenvalues as output and I don't want to specify each argument of factor() by hand (if this solution even works).
I also tried to calculate the eigenvalues by myself by calculating the determinant and solve determinat==0...
I also used determinat.factor() and solved it afterwards but the best result of this approach was the same as J.eigenvals().keys()[0].factor().
Do you have any idea how to solve this problem?
Thank you in advance
Alex
This sort of thing is asked for a lot (see also, for instance, this question: Expression simplification in SymPy), but there isn't really a good way in SymPy to do it. The problem is that such "partial" factorizations are not unique (there may be multiple ways to convert a polynomial into a sum of products).
I opened this issue about it in the SymPy issue tracker. I showed there a way that you can get close (here a is the term under the square root)
In [92]: collect(expand(a.subs(Ry*Y, x - Rz*Z)), x, func=factor).subs(x, Ry*Y + Rz*Z)
Out[92]:
2 2 2
- 4⋅Rz ⋅Z ⋅(Ryz⋅Rzy - 1) + 4⋅Rz⋅Z⋅(Ry⋅Y + Rz⋅Z)⋅(Ryz⋅Rzy - 1) + (Ry⋅Y + Rz⋅Z)
Here I am temporarily replacing Ry*Y + Rz*Z with a variable x so that I can get the squared term that you want.
I couldn't figure out a way to get closer to exactly what you want (i.e., factor the Ryz*Rzy - 1 out of the remaining terms).

Collecting like term of an expression in Sympy

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.

Categories

Resources