subfield.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  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, _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. extension = [_sympify(ext) for ext in extension]
  259. if x is not None:
  260. x, cls = sympify(x), Poly
  261. else:
  262. x, cls = Dummy('x'), PurePoly
  263. if not ex:
  264. gen, coeffs = extension[0], [1]
  265. g = minimal_polynomial(gen, x, polys=True)
  266. for ext in extension[1:]:
  267. if ext.is_Rational:
  268. coeffs.append(0)
  269. continue
  270. _, factors = factor_list(g, extension=ext)
  271. g = _choose_factor(factors, x, gen)
  272. s, _, g = g.sqf_norm()
  273. gen += s*ext
  274. coeffs.append(s)
  275. if not polys:
  276. return g.as_expr(), coeffs
  277. else:
  278. return cls(g), coeffs
  279. gen, coeffs = extension[0], [1]
  280. f = minimal_polynomial(gen, x, polys=True)
  281. K = QQ.algebraic_field((f, gen)) # incrementally constructed field
  282. reps = [K.unit] # representations of extension elements in K
  283. for ext in extension[1:]:
  284. if ext.is_Rational:
  285. coeffs.append(0) # rational ext is not included in the expression of a primitive element
  286. reps.append(K.convert(ext)) # but it is included in reps
  287. continue
  288. p = minimal_polynomial(ext, x, polys=True)
  289. L = QQ.algebraic_field((p, ext))
  290. _, factors = factor_list(f, domain=L)
  291. f = _choose_factor(factors, x, gen)
  292. s, g, f = f.sqf_norm()
  293. gen += s*ext
  294. coeffs.append(s)
  295. K = QQ.algebraic_field((f, gen))
  296. h = _switch_domain(g, K)
  297. erep = _linsolve(h.gcd(p)) # ext as element of K
  298. ogen = K.unit - s*erep # old gen as element of K
  299. reps = [dup_eval(_.rep, ogen, K) for _ in reps] + [erep]
  300. if K.ext.root.is_Rational: # all extensions are rational
  301. H = [K.convert(_).rep for _ in extension]
  302. coeffs = [0]*len(extension)
  303. f = cls(x, domain=QQ)
  304. else:
  305. H = [_.rep for _ in reps]
  306. if not polys:
  307. return f.as_expr(), coeffs, H
  308. else:
  309. return f, coeffs, H
  310. @public
  311. def to_number_field(extension, theta=None, *, gen=None, alias=None):
  312. r"""
  313. Express one algebraic number in the field generated by another.
  314. Explanation
  315. ===========
  316. Given two algebraic numbers $\eta, \theta$, this function either expresses
  317. $\eta$ as an element of $\mathbb{Q}(\theta)$, or else raises an exception
  318. if $\eta \not\in \mathbb{Q}(\theta)$.
  319. This function is essentially just a convenience, utilizing
  320. :py:func:`~.field_isomorphism` (our solution of the Subfield Problem) to
  321. solve this, the Field Membership Problem.
  322. As an additional convenience, this function allows you to pass a list of
  323. algebraic numbers $\alpha_1, \alpha_2, \ldots, \alpha_n$ instead of $\eta$.
  324. It then computes $\eta$ for you, as a solution of the Primitive Element
  325. Problem, using :py:func:`~.primitive_element` on the list of $\alpha_i$.
  326. Examples
  327. ========
  328. >>> from sympy import sqrt, to_number_field
  329. >>> eta = sqrt(2)
  330. >>> theta = sqrt(2) + sqrt(3)
  331. >>> a = to_number_field(eta, theta)
  332. >>> print(type(a))
  333. <class 'sympy.core.numbers.AlgebraicNumber'>
  334. >>> a.root
  335. sqrt(2) + sqrt(3)
  336. >>> print(a)
  337. sqrt(2)
  338. >>> a.coeffs()
  339. [1/2, 0, -9/2, 0]
  340. We get an :py:class:`~.AlgebraicNumber`, whose ``.root`` is $\theta$, whose
  341. value is $\eta$, and whose ``.coeffs()`` show how to write $\eta$ as a
  342. $\mathbb{Q}$-linear combination in falling powers of $\theta$.
  343. Parameters
  344. ==========
  345. extension : :py:class:`~.Expr` or list of :py:class:`~.Expr`
  346. Either the algebraic number that is to be expressed in the other field,
  347. or else a list of algebraic numbers, a primitive element for which is
  348. to be expressed in the other field.
  349. theta : :py:class:`~.Expr`, None, optional (default=None)
  350. If an :py:class:`~.Expr` representing an algebraic number, behavior is
  351. as described under **Explanation**. If ``None``, then this function
  352. reduces to a shorthand for calling :py:func:`~.primitive_element` on
  353. ``extension`` and turning the computed primitive element into an
  354. :py:class:`~.AlgebraicNumber`.
  355. gen : :py:class:`~.Symbol`, None, optional (default=None)
  356. If provided, this will be used as the generator symbol for the minimal
  357. polynomial in the returned :py:class:`~.AlgebraicNumber`.
  358. alias : str, :py:class:`~.Symbol`, None, optional (default=None)
  359. If provided, this will be used as the alias symbol for the returned
  360. :py:class:`~.AlgebraicNumber`.
  361. Returns
  362. =======
  363. AlgebraicNumber
  364. Belonging to $\mathbb{Q}(\theta)$ and equaling $\eta$.
  365. Raises
  366. ======
  367. IsomorphismFailed
  368. If $\eta \not\in \mathbb{Q}(\theta)$.
  369. See Also
  370. ========
  371. field_isomorphism
  372. primitive_element
  373. """
  374. if hasattr(extension, '__iter__'):
  375. extension = list(extension)
  376. else:
  377. extension = [extension]
  378. if len(extension) == 1 and isinstance(extension[0], tuple):
  379. return AlgebraicNumber(extension[0], alias=alias)
  380. minpoly, coeffs = primitive_element(extension, gen, polys=True)
  381. root = sum([ coeff*ext for coeff, ext in zip(coeffs, extension) ])
  382. if theta is None:
  383. return AlgebraicNumber((minpoly, root), alias=alias)
  384. else:
  385. theta = sympify(theta)
  386. if not theta.is_AlgebraicNumber:
  387. theta = AlgebraicNumber(theta, gen=gen, alias=alias)
  388. coeffs = field_isomorphism(root, theta)
  389. if coeffs is not None:
  390. return AlgebraicNumber(theta, coeffs, alias=alias)
  391. else:
  392. raise IsomorphismFailed(
  393. "%s is not in a subfield of %s" % (root, theta.root))