Round Floats within an expression - sympy

I ran into the following problem:
I have a sympy matrix of sympy expressions, each cell looking something like the following example, call it ex1:
In: ex1
Out: -mu_0_0 + t*((0.8*mu_0_1 + 2.22044604925031e-16)*Max(0, alpha_0_1_4)**2 + (0.8*mu_1_1 + 2.22044604925031e-16)*Max(0, alpha_0_1_3)**2 + (0.8*mu_3_1 + 2.22044604925031e-16)*Max(0, alpha_0_1_2)**2 + (0.8*mu_6_1 + 2.22044604925031e-16)*Max(0, alpha_0_1_1)**2 + (0.4*mu_10_0 + 0.4*mu_10_1 - 0.4)*Max(0, alpha_0_1_0)**2) + (-t + 1)*(0.498558013279766*mu_0_1 + 0.0238962672572783*mu_10_0 + 0.0238962672572783*mu_10_1 + 0.18596764210715*mu_1_1 + 0.0201673973158616*mu_3_1 + 0.0475144127826656*mu_6_1 - 0.023896267257278) + Max(0, -alpha_0_0_0)**2
The matrix is later to be lambdified. However, before that, I would like to get rid of the 2.22044604925031e-16 and like floats; these are artifcats from some other part of the code and should be 0.
I would like to clean the expressions in my matrix cells from these, but can't figure out how to.
ex1.round() crashes my python when applied to ex1, and it does not seem to work for simple expression such as
In: sympy.sympify(0.01*t).round(1)
TypeError: can't convert expression to float
If I try it via N, as
In: sympy.N(eq, n=3)
I get:
RecursionError: maximum recursion depth exceeded
(I am not even sure this would work anyway, as I understand this would just reduce the floats to n significant digits?)
Is there any way to clean these expressions in the way I want?
Thanks in advance!
I will leave the expression structure, in case it is of any help:
sympy.srepr(ex1)
"Add(Mul(Integer(-1), Symbol('mu_0_0')), Mul(Symbol('t'), Add(Mul(Add(Mul(Float('0.80000000000000004', prec=15), Symbol('mu_0_1')), Float('2.2204460492503131e-16', prec=15)), Pow(Max(Integer(0), Symbol('alpha_0_1_4')), Integer(2))), Mul(Add(Mul(Float('0.80000000000000004', prec=15), Symbol('mu_1_1')), Float('2.2204460492503131e-16', prec=15)), Pow(Max(Integer(0), Symbol('alpha_0_1_3')), Integer(2))), Mul(Add(Mul(Float('0.80000000000000004', prec=15), Symbol('mu_3_1')), Float('2.2204460492503131e-16', prec=15)), Pow(Max(Integer(0), Symbol('alpha_0_1_2')), Integer(2))), Mul(Add(Mul(Float('0.80000000000000004', prec=15), Symbol('mu_6_1')), Float('2.2204460492503131e-16', prec=15)), Pow(Max(Integer(0), Symbol('alpha_0_1_1')), Integer(2))), Mul(Add(Mul(Float('0.40000000000000002', prec=15), Symbol('mu_10_0')), Mul(Float('0.40000000000000002', prec=15), Symbol('mu_10_1')), Float('-0.3999999999999998', prec=15)), Pow(Max(Integer(0), Symbol('alpha_0_1_0')), Integer(2))))), Mul(Add(Mul(Integer(-1), Symbol('t')), Integer(1)), Add(Mul(Float('0.49855801327976634', prec=15), Symbol('mu_0_1')), Mul(Float('0.02389626725727826', prec=15), Symbol('mu_10_0')), Mul(Float('0.02389626725727826', prec=15), Symbol('mu_10_1')), Mul(Float('0.18596764210714989', prec=15), Symbol('mu_1_1')), Mul(Float('0.020167397315861646', prec=15), Symbol('mu_3_1')), Mul(Float('0.047514412782665633', prec=15), Symbol('mu_6_1')), Float('-0.023896267257278034', prec=15))), Pow(Max(Integer(0), Mul(Integer(-1), Symbol('alpha_0_0_0'))), Integer(2)))"

One can traverse the expression tree with preorder_traversal, and replace floats by their rounded values. In the example below, the floats are rounded to 1 digit after the decimal dot.
from sympy import *
x, y = symbols('x y')
ex1 = 3.1415*(2.00003*x + 3e-12) + x*(y + 0.0003*x - 4e-13)**2 + 1.234567
ex2 = ex1
for a in preorder_traversal(ex1):
if isinstance(a, Float):
ex2 = ex2.subs(a, round(a, 1))
print(ex1) # original
print(ex2) # rounded
Output (with original expression for comparison):
x*(0.0003*x + y - 4.0e-13)**2 + 6.283094245*x + 1.23456700000942
x*y**2 + 6.3*x + 1.2

Related

Sympy: how to print a list of expressions all LaTeX typeset?

I have a list of polynomial expressions, (in my case obtained as the output of a Groebner basis computation), that I would like to view. I am using Jupyter, and I have started off with
import sympy as sy
sy.init_printing()
This causes an individual expression to be given nicely typeset. For a non-Groebner example:
sy.var('x')
fs = sy.factor_list(x**99-1)
fs2 = [x[0] for x in fs[1]]
fs2
The result is a nice list of LaTeX-typeset expressions. But how do I print these expressions one at a time; or rather; one per line? I've tried:
for f in fs2:
sy.pprint(f)
but this produces ascii pretty printing, not LaTeX. In general the expressions I have tend to be long, and I really do want to look at them individually. I can of course do
fs2[0]
fs2[1]
fs2[2]
and so on, but this is tiresome, and hardly useful for a long list. Any ideas or advice? Thanks!
Jupyter (through IPython) has a convenience function called display which works well with SymPy:
import sympy as sy
sy.init_printing()
sy.var('x')
fs = sy.factor_list(x**99-1)
fs2 = [x[0] for x in fs[1]]
for f in fs2:
display(f)
Output:
You can also get the latex code for each of these polynomials by using the latex function:
import sympy as sy
from sympy.printing.latex import latex
sy.init_printing()
sy.var('x')
fs = sy.factor_list(x**99-1)
fs2 = [x[0] for x in fs[1]]
for f in fs2:
print(latex(f))
Output:
x - 1
x^{2} + x + 1
x^{6} + x^{3} + 1
x^{10} + x^{9} + x^{8} + x^{7} + x^{6} + x^{5} + x^{4} + x^{3} + x^{2} + x + 1
x^{20} - x^{19} + x^{17} - x^{16} + x^{14} - x^{13} + x^{11} - x^{10} + x^{9} - x^{7} + x^{6} - x^{4} + x^{3} - x + 1
x^{60} - x^{57} + x^{51} - x^{48} + x^{42} - x^{39} + x^{33} - x^{30} + x^{27} - x^{21} + x^{18} - x^{12} + x^{9} - x^{3} + 1

Sympy: How to simplify expression containing exp(I*x)

I want to achieve this kind of simplification:
sqrt(2)*sqrt(pi)*(p**2 + (-p**2 + 4*pi**2)*exp(2*I*p) - 4*pi**2)*exp(-I*p)/(p**2 - 4*pi**2)**2=-2*I*sqrt(2*pi)*sin(p)/(p**2 - 4*pi**2)
However, sympy.simplify can't simplify this expression:
f=sqrt(2)*sqrt(pi)*(p**2 + (-p**2 + 4*pi**2)*exp(2*I*p) -\
4*pi**2)*exp(-I*p)/(p**2 - 4*pi**2)**2
print(sympy.simplify(f))
#sqrt(2)*sqrt(pi)*(p**2 + (-p**2 + 4*pi**2)*exp(2*I*p) - 4*pi**2)*exp(-I*p)/(p**2 - 4*pi**2)**2
How to simplify this expression with SymPy?
Besides, I don't want to use Piecewise((sqrt(2)*I/(2*sqrt(pi)), Eq(p, -2*pi))...)
Just massage the expression a bit. You know that with fractions, you generally factorize them and then you cancel like-terms. Then you simplify after that:
from sympy import *
p = Symbol("p", real=True)
f = sqrt(2)*sqrt(pi)*(p**2 + (-p**2 + 4*pi**2)*exp(2*I*p) - 4*pi**2)*exp(-I*p)/(p**2 - 4*pi**2)**2
f = simplify(expand(cancel(factor(f))))
print(f)
Gives
-2*sqrt(2)*I*sqrt(pi)*sin(p)/(p**2 - 4*pi**2)

How to simplify lengthy symbolic expressons in SymPy

I have been working on some integrations and even though the system is working, it takes much more time to work than it should.
The problem is that the expressions are many pages, and even though they are 3 variables only, sy.simplify just crashes the Kernel after 4 hours or so.
Is there a way to make such lengthy expressions more compact?
EDIT:
Trying to recreate a test expression, using cse. I can't really substitute the symbols to make a final expression, equal to the 1st one
sy.var('a:c x')
testexp = sp.log(x)+a*(0.5*x)**2+(b*(0.5*x)**2+b+sp.log(x))/c
r, e = sy.cse(testexp)
FinalFunction = sy.lambdify(r[0:][0]+(a,b,c,x),e[0])
Points = sy.lambdify((a,b,c,x),r[0:][1])
FinalFunction(Points(1,1,1,1),1,1,1,1)
>>>NameError: name 'x1' is not defined
cse(expr) is sometimes a way to get a more compact representation since repeated subexpressions can be replaced with a single symbol. cse returns a list of repeated expressions and a list of expressions (a singleton if you only passed a single expression):
>>> from sympy import solve
>>> var('a:c x');solve(a*x**2+b*x+c, x)
(a, b, c, x)
[(-b + sqrt(-4*a*c + b**2))/(2*a), -(b + sqrt(-4*a*c + b**2))/(2*a)]
>>> r, e = cse(_)
>>> for i in r: pprint(Eq(*i))
...
_____________
╱ 2
x₀ = ╲╱ -4⋅a⋅c + b
1
x₁ = ───
2⋅a
>>> for i in e: pprint(i)
...
x₁⋅(-b + x₀)
-x₁⋅(b + x₀)
You are still going to have long expressions but they will be represented more compactly (and more efficiently for computatation) if cse is able to identify repeated subexpressions.
To use this in SymPy you can create two Lambdas: one to translate the variables into the replacement values and the other to use those values:
>>> v = (a,b,c,x)
>>> Pts = Lambda(v, tuple([i[1] for i in r]+list(v)))
>>> Pts(1,2,3,4)
(2*sqrt(2)*I, 1/2, 1, 2, 3, 4)
>>> Func = Lambda(tuple([i[0] for i in r]+list(v)), tuple(e))
>>> Func(*Pts(1,2,3,4))
(-1 + sqrt(2)*I, -1 - sqrt(2)*I)

SymPy: unable to simplify rather simple expression

I have an expression (expr, see below) that I am unable to simplify in SymPy. For real and positive x, expr is equivalent to x**3 + 2*x, but simplify and refine do not simplify the expression at all. (Mathematica does the simplication without any effort).
How to simplify this expression with SymPy?
from sympy import *
x = var('x')
expr = 16*x**3/(-x**2 + sqrt(8*x**2 + (x**2 - 2)**2) + 2)**2 - 2*2**(S(4)/5)*x*(-x**2 + sqrt(8*x**2 + (x**2 - 2)**2) + 2)**(S(3)/5) + 10*x
expr1 = simplify(expr) # does nothing
expr2 = refine(expr, Q.positive(x)) # does nothing
It can be done!
I rescind my earlier answer. Your expression can be simplified using Sympy. Here's how:
import sympy as sym
x = sym.symbols('x', positive=True)
expr = 16*x**3/(-x**2 + sym.sqrt(8*x**2 + (x**2 - 2)**2) + 2)**2 - 2*2**(sym.S(4)/5)*x*(-x**2 + sym.sqrt(8*x**2 + (x**2 - 2)**2) + 2)**(sym.S(3)/5) + 10*x
sym.simplify(sym.factor(sym.factor(sym.expand(sym.radsimp(expr))), deep=True))
Output:
x*(x**2 + 2)
Basically, I dug through all of the docs on sympy.simplify until I found that magic combination. Also, you have to define x as positive when you create the symbol, just as I did in the code above.
Comment on Mathematica
"Mathematica does the simplication without any effort"
I don't think you should ever underestimate the quantity of time and money that has gone into making the heuristic nightmare that is Mathematica's Simplify seem like it "just works". Sadly, in a lot of ways Sympy is still in it's infancy in comparison. sympy.simplify is one of those ways.

Matching coefficients with sympy

I am attempting to work a problem from a textbook in sympy, but sympy fails to find a solution which appears valid. For interest, it is the design of a PID controller using direct synthesis with a second order plus dead time model.
The whole problem can be reduced to finding K_C, tau_I and tau_D which will make
K_C*(s**2*tau_D*tau_I + s*tau_I + 1)/(s*tau_I)
= (s**2*tau_1*tau_2 + s*tau_1 + s*tau_2 + 1)/(K*s*(-phi + tau_c))
for given tau_1, tau_2, K and phi.
I have tried to solve this by matching coefficients:
import sympy
s, tau_c, tau_1, tau_2, phi, K = sympy.symbols('s, tau_c, tau_1, tau_2, phi, K')
target = (s**2*tau_1*tau_2 + s*tau_1 + s*tau_2 + 1)/(K*s*(-phi + tau_c))
K_C, tau_I, tau_D = sympy.symbols('K_C, tau_I, tau_D', real=True)
PID = K_C*(1 + 1/(tau_I*s) + tau_D*s)
eq = (target - PID).together()
eq *= sympy.denom(eq).simplify()
eq = sympy.poly(eq, s)
sympy.solve(eq.coeffs(), [K_C, tau_I, tau_D])
This returns an empty list. However, the textbook provides the following solution:
booksolution = {K_C: 1/K*(tau_1 + tau_2)/(tau_c - phi),
tau_I: tau_1 + tau_2,a
tau_D: tau_1*tau_2/(tau_1 + tau_2)}
Which appears to satisfy the equations I'm trying to solve:
[c.subs(booksolution).simplify() for c in eq.coeffs()]
returns
[0, 0, 0]
Can I massage this into a form which sympy can solve? What am I doing wong?
Edit: This finds the correct solution, but requires a little too much thought from my side to order the equations:
eqs = eq.coeffs()
solution = {}
solution[K_C] = sympy.solve(eqs[1], K_C)[0]
solution[tau_D] = sympy.solve(eqs[0], tau_D)[0].subs(solution)
solution[tau_I] = sympy.solve(eqs[2], tau_I)[0].subs(solution).simplify()
In SymPy 1.0 (to be released soon) I get this answer
In [25]: sympy.solve(eq.coeffs(), [K_C, tau_I, tau_D])
Out[25]:
⎡ ⎧ -(τ₁ + τ₂) τ₁⋅τ₂ ⎫⎤
⎢{K_C: 0, τ_I: 0}, ⎨K_C: ───────────, τ_D: ───────, τ_I: τ₁ + τ₂⎬⎥
⎣ ⎩ K⋅(φ - τ_c) τ₁ + τ₂ ⎭⎦
which looks like your textbook's solution.