-
-
Notifications
You must be signed in to change notification settings - Fork 4.4k
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
Solve polynomial--documentation guide #24013
Conversation
on new page and index, links from index to new page
✅ Hi, I am the SymPy bot (v167). I'm here to help you write a release notes entry. Please read the guide on how to write release notes. Your release notes are in good order. Here is what the release notes will look like:
This will be added to https://github.com/sympy/sympy/wiki/Release-Notes-for-1.12. Click here to see the pull request description that was parsed.
Update The release notes on the wiki have been updated. |
🟠Hi, I am the SymPy bot (v167). I've noticed that some of your commits add or delete files. Since this is sometimes done unintentionally, I wanted to alert you about it. This is an experimental feature of SymPy Bot. If you have any feedback on it, please comment at sympy/sympy-bot#75. The following commits add new files:
If these files were added/deleted on purpose, you can ignore this message. |
I just started this page and seek guidance on what it should include:
|
There are lots of functions:
The In [12]: p = expand((x - a**2)*(x + a + a**3))
In [13]: p
Out[13]:
5 3 3 2 2
- a + a ⋅x - a - a ⋅x + a⋅x + x
In [14]: factor(p)
Out[14]:
⎛ 2 ⎞ ⎛ 3 ⎞
⎝- a + x⎠⋅⎝a + a + x⎠ The The The In [15]: nroots(x**2 + sqrt(2)*x + pi)
Out[15]: [-0.707106781186548 - 1.62529771229452⋅ⅈ, -0.707106781186548 + 1.62529771229452⋅ⅈ] There is no restriction on the degree for which The The Symbolic algorithms that need to work consistently with the roots of potentially high degree polynomials generally do so without needing explicit representations of those roots (as would be returned by |
Benchmark results from GitHub Actions Lower numbers are good, higher numbers are bad. A ratio less than 1 Significantly changed benchmark results (PR vs master) Significantly changed benchmark results (master vs previous release) before after ratio
[41d90958] [d44f27e1]
<sympy-1.11.1^0>
- 960±3μs 621±4μs 0.65 solve.TimeSparseSystem.time_linear_eq_to_matrix(10)
- 2.79±0.01ms 1.17±0ms 0.42 solve.TimeSparseSystem.time_linear_eq_to_matrix(20)
- 5.64±0.01ms 1.71±0ms 0.30 solve.TimeSparseSystem.time_linear_eq_to_matrix(30)
Full benchmark results can be found as artifacts in GitHub Actions |
The examples here are too simple. I think it's important to illustrate that In [7]: p = x**5 - x + 1
In [8]: roots(p)
Out[8]: {}
In [9]: real_roots(p)
Out[9]:
⎡ ⎛ 5 ⎞⎤
⎣CRootOf⎝x - x + 1, 0⎠⎦
In [10]: Poly(p, x).all_roots()
Out[10]:
⎡ ⎛ 5 ⎞ ⎛ 5 ⎞ ⎛ 5 ⎞ ⎛ 5 ⎞
⎣CRootOf⎝x - x + 1, 0⎠, CRootOf⎝x - x + 1, 1⎠, CRootOf⎝x - x + 1, 2⎠, CRootOf⎝x - x + 1, 3⎠, CR
⎛ 5 ⎞⎤
ootOf⎝x - x + 1, 4⎠⎦
In [11]: r1, r2, r3, r4, r5 = Poly(p, x).all_roots()
In [12]: r1
Out[12]:
⎛ 5 ⎞
CRootOf⎝x - x + 1, 0⎠
In [13]: r1.n(100)
Out[13]:
-1.167303978261418684256045899854842180720560371525489039140082449275651903429527053180685205049728
673
In [14]: r1.is_real
Out[14]: True
In [15]: r2.n()
Out[15]: -0.181232444469875 - 1.08395410131771⋅ⅈ
In [16]: r3.n()
Out[16]: -0.181232444469875 + 1.08395410131771⋅ⅈ
In [17]: r2
Out[17]:
⎛ 5 ⎞
CRootOf⎝x - x + 1, 1⎠
In [18]: r2.conjugate()
Out[18]:
⎛ 5 ⎞
CRootOf⎝x - x + 1, 2⎠
In [19]: r2.is_real
Out[19]: False Using
Note that although
The other numerical methods like
https://en.wikipedia.org/wiki/Casus_irreducibilis I think it's important to show the complicated expressions that are generated for cubics and quartics by In [27]: r1, r2, r3, r4 = roots(x**4 + 3*x**2 + 2*x + 1)
In [28]: r1
Out[28]:
_______________________________________________________________________________________
╱ ______________
╱ ╱ 1 √237⋅ⅈ 4
╱ -4 - 2⋅3 ╱ - ─ + ────── + ──────────────────────────────────────────────────────────
╱ ╲╱ 8 36 __________________________________________________
╱ ╱ ______________
╱ ╱ 7 ╱ 1 √237⋅ⅈ
╱ ╱ -2 + ──────────────────── + 2⋅3 ╱ - ─ + ──────
╱ ╱ ______________ ╲╱ 8 36
╱ ╱ ╱ 1 √237⋅ⅈ
╱ ╱ 6⋅3 ╱ - ─ + ──────
╲╱ ╲╱ ╲╱ 8 36
───────────────────────────────────────────────────────────────────────────────────────────────────
2
________________________
7
- ────────────────────
______________ __________________________________________________
╱ 1 √237⋅ⅈ ╱ ______________
6⋅3 ╱ - ─ + ────── ╱ 7 ╱ 1 √237⋅ⅈ
╲╱ 8 36 ╱ -2 + ──────────────────── + 2⋅3 ╱ - ─ + ──────
╱ ______________ ╲╱ 8 36
╱ ╱ 1 √237⋅ⅈ
╱ 6⋅3 ╱ - ─ + ──────
╲╱ ╲╱ 8 36
──────────────────────── - ──────────────────────────────────────────────────────────
2
In [29]: r1.n()
Out[29]: -0.349745826211722 - 0.438990337475312⋅ⅈ It's worth considering that if all you can do to make sense of the expression is evaluate it numerically then perhaps it would have been better just to compute the roots numerically: In [30]: nroots(x**4 + 3*x**2 + 2*x + 1)
Out[30]:
[-0.349745826211722 - 0.438990337475312⋅ⅈ, -0.349745826211722 + 0.438990337475312⋅ⅈ, 0.349745826211
722 - 1.74697789611327⋅ⅈ, 0.349745826211722 + 1.74697789611327⋅ⅈ] If you wanted an exact representation then in fact the best exact representation is RootOf which can also be evaluated numerically: In [31]: Poly(x**4 + 3*x**2 + 2*x + 1).all_roots()
Out[31]:
⎡ ⎛ 4 2 ⎞ ⎛ 4 2 ⎞ ⎛ 4 2 ⎞
⎣CRootOf⎝x + 3⋅x + 2⋅x + 1, 0⎠, CRootOf⎝x + 3⋅x + 2⋅x + 1, 1⎠, CRootOf⎝x + 3⋅x + 2⋅x + 1, 2⎠,
⎛ 4 2 ⎞⎤
CRootOf⎝x + 3⋅x + 2⋅x + 1, 3⎠⎦ If you want numeric approximations of the real roots but you want to know exactly which roots are real then the best method is In [34]: [r.n(2) for r in real_roots(x**4 - 3*x**2 - 2*x + 1)]
Out[34]: [0.34, 1.9] Note that none of the approximate numeric methods can distinguish precisely which roots are real. Consider Wilkinson's polynomial: In [35]: p = prod((x - i) for i in range(1, 31))
In [36]: p
Out[36]:
(x - 30)⋅(x - 29)⋅(x - 28)⋅(x - 27)⋅(x - 26)⋅(x - 25)⋅(x - 24)⋅(x - 23)⋅(x - 22)⋅(x - 21)⋅(x - 20)⋅
(x - 19)⋅(x - 18)⋅(x - 17)⋅(x - 16)⋅(x - 15)⋅(x - 14)⋅(x - 13)⋅(x - 12)⋅(x - 11)⋅(x - 10)⋅(x - 9)⋅(
x - 8)⋅(x - 7)⋅(x - 6)⋅(x - 5)⋅(x - 4)⋅(x - 3)⋅(x - 2)⋅(x - 1)
In [37]: p = expand(p)
In [38]: p
Out[38]:
30 29 28 27 26 25 24
x - 465⋅x + 103385⋅x - 14631225⋅x + 1480321269⋅x - 114009431445⋅x + 6949189247325⋅x -
23 22 21 20
344092707928125⋅x + 14097793282984515⋅x - 484338676679532675⋅x + 14090257524223082475⋅x -
19 18 17
349600545868057540875⋅x + 7435941626111727234855⋅x - 136055808711963322871175⋅x + 21458832493
16 15 14
34501452139775⋅x - 29197210605623737977801375⋅x + 342563613932937660652700640⋅x - 34602661104
13 12 11
93898677911394000⋅x + 30006513636556697864066736800⋅x - 222457423246962063058403076000⋅x + 14
10 9
01937624086807501691142239744⋅x - 7454161471690660700139655157760⋅x + 33114629767614997850763390
8 7 6
570240⋅x - 121365366674745136523074652102400⋅x + 360930788158836812805614538878976⋅x - 851899888
5 4
505423112503184251412480⋅x + 1547794975254719737111781253120000⋅x - 20707922020245946836608666419
3 2
20000⋅x + 1902893785240928209998216560640000⋅x - 1059681761389533859949327155200000⋅x + 265252859
812191058636308480000000
In [39]: factor(p)
Out[39]:
(x - 30)⋅(x - 29)⋅(x - 28)⋅(x - 27)⋅(x - 26)⋅(x - 25)⋅(x - 24)⋅(x - 23)⋅(x - 22)⋅(x - 21)⋅(x - 20)⋅
(x - 19)⋅(x - 18)⋅(x - 17)⋅(x - 16)⋅(x - 15)⋅(x - 14)⋅(x - 13)⋅(x - 12)⋅(x - 11)⋅(x - 10)⋅(x - 9)⋅(
x - 8)⋅(x - 7)⋅(x - 6)⋅(x - 5)⋅(x - 4)⋅(x - 3)⋅(x - 2)⋅(x - 1)
In [42]: roots(p, multiple=True)
Out[42]:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
28, 29, 30]
In [40]: nroots(p)
---------------------------------------------------------------------------
NoConvergence
In [41]: np.roots(Poly(p).all_coeffs())
Out[41]:
array([32.07636983+0.j , 31.29103418+2.57752104j,
31.29103418-2.57752104j, 29.17520185+4.8010735j ,
29.17520185-4.8010735j , 26.19348536+6.28087068j,
26.19348536-6.28087068j, 22.87013876+6.83360078j,
22.87013876-6.83360078j, 19.66310309+6.55232555j,
19.66310309-6.55232555j, 16.84020824+5.6911237j ,
16.84020824-5.6911237j , 14.47784031+4.52035773j,
14.47784031-4.52035773j, 12.50458382+3.23884483j,
12.50458382-3.23884483j, 11.53631012+0.j ,
10.72355387+1.9306279j , 10.72355387-1.9306279j ,
9.02521141+0.80837533j, 9.02521141-0.80837533j,
8.83820369+0.j , 7.02160259+0.j ,
5.99874634+0.j , 5.00004638+0.j ,
3.99999929+0.j , 2.99999999+0.j ,
2. +0.j , 1. +0.j ]) Here the roots should all be real but In [43]: nroots(p, maxsteps=100)
Out[43]:
[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0,
19.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0, 30.0] It looks as if
Only exact arithmetic can make the distinction between roots that are close and multiple roots via the square free factorisation ( In [55]: factor(p)
Out[55]:
2
(3⋅x - 1)
──────────
9
In [56]: solve(p)
Out[56]: [1/3]
In [57]: roots(p)
Out[57]: {1/3: 2}
In [58]: real_roots(p)
Out[58]: [1/3, 1/3] Another thing that I think is worth mentioning is that sometimes for a very high degree polynomial it can be more efficient to use |
I think it would better to have a clear list of the possible functions to use near the top of this page with a short (e.g. one sentence) summary of when and why each might be used. |
There is an important point that users should be made aware of but isn't really covered here because you don't show any examples that have big horrible messy output. The point is this:
It is important to understand this point in order to understand why it is better to use RootOf even if radical expressions are possible. This is the reason that both The followup point which is mentioned is this:
The problem with emphasising this second point about the nonexistence of formulae in some cases is that it overlooks the fact that you probably don't want to use the formulae that do exist either. When RootOf isn't possible e.g. because of symbolic coefficients then it is better not to try and compute the roots at all for anything more complicated than a quadratic equation. It is certainly too much to explain how to handle that situation in this guide but in fact the real solution when RootOf and factor don't work is that it is better just to stick with the polynomial itself and consider it as a representation of its own roots rather than demanding any explicit representation for the roots. Techniques that use the polynomial as an implicit representation of its roots can be much more powerful and not limited by the issues discussed in this article. I'm not sure how best to convey this point to users but what it basically means is that the whole premise that you want to "solve a polynomial" is often misguided: usually the (factorised) polynomial already is the best way to represent its roots. |
Unfortunately, the SciPy documentation site is down, leading SymPy's sphinx tests and documentation builds to fail because of the intersphinx reference. |
There is an example of a quartic here, but it's printed in str form which doesn't line wrap, so perhaps the point isn't brought home very well. Maybe we should include the LaTeX form of the expression just to show how complex it really it is. |
We could also |
While I appreciate the point of emphasizing a very complex quartic solution by making all of it appear on the screen without scrolling, I don't think it's worth the effort--using either dollar math or I just added a comment to the code block that the radical formula returns very complex terms. This was something we discussed before for another example that produced very complex terms, and I believe @asmeurer suggested changing the CSS to wrap within code blocks. If we want to emphasize long (many-term) results, I think we should make an issue to do that general solution. |
Add note about 5+ degree polynomials
…o solve-polynomial
You should use You can also hard wrap the math, I believe using |
Got it, thanks. I converted the formula to LaTeX using SymPy's Regarding web page formatting, that expression fits in the body section of the page. I experimented and, if the expression were longer, it would break out of the body section, in which case a line break would be helpful. (I played around and a a line break works if you use double dollar signs, e.g. |
The expression did extend beyond the body width on my phone, so I pushed a commit with a line break. I'm not thrilled with how it looks on a computer screen because the horizontal line of the radical on the second line could be visually confused with a fraction line at first glance: |
Inline math is not expected to be that big. It's supposed to be "inline" with the body text. Large equations like this should definitely use the non-inline version. |
… of page, remove line break from long expression
I removed the hard line break but kept the non-inline formatting by using two dollar signs before and after the LaTeX, and it produces a scrollable container as desired. |
The cubic formula looks better now. Are there any other objections to merging this? As @bertiewooster noted, the link to the issues page will be removed from all the guides in a separate pull request. |
References to other Issues or PRs
Brief description of what is fixed or changed
Other comments
Release Notes