minpoly.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882
  1. """Minimal polynomials for algebraic numbers."""
  2. from functools import reduce
  3. from sympy.core.add import Add
  4. from sympy.core.exprtools import Factors
  5. from sympy.core.function import expand_mul, expand_multinomial, _mexpand
  6. from sympy.core.mul import Mul
  7. from sympy.core.numbers import (I, Rational, pi, _illegal)
  8. from sympy.core.singleton import S
  9. from sympy.core.symbol import Dummy
  10. from sympy.core.sympify import sympify
  11. from sympy.core.traversal import preorder_traversal
  12. from sympy.functions.elementary.exponential import exp
  13. from sympy.functions.elementary.miscellaneous import sqrt, cbrt
  14. from sympy.functions.elementary.trigonometric import cos, sin, tan
  15. from sympy.ntheory.factor_ import divisors
  16. from sympy.utilities.iterables import subsets
  17. from sympy.polys.domains import ZZ, QQ, FractionField
  18. from sympy.polys.orthopolys import dup_chebyshevt
  19. from sympy.polys.polyerrors import (
  20. NotAlgebraic,
  21. GeneratorsError,
  22. )
  23. from sympy.polys.polytools import (
  24. Poly, PurePoly, invert, factor_list, groebner, resultant,
  25. degree, poly_from_expr, parallel_poly_from_expr, lcm
  26. )
  27. from sympy.polys.polyutils import dict_from_expr, expr_from_dict
  28. from sympy.polys.ring_series import rs_compose_add
  29. from sympy.polys.rings import ring
  30. from sympy.polys.rootoftools import CRootOf
  31. from sympy.polys.specialpolys import cyclotomic_poly
  32. from sympy.utilities import (
  33. numbered_symbols, public, sift
  34. )
  35. def _choose_factor(factors, x, v, dom=QQ, prec=200, bound=5):
  36. """
  37. Return a factor having root ``v``
  38. It is assumed that one of the factors has root ``v``.
  39. """
  40. if isinstance(factors[0], tuple):
  41. factors = [f[0] for f in factors]
  42. if len(factors) == 1:
  43. return factors[0]
  44. prec1 = 10
  45. points = {}
  46. symbols = dom.symbols if hasattr(dom, 'symbols') else []
  47. while prec1 <= prec:
  48. # when dealing with non-Rational numbers we usually evaluate
  49. # with `subs` argument but we only need a ballpark evaluation
  50. fe = [f.as_expr().xreplace({x:v}) for f in factors]
  51. if v.is_number:
  52. fe = [f.n(prec) for f in fe]
  53. # assign integers [0, n) to symbols (if any)
  54. for n in subsets(range(bound), k=len(symbols), repetition=True):
  55. for s, i in zip(symbols, n):
  56. points[s] = i
  57. # evaluate the expression at these points
  58. candidates = [(abs(f.subs(points).n(prec1)), i)
  59. for i,f in enumerate(fe)]
  60. # if we get invalid numbers (e.g. from division by zero)
  61. # we try again
  62. if any(i in _illegal for i, _ in candidates):
  63. continue
  64. # find the smallest two -- if they differ significantly
  65. # then we assume we have found the factor that becomes
  66. # 0 when v is substituted into it
  67. can = sorted(candidates)
  68. (a, ix), (b, _) = can[:2]
  69. if b > a * 10**6: # XXX what to use?
  70. return factors[ix]
  71. prec1 *= 2
  72. raise NotImplementedError("multiple candidates for the minimal polynomial of %s" % v)
  73. def _is_sum_surds(p):
  74. return all(f.is_Rational or f.is_Pow and
  75. f.base.is_Rational and (2*f.exp).is_Integer and f.is_extended_real
  76. for t in Add.make_args(p) for f in Mul.make_args(t))
  77. def _separate_sq(p):
  78. """
  79. helper function for ``_minimal_polynomial_sq``
  80. It selects a rational ``g`` such that the polynomial ``p``
  81. consists of a sum of terms whose surds squared have gcd equal to ``g``
  82. and a sum of terms with surds squared prime with ``g``;
  83. then it takes the field norm to eliminate ``sqrt(g)``
  84. See simplify.simplify.split_surds and polytools.sqf_norm.
  85. Examples
  86. ========
  87. >>> from sympy import sqrt
  88. >>> from sympy.abc import x
  89. >>> from sympy.polys.numberfields.minpoly import _separate_sq
  90. >>> p= -x + sqrt(2) + sqrt(3) + sqrt(7)
  91. >>> p = _separate_sq(p); p
  92. -x**2 + 2*sqrt(3)*x + 2*sqrt(7)*x - 2*sqrt(21) - 8
  93. >>> p = _separate_sq(p); p
  94. -x**4 + 4*sqrt(7)*x**3 - 32*x**2 + 8*sqrt(7)*x + 20
  95. >>> p = _separate_sq(p); p
  96. -x**8 + 48*x**6 - 536*x**4 + 1728*x**2 - 400
  97. """
  98. def is_sqrt(expr):
  99. return expr.is_Pow and expr.exp is S.Half
  100. # p = c1*sqrt(q1) + ... + cn*sqrt(qn) -> a = [(c1, q1), .., (cn, qn)]
  101. a = []
  102. for y in p.args:
  103. if not y.is_Mul:
  104. if is_sqrt(y):
  105. a.append((S.One, y**2))
  106. elif y.is_Atom:
  107. a.append((y, S.One))
  108. elif y.is_Pow and y.exp.is_integer:
  109. a.append((y, S.One))
  110. else:
  111. raise NotImplementedError
  112. else:
  113. T, F = sift(y.args, is_sqrt, binary=True)
  114. a.append((Mul(*F), Mul(*T)**2))
  115. a.sort(key=lambda z: z[1])
  116. if a[-1][1] is S.One:
  117. # there are no surds
  118. return p
  119. surds = [z for y, z in a]
  120. for i in range(len(surds)):
  121. if surds[i] != 1:
  122. break
  123. from sympy.simplify.radsimp import _split_gcd
  124. g, b1, b2 = _split_gcd(*surds[i:])
  125. a1 = []
  126. a2 = []
  127. for y, z in a:
  128. if z in b1:
  129. a1.append(y*z**S.Half)
  130. else:
  131. a2.append(y*z**S.Half)
  132. p1 = Add(*a1)
  133. p2 = Add(*a2)
  134. p = _mexpand(p1**2) - _mexpand(p2**2)
  135. return p
  136. def _minimal_polynomial_sq(p, n, x):
  137. """
  138. Returns the minimal polynomial for the ``nth-root`` of a sum of surds
  139. or ``None`` if it fails.
  140. Parameters
  141. ==========
  142. p : sum of surds
  143. n : positive integer
  144. x : variable of the returned polynomial
  145. Examples
  146. ========
  147. >>> from sympy.polys.numberfields.minpoly import _minimal_polynomial_sq
  148. >>> from sympy import sqrt
  149. >>> from sympy.abc import x
  150. >>> q = 1 + sqrt(2) + sqrt(3)
  151. >>> _minimal_polynomial_sq(q, 3, x)
  152. x**12 - 4*x**9 - 4*x**6 + 16*x**3 - 8
  153. """
  154. p = sympify(p)
  155. n = sympify(n)
  156. if not n.is_Integer or not n > 0 or not _is_sum_surds(p):
  157. return None
  158. pn = p**Rational(1, n)
  159. # eliminate the square roots
  160. p -= x
  161. while 1:
  162. p1 = _separate_sq(p)
  163. if p1 is p:
  164. p = p1.subs({x:x**n})
  165. break
  166. else:
  167. p = p1
  168. # _separate_sq eliminates field extensions in a minimal way, so that
  169. # if n = 1 then `p = constant*(minimal_polynomial(p))`
  170. # if n > 1 it contains the minimal polynomial as a factor.
  171. if n == 1:
  172. p1 = Poly(p)
  173. if p.coeff(x**p1.degree(x)) < 0:
  174. p = -p
  175. p = p.primitive()[1]
  176. return p
  177. # by construction `p` has root `pn`
  178. # the minimal polynomial is the factor vanishing in x = pn
  179. factors = factor_list(p)[1]
  180. result = _choose_factor(factors, x, pn)
  181. return result
  182. def _minpoly_op_algebraic_element(op, ex1, ex2, x, dom, mp1=None, mp2=None):
  183. """
  184. return the minimal polynomial for ``op(ex1, ex2)``
  185. Parameters
  186. ==========
  187. op : operation ``Add`` or ``Mul``
  188. ex1, ex2 : expressions for the algebraic elements
  189. x : indeterminate of the polynomials
  190. dom: ground domain
  191. mp1, mp2 : minimal polynomials for ``ex1`` and ``ex2`` or None
  192. Examples
  193. ========
  194. >>> from sympy import sqrt, Add, Mul, QQ
  195. >>> from sympy.polys.numberfields.minpoly import _minpoly_op_algebraic_element
  196. >>> from sympy.abc import x, y
  197. >>> p1 = sqrt(sqrt(2) + 1)
  198. >>> p2 = sqrt(sqrt(2) - 1)
  199. >>> _minpoly_op_algebraic_element(Mul, p1, p2, x, QQ)
  200. x - 1
  201. >>> q1 = sqrt(y)
  202. >>> q2 = 1 / y
  203. >>> _minpoly_op_algebraic_element(Add, q1, q2, x, QQ.frac_field(y))
  204. x**2*y**2 - 2*x*y - y**3 + 1
  205. References
  206. ==========
  207. .. [1] https://en.wikipedia.org/wiki/Resultant
  208. .. [2] I.M. Isaacs, Proc. Amer. Math. Soc. 25 (1970), 638
  209. "Degrees of sums in a separable field extension".
  210. """
  211. y = Dummy(str(x))
  212. if mp1 is None:
  213. mp1 = _minpoly_compose(ex1, x, dom)
  214. if mp2 is None:
  215. mp2 = _minpoly_compose(ex2, y, dom)
  216. else:
  217. mp2 = mp2.subs({x: y})
  218. if op is Add:
  219. # mp1a = mp1.subs({x: x - y})
  220. if dom == QQ:
  221. R, X = ring('X', QQ)
  222. p1 = R(dict_from_expr(mp1)[0])
  223. p2 = R(dict_from_expr(mp2)[0])
  224. else:
  225. (p1, p2), _ = parallel_poly_from_expr((mp1, x - y), x, y)
  226. r = p1.compose(p2)
  227. mp1a = r.as_expr()
  228. elif op is Mul:
  229. mp1a = _muly(mp1, x, y)
  230. else:
  231. raise NotImplementedError('option not available')
  232. if op is Mul or dom != QQ:
  233. r = resultant(mp1a, mp2, gens=[y, x])
  234. else:
  235. r = rs_compose_add(p1, p2)
  236. r = expr_from_dict(r.as_expr_dict(), x)
  237. deg1 = degree(mp1, x)
  238. deg2 = degree(mp2, y)
  239. if op is Mul and deg1 == 1 or deg2 == 1:
  240. # if deg1 = 1, then mp1 = x - a; mp1a = x - y - a;
  241. # r = mp2(x - a), so that `r` is irreducible
  242. return r
  243. r = Poly(r, x, domain=dom)
  244. _, factors = r.factor_list()
  245. res = _choose_factor(factors, x, op(ex1, ex2), dom)
  246. return res.as_expr()
  247. def _invertx(p, x):
  248. """
  249. Returns ``expand_mul(x**degree(p, x)*p.subs(x, 1/x))``
  250. """
  251. p1 = poly_from_expr(p, x)[0]
  252. n = degree(p1)
  253. a = [c * x**(n - i) for (i,), c in p1.terms()]
  254. return Add(*a)
  255. def _muly(p, x, y):
  256. """
  257. Returns ``_mexpand(y**deg*p.subs({x:x / y}))``
  258. """
  259. p1 = poly_from_expr(p, x)[0]
  260. n = degree(p1)
  261. a = [c * x**i * y**(n - i) for (i,), c in p1.terms()]
  262. return Add(*a)
  263. def _minpoly_pow(ex, pw, x, dom, mp=None):
  264. """
  265. Returns ``minpoly(ex**pw, x)``
  266. Parameters
  267. ==========
  268. ex : algebraic element
  269. pw : rational number
  270. x : indeterminate of the polynomial
  271. dom: ground domain
  272. mp : minimal polynomial of ``p``
  273. Examples
  274. ========
  275. >>> from sympy import sqrt, QQ, Rational
  276. >>> from sympy.polys.numberfields.minpoly import _minpoly_pow, minpoly
  277. >>> from sympy.abc import x, y
  278. >>> p = sqrt(1 + sqrt(2))
  279. >>> _minpoly_pow(p, 2, x, QQ)
  280. x**2 - 2*x - 1
  281. >>> minpoly(p**2, x)
  282. x**2 - 2*x - 1
  283. >>> _minpoly_pow(y, Rational(1, 3), x, QQ.frac_field(y))
  284. x**3 - y
  285. >>> minpoly(y**Rational(1, 3), x)
  286. x**3 - y
  287. """
  288. pw = sympify(pw)
  289. if not mp:
  290. mp = _minpoly_compose(ex, x, dom)
  291. if not pw.is_rational:
  292. raise NotAlgebraic("%s does not seem to be an algebraic element" % ex)
  293. if pw < 0:
  294. if mp == x:
  295. raise ZeroDivisionError('%s is zero' % ex)
  296. mp = _invertx(mp, x)
  297. if pw == -1:
  298. return mp
  299. pw = -pw
  300. ex = 1/ex
  301. y = Dummy(str(x))
  302. mp = mp.subs({x: y})
  303. n, d = pw.as_numer_denom()
  304. res = Poly(resultant(mp, x**d - y**n, gens=[y]), x, domain=dom)
  305. _, factors = res.factor_list()
  306. res = _choose_factor(factors, x, ex**pw, dom)
  307. return res.as_expr()
  308. def _minpoly_add(x, dom, *a):
  309. """
  310. returns ``minpoly(Add(*a), dom, x)``
  311. """
  312. mp = _minpoly_op_algebraic_element(Add, a[0], a[1], x, dom)
  313. p = a[0] + a[1]
  314. for px in a[2:]:
  315. mp = _minpoly_op_algebraic_element(Add, p, px, x, dom, mp1=mp)
  316. p = p + px
  317. return mp
  318. def _minpoly_mul(x, dom, *a):
  319. """
  320. returns ``minpoly(Mul(*a), dom, x)``
  321. """
  322. mp = _minpoly_op_algebraic_element(Mul, a[0], a[1], x, dom)
  323. p = a[0] * a[1]
  324. for px in a[2:]:
  325. mp = _minpoly_op_algebraic_element(Mul, p, px, x, dom, mp1=mp)
  326. p = p * px
  327. return mp
  328. def _minpoly_sin(ex, x):
  329. """
  330. Returns the minimal polynomial of ``sin(ex)``
  331. see https://mathworld.wolfram.com/TrigonometryAngles.html
  332. """
  333. c, a = ex.args[0].as_coeff_Mul()
  334. if a is pi:
  335. if c.is_rational:
  336. n = c.q
  337. q = sympify(n)
  338. if q.is_prime:
  339. # for a = pi*p/q with q odd prime, using chebyshevt
  340. # write sin(q*a) = mp(sin(a))*sin(a);
  341. # the roots of mp(x) are sin(pi*p/q) for p = 1,..., q - 1
  342. a = dup_chebyshevt(n, ZZ)
  343. return Add(*[x**(n - i - 1)*a[i] for i in range(n)])
  344. if c.p == 1:
  345. if q == 9:
  346. return 64*x**6 - 96*x**4 + 36*x**2 - 3
  347. if n % 2 == 1:
  348. # for a = pi*p/q with q odd, use
  349. # sin(q*a) = 0 to see that the minimal polynomial must be
  350. # a factor of dup_chebyshevt(n, ZZ)
  351. a = dup_chebyshevt(n, ZZ)
  352. a = [x**(n - i)*a[i] for i in range(n + 1)]
  353. r = Add(*a)
  354. _, factors = factor_list(r)
  355. res = _choose_factor(factors, x, ex)
  356. return res
  357. expr = ((1 - cos(2*c*pi))/2)**S.Half
  358. res = _minpoly_compose(expr, x, QQ)
  359. return res
  360. raise NotAlgebraic("%s does not seem to be an algebraic element" % ex)
  361. def _minpoly_cos(ex, x):
  362. """
  363. Returns the minimal polynomial of ``cos(ex)``
  364. see https://mathworld.wolfram.com/TrigonometryAngles.html
  365. """
  366. c, a = ex.args[0].as_coeff_Mul()
  367. if a is pi:
  368. if c.is_rational:
  369. if c.p == 1:
  370. if c.q == 7:
  371. return 8*x**3 - 4*x**2 - 4*x + 1
  372. if c.q == 9:
  373. return 8*x**3 - 6*x - 1
  374. elif c.p == 2:
  375. q = sympify(c.q)
  376. if q.is_prime:
  377. s = _minpoly_sin(ex, x)
  378. return _mexpand(s.subs({x:sqrt((1 - x)/2)}))
  379. # for a = pi*p/q, cos(q*a) =T_q(cos(a)) = (-1)**p
  380. n = int(c.q)
  381. a = dup_chebyshevt(n, ZZ)
  382. a = [x**(n - i)*a[i] for i in range(n + 1)]
  383. r = Add(*a) - (-1)**c.p
  384. _, factors = factor_list(r)
  385. res = _choose_factor(factors, x, ex)
  386. return res
  387. raise NotAlgebraic("%s does not seem to be an algebraic element" % ex)
  388. def _minpoly_tan(ex, x):
  389. """
  390. Returns the minimal polynomial of ``tan(ex)``
  391. see https://github.com/sympy/sympy/issues/21430
  392. """
  393. c, a = ex.args[0].as_coeff_Mul()
  394. if a is pi:
  395. if c.is_rational:
  396. c = c * 2
  397. n = int(c.q)
  398. a = n if c.p % 2 == 0 else 1
  399. terms = []
  400. for k in range((c.p+1)%2, n+1, 2):
  401. terms.append(a*x**k)
  402. a = -(a*(n-k-1)*(n-k)) // ((k+1)*(k+2))
  403. r = Add(*terms)
  404. _, factors = factor_list(r)
  405. res = _choose_factor(factors, x, ex)
  406. return res
  407. raise NotAlgebraic("%s does not seem to be an algebraic element" % ex)
  408. def _minpoly_exp(ex, x):
  409. """
  410. Returns the minimal polynomial of ``exp(ex)``
  411. """
  412. c, a = ex.args[0].as_coeff_Mul()
  413. if a == I*pi:
  414. if c.is_rational:
  415. q = sympify(c.q)
  416. if c.p == 1 or c.p == -1:
  417. if q == 3:
  418. return x**2 - x + 1
  419. if q == 4:
  420. return x**4 + 1
  421. if q == 6:
  422. return x**4 - x**2 + 1
  423. if q == 8:
  424. return x**8 + 1
  425. if q == 9:
  426. return x**6 - x**3 + 1
  427. if q == 10:
  428. return x**8 - x**6 + x**4 - x**2 + 1
  429. if q.is_prime:
  430. s = 0
  431. for i in range(q):
  432. s += (-x)**i
  433. return s
  434. # x**(2*q) = product(factors)
  435. factors = [cyclotomic_poly(i, x) for i in divisors(2*q)]
  436. mp = _choose_factor(factors, x, ex)
  437. return mp
  438. else:
  439. raise NotAlgebraic("%s does not seem to be an algebraic element" % ex)
  440. raise NotAlgebraic("%s does not seem to be an algebraic element" % ex)
  441. def _minpoly_rootof(ex, x):
  442. """
  443. Returns the minimal polynomial of a ``CRootOf`` object.
  444. """
  445. p = ex.expr
  446. p = p.subs({ex.poly.gens[0]:x})
  447. _, factors = factor_list(p, x)
  448. result = _choose_factor(factors, x, ex)
  449. return result
  450. def _minpoly_compose(ex, x, dom):
  451. """
  452. Computes the minimal polynomial of an algebraic element
  453. using operations on minimal polynomials
  454. Examples
  455. ========
  456. >>> from sympy import minimal_polynomial, sqrt, Rational
  457. >>> from sympy.abc import x, y
  458. >>> minimal_polynomial(sqrt(2) + 3*Rational(1, 3), x, compose=True)
  459. x**2 - 2*x - 1
  460. >>> minimal_polynomial(sqrt(y) + 1/y, x, compose=True)
  461. x**2*y**2 - 2*x*y - y**3 + 1
  462. """
  463. if ex.is_Rational:
  464. return ex.q*x - ex.p
  465. if ex is I:
  466. _, factors = factor_list(x**2 + 1, x, domain=dom)
  467. return x**2 + 1 if len(factors) == 1 else x - I
  468. if ex is S.GoldenRatio:
  469. _, factors = factor_list(x**2 - x - 1, x, domain=dom)
  470. if len(factors) == 1:
  471. return x**2 - x - 1
  472. else:
  473. return _choose_factor(factors, x, (1 + sqrt(5))/2, dom=dom)
  474. if ex is S.TribonacciConstant:
  475. _, factors = factor_list(x**3 - x**2 - x - 1, x, domain=dom)
  476. if len(factors) == 1:
  477. return x**3 - x**2 - x - 1
  478. else:
  479. fac = (1 + cbrt(19 - 3*sqrt(33)) + cbrt(19 + 3*sqrt(33))) / 3
  480. return _choose_factor(factors, x, fac, dom=dom)
  481. if hasattr(dom, 'symbols') and ex in dom.symbols:
  482. return x - ex
  483. if dom.is_QQ and _is_sum_surds(ex):
  484. # eliminate the square roots
  485. v = ex
  486. ex -= x
  487. while 1:
  488. ex1 = _separate_sq(ex)
  489. if ex1 is ex:
  490. return _choose_factor(factor_list(ex)[1], x, v)
  491. else:
  492. ex = ex1
  493. if ex.is_Add:
  494. res = _minpoly_add(x, dom, *ex.args)
  495. elif ex.is_Mul:
  496. f = Factors(ex).factors
  497. r = sift(f.items(), lambda itx: itx[0].is_Rational and itx[1].is_Rational)
  498. if r[True] and dom == QQ:
  499. ex1 = Mul(*[bx**ex for bx, ex in r[False] + r[None]])
  500. r1 = dict(r[True])
  501. dens = [y.q for y in r1.values()]
  502. lcmdens = reduce(lcm, dens, 1)
  503. neg1 = S.NegativeOne
  504. expn1 = r1.pop(neg1, S.Zero)
  505. nums = [base**(y.p*lcmdens // y.q) for base, y in r1.items()]
  506. ex2 = Mul(*nums)
  507. mp1 = minimal_polynomial(ex1, x)
  508. # use the fact that in SymPy canonicalization products of integers
  509. # raised to rational powers are organized in relatively prime
  510. # bases, and that in ``base**(n/d)`` a perfect power is
  511. # simplified with the root
  512. # Powers of -1 have to be treated separately to preserve sign.
  513. mp2 = ex2.q*x**lcmdens - ex2.p*neg1**(expn1*lcmdens)
  514. ex2 = neg1**expn1 * ex2**Rational(1, lcmdens)
  515. res = _minpoly_op_algebraic_element(Mul, ex1, ex2, x, dom, mp1=mp1, mp2=mp2)
  516. else:
  517. res = _minpoly_mul(x, dom, *ex.args)
  518. elif ex.is_Pow:
  519. res = _minpoly_pow(ex.base, ex.exp, x, dom)
  520. elif ex.__class__ is sin:
  521. res = _minpoly_sin(ex, x)
  522. elif ex.__class__ is cos:
  523. res = _minpoly_cos(ex, x)
  524. elif ex.__class__ is tan:
  525. res = _minpoly_tan(ex, x)
  526. elif ex.__class__ is exp:
  527. res = _minpoly_exp(ex, x)
  528. elif ex.__class__ is CRootOf:
  529. res = _minpoly_rootof(ex, x)
  530. else:
  531. raise NotAlgebraic("%s does not seem to be an algebraic element" % ex)
  532. return res
  533. @public
  534. def minimal_polynomial(ex, x=None, compose=True, polys=False, domain=None):
  535. """
  536. Computes the minimal polynomial of an algebraic element.
  537. Parameters
  538. ==========
  539. ex : Expr
  540. Element or expression whose minimal polynomial is to be calculated.
  541. x : Symbol, optional
  542. Independent variable of the minimal polynomial
  543. compose : boolean, optional (default=True)
  544. Method to use for computing minimal polynomial. If ``compose=True``
  545. (default) then ``_minpoly_compose`` is used, if ``compose=False`` then
  546. groebner bases are used.
  547. polys : boolean, optional (default=False)
  548. If ``True`` returns a ``Poly`` object else an ``Expr`` object.
  549. domain : Domain, optional
  550. Ground domain
  551. Notes
  552. =====
  553. By default ``compose=True``, the minimal polynomial of the subexpressions of ``ex``
  554. are computed, then the arithmetic operations on them are performed using the resultant
  555. and factorization.
  556. If ``compose=False``, a bottom-up algorithm is used with ``groebner``.
  557. The default algorithm stalls less frequently.
  558. If no ground domain is given, it will be generated automatically from the expression.
  559. Examples
  560. ========
  561. >>> from sympy import minimal_polynomial, sqrt, solve, QQ
  562. >>> from sympy.abc import x, y
  563. >>> minimal_polynomial(sqrt(2), x)
  564. x**2 - 2
  565. >>> minimal_polynomial(sqrt(2), x, domain=QQ.algebraic_field(sqrt(2)))
  566. x - sqrt(2)
  567. >>> minimal_polynomial(sqrt(2) + sqrt(3), x)
  568. x**4 - 10*x**2 + 1
  569. >>> minimal_polynomial(solve(x**3 + x + 3)[0], x)
  570. x**3 + x + 3
  571. >>> minimal_polynomial(sqrt(y), x)
  572. x**2 - y
  573. """
  574. ex = sympify(ex)
  575. if ex.is_number:
  576. # not sure if it's always needed but try it for numbers (issue 8354)
  577. ex = _mexpand(ex, recursive=True)
  578. for expr in preorder_traversal(ex):
  579. if expr.is_AlgebraicNumber:
  580. compose = False
  581. break
  582. if x is not None:
  583. x, cls = sympify(x), Poly
  584. else:
  585. x, cls = Dummy('x'), PurePoly
  586. if not domain:
  587. if ex.free_symbols:
  588. domain = FractionField(QQ, list(ex.free_symbols))
  589. else:
  590. domain = QQ
  591. if hasattr(domain, 'symbols') and x in domain.symbols:
  592. raise GeneratorsError("the variable %s is an element of the ground "
  593. "domain %s" % (x, domain))
  594. if compose:
  595. result = _minpoly_compose(ex, x, domain)
  596. result = result.primitive()[1]
  597. c = result.coeff(x**degree(result, x))
  598. if c.is_negative:
  599. result = expand_mul(-result)
  600. return cls(result, x, field=True) if polys else result.collect(x)
  601. if not domain.is_QQ:
  602. raise NotImplementedError("groebner method only works for QQ")
  603. result = _minpoly_groebner(ex, x, cls)
  604. return cls(result, x, field=True) if polys else result.collect(x)
  605. def _minpoly_groebner(ex, x, cls):
  606. """
  607. Computes the minimal polynomial of an algebraic number
  608. using Groebner bases
  609. Examples
  610. ========
  611. >>> from sympy import minimal_polynomial, sqrt, Rational
  612. >>> from sympy.abc import x
  613. >>> minimal_polynomial(sqrt(2) + 3*Rational(1, 3), x, compose=False)
  614. x**2 - 2*x - 1
  615. """
  616. generator = numbered_symbols('a', cls=Dummy)
  617. mapping, symbols = {}, {}
  618. def update_mapping(ex, exp, base=None):
  619. a = next(generator)
  620. symbols[ex] = a
  621. if base is not None:
  622. mapping[ex] = a**exp + base
  623. else:
  624. mapping[ex] = exp.as_expr(a)
  625. return a
  626. def bottom_up_scan(ex):
  627. """
  628. Transform a given algebraic expression *ex* into a multivariate
  629. polynomial, by introducing fresh variables with defining equations.
  630. Explanation
  631. ===========
  632. The critical elements of the algebraic expression *ex* are root
  633. extractions, instances of :py:class:`~.AlgebraicNumber`, and negative
  634. powers.
  635. When we encounter a root extraction or an :py:class:`~.AlgebraicNumber`
  636. we replace this expression with a fresh variable ``a_i``, and record
  637. the defining polynomial for ``a_i``. For example, if ``a_0**(1/3)``
  638. occurs, we will replace it with ``a_1``, and record the new defining
  639. polynomial ``a_1**3 - a_0``.
  640. When we encounter a negative power we transform it into a positive
  641. power by algebraically inverting the base. This means computing the
  642. minimal polynomial in ``x`` for the base, inverting ``x`` modulo this
  643. poly (which generates a new polynomial) and then substituting the
  644. original base expression for ``x`` in this last polynomial.
  645. We return the transformed expression, and we record the defining
  646. equations for new symbols using the ``update_mapping()`` function.
  647. """
  648. if ex.is_Atom:
  649. if ex is S.ImaginaryUnit:
  650. if ex not in mapping:
  651. return update_mapping(ex, 2, 1)
  652. else:
  653. return symbols[ex]
  654. elif ex.is_Rational:
  655. return ex
  656. elif ex.is_Add:
  657. return Add(*[ bottom_up_scan(g) for g in ex.args ])
  658. elif ex.is_Mul:
  659. return Mul(*[ bottom_up_scan(g) for g in ex.args ])
  660. elif ex.is_Pow:
  661. if ex.exp.is_Rational:
  662. if ex.exp < 0:
  663. minpoly_base = _minpoly_groebner(ex.base, x, cls)
  664. inverse = invert(x, minpoly_base).as_expr()
  665. base_inv = inverse.subs(x, ex.base).expand()
  666. if ex.exp == -1:
  667. return bottom_up_scan(base_inv)
  668. else:
  669. ex = base_inv**(-ex.exp)
  670. if not ex.exp.is_Integer:
  671. base, exp = (
  672. ex.base**ex.exp.p).expand(), Rational(1, ex.exp.q)
  673. else:
  674. base, exp = ex.base, ex.exp
  675. base = bottom_up_scan(base)
  676. expr = base**exp
  677. if expr not in mapping:
  678. if exp.is_Integer:
  679. return expr.expand()
  680. else:
  681. return update_mapping(expr, 1 / exp, -base)
  682. else:
  683. return symbols[expr]
  684. elif ex.is_AlgebraicNumber:
  685. if ex not in mapping:
  686. return update_mapping(ex, ex.minpoly_of_element())
  687. else:
  688. return symbols[ex]
  689. raise NotAlgebraic("%s does not seem to be an algebraic number" % ex)
  690. def simpler_inverse(ex):
  691. """
  692. Returns True if it is more likely that the minimal polynomial
  693. algorithm works better with the inverse
  694. """
  695. if ex.is_Pow:
  696. if (1/ex.exp).is_integer and ex.exp < 0:
  697. if ex.base.is_Add:
  698. return True
  699. if ex.is_Mul:
  700. hit = True
  701. for p in ex.args:
  702. if p.is_Add:
  703. return False
  704. if p.is_Pow:
  705. if p.base.is_Add and p.exp > 0:
  706. return False
  707. if hit:
  708. return True
  709. return False
  710. inverted = False
  711. ex = expand_multinomial(ex)
  712. if ex.is_AlgebraicNumber:
  713. return ex.minpoly_of_element().as_expr(x)
  714. elif ex.is_Rational:
  715. result = ex.q*x - ex.p
  716. else:
  717. inverted = simpler_inverse(ex)
  718. if inverted:
  719. ex = ex**-1
  720. res = None
  721. if ex.is_Pow and (1/ex.exp).is_Integer:
  722. n = 1/ex.exp
  723. res = _minimal_polynomial_sq(ex.base, n, x)
  724. elif _is_sum_surds(ex):
  725. res = _minimal_polynomial_sq(ex, S.One, x)
  726. if res is not None:
  727. result = res
  728. if res is None:
  729. bus = bottom_up_scan(ex)
  730. F = [x - bus] + list(mapping.values())
  731. G = groebner(F, list(symbols.values()) + [x], order='lex')
  732. _, factors = factor_list(G[-1])
  733. # by construction G[-1] has root `ex`
  734. result = _choose_factor(factors, x, ex)
  735. if inverted:
  736. result = _invertx(result, x)
  737. if result.coeff(x**degree(result, x)) < 0:
  738. result = expand_mul(-result)
  739. return result
  740. @public
  741. def minpoly(ex, x=None, compose=True, polys=False, domain=None):
  742. """This is a synonym for :py:func:`~.minimal_polynomial`."""
  743. return minimal_polynomial(ex, x=x, compose=compose, polys=polys, domain=domain)