Ignore imaginary roots in sympy
Question:
I’m using sympy to solve a polynomial:
x = Symbol('x')
y = solve(int(row["scaleA"])*x**3 + int(row["scaleB"])*x**2 + int(row["scaleC"])*x + int(row["scaleD"]), x)
y is a list of possible solutions. However, I need to ignore the imaginary ones and only use the real solutions. Also, I would like the solution as a value not an expression. Right now it looks like:
[-2/3 - 55**(1/3)*(-1/2 - sqrt(3)*I/2)/3, -2/3 - 55**(1/3)*(-1/2 + sqrt(3)*I/2)/3, -55**(1/3)/3 - 2/3]
I need the last expression’s value (-2.22756). Are there functions in sympy to simplify this?
Answers:
I managed to simply ignore solutions containing the character "I"
and used .evalf()
to evaluate the expression. The code is now:
x = Symbol('x')
y = solve(int(row["scaleA"])*x**3 + int(row["scaleB"])*x**2 + int(row["scaleC"])*x + int(row["scaleD"]), x)
for root in y:
if "I" not in str(root):
print("This One:" + str(root.evalf()))
As Krastonov had mentioned mpmath provided an easier method:
y = polyroots([int(row["scaleA"]), int(row["scaleB"]), int(row["scaleC"]), int(row["scaleD"])-value])
for root in y:
if "j" not in str(root):
value = root
If you set x
to be real, SymPy will only give you the real solutions
x = Symbol('x', real=True)
solve(..., x)
solve()
doesn’t have a consistent output for various types of solutions, please use solveset(Eq,x,domain=S.Reals)
:
from sympy import ImageSet, S
x = Symbol('x')
y = solveset(int(row["scaleA"])*x**3 + int(row["scaleB"])*x**2+int(row["scaleC"])*x + int(row["scaleD"]), x, domain=S.Reals)
This is exactly the sort of thing that real_roots
is made for and is especially applicable to your case where the coefficients are integers:
x = Symbol('x')
eq = int(row["scaleA"])*x**3 + int(row["scaleB"])*x**2 + int(row["scaleC"])*x + int(row["scaleD"])
y = real_roots(eq, x) # gives [CRootOf(...), ...]
The value of CRootOf instances can be evaluated to whatever precision you need and should not contain any imaginary part. For example,
>>> [i.n(12) for i in real_roots(3*x**3 - 2*x**2 + 7*x - 9, x)]
[1.07951904858]
Note: As I recall, solve will send back roots that it wasn’t able to confirm met the assumptions (i.e. if they weren’t found to be false for the assumption then they are returned). Also, if you want more consistent output from solve, @PyRick, set the flag dict=True
.
To get rid of complex numbers with just SymPy (without mpmath) use solve
with expand_complex
or realroots
as mentioned above by @smichr.
from sympy import *
x = var("x")
# With symbol p:
p = var("p", positive=True)
# Without symbol p:
p = 1
a, b, c, d = 1, 15, 10, 1
term = p * (a*x**3 + b*x**2 + c*x + d)
pprint("nSolution 1:")
sol = solve(term, x)
for x in sol:
pprint(x)
tmp = expand_complex(x)
pprint(tmp)
pprint(N(tmp))
pprint("nSolution 2:")
sol = real_roots(term, x)
for x in sol:
pprint(N(x))
With symbols, use solve
with expand_complex
.
I’m using sympy to solve a polynomial:
x = Symbol('x')
y = solve(int(row["scaleA"])*x**3 + int(row["scaleB"])*x**2 + int(row["scaleC"])*x + int(row["scaleD"]), x)
y is a list of possible solutions. However, I need to ignore the imaginary ones and only use the real solutions. Also, I would like the solution as a value not an expression. Right now it looks like:
[-2/3 - 55**(1/3)*(-1/2 - sqrt(3)*I/2)/3, -2/3 - 55**(1/3)*(-1/2 + sqrt(3)*I/2)/3, -55**(1/3)/3 - 2/3]
I need the last expression’s value (-2.22756). Are there functions in sympy to simplify this?
I managed to simply ignore solutions containing the character "I"
and used .evalf()
to evaluate the expression. The code is now:
x = Symbol('x')
y = solve(int(row["scaleA"])*x**3 + int(row["scaleB"])*x**2 + int(row["scaleC"])*x + int(row["scaleD"]), x)
for root in y:
if "I" not in str(root):
print("This One:" + str(root.evalf()))
As Krastonov had mentioned mpmath provided an easier method:
y = polyroots([int(row["scaleA"]), int(row["scaleB"]), int(row["scaleC"]), int(row["scaleD"])-value])
for root in y:
if "j" not in str(root):
value = root
If you set x
to be real, SymPy will only give you the real solutions
x = Symbol('x', real=True)
solve(..., x)
solve()
doesn’t have a consistent output for various types of solutions, please use solveset(Eq,x,domain=S.Reals)
:
from sympy import ImageSet, S
x = Symbol('x')
y = solveset(int(row["scaleA"])*x**3 + int(row["scaleB"])*x**2+int(row["scaleC"])*x + int(row["scaleD"]), x, domain=S.Reals)
This is exactly the sort of thing that real_roots
is made for and is especially applicable to your case where the coefficients are integers:
x = Symbol('x')
eq = int(row["scaleA"])*x**3 + int(row["scaleB"])*x**2 + int(row["scaleC"])*x + int(row["scaleD"])
y = real_roots(eq, x) # gives [CRootOf(...), ...]
The value of CRootOf instances can be evaluated to whatever precision you need and should not contain any imaginary part. For example,
>>> [i.n(12) for i in real_roots(3*x**3 - 2*x**2 + 7*x - 9, x)]
[1.07951904858]
Note: As I recall, solve will send back roots that it wasn’t able to confirm met the assumptions (i.e. if they weren’t found to be false for the assumption then they are returned). Also, if you want more consistent output from solve, @PyRick, set the flag dict=True
.
To get rid of complex numbers with just SymPy (without mpmath) use solve
with expand_complex
or realroots
as mentioned above by @smichr.
from sympy import *
x = var("x")
# With symbol p:
p = var("p", positive=True)
# Without symbol p:
p = 1
a, b, c, d = 1, 15, 10, 1
term = p * (a*x**3 + b*x**2 + c*x + d)
pprint("nSolution 1:")
sol = solve(term, x)
for x in sol:
pprint(x)
tmp = expand_complex(x)
pprint(tmp)
pprint(N(tmp))
pprint("nSolution 2:")
sol = real_roots(term, x)
for x in sol:
pprint(N(x))
With symbols, use solve
with expand_complex
.