algebraicfield.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. """Implementation of :class:`AlgebraicField` class. """
  2. from sympy.core.add import Add
  3. from sympy.core.mul import Mul
  4. from sympy.core.singleton import S
  5. from sympy.polys.domains.characteristiczero import CharacteristicZero
  6. from sympy.polys.domains.field import Field
  7. from sympy.polys.domains.simpledomain import SimpleDomain
  8. from sympy.polys.polyclasses import ANP
  9. from sympy.polys.polyerrors import CoercionFailed, DomainError, NotAlgebraic, IsomorphismFailed
  10. from sympy.utilities import public
  11. @public
  12. class AlgebraicField(Field, CharacteristicZero, SimpleDomain):
  13. r"""Algebraic number field :ref:`QQ(a)`
  14. A :ref:`QQ(a)` domain represents an `algebraic number field`_
  15. `\mathbb{Q}(a)` as a :py:class:`~.Domain` in the domain system (see
  16. :ref:`polys-domainsintro`).
  17. A :py:class:`~.Poly` created from an expression involving `algebraic
  18. numbers`_ will treat the algebraic numbers as generators if the generators
  19. argument is not specified.
  20. >>> from sympy import Poly, Symbol, sqrt
  21. >>> x = Symbol('x')
  22. >>> Poly(x**2 + sqrt(2))
  23. Poly(x**2 + (sqrt(2)), x, sqrt(2), domain='ZZ')
  24. That is a multivariate polynomial with ``sqrt(2)`` treated as one of the
  25. generators (variables). If the generators are explicitly specified then
  26. ``sqrt(2)`` will be considered to be a coefficient but by default the
  27. :ref:`EX` domain is used. To make a :py:class:`~.Poly` with a :ref:`QQ(a)`
  28. domain the argument ``extension=True`` can be given.
  29. >>> Poly(x**2 + sqrt(2), x)
  30. Poly(x**2 + sqrt(2), x, domain='EX')
  31. >>> Poly(x**2 + sqrt(2), x, extension=True)
  32. Poly(x**2 + sqrt(2), x, domain='QQ<sqrt(2)>')
  33. A generator of the algebraic field extension can also be specified
  34. explicitly which is particularly useful if the coefficients are all
  35. rational but an extension field is needed (e.g. to factor the
  36. polynomial).
  37. >>> Poly(x**2 + 1)
  38. Poly(x**2 + 1, x, domain='ZZ')
  39. >>> Poly(x**2 + 1, extension=sqrt(2))
  40. Poly(x**2 + 1, x, domain='QQ<sqrt(2)>')
  41. It is possible to factorise a polynomial over a :ref:`QQ(a)` domain using
  42. the ``extension`` argument to :py:func:`~.factor` or by specifying the domain
  43. explicitly.
  44. >>> from sympy import factor, QQ
  45. >>> factor(x**2 - 2)
  46. x**2 - 2
  47. >>> factor(x**2 - 2, extension=sqrt(2))
  48. (x - sqrt(2))*(x + sqrt(2))
  49. >>> factor(x**2 - 2, domain='QQ<sqrt(2)>')
  50. (x - sqrt(2))*(x + sqrt(2))
  51. >>> factor(x**2 - 2, domain=QQ.algebraic_field(sqrt(2)))
  52. (x - sqrt(2))*(x + sqrt(2))
  53. The ``extension=True`` argument can be used but will only create an
  54. extension that contains the coefficients which is usually not enough to
  55. factorise the polynomial.
  56. >>> p = x**3 + sqrt(2)*x**2 - 2*x - 2*sqrt(2)
  57. >>> factor(p) # treats sqrt(2) as a symbol
  58. (x + sqrt(2))*(x**2 - 2)
  59. >>> factor(p, extension=True)
  60. (x - sqrt(2))*(x + sqrt(2))**2
  61. >>> factor(x**2 - 2, extension=True) # all rational coefficients
  62. x**2 - 2
  63. It is also possible to use :ref:`QQ(a)` with the :py:func:`~.cancel`
  64. and :py:func:`~.gcd` functions.
  65. >>> from sympy import cancel, gcd
  66. >>> cancel((x**2 - 2)/(x - sqrt(2)))
  67. (x**2 - 2)/(x - sqrt(2))
  68. >>> cancel((x**2 - 2)/(x - sqrt(2)), extension=sqrt(2))
  69. x + sqrt(2)
  70. >>> gcd(x**2 - 2, x - sqrt(2))
  71. 1
  72. >>> gcd(x**2 - 2, x - sqrt(2), extension=sqrt(2))
  73. x - sqrt(2)
  74. When using the domain directly :ref:`QQ(a)` can be used as a constructor
  75. to create instances which then support the operations ``+,-,*,**,/``. The
  76. :py:meth:`~.Domain.algebraic_field` method is used to construct a
  77. particular :ref:`QQ(a)` domain. The :py:meth:`~.Domain.from_sympy` method
  78. can be used to create domain elements from normal SymPy expressions.
  79. >>> K = QQ.algebraic_field(sqrt(2))
  80. >>> K
  81. QQ<sqrt(2)>
  82. >>> xk = K.from_sympy(3 + 4*sqrt(2))
  83. >>> xk # doctest: +SKIP
  84. ANP([4, 3], [1, 0, -2], QQ)
  85. Elements of :ref:`QQ(a)` are instances of :py:class:`~.ANP` which have
  86. limited printing support. The raw display shows the internal
  87. representation of the element as the list ``[4, 3]`` representing the
  88. coefficients of ``1`` and ``sqrt(2)`` for this element in the form
  89. ``a * sqrt(2) + b * 1`` where ``a`` and ``b`` are elements of :ref:`QQ`.
  90. The minimal polynomial for the generator ``(x**2 - 2)`` is also shown in
  91. the :ref:`dup-representation` as the list ``[1, 0, -2]``. We can use
  92. :py:meth:`~.Domain.to_sympy` to get a better printed form for the
  93. elements and to see the results of operations.
  94. >>> xk = K.from_sympy(3 + 4*sqrt(2))
  95. >>> yk = K.from_sympy(2 + 3*sqrt(2))
  96. >>> xk * yk # doctest: +SKIP
  97. ANP([17, 30], [1, 0, -2], QQ)
  98. >>> K.to_sympy(xk * yk)
  99. 17*sqrt(2) + 30
  100. >>> K.to_sympy(xk + yk)
  101. 5 + 7*sqrt(2)
  102. >>> K.to_sympy(xk ** 2)
  103. 24*sqrt(2) + 41
  104. >>> K.to_sympy(xk / yk)
  105. sqrt(2)/14 + 9/7
  106. Any expression representing an algebraic number can be used to generate
  107. a :ref:`QQ(a)` domain provided its `minimal polynomial`_ can be computed.
  108. The function :py:func:`~.minpoly` function is used for this.
  109. >>> from sympy import exp, I, pi, minpoly
  110. >>> g = exp(2*I*pi/3)
  111. >>> g
  112. exp(2*I*pi/3)
  113. >>> g.is_algebraic
  114. True
  115. >>> minpoly(g, x)
  116. x**2 + x + 1
  117. >>> factor(x**3 - 1, extension=g)
  118. (x - 1)*(x - exp(2*I*pi/3))*(x + 1 + exp(2*I*pi/3))
  119. It is also possible to make an algebraic field from multiple extension
  120. elements.
  121. >>> K = QQ.algebraic_field(sqrt(2), sqrt(3))
  122. >>> K
  123. QQ<sqrt(2) + sqrt(3)>
  124. >>> p = x**4 - 5*x**2 + 6
  125. >>> factor(p)
  126. (x**2 - 3)*(x**2 - 2)
  127. >>> factor(p, domain=K)
  128. (x - sqrt(2))*(x + sqrt(2))*(x - sqrt(3))*(x + sqrt(3))
  129. >>> factor(p, extension=[sqrt(2), sqrt(3)])
  130. (x - sqrt(2))*(x + sqrt(2))*(x - sqrt(3))*(x + sqrt(3))
  131. Multiple extension elements are always combined together to make a single
  132. `primitive element`_. In the case of ``[sqrt(2), sqrt(3)]`` the primitive
  133. element chosen is ``sqrt(2) + sqrt(3)`` which is why the domain displays
  134. as ``QQ<sqrt(2) + sqrt(3)>``. The minimal polynomial for the primitive
  135. element is computed using the :py:func:`~.primitive_element` function.
  136. >>> from sympy import primitive_element
  137. >>> primitive_element([sqrt(2), sqrt(3)], x)
  138. (x**4 - 10*x**2 + 1, [1, 1])
  139. >>> minpoly(sqrt(2) + sqrt(3), x)
  140. x**4 - 10*x**2 + 1
  141. The extension elements that generate the domain can be accessed from the
  142. domain using the :py:attr:`~.ext` and :py:attr:`~.orig_ext` attributes as
  143. instances of :py:class:`~.AlgebraicNumber`. The minimal polynomial for
  144. the primitive element as a :py:class:`~.DMP` instance is available as
  145. :py:attr:`~.mod`.
  146. >>> K = QQ.algebraic_field(sqrt(2), sqrt(3))
  147. >>> K
  148. QQ<sqrt(2) + sqrt(3)>
  149. >>> K.ext
  150. sqrt(2) + sqrt(3)
  151. >>> K.orig_ext
  152. (sqrt(2), sqrt(3))
  153. >>> K.mod
  154. DMP([1, 0, -10, 0, 1], QQ, None)
  155. The `discriminant`_ of the field can be obtained from the
  156. :py:meth:`~.discriminant` method, and an `integral basis`_ from the
  157. :py:meth:`~.integral_basis` method. The latter returns a list of
  158. :py:class:`~.ANP` instances by default, but can be made to return instances
  159. of :py:class:`~.Expr` or :py:class:`~.AlgebraicNumber` by passing a ``fmt``
  160. argument. The maximal order, or ring of integers, of the field can also be
  161. obtained from the :py:meth:`~.maximal_order` method, as a
  162. :py:class:`~sympy.polys.numberfields.modules.Submodule`.
  163. >>> zeta5 = exp(2*I*pi/5)
  164. >>> K = QQ.algebraic_field(zeta5)
  165. >>> K
  166. QQ<exp(2*I*pi/5)>
  167. >>> K.discriminant()
  168. 125
  169. >>> K = QQ.algebraic_field(sqrt(5))
  170. >>> K
  171. QQ<sqrt(5)>
  172. >>> K.integral_basis(fmt='sympy')
  173. [1, 1/2 + sqrt(5)/2]
  174. >>> K.maximal_order()
  175. Submodule[[2, 0], [1, 1]]/2
  176. The factorization of a rational prime into prime ideals of the field is
  177. computed by the :py:meth:`~.primes_above` method, which returns a list
  178. of :py:class:`~sympy.polys.numberfields.primes.PrimeIdeal` instances.
  179. >>> zeta7 = exp(2*I*pi/7)
  180. >>> K = QQ.algebraic_field(zeta7)
  181. >>> K
  182. QQ<exp(2*I*pi/7)>
  183. >>> K.primes_above(11)
  184. [(11, _x**3 + 5*_x**2 + 4*_x - 1), (11, _x**3 - 4*_x**2 - 5*_x - 1)]
  185. The Galois group of the Galois closure of the field can be computed (when
  186. the minimal polynomial of the field is of sufficiently small degree).
  187. >>> K.galois_group(by_name=True)[0]
  188. S6TransitiveSubgroups.C6
  189. Notes
  190. =====
  191. It is not currently possible to generate an algebraic extension over any
  192. domain other than :ref:`QQ`. Ideally it would be possible to generate
  193. extensions like ``QQ(x)(sqrt(x**2 - 2))``. This is equivalent to the
  194. quotient ring ``QQ(x)[y]/(y**2 - x**2 + 2)`` and there are two
  195. implementations of this kind of quotient ring/extension in the
  196. :py:class:`~.QuotientRing` and :py:class:`~.MonogenicFiniteExtension`
  197. classes. Each of those implementations needs some work to make them fully
  198. usable though.
  199. .. _algebraic number field: https://en.wikipedia.org/wiki/Algebraic_number_field
  200. .. _algebraic numbers: https://en.wikipedia.org/wiki/Algebraic_number
  201. .. _discriminant: https://en.wikipedia.org/wiki/Discriminant_of_an_algebraic_number_field
  202. .. _integral basis: https://en.wikipedia.org/wiki/Algebraic_number_field#Integral_basis
  203. .. _minimal polynomial: https://en.wikipedia.org/wiki/Minimal_polynomial_(field_theory)
  204. .. _primitive element: https://en.wikipedia.org/wiki/Primitive_element_theorem
  205. """
  206. dtype = ANP
  207. is_AlgebraicField = is_Algebraic = True
  208. is_Numerical = True
  209. has_assoc_Ring = False
  210. has_assoc_Field = True
  211. def __init__(self, dom, *ext, alias=None):
  212. r"""
  213. Parameters
  214. ==========
  215. dom : :py:class:`~.Domain`
  216. The base field over which this is an extension field.
  217. Currently only :ref:`QQ` is accepted.
  218. *ext : One or more :py:class:`~.Expr`
  219. Generators of the extension. These should be expressions that are
  220. algebraic over `\mathbb{Q}`.
  221. alias : str, :py:class:`~.Symbol`, None, optional (default=None)
  222. If provided, this will be used as the alias symbol for the
  223. primitive element of the :py:class:`~.AlgebraicField`.
  224. If ``None``, while ``ext`` consists of exactly one
  225. :py:class:`~.AlgebraicNumber`, its alias (if any) will be used.
  226. """
  227. if not dom.is_QQ:
  228. raise DomainError("ground domain must be a rational field")
  229. from sympy.polys.numberfields import to_number_field
  230. if len(ext) == 1 and isinstance(ext[0], tuple):
  231. orig_ext = ext[0][1:]
  232. else:
  233. orig_ext = ext
  234. if alias is None and len(ext) == 1:
  235. alias = getattr(ext[0], 'alias', None)
  236. self.orig_ext = orig_ext
  237. """
  238. Original elements given to generate the extension.
  239. >>> from sympy import QQ, sqrt
  240. >>> K = QQ.algebraic_field(sqrt(2), sqrt(3))
  241. >>> K.orig_ext
  242. (sqrt(2), sqrt(3))
  243. """
  244. self.ext = to_number_field(ext, alias=alias)
  245. """
  246. Primitive element used for the extension.
  247. >>> from sympy import QQ, sqrt
  248. >>> K = QQ.algebraic_field(sqrt(2), sqrt(3))
  249. >>> K.ext
  250. sqrt(2) + sqrt(3)
  251. """
  252. self.mod = self.ext.minpoly.rep
  253. """
  254. Minimal polynomial for the primitive element of the extension.
  255. >>> from sympy import QQ, sqrt
  256. >>> K = QQ.algebraic_field(sqrt(2))
  257. >>> K.mod
  258. DMP([1, 0, -2], QQ, None)
  259. """
  260. self.domain = self.dom = dom
  261. self.ngens = 1
  262. self.symbols = self.gens = (self.ext,)
  263. self.unit = self([dom(1), dom(0)])
  264. self.zero = self.dtype.zero(self.mod.rep, dom)
  265. self.one = self.dtype.one(self.mod.rep, dom)
  266. self._maximal_order = None
  267. self._discriminant = None
  268. self._nilradicals_mod_p = {}
  269. def new(self, element):
  270. return self.dtype(element, self.mod.rep, self.dom)
  271. def __str__(self):
  272. return str(self.dom) + '<' + str(self.ext) + '>'
  273. def __hash__(self):
  274. return hash((self.__class__.__name__, self.dtype, self.dom, self.ext))
  275. def __eq__(self, other):
  276. """Returns ``True`` if two domains are equivalent. """
  277. return isinstance(other, AlgebraicField) and \
  278. self.dtype == other.dtype and self.ext == other.ext
  279. def algebraic_field(self, *extension, alias=None):
  280. r"""Returns an algebraic field, i.e. `\mathbb{Q}(\alpha, \ldots)`. """
  281. return AlgebraicField(self.dom, *((self.ext,) + extension), alias=alias)
  282. def to_alg_num(self, a):
  283. """Convert ``a`` of ``dtype`` to an :py:class:`~.AlgebraicNumber`. """
  284. return self.ext.field_element(a)
  285. def to_sympy(self, a):
  286. """Convert ``a`` of ``dtype`` to a SymPy object. """
  287. # Precompute a converter to be reused:
  288. if not hasattr(self, '_converter'):
  289. self._converter = _make_converter(self)
  290. return self._converter(a)
  291. def from_sympy(self, a):
  292. """Convert SymPy's expression to ``dtype``. """
  293. try:
  294. return self([self.dom.from_sympy(a)])
  295. except CoercionFailed:
  296. pass
  297. from sympy.polys.numberfields import to_number_field
  298. try:
  299. return self(to_number_field(a, self.ext).native_coeffs())
  300. except (NotAlgebraic, IsomorphismFailed):
  301. raise CoercionFailed(
  302. "%s is not a valid algebraic number in %s" % (a, self))
  303. def from_ZZ(K1, a, K0):
  304. """Convert a Python ``int`` object to ``dtype``. """
  305. return K1(K1.dom.convert(a, K0))
  306. def from_ZZ_python(K1, a, K0):
  307. """Convert a Python ``int`` object to ``dtype``. """
  308. return K1(K1.dom.convert(a, K0))
  309. def from_QQ(K1, a, K0):
  310. """Convert a Python ``Fraction`` object to ``dtype``. """
  311. return K1(K1.dom.convert(a, K0))
  312. def from_QQ_python(K1, a, K0):
  313. """Convert a Python ``Fraction`` object to ``dtype``. """
  314. return K1(K1.dom.convert(a, K0))
  315. def from_ZZ_gmpy(K1, a, K0):
  316. """Convert a GMPY ``mpz`` object to ``dtype``. """
  317. return K1(K1.dom.convert(a, K0))
  318. def from_QQ_gmpy(K1, a, K0):
  319. """Convert a GMPY ``mpq`` object to ``dtype``. """
  320. return K1(K1.dom.convert(a, K0))
  321. def from_RealField(K1, a, K0):
  322. """Convert a mpmath ``mpf`` object to ``dtype``. """
  323. return K1(K1.dom.convert(a, K0))
  324. def get_ring(self):
  325. """Returns a ring associated with ``self``. """
  326. raise DomainError('there is no ring associated with %s' % self)
  327. def is_positive(self, a):
  328. """Returns True if ``a`` is positive. """
  329. return self.dom.is_positive(a.LC())
  330. def is_negative(self, a):
  331. """Returns True if ``a`` is negative. """
  332. return self.dom.is_negative(a.LC())
  333. def is_nonpositive(self, a):
  334. """Returns True if ``a`` is non-positive. """
  335. return self.dom.is_nonpositive(a.LC())
  336. def is_nonnegative(self, a):
  337. """Returns True if ``a`` is non-negative. """
  338. return self.dom.is_nonnegative(a.LC())
  339. def numer(self, a):
  340. """Returns numerator of ``a``. """
  341. return a
  342. def denom(self, a):
  343. """Returns denominator of ``a``. """
  344. return self.one
  345. def from_AlgebraicField(K1, a, K0):
  346. """Convert AlgebraicField element 'a' to another AlgebraicField """
  347. return K1.from_sympy(K0.to_sympy(a))
  348. def from_GaussianIntegerRing(K1, a, K0):
  349. """Convert a GaussianInteger element 'a' to ``dtype``. """
  350. return K1.from_sympy(K0.to_sympy(a))
  351. def from_GaussianRationalField(K1, a, K0):
  352. """Convert a GaussianRational element 'a' to ``dtype``. """
  353. return K1.from_sympy(K0.to_sympy(a))
  354. def _do_round_two(self):
  355. from sympy.polys.numberfields.basis import round_two
  356. ZK, dK = round_two(self, radicals=self._nilradicals_mod_p)
  357. self._maximal_order = ZK
  358. self._discriminant = dK
  359. def maximal_order(self):
  360. """
  361. Compute the maximal order, or ring of integers, of the field.
  362. Returns
  363. =======
  364. :py:class:`~sympy.polys.numberfields.modules.Submodule`.
  365. See Also
  366. ========
  367. integral_basis
  368. """
  369. if self._maximal_order is None:
  370. self._do_round_two()
  371. return self._maximal_order
  372. def integral_basis(self, fmt=None):
  373. r"""
  374. Get an integral basis for the field.
  375. Parameters
  376. ==========
  377. fmt : str, None, optional (default=None)
  378. If ``None``, return a list of :py:class:`~.ANP` instances.
  379. If ``"sympy"``, convert each element of the list to an
  380. :py:class:`~.Expr`, using ``self.to_sympy()``.
  381. If ``"alg"``, convert each element of the list to an
  382. :py:class:`~.AlgebraicNumber`, using ``self.to_alg_num()``.
  383. Examples
  384. ========
  385. >>> from sympy import QQ, AlgebraicNumber, sqrt
  386. >>> alpha = AlgebraicNumber(sqrt(5), alias='alpha')
  387. >>> k = QQ.algebraic_field(alpha)
  388. >>> B0 = k.integral_basis()
  389. >>> B1 = k.integral_basis(fmt='sympy')
  390. >>> B2 = k.integral_basis(fmt='alg')
  391. >>> print(B0[1]) # doctest: +SKIP
  392. ANP([mpq(1,2), mpq(1,2)], [mpq(1,1), mpq(0,1), mpq(-5,1)], QQ)
  393. >>> print(B1[1])
  394. 1/2 + alpha/2
  395. >>> print(B2[1])
  396. alpha/2 + 1/2
  397. In the last two cases we get legible expressions, which print somewhat
  398. differently because of the different types involved:
  399. >>> print(type(B1[1]))
  400. <class 'sympy.core.add.Add'>
  401. >>> print(type(B2[1]))
  402. <class 'sympy.core.numbers.AlgebraicNumber'>
  403. See Also
  404. ========
  405. to_sympy
  406. to_alg_num
  407. maximal_order
  408. """
  409. ZK = self.maximal_order()
  410. M = ZK.QQ_matrix
  411. n = M.shape[1]
  412. B = [self.new(list(reversed(M[:, j].flat()))) for j in range(n)]
  413. if fmt == 'sympy':
  414. return [self.to_sympy(b) for b in B]
  415. elif fmt == 'alg':
  416. return [self.to_alg_num(b) for b in B]
  417. return B
  418. def discriminant(self):
  419. """Get the discriminant of the field."""
  420. if self._discriminant is None:
  421. self._do_round_two()
  422. return self._discriminant
  423. def primes_above(self, p):
  424. """Compute the prime ideals lying above a given rational prime *p*."""
  425. from sympy.polys.numberfields.primes import prime_decomp
  426. ZK = self.maximal_order()
  427. dK = self.discriminant()
  428. rad = self._nilradicals_mod_p.get(p)
  429. return prime_decomp(p, ZK=ZK, dK=dK, radical=rad)
  430. def galois_group(self, by_name=False, max_tries=30, randomize=False):
  431. """
  432. Compute the Galois group of the Galois closure of this field.
  433. Examples
  434. ========
  435. If the field is Galois, the order of the group will equal the degree
  436. of the field:
  437. >>> from sympy import QQ
  438. >>> from sympy.abc import x
  439. >>> k = QQ.alg_field_from_poly(x**4 + 1)
  440. >>> G, _ = k.galois_group()
  441. >>> G.order()
  442. 4
  443. If the field is not Galois, then its Galois closure is a proper
  444. extension, and the order of the Galois group will be greater than the
  445. degree of the field:
  446. >>> k = QQ.alg_field_from_poly(x**4 - 2)
  447. >>> G, _ = k.galois_group()
  448. >>> G.order()
  449. 8
  450. See Also
  451. ========
  452. sympy.polys.numberfields.galoisgroups.galois_group
  453. """
  454. return self.ext.minpoly_of_element().galois_group(
  455. by_name=by_name, max_tries=max_tries, randomize=randomize)
  456. def _make_converter(K):
  457. """Construct the converter to convert back to Expr"""
  458. # Precompute the effect of converting to SymPy and expanding expressions
  459. # like (sqrt(2) + sqrt(3))**2. Asking Expr to do the expansion on every
  460. # conversion from K to Expr is slow. Here we compute the expansions for
  461. # each power of the generator and collect together the resulting algebraic
  462. # terms and the rational coefficients into a matrix.
  463. gen = K.ext.as_expr()
  464. todom = K.dom.from_sympy
  465. # We'll let Expr compute the expansions. We won't make any presumptions
  466. # about what this results in except that it is QQ-linear in some terms
  467. # that we will call algebraics. The final result will be expressed in
  468. # terms of those.
  469. powers = [S.One, gen]
  470. for n in range(2, K.mod.degree()):
  471. powers.append((gen * powers[-1]).expand())
  472. # Collect the rational coefficients and algebraic Expr that can
  473. # map the ANP coefficients into an expanded SymPy expression
  474. terms = [dict(t.as_coeff_Mul()[::-1] for t in Add.make_args(p)) for p in powers]
  475. algebraics = set().union(*terms)
  476. matrix = [[todom(t.get(a, S.Zero)) for t in terms] for a in algebraics]
  477. # Create a function to do the conversion efficiently:
  478. def converter(a):
  479. """Convert a to Expr using converter"""
  480. ai = a.rep[::-1]
  481. tosympy = K.dom.to_sympy
  482. coeffs_dom = [sum(mij*aj for mij, aj in zip(mi, ai)) for mi in matrix]
  483. coeffs_sympy = [tosympy(c) for c in coeffs_dom]
  484. res = Add(*(Mul(c, a) for c, a in zip(coeffs_sympy, algebraics)))
  485. return res
  486. return converter