Ask Your Question

What is the best way to return only real solutions?

asked 2012-09-09 16:23:39 +0100

giniu gravatar image

updated 2012-09-09 16:38:44 +0100

I'm trying to write a function that filters out non-real solutions returned by solve. For now, I'm using

def select_real(xs):
   return map(lambda eq: eq.lhs() == eq.rhs().real(), filter(lambda eq: not bool(eq.rhs().imag()), xs))

and it works for some simple cases like this

sage: select_real(solve(x^3+8==0, x))
[x == -2]

but this assumes multiple things, like fact that the solve retuns list in the form of actual solutions and imaginary part can be calculated. Generally it is only an ugly hack that I don't like.

I looked at assume(x, 'real') but found out that it does not work ( Ticket #11941 ). I also tried to do check using "in RR", but in above case all 3 solutions gave False. Also at first I tried using eq.full_simplify() in map step above, but it turns out that simplifying returns different root than using real/imag:

sage: ((-8)^(1/3)).full_simplify()
sage: ((-8)^(1/3)).real()

After all that, I'm out of ideas. What is considered the best way to obtain only real solutions? Thanks in advance.


I need such functionality because actually I'm preparing materials for high-schoolers who yet do not know about complex numbers, and shouldn't be introduced to them at that moment. My current version (above+extra full_simplify and uniq which are not really part of question) allows me to do for example this:

sage: var('x,a,b,c')                        
(x, a, b, c)
sage: assume(b^2-4*a*c>0)
sage: select_real(solve(a*x^2+b*x+c==0, x))
[x == -1/2*(b - sqrt(-4*a*c + b^2))/a, x == -1/2*(b + sqrt(-4*a*c + b^2))/a]
sage: forget(assumptions())
sage: assume(b^2-4*a*c==0)
sage: select_real(solve(a*x^2+b*x+c==0, x))
[x == -1/2*b/a]
sage: forget(assumptions())                
sage: assume(b^2-4*a*c<0)                  
sage: select_real(solve(a*x^2+b*x+c==0, x))
edit retag flag offensive close merge delete


One way I think might work is to convert the output of solve($eqn, $var) into string for example, str(solve(a*x^2+b*x+c==0, x)) and look for contents between 'sqrt(' and ')' and see if its negative (then you can omit it). you can also look on the left and right side of '^'. First right side to see if its a fractional power and if it is, you can check the expression being raised to the fractional power, if it is negative.(then you can omit it)

ebs gravatar imageebs ( 2012-09-09 22:45:39 +0100 )edit

3 Answers

Sort by ยป oldest newest most voted

answered 2020-08-16 06:26:21 +0100

slelievre gravatar image

For polynomial equations with rational or algebraic coefficients, the best is to use the roots method applied to a proper polynomial in a polynomial ring, instead of using symbolic expressions and solve.

Here is an example.

Define a polynomial ring and a polynomial:

sage: R.<x> = QQ[]
sage: p = x^3 + 8

Compute the roots in AA (the field of real algebraic numbers):

sage: rr = p.roots(AA, multiplicities=False)
sage: rr

So there is exactly one root, which seems to be minus two.

Use the exactify method to make Sage decide if it is actually that (it is!).

sage: _ = [r.exactify() for r in rr]
sage: rr
edit flag offensive delete link more


Try x.is_real? : this may help, but also may fail : it can be shown that the nullity of a symbolic expression (in this case, the imaginary part of a root) can be undecidable.

The recommendation of slelievre is extremely good, and worth trying to be tried in more complex case, using substitutions to replace non-polynomial subexpressions by variables...

Emmanuel Charpentier gravatar imageEmmanuel Charpentier ( 2020-08-17 00:45:22 +0100 )edit

answered 2012-09-09 23:03:43 +0100

calc314 gravatar image

Here is another hack. You can use the solver in sympy. For example:

from sympy.solvers import solve
ans=solve(x^3+8, x)
print ans
[a for a in ans if imag(a)==0]

gives the result

[1 + 3**(1/2)*I, -2, 1 - 3**(1/2)*I]

This works fine when you have actual values in the equations. There are issues, though, when working completely symbolically. Unfortunately, it won't work with the assume command in Sage. There is an Assume command in sympy, but I seem to be unable to get it to work in Sage.

edit flag offensive delete link more


Here is a link to the `sympy` solver documentation:

calc314 gravatar imagecalc314 ( 2012-09-09 23:05:49 +0100 )edit

answered 2020-08-16 05:58:28 +0100

cybervigilante gravatar image

updated 2020-10-02 01:47:46 +0100

This strikes me as the easiest hack:

x, y, z = var('x, y, z')
P = solve([x^2 * y * z == 18, x * y^3 * z == 24, x * y * z^4 == 6], x, y, z)
for solutions in P:
    if 'i' not in str(solutions).lower():
edit flag offensive delete link more


More directly:

sage: solns = [x == -I, x == I, x == -sqrt(3), x == sqrt(3)]
sage: real_solns = [s.rhs() for s in solns if s.rhs().is_real()]
sage: real_solns
[-sqrt(3), sqrt(3)]
slelievre gravatar imageslelievre ( 2020-08-16 06:30:21 +0100 )edit

Note that the solution string might have I even though the solution is real. This happens for instance in many cases for roots of cubic equations. More detail at Ask Sage question 53835: Plotted real intersection but solve only shows imaginary.

slelievre gravatar imageslelievre ( 2020-10-11 22:30:25 +0100 )edit

Your Answer

Please start posting anonymously - your entry will be published after you log in or create a new account.

Add Answer

Question Tools

1 follower


Asked: 2012-09-09 16:23:39 +0100

Seen: 5,229 times

Last updated: Oct 02 '20