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