polyroots.py 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227
  1. """Algorithms for computing symbolic roots of polynomials. """
  2. import math
  3. from functools import reduce
  4. from sympy.core import S, I, pi
  5. from sympy.core.exprtools import factor_terms
  6. from sympy.core.function import _mexpand
  7. from sympy.core.logic import fuzzy_not
  8. from sympy.core.mul import expand_2arg, Mul
  9. from sympy.core.intfunc import igcd
  10. from sympy.core.numbers import Rational, comp
  11. from sympy.core.power import Pow
  12. from sympy.core.relational import Eq
  13. from sympy.core.sorting import ordered
  14. from sympy.core.symbol import Dummy, Symbol, symbols
  15. from sympy.core.sympify import sympify
  16. from sympy.functions import exp, im, cos, acos, Piecewise
  17. from sympy.functions.elementary.miscellaneous import root, sqrt
  18. from sympy.ntheory import divisors, isprime, nextprime
  19. from sympy.polys.domains import EX
  20. from sympy.polys.polyerrors import (PolynomialError, GeneratorsNeeded,
  21. DomainError, UnsolvableFactorError)
  22. from sympy.polys.polyquinticconst import PolyQuintic
  23. from sympy.polys.polytools import Poly, cancel, factor, gcd_list, discriminant
  24. from sympy.polys.rationaltools import together
  25. from sympy.polys.specialpolys import cyclotomic_poly
  26. from sympy.utilities import public
  27. from sympy.utilities.misc import filldedent
  28. z = Symbol('z') # importing from abc cause O to be lost as clashing symbol
  29. def roots_linear(f):
  30. """Returns a list of roots of a linear polynomial."""
  31. r = -f.nth(0)/f.nth(1)
  32. dom = f.get_domain()
  33. if not dom.is_Numerical:
  34. if dom.is_Composite:
  35. r = factor(r)
  36. else:
  37. from sympy.simplify.simplify import simplify
  38. r = simplify(r)
  39. return [r]
  40. def roots_quadratic(f):
  41. """Returns a list of roots of a quadratic polynomial. If the domain is ZZ
  42. then the roots will be sorted with negatives coming before positives.
  43. The ordering will be the same for any numerical coefficients as long as
  44. the assumptions tested are correct, otherwise the ordering will not be
  45. sorted (but will be canonical).
  46. """
  47. a, b, c = f.all_coeffs()
  48. dom = f.get_domain()
  49. def _sqrt(d):
  50. # remove squares from square root since both will be represented
  51. # in the results; a similar thing is happening in roots() but
  52. # must be duplicated here because not all quadratics are binomials
  53. co = []
  54. other = []
  55. for di in Mul.make_args(d):
  56. if di.is_Pow and di.exp.is_Integer and di.exp % 2 == 0:
  57. co.append(Pow(di.base, di.exp//2))
  58. else:
  59. other.append(di)
  60. if co:
  61. d = Mul(*other)
  62. co = Mul(*co)
  63. return co*sqrt(d)
  64. return sqrt(d)
  65. def _simplify(expr):
  66. if dom.is_Composite:
  67. return factor(expr)
  68. else:
  69. from sympy.simplify.simplify import simplify
  70. return simplify(expr)
  71. if c is S.Zero:
  72. r0, r1 = S.Zero, -b/a
  73. if not dom.is_Numerical:
  74. r1 = _simplify(r1)
  75. elif r1.is_negative:
  76. r0, r1 = r1, r0
  77. elif b is S.Zero:
  78. r = -c/a
  79. if not dom.is_Numerical:
  80. r = _simplify(r)
  81. R = _sqrt(r)
  82. r0 = -R
  83. r1 = R
  84. else:
  85. d = b**2 - 4*a*c
  86. A = 2*a
  87. B = -b/A
  88. if not dom.is_Numerical:
  89. d = _simplify(d)
  90. B = _simplify(B)
  91. D = factor_terms(_sqrt(d)/A)
  92. r0 = B - D
  93. r1 = B + D
  94. if a.is_negative:
  95. r0, r1 = r1, r0
  96. elif not dom.is_Numerical:
  97. r0, r1 = [expand_2arg(i) for i in (r0, r1)]
  98. return [r0, r1]
  99. def roots_cubic(f, trig=False):
  100. """Returns a list of roots of a cubic polynomial.
  101. References
  102. ==========
  103. [1] https://en.wikipedia.org/wiki/Cubic_function, General formula for roots,
  104. (accessed November 17, 2014).
  105. """
  106. if trig:
  107. a, b, c, d = f.all_coeffs()
  108. p = (3*a*c - b**2)/(3*a**2)
  109. q = (2*b**3 - 9*a*b*c + 27*a**2*d)/(27*a**3)
  110. D = 18*a*b*c*d - 4*b**3*d + b**2*c**2 - 4*a*c**3 - 27*a**2*d**2
  111. if (D > 0) == True:
  112. rv = []
  113. for k in range(3):
  114. rv.append(2*sqrt(-p/3)*cos(acos(q/p*sqrt(-3/p)*Rational(3, 2))/3 - k*pi*Rational(2, 3)))
  115. return [i - b/3/a for i in rv]
  116. # a*x**3 + b*x**2 + c*x + d -> x**3 + a*x**2 + b*x + c
  117. _, a, b, c = f.monic().all_coeffs()
  118. if c is S.Zero:
  119. x1, x2 = roots([1, a, b], multiple=True)
  120. return [x1, S.Zero, x2]
  121. # x**3 + a*x**2 + b*x + c -> u**3 + p*u + q
  122. p = b - a**2/3
  123. q = c - a*b/3 + 2*a**3/27
  124. pon3 = p/3
  125. aon3 = a/3
  126. u1 = None
  127. if p is S.Zero:
  128. if q is S.Zero:
  129. return [-aon3]*3
  130. u1 = -root(q, 3) if q.is_positive else root(-q, 3)
  131. elif q is S.Zero:
  132. y1, y2 = roots([1, 0, p], multiple=True)
  133. return [tmp - aon3 for tmp in [y1, S.Zero, y2]]
  134. elif q.is_real and q.is_negative:
  135. u1 = -root(-q/2 + sqrt(q**2/4 + pon3**3), 3)
  136. coeff = I*sqrt(3)/2
  137. if u1 is None:
  138. u1 = S.One
  139. u2 = Rational(-1, 2) + coeff
  140. u3 = Rational(-1, 2) - coeff
  141. b, c, d = a, b, c # a, b, c, d = S.One, a, b, c
  142. D0 = b**2 - 3*c # b**2 - 3*a*c
  143. D1 = 2*b**3 - 9*b*c + 27*d # 2*b**3 - 9*a*b*c + 27*a**2*d
  144. C = root((D1 + sqrt(D1**2 - 4*D0**3))/2, 3)
  145. return [-(b + uk*C + D0/C/uk)/3 for uk in [u1, u2, u3]] # -(b + uk*C + D0/C/uk)/3/a
  146. u2 = u1*(Rational(-1, 2) + coeff)
  147. u3 = u1*(Rational(-1, 2) - coeff)
  148. if p is S.Zero:
  149. return [u1 - aon3, u2 - aon3, u3 - aon3]
  150. soln = [
  151. -u1 + pon3/u1 - aon3,
  152. -u2 + pon3/u2 - aon3,
  153. -u3 + pon3/u3 - aon3
  154. ]
  155. return soln
  156. def _roots_quartic_euler(p, q, r, a):
  157. """
  158. Descartes-Euler solution of the quartic equation
  159. Parameters
  160. ==========
  161. p, q, r: coefficients of ``x**4 + p*x**2 + q*x + r``
  162. a: shift of the roots
  163. Notes
  164. =====
  165. This is a helper function for ``roots_quartic``.
  166. Look for solutions of the form ::
  167. ``x1 = sqrt(R) - sqrt(A + B*sqrt(R))``
  168. ``x2 = -sqrt(R) - sqrt(A - B*sqrt(R))``
  169. ``x3 = -sqrt(R) + sqrt(A - B*sqrt(R))``
  170. ``x4 = sqrt(R) + sqrt(A + B*sqrt(R))``
  171. To satisfy the quartic equation one must have
  172. ``p = -2*(R + A); q = -4*B*R; r = (R - A)**2 - B**2*R``
  173. so that ``R`` must satisfy the Descartes-Euler resolvent equation
  174. ``64*R**3 + 32*p*R**2 + (4*p**2 - 16*r)*R - q**2 = 0``
  175. If the resolvent does not have a rational solution, return None;
  176. in that case it is likely that the Ferrari method gives a simpler
  177. solution.
  178. Examples
  179. ========
  180. >>> from sympy import S
  181. >>> from sympy.polys.polyroots import _roots_quartic_euler
  182. >>> p, q, r = -S(64)/5, -S(512)/125, -S(1024)/3125
  183. >>> _roots_quartic_euler(p, q, r, S(0))[0]
  184. -sqrt(32*sqrt(5)/125 + 16/5) + 4*sqrt(5)/5
  185. """
  186. # solve the resolvent equation
  187. x = Dummy('x')
  188. eq = 64*x**3 + 32*p*x**2 + (4*p**2 - 16*r)*x - q**2
  189. xsols = list(roots(Poly(eq, x), cubics=False).keys())
  190. xsols = [sol for sol in xsols if sol.is_rational and sol.is_nonzero]
  191. if not xsols:
  192. return None
  193. R = max(xsols)
  194. c1 = sqrt(R)
  195. B = -q*c1/(4*R)
  196. A = -R - p/2
  197. c2 = sqrt(A + B)
  198. c3 = sqrt(A - B)
  199. return [c1 - c2 - a, -c1 - c3 - a, -c1 + c3 - a, c1 + c2 - a]
  200. def roots_quartic(f):
  201. r"""
  202. Returns a list of roots of a quartic polynomial.
  203. There are many references for solving quartic expressions available [1-5].
  204. This reviewer has found that many of them require one to select from among
  205. 2 or more possible sets of solutions and that some solutions work when one
  206. is searching for real roots but do not work when searching for complex roots
  207. (though this is not always stated clearly). The following routine has been
  208. tested and found to be correct for 0, 2 or 4 complex roots.
  209. The quasisymmetric case solution [6] looks for quartics that have the form
  210. `x**4 + A*x**3 + B*x**2 + C*x + D = 0` where `(C/A)**2 = D`.
  211. Although no general solution that is always applicable for all
  212. coefficients is known to this reviewer, certain conditions are tested
  213. to determine the simplest 4 expressions that can be returned:
  214. 1) `f = c + a*(a**2/8 - b/2) == 0`
  215. 2) `g = d - a*(a*(3*a**2/256 - b/16) + c/4) = 0`
  216. 3) if `f != 0` and `g != 0` and `p = -d + a*c/4 - b**2/12` then
  217. a) `p == 0`
  218. b) `p != 0`
  219. Examples
  220. ========
  221. >>> from sympy import Poly
  222. >>> from sympy.polys.polyroots import roots_quartic
  223. >>> r = roots_quartic(Poly('x**4-6*x**3+17*x**2-26*x+20'))
  224. >>> # 4 complex roots: 1+-I*sqrt(3), 2+-I
  225. >>> sorted(str(tmp.evalf(n=2)) for tmp in r)
  226. ['1.0 + 1.7*I', '1.0 - 1.7*I', '2.0 + 1.0*I', '2.0 - 1.0*I']
  227. References
  228. ==========
  229. 1. http://mathforum.org/dr.math/faq/faq.cubic.equations.html
  230. 2. https://en.wikipedia.org/wiki/Quartic_function#Summary_of_Ferrari.27s_method
  231. 3. https://planetmath.org/encyclopedia/GaloisTheoreticDerivationOfTheQuarticFormula.html
  232. 4. https://people.bath.ac.uk/masjhd/JHD-CA.pdf
  233. 5. http://www.albmath.org/files/Math_5713.pdf
  234. 6. https://web.archive.org/web/20171002081448/http://www.statemaster.com/encyclopedia/Quartic-equation
  235. 7. https://eqworld.ipmnet.ru/en/solutions/ae/ae0108.pdf
  236. """
  237. _, a, b, c, d = f.monic().all_coeffs()
  238. if not d:
  239. return [S.Zero] + roots([1, a, b, c], multiple=True)
  240. elif (c/a)**2 == d:
  241. x, m = f.gen, c/a
  242. g = Poly(x**2 + a*x + b - 2*m, x)
  243. z1, z2 = roots_quadratic(g)
  244. h1 = Poly(x**2 - z1*x + m, x)
  245. h2 = Poly(x**2 - z2*x + m, x)
  246. r1 = roots_quadratic(h1)
  247. r2 = roots_quadratic(h2)
  248. return r1 + r2
  249. else:
  250. a2 = a**2
  251. e = b - 3*a2/8
  252. f = _mexpand(c + a*(a2/8 - b/2))
  253. aon4 = a/4
  254. g = _mexpand(d - aon4*(a*(3*a2/64 - b/4) + c))
  255. if f.is_zero:
  256. y1, y2 = [sqrt(tmp) for tmp in
  257. roots([1, e, g], multiple=True)]
  258. return [tmp - aon4 for tmp in [-y1, -y2, y1, y2]]
  259. if g.is_zero:
  260. y = [S.Zero] + roots([1, 0, e, f], multiple=True)
  261. return [tmp - aon4 for tmp in y]
  262. else:
  263. # Descartes-Euler method, see [7]
  264. sols = _roots_quartic_euler(e, f, g, aon4)
  265. if sols:
  266. return sols
  267. # Ferrari method, see [1, 2]
  268. p = -e**2/12 - g
  269. q = -e**3/108 + e*g/3 - f**2/8
  270. TH = Rational(1, 3)
  271. def _ans(y):
  272. w = sqrt(e + 2*y)
  273. arg1 = 3*e + 2*y
  274. arg2 = 2*f/w
  275. ans = []
  276. for s in [-1, 1]:
  277. root = sqrt(-(arg1 + s*arg2))
  278. for t in [-1, 1]:
  279. ans.append((s*w - t*root)/2 - aon4)
  280. return ans
  281. # whether a Piecewise is returned or not
  282. # depends on knowing p, so try to put
  283. # in a simple form
  284. p = _mexpand(p)
  285. # p == 0 case
  286. y1 = e*Rational(-5, 6) - q**TH
  287. if p.is_zero:
  288. return _ans(y1)
  289. # if p != 0 then u below is not 0
  290. root = sqrt(q**2/4 + p**3/27)
  291. r = -q/2 + root # or -q/2 - root
  292. u = r**TH # primary root of solve(x**3 - r, x)
  293. y2 = e*Rational(-5, 6) + u - p/u/3
  294. if fuzzy_not(p.is_zero):
  295. return _ans(y2)
  296. # sort it out once they know the values of the coefficients
  297. return [Piecewise((a1, Eq(p, 0)), (a2, True))
  298. for a1, a2 in zip(_ans(y1), _ans(y2))]
  299. def roots_binomial(f):
  300. """Returns a list of roots of a binomial polynomial. If the domain is ZZ
  301. then the roots will be sorted with negatives coming before positives.
  302. The ordering will be the same for any numerical coefficients as long as
  303. the assumptions tested are correct, otherwise the ordering will not be
  304. sorted (but will be canonical).
  305. """
  306. n = f.degree()
  307. a, b = f.nth(n), f.nth(0)
  308. base = -cancel(b/a)
  309. alpha = root(base, n)
  310. if alpha.is_number:
  311. alpha = alpha.expand(complex=True)
  312. # define some parameters that will allow us to order the roots.
  313. # If the domain is ZZ this is guaranteed to return roots sorted
  314. # with reals before non-real roots and non-real sorted according
  315. # to real part and imaginary part, e.g. -1, 1, -1 + I, 2 - I
  316. neg = base.is_negative
  317. even = n % 2 == 0
  318. if neg:
  319. if even == True and (base + 1).is_positive:
  320. big = True
  321. else:
  322. big = False
  323. # get the indices in the right order so the computed
  324. # roots will be sorted when the domain is ZZ
  325. ks = []
  326. imax = n//2
  327. if even:
  328. ks.append(imax)
  329. imax -= 1
  330. if not neg:
  331. ks.append(0)
  332. for i in range(imax, 0, -1):
  333. if neg:
  334. ks.extend([i, -i])
  335. else:
  336. ks.extend([-i, i])
  337. if neg:
  338. ks.append(0)
  339. if big:
  340. for i in range(0, len(ks), 2):
  341. pair = ks[i: i + 2]
  342. pair = list(reversed(pair))
  343. # compute the roots
  344. roots, d = [], 2*I*pi/n
  345. for k in ks:
  346. zeta = exp(k*d).expand(complex=True)
  347. roots.append((alpha*zeta).expand(power_base=False))
  348. return roots
  349. def _inv_totient_estimate(m):
  350. """
  351. Find ``(L, U)`` such that ``L <= phi^-1(m) <= U``.
  352. Examples
  353. ========
  354. >>> from sympy.polys.polyroots import _inv_totient_estimate
  355. >>> _inv_totient_estimate(192)
  356. (192, 840)
  357. >>> _inv_totient_estimate(400)
  358. (400, 1750)
  359. """
  360. primes = [ d + 1 for d in divisors(m) if isprime(d + 1) ]
  361. a, b = 1, 1
  362. for p in primes:
  363. a *= p
  364. b *= p - 1
  365. L = m
  366. U = int(math.ceil(m*(float(a)/b)))
  367. P = p = 2
  368. primes = []
  369. while P <= U:
  370. p = nextprime(p)
  371. primes.append(p)
  372. P *= p
  373. P //= p
  374. b = 1
  375. for p in primes[:-1]:
  376. b *= p - 1
  377. U = int(math.ceil(m*(float(P)/b)))
  378. return L, U
  379. def roots_cyclotomic(f, factor=False):
  380. """Compute roots of cyclotomic polynomials. """
  381. L, U = _inv_totient_estimate(f.degree())
  382. for n in range(L, U + 1):
  383. g = cyclotomic_poly(n, f.gen, polys=True)
  384. if f.expr == g.expr:
  385. break
  386. else: # pragma: no cover
  387. raise RuntimeError("failed to find index of a cyclotomic polynomial")
  388. roots = []
  389. if not factor:
  390. # get the indices in the right order so the computed
  391. # roots will be sorted
  392. h = n//2
  393. ks = [i for i in range(1, n + 1) if igcd(i, n) == 1]
  394. ks.sort(key=lambda x: (x, -1) if x <= h else (abs(x - n), 1))
  395. d = 2*I*pi/n
  396. for k in reversed(ks):
  397. roots.append(exp(k*d).expand(complex=True))
  398. else:
  399. g = Poly(f, extension=root(-1, n))
  400. for h, _ in ordered(g.factor_list()[1]):
  401. roots.append(-h.TC())
  402. return roots
  403. def roots_quintic(f):
  404. """
  405. Calculate exact roots of a solvable irreducible quintic with rational coefficients.
  406. Return an empty list if the quintic is reducible or not solvable.
  407. """
  408. result = []
  409. coeff_5, coeff_4, p_, q_, r_, s_ = f.all_coeffs()
  410. if not all(coeff.is_Rational for coeff in (coeff_5, coeff_4, p_, q_, r_, s_)):
  411. return result
  412. if coeff_5 != 1:
  413. f = Poly(f / coeff_5)
  414. _, coeff_4, p_, q_, r_, s_ = f.all_coeffs()
  415. # Cancel coeff_4 to form x^5 + px^3 + qx^2 + rx + s
  416. if coeff_4:
  417. p = p_ - 2*coeff_4*coeff_4/5
  418. q = q_ - 3*coeff_4*p_/5 + 4*coeff_4**3/25
  419. r = r_ - 2*coeff_4*q_/5 + 3*coeff_4**2*p_/25 - 3*coeff_4**4/125
  420. s = s_ - coeff_4*r_/5 + coeff_4**2*q_/25 - coeff_4**3*p_/125 + 4*coeff_4**5/3125
  421. x = f.gen
  422. f = Poly(x**5 + p*x**3 + q*x**2 + r*x + s)
  423. else:
  424. p, q, r, s = p_, q_, r_, s_
  425. quintic = PolyQuintic(f)
  426. # Eqn standardized. Algo for solving starts here
  427. if not f.is_irreducible:
  428. return result
  429. f20 = quintic.f20
  430. # Check if f20 has linear factors over domain Z
  431. if f20.is_irreducible:
  432. return result
  433. # Now, we know that f is solvable
  434. for _factor in f20.factor_list()[1]:
  435. if _factor[0].is_linear:
  436. theta = _factor[0].root(0)
  437. break
  438. d = discriminant(f)
  439. delta = sqrt(d)
  440. # zeta = a fifth root of unity
  441. zeta1, zeta2, zeta3, zeta4 = quintic.zeta
  442. T = quintic.T(theta, d)
  443. tol = S(1e-10)
  444. alpha = T[1] + T[2]*delta
  445. alpha_bar = T[1] - T[2]*delta
  446. beta = T[3] + T[4]*delta
  447. beta_bar = T[3] - T[4]*delta
  448. disc = alpha**2 - 4*beta
  449. disc_bar = alpha_bar**2 - 4*beta_bar
  450. l0 = quintic.l0(theta)
  451. Stwo = S(2)
  452. l1 = _quintic_simplify((-alpha + sqrt(disc)) / Stwo)
  453. l4 = _quintic_simplify((-alpha - sqrt(disc)) / Stwo)
  454. l2 = _quintic_simplify((-alpha_bar + sqrt(disc_bar)) / Stwo)
  455. l3 = _quintic_simplify((-alpha_bar - sqrt(disc_bar)) / Stwo)
  456. order = quintic.order(theta, d)
  457. test = (order*delta.n()) - ( (l1.n() - l4.n())*(l2.n() - l3.n()) )
  458. # Comparing floats
  459. if not comp(test, 0, tol):
  460. l2, l3 = l3, l2
  461. # Now we have correct order of l's
  462. R1 = l0 + l1*zeta1 + l2*zeta2 + l3*zeta3 + l4*zeta4
  463. R2 = l0 + l3*zeta1 + l1*zeta2 + l4*zeta3 + l2*zeta4
  464. R3 = l0 + l2*zeta1 + l4*zeta2 + l1*zeta3 + l3*zeta4
  465. R4 = l0 + l4*zeta1 + l3*zeta2 + l2*zeta3 + l1*zeta4
  466. Res = [None, [None]*5, [None]*5, [None]*5, [None]*5]
  467. Res_n = [None, [None]*5, [None]*5, [None]*5, [None]*5]
  468. # Simplifying improves performance a lot for exact expressions
  469. R1 = _quintic_simplify(R1)
  470. R2 = _quintic_simplify(R2)
  471. R3 = _quintic_simplify(R3)
  472. R4 = _quintic_simplify(R4)
  473. # hard-coded results for [factor(i) for i in _vsolve(x**5 - a - I*b, x)]
  474. x0 = z**(S(1)/5)
  475. x1 = sqrt(2)
  476. x2 = sqrt(5)
  477. x3 = sqrt(5 - x2)
  478. x4 = I*x2
  479. x5 = x4 + I
  480. x6 = I*x0/4
  481. x7 = x1*sqrt(x2 + 5)
  482. sol = [x0, -x6*(x1*x3 - x5), x6*(x1*x3 + x5), -x6*(x4 + x7 - I), x6*(-x4 + x7 + I)]
  483. R1 = R1.as_real_imag()
  484. R2 = R2.as_real_imag()
  485. R3 = R3.as_real_imag()
  486. R4 = R4.as_real_imag()
  487. for i, s in enumerate(sol):
  488. Res[1][i] = _quintic_simplify(s.xreplace({z: R1[0] + I*R1[1]}))
  489. Res[2][i] = _quintic_simplify(s.xreplace({z: R2[0] + I*R2[1]}))
  490. Res[3][i] = _quintic_simplify(s.xreplace({z: R3[0] + I*R3[1]}))
  491. Res[4][i] = _quintic_simplify(s.xreplace({z: R4[0] + I*R4[1]}))
  492. for i in range(1, 5):
  493. for j in range(5):
  494. Res_n[i][j] = Res[i][j].n()
  495. Res[i][j] = _quintic_simplify(Res[i][j])
  496. r1 = Res[1][0]
  497. r1_n = Res_n[1][0]
  498. for i in range(5):
  499. if comp(im(r1_n*Res_n[4][i]), 0, tol):
  500. r4 = Res[4][i]
  501. break
  502. # Now we have various Res values. Each will be a list of five
  503. # values. We have to pick one r value from those five for each Res
  504. u, v = quintic.uv(theta, d)
  505. testplus = (u + v*delta*sqrt(5)).n()
  506. testminus = (u - v*delta*sqrt(5)).n()
  507. # Evaluated numbers suffixed with _n
  508. # We will use evaluated numbers for calculation. Much faster.
  509. r4_n = r4.n()
  510. r2 = r3 = None
  511. for i in range(5):
  512. r2temp_n = Res_n[2][i]
  513. for j in range(5):
  514. # Again storing away the exact number and using
  515. # evaluated numbers in computations
  516. r3temp_n = Res_n[3][j]
  517. if (comp((r1_n*r2temp_n**2 + r4_n*r3temp_n**2 - testplus).n(), 0, tol) and
  518. comp((r3temp_n*r1_n**2 + r2temp_n*r4_n**2 - testminus).n(), 0, tol)):
  519. r2 = Res[2][i]
  520. r3 = Res[3][j]
  521. break
  522. if r2 is not None:
  523. break
  524. else:
  525. return [] # fall back to normal solve
  526. # Now, we have r's so we can get roots
  527. x1 = (r1 + r2 + r3 + r4)/5
  528. x2 = (r1*zeta4 + r2*zeta3 + r3*zeta2 + r4*zeta1)/5
  529. x3 = (r1*zeta3 + r2*zeta1 + r3*zeta4 + r4*zeta2)/5
  530. x4 = (r1*zeta2 + r2*zeta4 + r3*zeta1 + r4*zeta3)/5
  531. x5 = (r1*zeta1 + r2*zeta2 + r3*zeta3 + r4*zeta4)/5
  532. result = [x1, x2, x3, x4, x5]
  533. # Now check if solutions are distinct
  534. saw = set()
  535. for r in result:
  536. r = r.n(2)
  537. if r in saw:
  538. # Roots were identical. Abort, return []
  539. # and fall back to usual solve
  540. return []
  541. saw.add(r)
  542. # Restore to original equation where coeff_4 is nonzero
  543. if coeff_4:
  544. result = [x - coeff_4 / 5 for x in result]
  545. return result
  546. def _quintic_simplify(expr):
  547. from sympy.simplify.simplify import powsimp
  548. expr = powsimp(expr)
  549. expr = cancel(expr)
  550. return together(expr)
  551. def _integer_basis(poly):
  552. """Compute coefficient basis for a polynomial over integers.
  553. Returns the integer ``div`` such that substituting ``x = div*y``
  554. ``p(x) = m*q(y)`` where the coefficients of ``q`` are smaller
  555. than those of ``p``.
  556. For example ``x**5 + 512*x + 1024 = 0``
  557. with ``div = 4`` becomes ``y**5 + 2*y + 1 = 0``
  558. Returns the integer ``div`` or ``None`` if there is no possible scaling.
  559. Examples
  560. ========
  561. >>> from sympy.polys import Poly
  562. >>> from sympy.abc import x
  563. >>> from sympy.polys.polyroots import _integer_basis
  564. >>> p = Poly(x**5 + 512*x + 1024, x, domain='ZZ')
  565. >>> _integer_basis(p)
  566. 4
  567. """
  568. monoms, coeffs = list(zip(*poly.terms()))
  569. monoms, = list(zip(*monoms))
  570. coeffs = list(map(abs, coeffs))
  571. if coeffs[0] < coeffs[-1]:
  572. coeffs = list(reversed(coeffs))
  573. n = monoms[0]
  574. monoms = [n - i for i in reversed(monoms)]
  575. else:
  576. return None
  577. monoms = monoms[:-1]
  578. coeffs = coeffs[:-1]
  579. # Special case for two-term polynominals
  580. if len(monoms) == 1:
  581. r = Pow(coeffs[0], S.One/monoms[0])
  582. if r.is_Integer:
  583. return int(r)
  584. else:
  585. return None
  586. divs = reversed(divisors(gcd_list(coeffs))[1:])
  587. try:
  588. div = next(divs)
  589. except StopIteration:
  590. return None
  591. while True:
  592. for monom, coeff in zip(monoms, coeffs):
  593. if coeff % div**monom != 0:
  594. try:
  595. div = next(divs)
  596. except StopIteration:
  597. return None
  598. else:
  599. break
  600. else:
  601. return div
  602. def preprocess_roots(poly):
  603. """Try to get rid of symbolic coefficients from ``poly``. """
  604. coeff = S.One
  605. poly_func = poly.func
  606. try:
  607. _, poly = poly.clear_denoms(convert=True)
  608. except DomainError:
  609. return coeff, poly
  610. poly = poly.primitive()[1]
  611. poly = poly.retract()
  612. # TODO: This is fragile. Figure out how to make this independent of construct_domain().
  613. if poly.get_domain().is_Poly and all(c.is_term for c in poly.rep.coeffs()):
  614. poly = poly.inject()
  615. strips = list(zip(*poly.monoms()))
  616. gens = list(poly.gens[1:])
  617. base, strips = strips[0], strips[1:]
  618. for gen, strip in zip(list(gens), strips):
  619. reverse = False
  620. if strip[0] < strip[-1]:
  621. strip = reversed(strip)
  622. reverse = True
  623. ratio = None
  624. for a, b in zip(base, strip):
  625. if not a and not b:
  626. continue
  627. elif not a or not b:
  628. break
  629. elif b % a != 0:
  630. break
  631. else:
  632. _ratio = b // a
  633. if ratio is None:
  634. ratio = _ratio
  635. elif ratio != _ratio:
  636. break
  637. else:
  638. if reverse:
  639. ratio = -ratio
  640. poly = poly.eval(gen, 1)
  641. coeff *= gen**(-ratio)
  642. gens.remove(gen)
  643. if gens:
  644. poly = poly.eject(*gens)
  645. if poly.is_univariate and poly.get_domain().is_ZZ:
  646. basis = _integer_basis(poly)
  647. if basis is not None:
  648. n = poly.degree()
  649. def func(k, coeff):
  650. return coeff//basis**(n - k[0])
  651. poly = poly.termwise(func)
  652. coeff *= basis
  653. if not isinstance(poly, poly_func):
  654. poly = poly_func(poly)
  655. return coeff, poly
  656. @public
  657. def roots(f, *gens,
  658. auto=True,
  659. cubics=True,
  660. trig=False,
  661. quartics=True,
  662. quintics=False,
  663. multiple=False,
  664. filter=None,
  665. predicate=None,
  666. strict=False,
  667. **flags):
  668. """
  669. Computes symbolic roots of a univariate polynomial.
  670. Given a univariate polynomial f with symbolic coefficients (or
  671. a list of the polynomial's coefficients), returns a dictionary
  672. with its roots and their multiplicities.
  673. Only roots expressible via radicals will be returned. To get
  674. a complete set of roots use RootOf class or numerical methods
  675. instead. By default cubic and quartic formulas are used in
  676. the algorithm. To disable them because of unreadable output
  677. set ``cubics=False`` or ``quartics=False`` respectively. If cubic
  678. roots are real but are expressed in terms of complex numbers
  679. (casus irreducibilis [1]) the ``trig`` flag can be set to True to
  680. have the solutions returned in terms of cosine and inverse cosine
  681. functions.
  682. To get roots from a specific domain set the ``filter`` flag with
  683. one of the following specifiers: Z, Q, R, I, C. By default all
  684. roots are returned (this is equivalent to setting ``filter='C'``).
  685. By default a dictionary is returned giving a compact result in
  686. case of multiple roots. However to get a list containing all
  687. those roots set the ``multiple`` flag to True; the list will
  688. have identical roots appearing next to each other in the result.
  689. (For a given Poly, the all_roots method will give the roots in
  690. sorted numerical order.)
  691. If the ``strict`` flag is True, ``UnsolvableFactorError`` will be
  692. raised if the roots found are known to be incomplete (because
  693. some roots are not expressible in radicals).
  694. Examples
  695. ========
  696. >>> from sympy import Poly, roots, degree
  697. >>> from sympy.abc import x, y
  698. >>> roots(x**2 - 1, x)
  699. {-1: 1, 1: 1}
  700. >>> p = Poly(x**2-1, x)
  701. >>> roots(p)
  702. {-1: 1, 1: 1}
  703. >>> p = Poly(x**2-y, x, y)
  704. >>> roots(Poly(p, x))
  705. {-sqrt(y): 1, sqrt(y): 1}
  706. >>> roots(x**2 - y, x)
  707. {-sqrt(y): 1, sqrt(y): 1}
  708. >>> roots([1, 0, -1])
  709. {-1: 1, 1: 1}
  710. ``roots`` will only return roots expressible in radicals. If
  711. the given polynomial has some or all of its roots inexpressible in
  712. radicals, the result of ``roots`` will be incomplete or empty
  713. respectively.
  714. Example where result is incomplete:
  715. >>> roots((x-1)*(x**5-x+1), x)
  716. {1: 1}
  717. In this case, the polynomial has an unsolvable quintic factor
  718. whose roots cannot be expressed by radicals. The polynomial has a
  719. rational root (due to the factor `(x-1)`), which is returned since
  720. ``roots`` always finds all rational roots.
  721. Example where result is empty:
  722. >>> roots(x**7-3*x**2+1, x)
  723. {}
  724. Here, the polynomial has no roots expressible in radicals, so
  725. ``roots`` returns an empty dictionary.
  726. The result produced by ``roots`` is complete if and only if the
  727. sum of the multiplicity of each root is equal to the degree of
  728. the polynomial. If strict=True, UnsolvableFactorError will be
  729. raised if the result is incomplete.
  730. The result can be be checked for completeness as follows:
  731. >>> f = x**3-2*x**2+1
  732. >>> sum(roots(f, x).values()) == degree(f, x)
  733. True
  734. >>> f = (x-1)*(x**5-x+1)
  735. >>> sum(roots(f, x).values()) == degree(f, x)
  736. False
  737. References
  738. ==========
  739. .. [1] https://en.wikipedia.org/wiki/Cubic_equation#Trigonometric_and_hyperbolic_solutions
  740. """
  741. from sympy.polys.polytools import to_rational_coeffs
  742. flags = dict(flags)
  743. if isinstance(f, list):
  744. if gens:
  745. raise ValueError('redundant generators given')
  746. x = Dummy('x')
  747. poly, i = {}, len(f) - 1
  748. for coeff in f:
  749. poly[i], i = sympify(coeff), i - 1
  750. f = Poly(poly, x, field=True)
  751. else:
  752. try:
  753. F = Poly(f, *gens, **flags)
  754. if not isinstance(f, Poly) and not F.gen.is_Symbol:
  755. raise PolynomialError("generator must be a Symbol")
  756. f = F
  757. except GeneratorsNeeded:
  758. if multiple:
  759. return []
  760. else:
  761. return {}
  762. else:
  763. n = f.degree()
  764. if f.length() == 2 and n > 2:
  765. # check for foo**n in constant if dep is c*gen**m
  766. con, dep = f.as_expr().as_independent(*f.gens)
  767. fcon = -(-con).factor()
  768. if fcon != con:
  769. con = fcon
  770. bases = []
  771. for i in Mul.make_args(con):
  772. if i.is_Pow:
  773. b, e = i.as_base_exp()
  774. if e.is_Integer and b.is_Add:
  775. bases.append((b, Dummy(positive=True)))
  776. if bases:
  777. rv = roots(Poly((dep + con).xreplace(dict(bases)),
  778. *f.gens), *F.gens,
  779. auto=auto,
  780. cubics=cubics,
  781. trig=trig,
  782. quartics=quartics,
  783. quintics=quintics,
  784. multiple=multiple,
  785. filter=filter,
  786. predicate=predicate,
  787. **flags)
  788. return {factor_terms(k.xreplace(
  789. {v: k for k, v in bases})
  790. ): v for k, v in rv.items()}
  791. if f.is_multivariate:
  792. raise PolynomialError('multivariate polynomials are not supported')
  793. def _update_dict(result, zeros, currentroot, k):
  794. if currentroot == S.Zero:
  795. if S.Zero in zeros:
  796. zeros[S.Zero] += k
  797. else:
  798. zeros[S.Zero] = k
  799. if currentroot in result:
  800. result[currentroot] += k
  801. else:
  802. result[currentroot] = k
  803. def _try_decompose(f):
  804. """Find roots using functional decomposition. """
  805. factors, roots = f.decompose(), []
  806. for currentroot in _try_heuristics(factors[0]):
  807. roots.append(currentroot)
  808. for currentfactor in factors[1:]:
  809. previous, roots = list(roots), []
  810. for currentroot in previous:
  811. g = currentfactor - Poly(currentroot, f.gen)
  812. for currentroot in _try_heuristics(g):
  813. roots.append(currentroot)
  814. return roots
  815. def _try_heuristics(f):
  816. """Find roots using formulas and some tricks. """
  817. if f.is_ground:
  818. return []
  819. if f.is_monomial:
  820. return [S.Zero]*f.degree()
  821. if f.length() == 2:
  822. if f.degree() == 1:
  823. return list(map(cancel, roots_linear(f)))
  824. else:
  825. return roots_binomial(f)
  826. result = []
  827. for i in [-1, 1]:
  828. if not f.eval(i):
  829. f = f.quo(Poly(f.gen - i, f.gen))
  830. result.append(i)
  831. break
  832. n = f.degree()
  833. if n == 1:
  834. result += list(map(cancel, roots_linear(f)))
  835. elif n == 2:
  836. result += list(map(cancel, roots_quadratic(f)))
  837. elif f.is_cyclotomic:
  838. result += roots_cyclotomic(f)
  839. elif n == 3 and cubics:
  840. result += roots_cubic(f, trig=trig)
  841. elif n == 4 and quartics:
  842. result += roots_quartic(f)
  843. elif n == 5 and quintics:
  844. result += roots_quintic(f)
  845. return result
  846. # Convert the generators to symbols
  847. dumgens = symbols('x:%d' % len(f.gens), cls=Dummy)
  848. f = f.per(f.rep, dumgens)
  849. (k,), f = f.terms_gcd()
  850. if not k:
  851. zeros = {}
  852. else:
  853. zeros = {S.Zero: k}
  854. coeff, f = preprocess_roots(f)
  855. if auto and f.get_domain().is_Ring:
  856. f = f.to_field()
  857. # Use EX instead of ZZ_I or QQ_I
  858. if f.get_domain().is_QQ_I:
  859. f = f.per(f.rep.convert(EX))
  860. rescale_x = None
  861. translate_x = None
  862. result = {}
  863. if not f.is_ground:
  864. dom = f.get_domain()
  865. if not dom.is_Exact and dom.is_Numerical:
  866. for r in f.nroots():
  867. _update_dict(result, zeros, r, 1)
  868. elif f.degree() == 1:
  869. _update_dict(result, zeros, roots_linear(f)[0], 1)
  870. elif f.length() == 2:
  871. roots_fun = roots_quadratic if f.degree() == 2 else roots_binomial
  872. for r in roots_fun(f):
  873. _update_dict(result, zeros, r, 1)
  874. else:
  875. _, factors = Poly(f.as_expr()).factor_list()
  876. if len(factors) == 1 and f.degree() == 2:
  877. for r in roots_quadratic(f):
  878. _update_dict(result, zeros, r, 1)
  879. else:
  880. if len(factors) == 1 and factors[0][1] == 1:
  881. if f.get_domain().is_EX:
  882. res = to_rational_coeffs(f)
  883. if res:
  884. if res[0] is None:
  885. translate_x, f = res[2:]
  886. else:
  887. rescale_x, f = res[1], res[-1]
  888. result = roots(f)
  889. if not result:
  890. for currentroot in _try_decompose(f):
  891. _update_dict(result, zeros, currentroot, 1)
  892. else:
  893. for r in _try_heuristics(f):
  894. _update_dict(result, zeros, r, 1)
  895. else:
  896. for currentroot in _try_decompose(f):
  897. _update_dict(result, zeros, currentroot, 1)
  898. else:
  899. for currentfactor, k in factors:
  900. for r in _try_heuristics(Poly(currentfactor, f.gen, field=True)):
  901. _update_dict(result, zeros, r, k)
  902. if coeff is not S.One:
  903. _result, result, = result, {}
  904. for currentroot, k in _result.items():
  905. result[coeff*currentroot] = k
  906. if filter not in [None, 'C']:
  907. handlers = {
  908. 'Z': lambda r: r.is_Integer,
  909. 'Q': lambda r: r.is_Rational,
  910. 'R': lambda r: all(a.is_real for a in r.as_numer_denom()),
  911. 'I': lambda r: r.is_imaginary,
  912. }
  913. try:
  914. query = handlers[filter]
  915. except KeyError:
  916. raise ValueError("Invalid filter: %s" % filter)
  917. for zero in dict(result).keys():
  918. if not query(zero):
  919. del result[zero]
  920. if predicate is not None:
  921. for zero in dict(result).keys():
  922. if not predicate(zero):
  923. del result[zero]
  924. if rescale_x:
  925. result1 = {}
  926. for k, v in result.items():
  927. result1[k*rescale_x] = v
  928. result = result1
  929. if translate_x:
  930. result1 = {}
  931. for k, v in result.items():
  932. result1[k + translate_x] = v
  933. result = result1
  934. # adding zero roots after non-trivial roots have been translated
  935. result.update(zeros)
  936. if strict and sum(result.values()) < f.degree():
  937. raise UnsolvableFactorError(filldedent('''
  938. Strict mode: some factors cannot be solved in radicals, so
  939. a complete list of solutions cannot be returned. Call
  940. roots with strict=False to get solutions expressible in
  941. radicals (if there are any).
  942. '''))
  943. if not multiple:
  944. return result
  945. else:
  946. zeros = []
  947. for zero in ordered(result):
  948. zeros.extend([zero]*result[zero])
  949. return zeros
  950. def root_factors(f, *gens, filter=None, **args):
  951. """
  952. Returns all factors of a univariate polynomial.
  953. Examples
  954. ========
  955. >>> from sympy.abc import x, y
  956. >>> from sympy.polys.polyroots import root_factors
  957. >>> root_factors(x**2 - y, x)
  958. [x - sqrt(y), x + sqrt(y)]
  959. """
  960. args = dict(args)
  961. F = Poly(f, *gens, **args)
  962. if not F.is_Poly:
  963. return [f]
  964. if F.is_multivariate:
  965. raise ValueError('multivariate polynomials are not supported')
  966. x = F.gens[0]
  967. zeros = roots(F, filter=filter)
  968. if not zeros:
  969. factors = [F]
  970. else:
  971. factors, N = [], 0
  972. for r, n in ordered(zeros.items()):
  973. factors, N = factors + [Poly(x - r, x)]*n, N + n
  974. if N < F.degree():
  975. G = reduce(lambda p, q: p*q, factors)
  976. factors.append(F.quo(G))
  977. if not isinstance(f, Poly):
  978. factors = [ f.as_expr() for f in factors ]
  979. return factors