basis.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. """Computing integral bases for number fields. """
  2. from sympy.polys.polytools import Poly
  3. from sympy.polys.domains.algebraicfield import AlgebraicField
  4. from sympy.polys.domains.integerring import ZZ
  5. from sympy.polys.domains.rationalfield import QQ
  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 an irreducible polynomial
  83. *T* over :ref:`ZZ` or :ref:`QQ`. This computes an integral basis and the
  84. discriminant for the field $K = \mathbb{Q}[x]/(T(x))$.
  85. Alternatively, you may pass an :py:class:`~.AlgebraicField` instance, in
  86. place of the polynomial *T*, in which case the algorithm is applied to the
  87. minimal polynomial for the field's primitive element.
  88. Ordinarily this function need not be called directly, as one can instead
  89. access the :py:meth:`~.AlgebraicField.maximal_order`,
  90. :py:meth:`~.AlgebraicField.integral_basis`, and
  91. :py:meth:`~.AlgebraicField.discriminant` methods of an
  92. :py:class:`~.AlgebraicField`.
  93. Examples
  94. ========
  95. Working through an AlgebraicField:
  96. >>> from sympy import Poly, QQ
  97. >>> from sympy.abc import x
  98. >>> T = Poly(x ** 3 + x ** 2 - 2 * x + 8)
  99. >>> K = QQ.alg_field_from_poly(T, "theta")
  100. >>> print(K.maximal_order())
  101. Submodule[[2, 0, 0], [0, 2, 0], [0, 1, 1]]/2
  102. >>> print(K.discriminant())
  103. -503
  104. >>> print(K.integral_basis(fmt='sympy'))
  105. [1, theta, theta/2 + theta**2/2]
  106. Calling directly:
  107. >>> from sympy import Poly
  108. >>> from sympy.abc import x
  109. >>> from sympy.polys.numberfields.basis import round_two
  110. >>> T = Poly(x ** 3 + x ** 2 - 2 * x + 8)
  111. >>> print(round_two(T))
  112. (Submodule[[2, 0, 0], [0, 2, 0], [0, 1, 1]]/2, -503)
  113. The nilradicals mod $p$ that are sometimes computed during the Round Two
  114. algorithm may be useful in further calculations. Pass a dictionary under
  115. `radicals` to receive these:
  116. >>> T = Poly(x**3 + 3*x**2 + 5)
  117. >>> rad = {}
  118. >>> ZK, dK = round_two(T, radicals=rad)
  119. >>> print(rad)
  120. {3: Submodule[[-1, 1, 0], [-1, 0, 1]]}
  121. Parameters
  122. ==========
  123. T : :py:class:`~.Poly`, :py:class:`~.AlgebraicField`
  124. Either (1) the irreducible polynomial over :ref:`ZZ` or :ref:`QQ`
  125. defining the number field, or (2) an :py:class:`~.AlgebraicField`
  126. representing the number field itself.
  127. radicals : dict, optional
  128. This is a way for any $p$-radicals (if computed) to be returned by
  129. reference. If desired, pass an empty dictionary. If the algorithm
  130. reaches the point where it computes the nilradical mod $p$ of the ring
  131. of integers $Z_K$, then an $\mathbb{F}_p$-basis for this ideal will be
  132. stored in this dictionary under the key ``p``. This can be useful for
  133. other algorithms, such as prime decomposition.
  134. Returns
  135. =======
  136. Pair ``(ZK, dK)``, where:
  137. ``ZK`` is a :py:class:`~sympy.polys.numberfields.modules.Submodule`
  138. representing the maximal order.
  139. ``dK`` is the discriminant of the field $K = \mathbb{Q}[x]/(T(x))$.
  140. See Also
  141. ========
  142. .AlgebraicField.maximal_order
  143. .AlgebraicField.integral_basis
  144. .AlgebraicField.discriminant
  145. References
  146. ==========
  147. .. [1] Cohen, H. *A Course in Computational Algebraic Number Theory.*
  148. """
  149. K = None
  150. if isinstance(T, AlgebraicField):
  151. K, T = T, T.ext.minpoly_of_element()
  152. if ( not T.is_univariate
  153. or not T.is_irreducible
  154. or T.domain not in [ZZ, QQ]):
  155. raise ValueError('Round 2 requires an irreducible univariate polynomial over ZZ or QQ.')
  156. T, _ = T.make_monic_over_integers_by_scaling_roots()
  157. n = T.degree()
  158. D = T.discriminant()
  159. D_modulus = ZZ.from_sympy(abs(D))
  160. # D must be 0 or 1 mod 4 (see Cohen Sec 4.4), which ensures we can write
  161. # it in the form D = D_0 * F**2, where D_0 is 1 or a fundamental discriminant.
  162. _, F = extract_fundamental_discriminant(D)
  163. Ztheta = PowerBasis(K or T)
  164. H = Ztheta.whole_submodule()
  165. nilrad = None
  166. while F:
  167. # Next prime:
  168. p, e = F.popitem()
  169. U_bar, m = _apply_Dedekind_criterion(T, p)
  170. if m == 0:
  171. continue
  172. # For a given prime p, the first enlargement of the order spanned by
  173. # the current basis can be done in a simple way:
  174. U = Ztheta.element_from_poly(Poly(U_bar, domain=ZZ))
  175. # TODO:
  176. # Theory says only first m columns of the U//p*H term below are needed.
  177. # Could be slightly more efficient to use only those. Maybe `Submodule`
  178. # class should support a slice operator?
  179. H = H.add(U // p * H, hnf_modulus=D_modulus)
  180. if e <= m:
  181. continue
  182. # A second, and possibly more, enlargements for p will be needed.
  183. # These enlargements require a more involved procedure.
  184. q = p
  185. while q < n:
  186. q *= p
  187. H1, nilrad = _second_enlargement(H, p, q)
  188. while H1 != H:
  189. H = H1
  190. H1, nilrad = _second_enlargement(H, p, q)
  191. # Note: We do not store all nilradicals mod p, only the very last. This is
  192. # because, unless computed against the entire integral basis, it might not
  193. # be accurate. (In other words, if H was not already equal to ZK when we
  194. # passed it to `_second_enlargement`, then we can't trust the nilradical
  195. # so computed.) Example: if T(x) = x ** 3 + 15 * x ** 2 - 9 * x + 13, then
  196. # F is divisible by 2, 3, and 7, and the nilradical mod 2 as computed above
  197. # will not be accurate for the full, maximal order ZK.
  198. if nilrad is not None and isinstance(radicals, dict):
  199. radicals[p] = nilrad
  200. ZK = H
  201. # Pre-set expensive boolean properties which we already know to be true:
  202. ZK._starts_with_unity = True
  203. ZK._is_sq_maxrank_HNF = True
  204. dK = (D * ZK.matrix.det() ** 2) // ZK.denom ** (2 * n)
  205. return ZK, dK