图片解析应用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

880 lines
27 KiB

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