Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solving solvable quintics: First implementation #1746

Merged
merged 35 commits into from Mar 14, 2013

Conversation

prasoon2211
Copy link

Please see this:
http://code.google.com/p/sympy/issues/detail?id=3548

Also, see this:
https://groups.google.com/forum/?fromgroups=#!topic/sympy/xXyiOUWH0SU

According to the discussions on above links, this PR introduces solving quintics exactly (not numerically) when they are solvable.

There are a lot of constants involved. So, I created a new file and made a new class for quintics containing required constants.
Also note that solving quintics exactly takes( for x5 + 15*x +12 =0 ) ~ 40 secs. The long time is attributed to solving five equations of the form x5 - R =0 somewhere in the middle of the code.

Also, I have used simplify a few times. It seems that the code became almost 3 times as fast with using simplify, than without it.

There is an extra flag; quintics. When set to true, the new roots_quintic method is called and if the quintic is solvable, the solution is returned. I am attaching a screenshot :
sympy

Here you can see the solution of x*_5 + 15_x +12 = 0.

I have not added any tests.
@asmeurer Please look at the code. Let me know for changes required.

self.p, self.q, self.r, self.s = poly.all_coeffs()[2:]
self.F = self.getF()

def getf20(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be more pythonic to drop the "get" from all these function names and decorate them with @Property. For example,

@property
def f20(self):
    ...

And the access it as PolyQuintic().f20.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the ones that take arguments, keep them as functions, but still drop the "get".

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@asmeurer
Copy link
Member

Add a reference to the the paper and Mathematica file in a comment at the top of the quintics file.

@asmeurer
Copy link
Member

Just so you know, this does need tests. I'm not sure how best to do it. We need to make sure the formulas are correctly input, and that all the lines are covered. You can do the latter by running the coverage tool bin/coverage_report. @smichr is there a precedent on how we test long solutions like this?

@asmeurer
Copy link
Member

If you substitute those solutions back into the quintic and simplify, does it go to zero?

Res[3] = solve_five(sol**5 - R3, sol)
Res[4] = solve_five(sol**5 - R4, sol)

for i in range(1, 5, 1):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The third argument is not necessary.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@prasoon2211
Copy link
Author

I'll make the changes and commit soon.

@prasoon2211
Copy link
Author

As far as checking the solutions is concerned, here's what I did:

>>> f = x**5 + 15*x +12
>>> soln1 = solve(f, quintic=True)
>>> for sol in soln1: print sol.n()
1.55919098913088 - 1.41297967386832*I
-1.16885627308425 - 1.45103836960044*I
-0.780669432093258 - 0.e-23*I
1.55919098913088 + 1.41297967386832*I
-1.16885627308425 + 1.45103836960044*I

>>> soln2 = solve(f)
>>> for sol in soln2: print sol.n()
-0.780669432093258
-1.16885627308425 - 1.45103836960044*I
-1.16885627308425 + 1.45103836960044*I
1.55919098913088 - 1.41297967386832*I
1.55919098913088 + 1.41297967386832*I

As you can see, the roots are same.
As far as the tests are concerned, I suppose we can check as I did above.


r1 = Res[1][0]
for root in Res[4]:
if Abs(im(r1*root)) < S(0.00001):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps pure_complex should be used here. That 1e-5 test looks like it would be easy to break.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite see the point of using pure_complex. It would just give me (re, im) of r1*root while I only want the imaginary part for which I am using im().

As an example:

In [42]: z = 23.234542525 + I*3653.34653636

In [43]: pure_complex(z)
Out[43]: (23.2345425250000, 3653.34653636000)

In [44]: re
Out[44]: sympy.functions.elementary.complexes.re

In [45]: re(z), im(z)
Out[45]: (23.2345425250000, 3653.34653636000)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are just testing that the imaginary part is insignificant, right? Then perhaps check that im(foo.n(chop=True)) == 0

>>> im(S(3))
0
>>> im(S(3)+I)
1
>>> im(S(3)+I*1e-22)
1.00000000000000e-22
>>> im((S(3)+I*1e-22).n(chop=True))
0
>>> im((S(3)+I*1e-12).n(chop=True))
1.00000000000000e-12

But again, you get into the "how small is small" issue so perhaps the magnitude of the im part should be compared to the real part ith the comp function again

>>> im((S(3)*1e-22+I*1e-22).n(chop=True))  # this shouldn't have chopped, IMO
0

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, that is an issue. In this case, I think that 10**-6 is small enough though I suppose we can reduce it further to about 10**-10. The thing is that we don't want to have too small a value to compare against since then the difference might not small enough.
While debugging, I observed that if the difference isn't exactly zero, then it was of the order 10**-15 to 10**-17. So, I think 10**-10 should be small enough but not too small that it undermines the purpose if comparing.

@prasoon2211
Copy link
Author

I think there's some problem somewhere in the PolyQuintic class. In case of some other solvable equations, for which f20 doesn't have a zero root, we are not getting correct results. I'll look into it.

@prasoon2211
Copy link
Author

I've implemented the comp function, as smichr suggested. The tolerance has been decreased to 1e-10. Also, there was a typo in PolyQuintic class that was giving incorrect results. That has been fixed. Also, here's a screenshot of solution of x**5 - 5*x +12
sympy

You can match the solutions from here:
http://www.wolframalpha.com/input/?i=solve%28+x%5E5+-5x+%2B12%29

Also, I tried to check the numerical solution with SymPy:

In [7]: ss = solve(f)

In [8]: for s1 in ss:
   ...:     print s1.n()
   ...:     
-1.84208596619025
-1.84208596619025
-1.84208596619025
1.2728972239225 - 0.719798681483861*I
1.2728972239225 + 0.719798681483861*I

As you can clearly see, I've stumbled upon another bug. The solve function cannot find two of the roots of f. (Please see the wolframalpha solution link given above). I'll open an issue for that.


for r2temp in Res[2]:
for r3temp in Res[3]:
# Again storig away exact number and using
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo

@prasoon2211
Copy link
Author

Now,

In [6]: f = 2*x**5 + 15*x +12

In [7]: solve(f, quintics=True)
Out[7]: 
[RootOf(2*x**5 + 15*x + 12, 0),
 RootOf(2*x**5 + 15*x + 12, 1),
 RootOf(2*x**5 + 15*x + 12, 2),
 RootOf(2*x**5 + 15*x + 12, 3),
 RootOf(2*x**5 + 15*x + 12, 4)]

Clearly, if the quintic is not solvable, or, if upon dividing all the coefficients by the coeff of the leading term we do not get all integers, then it falls back to ordinary solve.

@prasoon2211
Copy link
Author

Also, the method seems much faster on a personal machine than on a shared machine. On shared machines, it took ~ 1 minute to solve x**2 + 15*x + 12 but on a non-shared machine, it takes only about 15 - 20 seconds.

In [2]: f = x**5 - 5*x +12

In [3]: time s = solve(f, quintics=True)
CPU times: user 14.82 s, sys: 0.01 s, total: 14.83 s
Wall time: 14.96 s

In [4]: f = x**5 + 15*x +12

In [5]: time s = solve(f, quintics=True)
CPU times: user 22.94 s, sys: 0.01 s, total: 22.96 s
Wall time: 16.15 s

@asmeurer
Copy link
Member

asmeurer commented Feb 3, 2013

Doesn't the algorithm work with rational coefficients?

@prasoon2211
Copy link
Author

Let me check.

@prasoon2211
Copy link
Author

Yes. It should work with rational coefficients too. But the quintic 2*x**5 + 15*x + 12 isn't solvable. I'll change the check from is_integer to is_rational.
Anyway, have a look a this:

In [7]: f = x**5 - 110*x**3 - 55*x**2 + 2310*x + 979

In [8]: time s = solve(f, quintics=True)
CPU times: user 470.75 s, sys: 0.10 s, total: 470.84 s
Wall time: 483.58 s

In [9]: s
Out[9]: 
[11**(1/5)*2**(9/10)*(5*cos(-atan(5*(3*sqrt(2) + 2*sqrt(10))*sqrt(sqrt(5) + 5)/(-25*sqrt(5) + 89))/5 + pi/5)/2 + 5*I*(-sqrt(-2*sqrt(5) + 10)*cos(atan(5*(3*sqrt(2) + 2*sqrt(10))*sqrt(sqrt(5) + 5)/(-25*sqrt(5) + 89))/5) + (1 + sqrt(5))*sin(atan(5*(3*sqrt(2) + 2*sqrt(10))*sqrt(sqrt(5) + 5)/(-25*sqrt(5) + 89))/5))/8)*(-375*sqrt(5) + 75*sqrt(5)*(sqrt(5) + 5) + 2287)**(1/10)/5 + 11**(1/5)*(-1625*sqrt(5)*(sqrt(5) + 5) + 8125*sqrt(5) + 29421)**(1/10)*(5*2**(3/5)*sqrt(5)*(-(1 + sqrt(5))*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + sqrt(-2*sqrt(5) + 10)*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(1/10)*sqrt(-sqrt(5) + 5)*(-2*sqrt(-2*sqrt(5) + 10)*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) - 2*(1 + sqrt(5))*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(3/5)*(-(1 + sqrt(5))*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + sqrt(-2*sqrt(5) + 10)*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(1/10)*sqrt(-5 + sqrt(5))*(-2*(1 + sqrt(5))*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + 2*sqrt(-2*sqrt(5) + 10)*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(3/5)*I*((1 + sqrt(5))*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + sqrt(-2*sqrt(5) + 10)*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(3/5)*sqrt(5)*I*((1 + sqrt(5))*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + sqrt(-2*sqrt(5) + 10)*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32)/5 + 11**(1/5)*(-1625*sqrt(5)*(sqrt(5) + 5) + 8125*sqrt(5) + 29421)**(1/10)*(5*2**(3/5)*sqrt(5)*(-(1 + sqrt(5))*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + sqrt(-2*sqrt(5) + 10)*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(1/10)*sqrt(-sqrt(5) + 5)*(-2*sqrt(-2*sqrt(5) + 10)*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) - 2*(1 + sqrt(5))*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(3/5)*(-(1 + sqrt(5))*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + sqrt(-2*sqrt(5) + 10)*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(3/5)*sqrt(5)*I*(-sqrt(-2*sqrt(5) + 10)*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) - (1 + sqrt(5))*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(3/5)*I*(-sqrt(-2*sqrt(5) + 10)*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) - (1 + sqrt(5))*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(1/10)*sqrt(-5 + sqrt(5))*(-2*sqrt(-2*sqrt(5) + 10)*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + 2*(1 + sqrt(5))*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32)/5 + 11**(1/5)*2**(9/10)*(5*cos(-atan(5*(3*sqrt(2) + 2*sqrt(10))*sqrt(sqrt(5) + 5)/(-25*sqrt(5) + 89))/5 + pi/5)/2 + 5*I*(-(1 + sqrt(5))*sin(atan(5*(3*sqrt(2) + 2*sqrt(10))*sqrt(sqrt(5) + 5)/(-25*sqrt(5) + 89))/5) + sqrt(-2*sqrt(5) + 10)*cos(atan(5*(3*sqrt(2) + 2*sqrt(10))*sqrt(sqrt(5) + 5)/(-25*sqrt(5) + 89))/5))/8)*(-375*sqrt(5) + 75*sqrt(5)*(sqrt(5) + 5) + 2287)**(1/10)/5,
 11**(1/5)*(-1/4 + sqrt(5)/4 + I*sqrt(sqrt(5)/8 + 5/8))*(-1625*sqrt(5)*(sqrt(5) + 5) + 8125*sqrt(5) + 29421)**(1/10)*(5*2**(3/5)*sqrt(5)*(-(1 + sqrt(5))*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + sqrt(-2*sqrt(5) + 10)*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(1/10)*sqrt(-sqrt(5) + 5)*(-2*sqrt(-2*sqrt(5) + 10)*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) - 2*(1 + sqrt(5))*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(3/5)*(-(1 + sqrt(5))*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + sqrt(-2*sqrt(5) + 10)*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(1/10)*sqrt(-5 + sqrt(5))*(-2*(1 + sqrt(5))*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + 2*sqrt(-2*sqrt(5) + 10)*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(3/5)*I*((1 + sqrt(5))*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + sqrt(-2*sqrt(5) + 10)*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(3/5)*sqrt(5)*I*((1 + sqrt(5))*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + sqrt(-2*sqrt(5) + 10)*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32)/5 + 11**(1/5)*2**(9/10)*(5*cos(-atan(5*(3*sqrt(2) + 2*sqrt(10))*sqrt(sqrt(5) + 5)/(-25*sqrt(5) + 89))/5 + pi/5)/2 + 5*I*(-(1 + sqrt(5))*sin(atan(5*(3*sqrt(2) + 2*sqrt(10))*sqrt(sqrt(5) + 5)/(-25*sqrt(5) + 89))/5) + sqrt(-2*sqrt(5) + 10)*cos(atan(5*(3*sqrt(2) + 2*sqrt(10))*sqrt(sqrt(5) + 5)/(-25*sqrt(5) + 89))/5))/8)*(-1/4 + sqrt(5)/4 + I*sqrt(sqrt(5)/8 + 5/8))**3*(-375*sqrt(5) + 75*sqrt(5)*(sqrt(5) + 5) + 2287)**(1/10)/5 + 11**(1/5)*2**(9/10)*(5*cos(-atan(5*(3*sqrt(2) + 2*sqrt(10))*sqrt(sqrt(5) + 5)/(-25*sqrt(5) + 89))/5 + pi/5)/2 + 5*I*(-sqrt(-2*sqrt(5) + 10)*cos(atan(5*(3*sqrt(2) + 2*sqrt(10))*sqrt(sqrt(5) + 5)/(-25*sqrt(5) + 89))/5) + (1 + sqrt(5))*sin(atan(5*(3*sqrt(2) + 2*sqrt(10))*sqrt(sqrt(5) + 5)/(-25*sqrt(5) + 89))/5))/8)*(-1/4 + sqrt(5)/4 + I*sqrt(sqrt(5)/8 + 5/8))**2*(-375*sqrt(5) + 75*sqrt(5)*(sqrt(5) + 5) + 2287)**(1/10)/5 + 11**(1/5)*(-1/4 + sqrt(5)/4 + I*sqrt(sqrt(5)/8 + 5/8))**4*(-1625*sqrt(5)*(sqrt(5) + 5) + 8125*sqrt(5) + 29421)**(1/10)*(5*2**(3/5)*sqrt(5)*(-(1 + sqrt(5))*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + sqrt(-2*sqrt(5) + 10)*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(1/10)*sqrt(-sqrt(5) + 5)*(-2*sqrt(-2*sqrt(5) + 10)*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) - 2*(1 + sqrt(5))*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(3/5)*(-(1 + sqrt(5))*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + sqrt(-2*sqrt(5) + 10)*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(3/5)*sqrt(5)*I*(-sqrt(-2*sqrt(5) + 10)*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) - (1 + sqrt(5))*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(3/5)*I*(-sqrt(-2*sqrt(5) + 10)*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) - (1 + sqrt(5))*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(1/10)*sqrt(-5 + sqrt(5))*(-2*sqrt(-2*sqrt(5) + 10)*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + 2*(1 + sqrt(5))*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32)/5,
 11**(1/5)*(-1/4 + sqrt(5)/4 + I*sqrt(sqrt(5)/8 + 5/8))*(-1625*sqrt(5)*(sqrt(5) + 5) + 8125*sqrt(5) + 29421)**(1/10)*(5*2**(3/5)*sqrt(5)*(-(1 + sqrt(5))*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + sqrt(-2*sqrt(5) + 10)*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(1/10)*sqrt(-sqrt(5) + 5)*(-2*sqrt(-2*sqrt(5) + 10)*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) - 2*(1 + sqrt(5))*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(3/5)*(-(1 + sqrt(5))*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + sqrt(-2*sqrt(5) + 10)*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(3/5)*sqrt(5)*I*(-sqrt(-2*sqrt(5) + 10)*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) - (1 + sqrt(5))*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(3/5)*I*(-sqrt(-2*sqrt(5) + 10)*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) - (1 + sqrt(5))*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(1/10)*sqrt(-5 + sqrt(5))*(-2*sqrt(-2*sqrt(5) + 10)*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + 2*(1 + sqrt(5))*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32)/5 + 11**(1/5)*2**(9/10)*(5*cos(-atan(5*(3*sqrt(2) + 2*sqrt(10))*sqrt(sqrt(5) + 5)/(-25*sqrt(5) + 89))/5 + pi/5)/2 + 5*I*(-sqrt(-2*sqrt(5) + 10)*cos(atan(5*(3*sqrt(2) + 2*sqrt(10))*sqrt(sqrt(5) + 5)/(-25*sqrt(5) + 89))/5) + (1 + sqrt(5))*sin(atan(5*(3*sqrt(2) + 2*sqrt(10))*sqrt(sqrt(5) + 5)/(-25*sqrt(5) + 89))/5))/8)*(-1/4 + sqrt(5)/4 + I*sqrt(sqrt(5)/8 + 5/8))**3*(-375*sqrt(5) + 75*sqrt(5)*(sqrt(5) + 5) + 2287)**(1/10)/5 + 11**(1/5)*2**(9/10)*(5*cos(-atan(5*(3*sqrt(2) + 2*sqrt(10))*sqrt(sqrt(5) + 5)/(-25*sqrt(5) + 89))/5 + pi/5)/2 + 5*I*(-(1 + sqrt(5))*sin(atan(5*(3*sqrt(2) + 2*sqrt(10))*sqrt(sqrt(5) + 5)/(-25*sqrt(5) + 89))/5) + sqrt(-2*sqrt(5) + 10)*cos(atan(5*(3*sqrt(2) + 2*sqrt(10))*sqrt(sqrt(5) + 5)/(-25*sqrt(5) + 89))/5))/8)*(-1/4 + sqrt(5)/4 + I*sqrt(sqrt(5)/8 + 5/8))**2*(-375*sqrt(5) + 75*sqrt(5)*(sqrt(5) + 5) + 2287)**(1/10)/5 + 11**(1/5)*(-1/4 + sqrt(5)/4 + I*sqrt(sqrt(5)/8 + 5/8))**4*(-1625*sqrt(5)*(sqrt(5) + 5) + 8125*sqrt(5) + 29421)**(1/10)*(5*2**(3/5)*sqrt(5)*(-(1 + sqrt(5))*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + sqrt(-2*sqrt(5) + 10)*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(1/10)*sqrt(-sqrt(5) + 5)*(-2*sqrt(-2*sqrt(5) + 10)*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) - 2*(1 + sqrt(5))*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(3/5)*(-(1 + sqrt(5))*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + sqrt(-2*sqrt(5) + 10)*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(1/10)*sqrt(-5 + sqrt(5))*(-2*(1 + sqrt(5))*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + 2*sqrt(-2*sqrt(5) + 10)*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(3/5)*I*((1 + sqrt(5))*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + sqrt(-2*sqrt(5) + 10)*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(3/5)*sqrt(5)*I*((1 + sqrt(5))*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + sqrt(-2*sqrt(5) + 10)*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32)/5,
 11**(1/5)*2**(9/10)*(5*cos(-atan(5*(3*sqrt(2) + 2*sqrt(10))*sqrt(sqrt(5) + 5)/(-25*sqrt(5) + 89))/5 + pi/5)/2 + 5*I*(-(1 + sqrt(5))*sin(atan(5*(3*sqrt(2) + 2*sqrt(10))*sqrt(sqrt(5) + 5)/(-25*sqrt(5) + 89))/5) + sqrt(-2*sqrt(5) + 10)*cos(atan(5*(3*sqrt(2) + 2*sqrt(10))*sqrt(sqrt(5) + 5)/(-25*sqrt(5) + 89))/5))/8)*(-1/4 + sqrt(5)/4 + I*sqrt(sqrt(5)/8 + 5/8))**4*(-375*sqrt(5) + 75*sqrt(5)*(sqrt(5) + 5) + 2287)**(1/10)/5 + 11**(1/5)*(-1/4 + sqrt(5)/4 + I*sqrt(sqrt(5)/8 + 5/8))**2*(-1625*sqrt(5)*(sqrt(5) + 5) + 8125*sqrt(5) + 29421)**(1/10)*(5*2**(3/5)*sqrt(5)*(-(1 + sqrt(5))*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + sqrt(-2*sqrt(5) + 10)*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(1/10)*sqrt(-sqrt(5) + 5)*(-2*sqrt(-2*sqrt(5) + 10)*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) - 2*(1 + sqrt(5))*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(3/5)*(-(1 + sqrt(5))*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + sqrt(-2*sqrt(5) + 10)*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(3/5)*sqrt(5)*I*(-sqrt(-2*sqrt(5) + 10)*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) - (1 + sqrt(5))*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(3/5)*I*(-sqrt(-2*sqrt(5) + 10)*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) - (1 + sqrt(5))*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(1/10)*sqrt(-5 + sqrt(5))*(-2*sqrt(-2*sqrt(5) + 10)*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + 2*(1 + sqrt(5))*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32)/5 + 11**(1/5)*(-1/4 + sqrt(5)/4 + I*sqrt(sqrt(5)/8 + 5/8))**3*(-1625*sqrt(5)*(sqrt(5) + 5) + 8125*sqrt(5) + 29421)**(1/10)*(5*2**(3/5)*sqrt(5)*(-(1 + sqrt(5))*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + sqrt(-2*sqrt(5) + 10)*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(1/10)*sqrt(-sqrt(5) + 5)*(-2*sqrt(-2*sqrt(5) + 10)*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) - 2*(1 + sqrt(5))*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(3/5)*(-(1 + sqrt(5))*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + sqrt(-2*sqrt(5) + 10)*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(1/10)*sqrt(-5 + sqrt(5))*(-2*(1 + sqrt(5))*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + 2*sqrt(-2*sqrt(5) + 10)*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(3/5)*I*((1 + sqrt(5))*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + sqrt(-2*sqrt(5) + 10)*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(3/5)*sqrt(5)*I*((1 + sqrt(5))*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + sqrt(-2*sqrt(5) + 10)*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32)/5 + 11**(1/5)*2**(9/10)*(5*cos(-atan(5*(3*sqrt(2) + 2*sqrt(10))*sqrt(sqrt(5) + 5)/(-25*sqrt(5) + 89))/5 + pi/5)/2 + 5*I*(-sqrt(-2*sqrt(5) + 10)*cos(atan(5*(3*sqrt(2) + 2*sqrt(10))*sqrt(sqrt(5) + 5)/(-25*sqrt(5) + 89))/5) + (1 + sqrt(5))*sin(atan(5*(3*sqrt(2) + 2*sqrt(10))*sqrt(sqrt(5) + 5)/(-25*sqrt(5) + 89))/5))/8)*(-1/4 + sqrt(5)/4 + I*sqrt(sqrt(5)/8 + 5/8))*(-375*sqrt(5) + 75*sqrt(5)*(sqrt(5) + 5) + 2287)**(1/10)/5,
 11**(1/5)*2**(9/10)*(5*cos(-atan(5*(3*sqrt(2) + 2*sqrt(10))*sqrt(sqrt(5) + 5)/(-25*sqrt(5) + 89))/5 + pi/5)/2 + 5*I*(-sqrt(-2*sqrt(5) + 10)*cos(atan(5*(3*sqrt(2) + 2*sqrt(10))*sqrt(sqrt(5) + 5)/(-25*sqrt(5) + 89))/5) + (1 + sqrt(5))*sin(atan(5*(3*sqrt(2) + 2*sqrt(10))*sqrt(sqrt(5) + 5)/(-25*sqrt(5) + 89))/5))/8)*(-1/4 + sqrt(5)/4 + I*sqrt(sqrt(5)/8 + 5/8))**4*(-375*sqrt(5) + 75*sqrt(5)*(sqrt(5) + 5) + 2287)**(1/10)/5 + 11**(1/5)*(-1/4 + sqrt(5)/4 + I*sqrt(sqrt(5)/8 + 5/8))**2*(-1625*sqrt(5)*(sqrt(5) + 5) + 8125*sqrt(5) + 29421)**(1/10)*(5*2**(3/5)*sqrt(5)*(-(1 + sqrt(5))*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + sqrt(-2*sqrt(5) + 10)*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(1/10)*sqrt(-sqrt(5) + 5)*(-2*sqrt(-2*sqrt(5) + 10)*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) - 2*(1 + sqrt(5))*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(3/5)*(-(1 + sqrt(5))*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + sqrt(-2*sqrt(5) + 10)*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(1/10)*sqrt(-5 + sqrt(5))*(-2*(1 + sqrt(5))*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + 2*sqrt(-2*sqrt(5) + 10)*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(3/5)*I*((1 + sqrt(5))*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + sqrt(-2*sqrt(5) + 10)*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(3/5)*sqrt(5)*I*((1 + sqrt(5))*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + sqrt(-2*sqrt(5) + 10)*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32)/5 + 11**(1/5)*(-1/4 + sqrt(5)/4 + I*sqrt(sqrt(5)/8 + 5/8))**3*(-1625*sqrt(5)*(sqrt(5) + 5) + 8125*sqrt(5) + 29421)**(1/10)*(5*2**(3/5)*sqrt(5)*(-(1 + sqrt(5))*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + sqrt(-2*sqrt(5) + 10)*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(1/10)*sqrt(-sqrt(5) + 5)*(-2*sqrt(-2*sqrt(5) + 10)*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) - 2*(1 + sqrt(5))*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(3/5)*(-(1 + sqrt(5))*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + sqrt(-2*sqrt(5) + 10)*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(3/5)*sqrt(5)*I*(-sqrt(-2*sqrt(5) + 10)*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) - (1 + sqrt(5))*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(3/5)*I*(-sqrt(-2*sqrt(5) + 10)*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) - (1 + sqrt(5))*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32 + 5*2**(1/10)*sqrt(-5 + sqrt(5))*(-2*sqrt(-2*sqrt(5) + 10)*sin(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5) + 2*(1 + sqrt(5))*cos(atan(5*(-13*sqrt(2) + 5*sqrt(10))*sqrt(sqrt(5) + 5)/(2*(25*sqrt(5) + 89)))/5))/32)/5 + 11**(1/5)*2**(9/10)*(5*cos(-atan(5*(3*sqrt(2) + 2*sqrt(10))*sqrt(sqrt(5) + 5)/(-25*sqrt(5) + 89))/5 + pi/5)/2 + 5*I*(-(1 + sqrt(5))*sin(atan(5*(3*sqrt(2) + 2*sqrt(10))*sqrt(sqrt(5) + 5)/(-25*sqrt(5) + 89))/5) + sqrt(-2*sqrt(5) + 10)*cos(atan(5*(3*sqrt(2) + 2*sqrt(10))*sqrt(sqrt(5) + 5)/(-25*sqrt(5) + 89))/5))/8)*(-1/4 + sqrt(5)/4 + I*sqrt(sqrt(5)/8 + 5/8))*(-375*sqrt(5) + 75*sqrt(5)*(sqrt(5) + 5) + 2287)**(1/10)/5]

In [10]: for root in s:
   ....:     print root.n()
   ....:     
-0.423148382732851 + 0.e-20*I
-5.54860733945285 + 0.e-20*I
-8.59492973614497 + 0.e-20*I
9.41253532831181 + 0.e-20*I
5.15415013001886 + 0.e-20*I

The solutions match. http://www.wolframalpha.com/input/?i=solve+x**5+-+110*x**3+-+55*x**2+%2B+2310*x+%2B+979&dataset=

@prasoon2211
Copy link
Author

It seems we can now add tests.

@asmeurer
Copy link
Member

asmeurer commented Feb 3, 2013

But that's a really long time. Why does it take so long?

@prasoon2211
Copy link
Author

Well, as I said before, when the quintics are like: x5 + a*x + b =0, then we can get away with 15 - 20 secs of solution time. Let me call this equation (1)
For quintics such as x
5+ p_x__3 + q_x*_2 + r_x + s =0, it takes much longer. Let me call this (2). Here's why:

  1. First we have the huge calculations for coefficients. With (1), p, and q are zero, so, most of the terms in the huge coefficients are zero. In (2), these terms are non zero and that requires, I would guess from intuition, three times as much computation time for (2), and we haven't even started with the solution yet.
  2. A very large share of computation time is taken by lines 395 - 398. ( https://github.com/prasoon2211/sympy/blob/59f74d051cd3553da58623f87740dac6af3d7402/sympy/polys/polyroots.py#L395 ). Here, the solutions are calculated exactly and that takes huge times because the R values here contain ~ 10 terms and we are taking 5th roots of sum of these. As a matter of fact, about 80% of time is utilized in these 4 lines. This step is much quicker for (1) because R contains just 3 - 4 simple terms. I guess if we had quicker algorithm for solve() used on lines 395 - 398, then it could have been faster.
  3. The simplifying operations. Throughout the roots_quintic function, we have a couple of simplifying operations. These also take long times for longer operands in (2) comapred to (1). But these operations are necessary because without them, solution times become much larger.

In the end, I suppose we can't have too much speed for (2) at least. If this were a complied language, I suppose the solution time could have been reduced to about 60 secs.
Anyway, even mathematica doesn't solve quintics exactly, to my knowledge. So, I suppose that's something we have now and they don't :)

@asmeurer
Copy link
Member

asmeurer commented Feb 3, 2013

If you comment out those simplify calls, how long does it take? And can you give me an example of a before and after with one of them, so I can see what kind of simplification it is doing?

@asmeurer
Copy link
Member

asmeurer commented Feb 3, 2013

I guess this is coming from the design of solve that is already there (so maybe @smichr can answer this better), but why not always use this algorithm when it can be applied (in other words, why do we have the quintics flag)? Ditto for cubic and quartic.

@@ -501,6 +641,7 @@ def roots(f, *gens, **flags):
auto = flags.pop('auto', True)
cubics = flags.pop('cubics', True)
quartics = flags.pop('quartics', True)
quintics = flags.pop('quintics', False)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, so I guess cubics and quartics default to True. So why did you make this default to False? It's not very useful if it's never enabled.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is intentional. As you have seen, while there are quintics which can be evaluated quickly, there are also those which can take ~ 7 minutes to solve. Surely, we don't want the user to wait for 7 minutes to get a solution? This way, we can document this and if the user wants, then he can go for exact solutions knowing full well that the solution may take a long time to compute.
Also, another reason is this: Suppose that the user has a script. In that script somewhere, there's a solvable quintic. So, to the user, the script may seem to be hanged because of the quintic. I'm sure that won't be a good thing as far as the user satisfaction is concerned.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, the solution here is obviously to make things fast, so that it never takes a long time. I am convinced that it can be done. We just need to figure out what is slow, and how to make it faster.

And anyway, my philosophy is that if it is off by default, then it might as well not exist, because 99% of users will never know it was there. I also believe that it's better to have a slow function that returns a solution than a fast function that doesn't. So I think even if it is slow that this should be True by default.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though we can always start with it being off, merge it, and then after it gets more tested, flip the default to True.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Except if it's off, no one will use it, and it will never be tested.

@asmeurer
Copy link
Member

asmeurer commented Feb 3, 2013

In short, we need to reduce the time of this by a lot. At this rate, it will be impossible to even test the algorithm.

if coeff_5 != 1:
l = [p/coeff_5, q/coeff_5, r/coeff_5, s/coeff_5]
for coeff in l:
if not coeff.is_rational:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should use is_Rational (with a capital R). Otherwise, this will work with something like Symbol('a', rational=True), which we don't want.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can simplify statements like this by using all or any and a list comprehension. This can be written as

if not all(coeff.is_Rational for coeff in l):
    return result

@asmeurer
Copy link
Member

asmeurer commented Feb 3, 2013

You might want to try using line_profilier/kernprof to get an idea of exactly what parts of the algorithm are slow.

@prasoon2211
Copy link
Author

Here are the comparisons for f(x) = x*_5 + 15_x + 12:
Solve with simplify: 16.93 sec
Solve w/o simplify: 41.42 sec

For the same function:

R1 with simplify: -75*sqrt(5)*sqrt(77*sqrt(5) + 327) - 1875 - 75*sqrt(10)*sqrt(24*sqrt(5) + 149)/2 + 375*sqrt(2)*sqrt(24*sqrt(5) + 149)/2

R1 w/o simplify: 1500 + (-375 + 750*sqrt(5) + 75*sqrt(-625 + 29*sqrt(5)))*(-1/4 + sqrt(5)/4 + I*sqrt(sqrt(5)/8 + 5/8))**3 + (-1/4 + sqrt(5)/4 + I*sqrt(sqrt(5)/8 + 5/8))*(-750*sqrt(5) - 375 + 75*sqrt(-625 - 29*sqrt(5))) + (-1/4 + sqrt(5)/4 + I*sqrt(sqrt(5)/8 + 5/8))**4*(-750*sqrt(5) - 375 - 75*sqrt(-625 - 29*sqrt(5))) + (-375 + 750*sqrt(5) - 75*sqrt(-625 + 29*sqrt(5)))*(-1/4 + sqrt(5)/4 + I*sqrt(sqrt(5)/8 + 5/8))**2

I subtracted these outputs and evaluated them. I got 0.e-123 + 0.e-122*I

d = discriminant(f)
delta = sqrt(d)
# zeta = a fifth root of unity
zeta = cos(2*pi/5) + I*sin(2*pi/5)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, here is one thing. If zeta = exp(2*I/5), then zeta**2 = exp(4*I/5) (and so on). So I think we can more efficiently compute the powers of zeta in R1, R2, R3, and R4.

And even if we couldn't, we could at least expand them only once and then use that in each, removing the duplicated computations in lines 395-398.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And I'm curious why you use exp(2*pi/5) as your primitive root instead of just exp(pi/5). Does the paper call for this?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exp(I*pi/5) isn't a fifth root of unity; exp(I*pi/5)**5 = -1.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh right, I forgot it has to be exp(2*pi*I*k/n) (I forgot the 2).

@asmeurer
Copy link
Member

asmeurer commented Feb 3, 2013

I don't understand why it is happening, but the Travis error is is legitimately something that you broke with this branch.

@asmeurer
Copy link
Member

asmeurer commented Mar 9, 2013

SymPy Bot Summary: ❗ There were merge conflicts (could not merge prasoon2211/quintic (6efff32) into master (49eea7e)); could not test the branch.
@prasoon2211: Please rebase or merge your branch with master. See the report for a list of the merge conflicts.

@asmeurer
Copy link
Member

asmeurer commented Mar 9, 2013

Looks like the merge conflicts come from #1813. @smichr can tell you how to correctly handle them.

@prasoon2211
Copy link
Author

Okay then. I'll fix the merge conflict and fix the tests. Thanks.

@smichr
Copy link
Member

smichr commented Mar 9, 2013

Sometimes when I get a messy rebase I prefer to just re-add my changes by hand. You have to make your changes in context of the updated solver.

@asmeurer
Copy link
Member

This doesn't look messy. It's just one conflict. It's just that it might not be clear how to correctly resolve it if you don't know what the code is doing.

@asmeurer
Copy link
Member

By the way @certik, I just realized that your method for bisecting cache bugs is no longer relevant, since the cache is always cleared for each test file (and it has been that way for some time). See https://github.com/sympy/sympy/blob/master/sympy/utilities/runtests.py#L845 and 92ef41f.

@smichr
Copy link
Member

smichr commented Mar 10, 2013

re-add my changes by hand

So go to master; copy solvers.py; go to your quintics branch; open solvers.py and paste what was in master and save it. You should now be in a green merge state. Now make your changes to the solvers.py file. Look for the word 'quartic' and make your changes there.

@smichr
Copy link
Member

smichr commented Mar 10, 2013

btw, there are two places in solvers.py where quartics are being allowed...I'm not sure if they should both be modified or not, but you should look at both of them and raise any questions you may have.

@prasoon2211
Copy link
Author

I have just merged from master and the conflicts have been resolved. And yes, quintics will go at both places where quartics is. I am still trying to find a way for the tests to work. As I mentioned somewhere above, if we check for two different quintics, then the tests hang. But if we test just one of them, tests run fine. So, I'm trying to make a workaround using this. Thanks for you help.

@smichr
Copy link
Member

smichr commented Mar 10, 2013

The test pass ok, here. I guess having it XFAILed is ok, but won't this hang in practice for the same reason that it is hanging during tests? (So does that mean that someone will only be able to solve a quintic once with SymPy and the second time, in a given session, it will hang?)

Since you are just finishing this, I wonder if you would be a good candidate to look into issue 1890, especially my comment 1: I suspect RootOf instances should be returned rather than symbolic forms that may be wrong. Alternatively Piecewise could be used to returned a solution where different pieces correspond to the different forms of the roots for different conditions on the coefficients.

@prasoon2211
Copy link
Author

As to your first question, no, it won't be a problem. A user can solve any number of quintics with the interactive session, as well as with standalone scripts. The problem here seems to be explicitly with the way the testing framework works.
I'll look into 1890 as soon as I have some time. Right now, I am trying to analyse the working of vector calculus in the mechanics module.

@jrioux
Copy link
Member

jrioux commented Mar 11, 2013

SymPy Bot Summary: ✳️ Passed after merging prasoon2211/quintic (4f45c5a) into master (07933cd).
✳️ PyPy 2.0.0-beta-1; 2.7.3-final-42: pass
✳️ Python 2.7.2-final-0: pass
✳️ Python 3.2.1-final-0: pass
✳️ Sphinx 1.1.3: pass
Docs build command: make clean && make html-errors && make latex && cd _build/latex && xelatex sympy-*.tex

@@ -38,7 +38,6 @@ def clashing():

ns = {}
exec 'from sympy import *' in ns

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would leave that blank line in place.

And the constants file line lengths are egregiously long. If you write them in parentheses they can be broken at will, typically after a + or -.

y = (
    x +
    y +
    z)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add the line (it got removed when I was trying to fix the clashing bug earlier). And yes, the question about length of lines in the constants file did bother me initially. In the end I decided to keep one constant on one line. The reason for this being - the constant definitions don't contain any programming logic. They are just definitions. For someone reading the code, I think, it improves readability to keep one long constant definition on a separate line. This way, the user doesn't get bogged down with 100s of lines of constant definitions that don't have anything to with the actual implementation of the solver logic.
That being said, I have nothing against the 80 char limit in particular (I am obviously talking in reference of the constants file). If indeed wrapping lines to 80 chars is required, please let me know.

@smichr
Copy link
Member

smichr commented Mar 12, 2013

How were the constants obtained and how do we know there is no typo in them?

@prasoon2211
Copy link
Author

The constants we obtained from a mathematica notebook file that is mentioned in the docstring. I ran a script to convert them to SymPy specific format.
We know that there is no typo in them because:

  1. The code coverage is 100%, and,
  2. The solutions obtained for solvable quintics are correct (cross checked with wolframalpha and by back-substitution)

Those 2 points in conjunction prove that there exist no typos in the code :)

@smichr
Copy link
Member

smichr commented Mar 12, 2013

The solutions obtained for solvable quintics are correct (cross checked with wolframalpha and by back-substitution)

Symbolically or numerically checked? And are all constants used for every calculation?

@prasoon2211
Copy link
Author

Checked numerically. Mathematica doesn't provide exact solutions to solvable quintics. And yes, every single constant is used for a solution.

@smichr
Copy link
Member

smichr commented Mar 14, 2013

OK, I changed q**8 to q**7 in f20 and all the tests pass...then I deleted it and all tests still pass. But I see from the paper that q**8 is right. So...we'll trust the notebook and your parsing of it and commit this.

@smichr smichr merged commit 4f45c5a into sympy:master Mar 14, 2013
@prasoon2211
Copy link
Author

The tests are passing because f20 isn't used directly in the solution. f20 is just used to check whether quintics is solvable or not. So, when you did the change, it just changed the f(x) defined in test to became non-solvable. Then, the polynomial is just solved numerically within SymPy and if you look closely at the tests, the test just see whether the solution make f(x) zero at the required values, whether these solutions be numerical or exact.
Thus the tests pass. I hope my point was clear.

@asmeurer
Copy link
Member

But the tests should be structured so that changes like that do cause them to fail.

@prasoon2211
Copy link
Author

Yes. I'll do that.

On 3/28/13, Aaron Meurer notifications@github.com wrote:

But the tests should be structured so that changes like that do cause them
to fail.


Reply to this email directly or view it on GitHub:
#1746 (comment)


f = x**5 - 15*x**3 - 5*x**2 + 10*x + 20
s = solve(f)
for root in s:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not able to get the exact solutions of f, what I get is unevaluated RootOf object. I doubt this is the expected behaviour.

In [31]: solve(f, quintics=True)
Out[31]: 
[RootOf(x**5 - 15*x**3 - 5*x**2 + 10*x + 20, 0),
 RootOf(x**5 - 15*x**3 - 5*x**2 + 10*x + 20, 1),
 RootOf(x**5 - 15*x**3 - 5*x**2 + 10*x + 20, 2),
 RootOf(x**5 - 15*x**3 - 5*x**2 + 10*x + 20, 3),
 RootOf(x**5 - 15*x**3 - 5*x**2 + 10*x + 20, 4)]

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you see line 216, you shall find that this is indeed the expected behaviour. I'm sorry that I can't be of more help regarding this issue - I'm terribly bogged down by other matters. But, I should like to refer you to the docstring at the top of sympy/polys/polyquinticconst.py. The links provided there will be of much help, I think.

@coveralls
Copy link

Coverage Status

Changes Unknown when pulling 4f45c5a on prasoon2211:quintic into * on sympy:master*.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants