subfield.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. r"""
  2. Functions in ``polys.numberfields.subfield`` solve the "Subfield Problem" and
  3. allied problems, for algebraic number fields.
  4. Following Cohen (see [Cohen93]_ Section 4.5), we can define the main problem as
  5. follows:
  6. * **Subfield Problem:**
  7. Given two number fields $\mathbb{Q}(\alpha)$, $\mathbb{Q}(\beta)$
  8. via the minimal polynomials for their generators $\alpha$ and $\beta$, decide
  9. whether one field is isomorphic to a subfield of the other.
  10. From a solution to this problem flow solutions to the following problems as
  11. well:
  12. * **Primitive Element Problem:**
  13. Given several algebraic numbers
  14. $\alpha_1, \ldots, \alpha_m$, compute a single algebraic number $\theta$
  15. such that $\mathbb{Q}(\alpha_1, \ldots, \alpha_m) = \mathbb{Q}(\theta)$.
  16. * **Field Isomorphism Problem:**
  17. Decide whether two number fields
  18. $\mathbb{Q}(\alpha)$, $\mathbb{Q}(\beta)$ are isomorphic.
  19. * **Field Membership Problem:**
  20. Given two algebraic numbers $\alpha$,
  21. $\beta$, decide whether $\alpha \in \mathbb{Q}(\beta)$, and if so write
  22. $\alpha = f(\beta)$ for some $f(x) \in \mathbb{Q}[x]$.
  23. """
  24. from sympy.core.add import Add
  25. from sympy.core.numbers import AlgebraicNumber
  26. from sympy.core.singleton import S
  27. from sympy.core.symbol import Dummy
  28. from sympy.core.sympify import sympify
  29. from sympy.ntheory import sieve
  30. from sympy.polys.densetools import dup_eval
  31. from sympy.polys.domains import QQ
  32. from sympy.polys.numberfields.minpoly import _choose_factor, minimal_polynomial
  33. from sympy.polys.polyerrors import IsomorphismFailed
  34. from sympy.polys.polytools import Poly, PurePoly, factor_list
  35. from sympy.utilities import public
  36. from mpmath import MPContext
  37. def is_isomorphism_possible(a, b):
  38. """Necessary but not sufficient test for isomorphism. """
  39. n = a.minpoly.degree()
  40. m = b.minpoly.degree()
  41. if m % n != 0:
  42. return False
  43. if n == m:
  44. return True
  45. da = a.minpoly.discriminant()
  46. db = b.minpoly.discriminant()
  47. i, k, half = 1, m//n, db//2
  48. while True:
  49. p = sieve[i]
  50. P = p**k
  51. if P > half:
  52. break
  53. if ((da % p) % 2) and not (db % P):
  54. return False
  55. i += 1
  56. return True
  57. def field_isomorphism_pslq(a, b):
  58. """Construct field isomorphism using PSLQ algorithm. """
  59. if not a.root.is_real or not b.root.is_real:
  60. raise NotImplementedError("PSLQ doesn't support complex coefficients")
  61. f = a.minpoly
  62. g = b.minpoly.replace(f.gen)
  63. n, m, prev = 100, b.minpoly.degree(), None
  64. ctx = MPContext()
  65. for i in range(1, 5):
  66. A = a.root.evalf(n)
  67. B = b.root.evalf(n)
  68. basis = [1, B] + [ B**i for i in range(2, m) ] + [-A]
  69. ctx.dps = n
  70. coeffs = ctx.pslq(basis, maxcoeff=10**10, maxsteps=1000)
  71. if coeffs is None:
  72. # PSLQ can't find an integer linear combination. Give up.
  73. break
  74. if coeffs != prev:
  75. prev = coeffs
  76. else:
  77. # Increasing precision didn't produce anything new. Give up.
  78. break
  79. # We have
  80. # c0 + c1*B + c2*B^2 + ... + cm-1*B^(m-1) - cm*A ~ 0.
  81. # So bring cm*A to the other side, and divide through by cm,
  82. # for an approximate representation of A as a polynomial in B.
  83. # (We know cm != 0 since `b.minpoly` is irreducible.)
  84. coeffs = [S(c)/coeffs[-1] for c in coeffs[:-1]]
  85. # Throw away leading zeros.
  86. while not coeffs[-1]:
  87. coeffs.pop()
  88. coeffs = list(reversed(coeffs))
  89. h = Poly(coeffs, f.gen, domain='QQ')
  90. # We only have A ~ h(B). We must check whether the relation is exact.
  91. if f.compose(h).rem(g).is_zero:
  92. # Now we know that h(b) is in fact equal to _some conjugate of_ a.
  93. # But from the very precise approximation A ~ h(B) we can assume
  94. # the conjugate is a itself.
  95. return coeffs
  96. else:
  97. n *= 2
  98. return None
  99. def field_isomorphism_factor(a, b):
  100. """Construct field isomorphism via factorization. """
  101. _, factors = factor_list(a.minpoly, extension=b)
  102. for f, _ in factors:
  103. if f.degree() == 1:
  104. # Any linear factor f(x) represents some conjugate of a in QQ(b).
  105. # We want to know whether this linear factor represents a itself.
  106. # Let f = x - c
  107. c = -f.rep.TC()
  108. # Write c as polynomial in b
  109. coeffs = c.to_sympy_list()
  110. d, terms = len(coeffs) - 1, []
  111. for i, coeff in enumerate(coeffs):
  112. terms.append(coeff*b.root**(d - i))
  113. r = Add(*terms)
  114. # Check whether we got the number a
  115. if a.minpoly.same_root(r, a):
  116. return coeffs
  117. # If none of the linear factors represented a in QQ(b), then in fact a is
  118. # not an element of QQ(b).
  119. return None
  120. @public
  121. def field_isomorphism(a, b, *, fast=True):
  122. r"""
  123. Find an embedding of one number field into another.
  124. Explanation
  125. ===========
  126. This function looks for an isomorphism from $\mathbb{Q}(a)$ onto some
  127. subfield of $\mathbb{Q}(b)$. Thus, it solves the Subfield Problem.
  128. Examples
  129. ========
  130. >>> from sympy import sqrt, field_isomorphism, I
  131. >>> print(field_isomorphism(3, sqrt(2))) # doctest: +SKIP
  132. [3]
  133. >>> print(field_isomorphism( I*sqrt(3), I*sqrt(3)/2)) # doctest: +SKIP
  134. [2, 0]
  135. Parameters
  136. ==========
  137. a : :py:class:`~.Expr`
  138. Any expression representing an algebraic number.
  139. b : :py:class:`~.Expr`
  140. Any expression representing an algebraic number.
  141. fast : boolean, optional (default=True)
  142. If ``True``, we first attempt a potentially faster way of computing the
  143. isomorphism, falling back on a slower method if this fails. If
  144. ``False``, we go directly to the slower method, which is guaranteed to
  145. return a result.
  146. Returns
  147. =======
  148. List of rational numbers, or None
  149. If $\mathbb{Q}(a)$ is not isomorphic to some subfield of
  150. $\mathbb{Q}(b)$, then return ``None``. Otherwise, return a list of
  151. rational numbers representing an element of $\mathbb{Q}(b)$ to which
  152. $a$ may be mapped, in order to define a monomorphism, i.e. an
  153. isomorphism from $\mathbb{Q}(a)$ to some subfield of $\mathbb{Q}(b)$.
  154. The elements of the list are the coefficients of falling powers of $b$.
  155. """
  156. a, b = sympify(a), sympify(b)
  157. if not a.is_AlgebraicNumber:
  158. a = AlgebraicNumber(a)
  159. if not b.is_AlgebraicNumber:
  160. b = AlgebraicNumber(b)
  161. a = a.to_primitive_element()
  162. b = b.to_primitive_element()
  163. if a == b:
  164. return a.coeffs()
  165. n = a.minpoly.degree()
  166. m = b.minpoly.degree()
  167. if n == 1:
  168. return [a.root]
  169. if m % n != 0:
  170. return None
  171. if fast:
  172. try:
  173. result = field_isomorphism_pslq(a, b)
  174. if result is not None:
  175. return result
  176. except NotImplementedError:
  177. pass
  178. return field_isomorphism_factor(a, b)
  179. def _switch_domain(g, K):
  180. # An algebraic relation f(a, b) = 0 over Q can also be written
  181. # g(b) = 0 where g is in Q(a)[x] and h(a) = 0 where h is in Q(b)[x].
  182. # This function transforms g into h where Q(b) = K.
  183. frep = g.rep.inject()
  184. hrep = frep.eject(K, front=True)
  185. return g.new(hrep, g.gens[0])
  186. def _linsolve(p):
  187. # Compute root of linear polynomial.
  188. c, d = p.rep.rep
  189. return -d/c
  190. @public
  191. def primitive_element(extension, x=None, *, ex=False, polys=False):
  192. r"""
  193. Find a single generator for a number field given by several generators.
  194. Explanation
  195. ===========
  196. The basic problem is this: Given several algebraic numbers
  197. $\alpha_1, \alpha_2, \ldots, \alpha_n$, find a single algebraic number
  198. $\theta$ such that
  199. $\mathbb{Q}(\alpha_1, \alpha_2, \ldots, \alpha_n) = \mathbb{Q}(\theta)$.
  200. This function actually guarantees that $\theta$ will be a linear
  201. combination of the $\alpha_i$, with non-negative integer coefficients.
  202. Furthermore, if desired, this function will tell you how to express each
  203. $\alpha_i$ as a $\mathbb{Q}$-linear combination of the powers of $\theta$.
  204. Examples
  205. ========
  206. >>> from sympy import primitive_element, sqrt, S, minpoly, simplify
  207. >>> from sympy.abc import x
  208. >>> f, lincomb, reps = primitive_element([sqrt(2), sqrt(3)], x, ex=True)
  209. Then ``lincomb`` tells us the primitive element as a linear combination of
  210. the given generators ``sqrt(2)`` and ``sqrt(3)``.
  211. >>> print(lincomb)
  212. [1, 1]
  213. This means the primtiive element is $\sqrt{2} + \sqrt{3}$.
  214. Meanwhile ``f`` is the minimal polynomial for this primitive element.
  215. >>> print(f)
  216. x**4 - 10*x**2 + 1
  217. >>> print(minpoly(sqrt(2) + sqrt(3), x))
  218. x**4 - 10*x**2 + 1
  219. Finally, ``reps`` (which was returned only because we set keyword arg
  220. ``ex=True``) tells us how to recover each of the generators $\sqrt{2}$ and
  221. $\sqrt{3}$ as $\mathbb{Q}$-linear combinations of the powers of the
  222. primitive element $\sqrt{2} + \sqrt{3}$.
  223. >>> print([S(r) for r in reps[0]])
  224. [1/2, 0, -9/2, 0]
  225. >>> theta = sqrt(2) + sqrt(3)
  226. >>> print(simplify(theta**3/2 - 9*theta/2))
  227. sqrt(2)
  228. >>> print([S(r) for r in reps[1]])
  229. [-1/2, 0, 11/2, 0]
  230. >>> print(simplify(-theta**3/2 + 11*theta/2))
  231. sqrt(3)
  232. Parameters
  233. ==========
  234. extension : list of :py:class:`~.Expr`
  235. Each expression must represent an algebraic number $\alpha_i$.
  236. x : :py:class:`~.Symbol`, optional (default=None)
  237. The desired symbol to appear in the computed minimal polynomial for the
  238. primitive element $\theta$. If ``None``, we use a dummy symbol.
  239. ex : boolean, optional (default=False)
  240. If and only if ``True``, compute the representation of each $\alpha_i$
  241. as a $\mathbb{Q}$-linear combination over the powers of $\theta$.
  242. polys : boolean, optional (default=False)
  243. If ``True``, return the minimal polynomial as a :py:class:`~.Poly`.
  244. Otherwise return it as an :py:class:`~.Expr`.
  245. Returns
  246. =======
  247. Pair (f, coeffs) or triple (f, coeffs, reps), where:
  248. ``f`` is the minimal polynomial for the primitive element.
  249. ``coeffs`` gives the primitive element as a linear combination of the
  250. given generators.
  251. ``reps`` is present if and only if argument ``ex=True`` was passed,
  252. and is a list of lists of rational numbers. Each list gives the
  253. coefficients of falling powers of the primitive element, to recover
  254. one of the original, given generators.
  255. """
  256. if not extension:
  257. raise ValueError("Cannot compute primitive element for empty extension")
  258. if x is not None:
  259. x, cls = sympify(x), Poly
  260. else:
  261. x, cls = Dummy('x'), PurePoly
  262. if not ex:
  263. gen, coeffs = extension[0], [1]
  264. g = minimal_polynomial(gen, x, polys=True)
  265. for ext in extension[1:]:
  266. _, factors = factor_list(g, extension=ext)
  267. g = _choose_factor(factors, x, gen)
  268. s, _, g = g.sqf_norm()
  269. gen += s*ext
  270. coeffs.append(s)
  271. if not polys:
  272. return g.as_expr(), coeffs
  273. else:
  274. return cls(g), coeffs
  275. gen, coeffs = extension[0], [1]
  276. f = minimal_polynomial(gen, x, polys=True)
  277. K = QQ.algebraic_field((f, gen)) # incrementally constructed field
  278. reps = [K.unit] # representations of extension elements in K
  279. for ext in extension[1:]:
  280. p = minimal_polynomial(ext, x, polys=True)
  281. L = QQ.algebraic_field((p, ext))
  282. _, factors = factor_list(f, domain=L)
  283. f = _choose_factor(factors, x, gen)
  284. s, g, f = f.sqf_norm()
  285. gen += s*ext
  286. coeffs.append(s)
  287. K = QQ.algebraic_field((f, gen))
  288. h = _switch_domain(g, K)
  289. erep = _linsolve(h.gcd(p)) # ext as element of K
  290. ogen = K.unit - s*erep # old gen as element of K
  291. reps = [dup_eval(_.rep, ogen, K) for _ in reps] + [erep]
  292. H = [_.rep for _ in reps]
  293. if not polys:
  294. return f.as_expr(), coeffs, H
  295. else:
  296. return f, coeffs, H
  297. @public
  298. def to_number_field(extension, theta=None, *, gen=None):
  299. r"""
  300. Express one algebraic number in the field generated by another.
  301. Explanation
  302. ===========
  303. Given two algebraic numbers $\eta, \theta$, this function either expresses
  304. $\eta$ as an element of $\mathbb{Q}(\theta)$, or else raises an exception
  305. if $\eta \not\in \mathbb{Q}(\theta)$.
  306. This function is essentially just a convenience, utilizing
  307. :py:func:`~.field_isomorphism` (our solution of the Subfield Problem) to
  308. solve this, the Field Membership Problem.
  309. As an additional convenience, this function allows you to pass a list of
  310. algebraic numbers $\alpha_1, \alpha_2, \ldots, \alpha_n$ instead of $\eta$.
  311. It then computes $\eta$ for you, as a solution of the Primitive Element
  312. Problem, using :py:func:`~.primitive_element` on the list of $\alpha_i$.
  313. Examples
  314. ========
  315. >>> from sympy import sqrt, to_number_field
  316. >>> eta = sqrt(2)
  317. >>> theta = sqrt(2) + sqrt(3)
  318. >>> a = to_number_field(eta, theta)
  319. >>> print(type(a))
  320. <class 'sympy.core.numbers.AlgebraicNumber'>
  321. >>> a.root
  322. sqrt(2) + sqrt(3)
  323. >>> print(a)
  324. sqrt(2)
  325. >>> a.coeffs()
  326. [1/2, 0, -9/2, 0]
  327. We get an :py:class:`~.AlgebraicNumber`, whose ``.root`` is $\theta$, whose
  328. value is $\eta$, and whose ``.coeffs()`` show how to write $\eta$ as a
  329. $\mathbb{Q}$-linear combination in falling powers of $\theta$.
  330. Parameters
  331. ==========
  332. extension : :py:class:`~.Expr` or list of :py:class:`~.Expr`
  333. Either the algebraic number that is to be expressed in the other field,
  334. or else a list of algebraic numbers, a primitive element for which is
  335. to be expressed in the other field.
  336. theta : :py:class:`~.Expr`, None, optional (default=None)
  337. If an :py:class:`~.Expr` representing an algebraic number, behavior is
  338. as described under **Explanation**. If ``None``, then this function
  339. reduces to a shorthand for calling :py:func:`~.primitive_element` on
  340. ``extension`` and turning the computed primitive element into an
  341. :py:class:`~.AlgebraicNumber`.
  342. gen : :py:class:`~.Symbol`, None, optional (default=None)
  343. If provided, this will be used as the generator symbol for the returned
  344. :py:class:`~.AlgebraicNumber`.
  345. Returns
  346. =======
  347. AlgebraicNumber
  348. Belonging to $\mathbb{Q}(\theta)$ and equaling $\eta$.
  349. Raises
  350. ======
  351. IsomorphismFailed
  352. If $\eta \not\in \mathbb{Q}(\theta)$.
  353. See Also
  354. ========
  355. field_isomorphism
  356. primitive_element
  357. """
  358. if hasattr(extension, '__iter__'):
  359. extension = list(extension)
  360. else:
  361. extension = [extension]
  362. if len(extension) == 1 and isinstance(extension[0], tuple):
  363. return AlgebraicNumber(extension[0])
  364. minpoly, coeffs = primitive_element(extension, gen, polys=True)
  365. root = sum([ coeff*ext for coeff, ext in zip(coeffs, extension) ])
  366. if theta is None:
  367. return AlgebraicNumber((minpoly, root))
  368. else:
  369. theta = sympify(theta)
  370. if not theta.is_AlgebraicNumber:
  371. theta = AlgebraicNumber(theta, gen=gen)
  372. coeffs = field_isomorphism(root, theta)
  373. if coeffs is not None:
  374. return AlgebraicNumber(theta, coeffs)
  375. else:
  376. raise IsomorphismFailed(
  377. "%s is not in a subfield of %s" % (root, theta.root))