basis.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. """Computing integral bases for number fields. """
  2. from sympy.polys.polytools import Poly
  3. from sympy.polys.domains.integerring import ZZ
  4. from sympy.polys.domains.rationalfield import QQ
  5. from sympy.polys.polyerrors import CoercionFailed
  6. from sympy.utilities.decorator import public
  7. from .modules import ModuleEndomorphism, ModuleHomomorphism, PowerBasis
  8. from .utilities import extract_fundamental_discriminant
  9. def _apply_Dedekind_criterion(T, p):
  10. r"""
  11. Apply the "Dedekind criterion" to test whether the order needs to be
  12. enlarged relative to a given prime *p*.
  13. """
  14. x = T.gen
  15. T_bar = Poly(T, modulus=p)
  16. lc, fl = T_bar.factor_list()
  17. assert lc == 1
  18. g_bar = Poly(1, x, modulus=p)
  19. for ti_bar, _ in fl:
  20. g_bar *= ti_bar
  21. h_bar = T_bar // g_bar
  22. g = Poly(g_bar, domain=ZZ)
  23. h = Poly(h_bar, domain=ZZ)
  24. f = (g * h - T) // p
  25. f_bar = Poly(f, modulus=p)
  26. Z_bar = f_bar
  27. for b in [g_bar, h_bar]:
  28. Z_bar = Z_bar.gcd(b)
  29. U_bar = T_bar // Z_bar
  30. m = Z_bar.degree()
  31. return U_bar, m
  32. def nilradical_mod_p(H, p, q=None):
  33. r"""
  34. Compute the nilradical mod *p* for a given order *H*, and prime *p*.
  35. Explanation
  36. ===========
  37. This is the ideal $I$ in $H/pH$ consisting of all elements some positive
  38. power of which is zero in this quotient ring, i.e. is a multiple of *p*.
  39. Parameters
  40. ==========
  41. H : :py:class:`~.Submodule`
  42. The given order.
  43. p : int
  44. The rational prime.
  45. q : int, optional
  46. If known, the smallest power of *p* that is $>=$ the dimension of *H*.
  47. If not provided, we compute it here.
  48. Returns
  49. =======
  50. :py:class:`~.Module` representing the nilradical mod *p* in *H*.
  51. References
  52. ==========
  53. .. [1] Cohen, H. *A Course in Computational Algebraic Number Theory*.
  54. (See Lemma 6.1.6.)
  55. """
  56. n = H.n
  57. if q is None:
  58. q = p
  59. while q < n:
  60. q *= p
  61. phi = ModuleEndomorphism(H, lambda x: x**q)
  62. return phi.kernel(modulus=p)
  63. def _second_enlargement(H, p, q):
  64. r"""
  65. Perform the second enlargement in the Round Two algorithm.
  66. """
  67. Ip = nilradical_mod_p(H, p, q=q)
  68. B = H.parent.submodule_from_matrix(H.matrix * Ip.matrix, denom=H.denom)
  69. C = B + p*H
  70. E = C.endomorphism_ring()
  71. phi = ModuleHomomorphism(H, E, lambda x: E.inner_endomorphism(x))
  72. gamma = phi.kernel(modulus=p)
  73. G = H.parent.submodule_from_matrix(H.matrix * gamma.matrix, denom=H.denom * p)
  74. H1 = G + H
  75. return H1, Ip
  76. @public
  77. def round_two(T, radicals=None):
  78. r"""
  79. Zassenhaus's "Round 2" algorithm.
  80. Explanation
  81. ===========
  82. Carry out Zassenhaus's "Round 2" algorithm on a monic irreducible
  83. polynomial *T* over :ref:`ZZ`. This computes an integral basis and the
  84. discriminant for the field $K = \mathbb{Q}[x]/(T(x))$.
  85. Ordinarily this function need not be called directly, as one can instead
  86. access the :py:meth:`~.AlgebraicField.maximal_order`,
  87. :py:meth:`~.AlgebraicField.integral_basis`, and
  88. :py:meth:`~.AlgebraicField.discriminant` methods of an
  89. :py:class:`~.AlgebraicField`.
  90. Examples
  91. ========
  92. Working through an AlgebraicField:
  93. >>> from sympy import Poly, QQ
  94. >>> from sympy.abc import x, theta
  95. >>> T = Poly(x ** 3 + x ** 2 - 2 * x + 8)
  96. >>> K = QQ.algebraic_field((T, theta))
  97. >>> print(K.maximal_order())
  98. Submodule[[2, 0, 0], [0, 2, 0], [0, 1, 1]]/2
  99. >>> print(K.discriminant())
  100. -503
  101. >>> print(K.integral_basis(fmt='sympy'))
  102. [1, theta, theta**2/2 + theta/2]
  103. Calling directly:
  104. >>> from sympy import Poly
  105. >>> from sympy.abc import x
  106. >>> from sympy.polys.numberfields.basis import round_two
  107. >>> T = Poly(x ** 3 + x ** 2 - 2 * x + 8)
  108. >>> print(round_two(T))
  109. (Submodule[[2, 0, 0], [0, 2, 0], [0, 1, 1]]/2, -503)
  110. The nilradicals mod $p$ that are sometimes computed during the Round Two
  111. algorithm may be useful in further calculations. Pass a dictionary under
  112. `radicals` to receive these:
  113. >>> T = Poly(x**3 + 3*x**2 + 5)
  114. >>> rad = {}
  115. >>> ZK, dK = round_two(T, radicals=rad)
  116. >>> print(rad)
  117. {3: Submodule[[-1, 1, 0], [-1, 0, 1]]}
  118. Parameters
  119. ==========
  120. T : :py:class:`~.Poly`
  121. The irreducible monic polynomial over :ref:`ZZ` defining the number
  122. field.
  123. radicals : dict, optional
  124. This is a way for any $p$-radicals (if computed) to be returned by
  125. reference. If desired, pass an empty dictionary. If the algorithm
  126. reaches the point where it computes the nilradical mod $p$ of the ring
  127. of integers $Z_K$, then an $\mathbb{F}_p$-basis for this ideal will be
  128. stored in this dictionary under the key ``p``. This can be useful for
  129. other algorithms, such as prime decomposition.
  130. Returns
  131. =======
  132. Pair ``(ZK, dK)``, where:
  133. ``ZK`` is a :py:class:`~sympy.polys.numberfields.modules.Submodule`
  134. representing the maximal order.
  135. ``dK`` is the discriminant of the field $K = \mathbb{Q}[x]/(T(x))$.
  136. See Also
  137. ========
  138. .AlgebraicField.maximal_order
  139. .AlgebraicField.integral_basis
  140. .AlgebraicField.discriminant
  141. References
  142. ==========
  143. .. [1] Cohen, H. *A Course in Computational Algebraic Number Theory.*
  144. """
  145. if T.domain == QQ:
  146. try:
  147. T = Poly(T, domain=ZZ)
  148. except CoercionFailed:
  149. pass # Let the error be raised by the next clause.
  150. if ( not T.is_univariate
  151. or not T.is_irreducible
  152. or not T.is_monic
  153. or not T.domain == ZZ):
  154. raise ValueError('Round 2 requires a monic irreducible univariate polynomial over ZZ.')
  155. n = T.degree()
  156. D = T.discriminant()
  157. D_modulus = ZZ.from_sympy(abs(D))
  158. # D must be 0 or 1 mod 4 (see Cohen Sec 4.4), which ensures we can write
  159. # it in the form D = D_0 * F**2, where D_0 is 1 or a fundamental discriminant.
  160. _, F = extract_fundamental_discriminant(D)
  161. Ztheta = PowerBasis(T)
  162. H = Ztheta.whole_submodule()
  163. nilrad = None
  164. while F:
  165. # Next prime:
  166. p, e = F.popitem()
  167. U_bar, m = _apply_Dedekind_criterion(T, p)
  168. if m == 0:
  169. continue
  170. # For a given prime p, the first enlargement of the order spanned by
  171. # the current basis can be done in a simple way:
  172. U = Ztheta.element_from_poly(Poly(U_bar, domain=ZZ))
  173. # TODO:
  174. # Theory says only first m columns of the U//p*H term below are needed.
  175. # Could be slightly more efficient to use only those. Maybe `Submodule`
  176. # class should support a slice operator?
  177. H = H.add(U // p * H, hnf_modulus=D_modulus)
  178. if e <= m:
  179. continue
  180. # A second, and possibly more, enlargements for p will be needed.
  181. # These enlargements require a more involved procedure.
  182. q = p
  183. while q < n:
  184. q *= p
  185. H1, nilrad = _second_enlargement(H, p, q)
  186. while H1 != H:
  187. H = H1
  188. H1, nilrad = _second_enlargement(H, p, q)
  189. # Note: We do not store all nilradicals mod p, only the very last. This is
  190. # because, unless computed against the entire integral basis, it might not
  191. # be accurate. (In other words, if H was not already equal to ZK when we
  192. # passed it to `_second_enlargement`, then we can't trust the nilradical
  193. # so computed.) Example: if T(x) = x ** 3 + 15 * x ** 2 - 9 * x + 13, then
  194. # F is divisible by 2, 3, and 7, and the nilradical mod 2 as computed above
  195. # will not be accurate for the full, maximal order ZK.
  196. if nilrad is not None and isinstance(radicals, dict):
  197. radicals[p] = nilrad
  198. ZK = H
  199. # Pre-set expensive boolean properties which we already know to be true:
  200. ZK._starts_with_unity = True
  201. ZK._is_sq_maxrank_HNF = True
  202. dK = (D * ZK.matrix.det() ** 2) // ZK.denom ** (2 * n)
  203. return ZK, dK