def test_round_two(): # Poly must be monic, irreducible, and over ZZ: raises(ValueError, lambda: round_two(Poly(3 * x ** 2 + 1))) raises(ValueError, lambda: round_two(Poly(x ** 2 - 1))) raises(ValueError, lambda: round_two(Poly(x ** 2 + QQ(1, 2)))) # Test on many fields: cases = ( # A couple of cyclotomic fields: (cyclotomic_poly(5), DomainMatrix.eye(4, QQ), 125), (cyclotomic_poly(7), DomainMatrix.eye(6, QQ), -16807), # A couple of quadratic fields (one 1 mod 4, one 3 mod 4): (x ** 2 - 5, DM([[1, (1, 2)], [0, (1, 2)]], QQ), 5), (x ** 2 - 7, DM([[1, 0], [0, 1]], QQ), 28), # Dedekind's example of a field with 2 as essential disc divisor: (x ** 3 + x ** 2 - 2 * x + 8, DM([[1, 0, 0], [0, 1, 0], [0, (1, 2), (1, 2)]], QQ).transpose(), -503), # A bunch of cubics with various forms for F -- all of these require # second or third enlargements. (Five of them require a third, while the rest require just a second.) # F = 2^2 (x**3 + 3 * x**2 - 4 * x + 4, DM([((1, 2), (1, 4), (1, 4)), (0, (1, 2), (1, 2)), (0, 0, 1)], QQ).transpose(), -83), # F = 2^2 * 3 (x**3 + 3 * x**2 + 3 * x - 3, DM([((1, 2), 0, (1, 2)), (0, 1, 0), (0, 0, 1)], QQ).transpose(), -108), # F = 2^3 (x**3 + 5 * x**2 - x + 3, DM([((1, 4), 0, (3, 4)), (0, (1, 2), (1, 2)), (0, 0, 1)], QQ).transpose(), -31), # F = 2^2 * 5 (x**3 + 5 * x**2 - 5 * x - 5, DM([((1, 2), 0, (1, 2)), (0, 1, 0), (0, 0, 1)], QQ).transpose(), 1300), # F = 3^2 (x**3 + 3 * x**2 + 5, DM([((1, 3), (1, 3), (1, 3)), (0, 1, 0), (0, 0, 1)], QQ).transpose(), -135), # F = 3^3 (x**3 + 6 * x**2 + 3 * x - 1, DM([((1, 3), (1, 3), (1, 3)), (0, 1, 0), (0, 0, 1)], QQ).transpose(), 81), # F = 2^2 * 3^2 (x**3 + 6 * x**2 + 4, DM([((1, 3), (2, 3), (1, 3)), (0, 1, 0), (0, 0, (1, 2))], QQ).transpose(), -108), # F = 2^3 * 7 (x**3 + 7 * x**2 + 7 * x - 7, DM([((1, 4), 0, (3, 4)), (0, (1, 2), (1, 2)), (0, 0, 1)], QQ).transpose(), 49), # F = 2^2 * 13 (x**3 + 7 * x**2 - x + 5, DM([((1, 2), 0, (1, 2)), (0, 1, 0), (0, 0, 1)], QQ).transpose(), -2028), # F = 2^4 (x**3 + 7 * x**2 - 5 * x + 5, DM([((1, 4), 0, (3, 4)), (0, (1, 2), (1, 2)), (0, 0, 1)], QQ).transpose(), -140), # F = 5^2 (x**3 + 4 * x**2 - 3 * x + 7, DM([((1, 5), (4, 5), (4, 5)), (0, 1, 0), (0, 0, 1)], QQ).transpose(), -175), # F = 7^2 (x**3 + 8 * x**2 + 5 * x - 1, DM([((1, 7), (6, 7), (2, 7)), (0, 1, 0), (0, 0, 1)], QQ).transpose(), 49), # F = 2 * 5 * 7 (x**3 + 8 * x**2 - 2 * x + 6, DM([(1, 0, 0), (0, 1, 0), (0, 0, 1)], QQ).transpose(), -14700), # F = 2^2 * 3 * 5 (x**3 + 6 * x**2 - 3 * x + 8, DM([(1, 0, 0), (0, (1, 4), (1, 4)), (0, 0, 1)], QQ).transpose(), -675), # F = 2 * 3^2 * 7 (x**3 + 9 * x**2 + 6 * x - 8, DM([(1, 0, 0), (0, (1, 2), (1, 2)), (0, 0, 1)], QQ).transpose(), 3969), # F = 2^2 * 3^2 * 7 (x**3 + 15 * x**2 - 9 * x + 13, DM([((1, 6), (1, 3), (1, 6)), (0, 1, 0), (0, 0, 1)], QQ).transpose(), -5292), ) for f, B_exp, d_exp in cases: K = QQ.alg_field_from_poly(f) B = K.maximal_order().QQ_matrix d = K.discriminant() assert d == d_exp # The computed basis need not equal the expected one, but their quotient # must be unimodular: assert (B.inv()*B_exp).det()**2 == 1
def test_repr(): T = Poly(x**2 + 7) ZK, dK = round_two(T) P = prime_decomp(2, T, dK=dK, ZK=ZK) assert repr(P[0]) == '[ (2, (3*x + 1)/2) e=1, f=1 ]' assert P[0].repr(field_gen=theta) == '[ (2, (3*theta + 1)/2) e=1, f=1 ]' assert P[0].repr(field_gen=theta, just_gens=True) == '(2, (3*theta + 1)/2)'
def test_valuation_at_prime_ideal(): p = 7 T = Poly(cyclotomic_poly(p)) ZK, dK = round_two(T) P = prime_decomp(p, T, dK=dK, ZK=ZK) assert len(P) == 1 P0 = P[0] v = P0.valuation(p * ZK) assert v == P0.e # Test easy 0 case: assert P0.valuation(5 * ZK) == 0
def test_decomp_6(): # Another case where 2 divides the index. This is Dedekind's example of # an essential discriminant divisor. (See Cohen, Excercise 6.10.) T = Poly(x**3 + x**2 - 2 * x + 8) rad = {} ZK, dK = round_two(T, radicals=rad) p = 2 P = prime_decomp(p, T, dK=dK, ZK=ZK, radical=rad.get(p)) assert len(P) == 3 assert all(Pi.e == Pi.f == 1 for Pi in P) assert prod(Pi**Pi.e for Pi in P) == p * ZK
def test_decomp_4(): T = Poly(x**2 - 21) rad = {} ZK, dK = round_two(T, radicals=rad) # 21 is 1 mod 4, so field disc is 3*7, and theory says the # rational primes 3, 7 should be the square of a prime ideal. for p in [3, 7]: P = prime_decomp(p, T, dK=dK, ZK=ZK, radical=rad.get(p)) assert len(P) == 1 assert P[0].e == 2 assert P[0]**2 == p * ZK
def test_decomp_3(): T = Poly(x**2 - 35) rad = {} ZK, dK = round_two(T, radicals=rad) # 35 is 3 mod 4, so field disc is 4*5*7, and theory says each of the # rational primes 2, 5, 7 should be the square of a prime ideal. for p in [2, 5, 7]: P = prime_decomp(p, T, dK=dK, ZK=ZK, radical=rad.get(p)) assert len(P) == 1 assert P[0].e == 2 assert P[0]**2 == p * ZK
def test_pretty_printing(): d = -7 T = Poly(x**2 - d) rad = {} ZK, dK = round_two(T, radicals=rad) p = 2 P = prime_decomp(p, T, dK=dK, ZK=ZK, radical=rad.get(p)) assert repr(P[0]) == '[ (2, (3*x + 1)/2) e=1, f=1 ]' assert P[0]._pretty(field_gen=theta) == '[ (2, (3*theta + 1)/2) e=1, f=1 ]' assert P[0]._pretty(field_gen=theta, just_gens=True) == '(2, (3*theta + 1)/2)'
def test_decomp_8(): # This time we consider various cubics, and try factoring all primes # dividing the index. cases = ( x**3 + 3 * x**2 - 4 * x + 4, x**3 + 3 * x**2 + 3 * x - 3, x**3 + 5 * x**2 - x + 3, x**3 + 5 * x**2 - 5 * x - 5, x**3 + 3 * x**2 + 5, x**3 + 6 * x**2 + 3 * x - 1, x**3 + 6 * x**2 + 4, x**3 + 7 * x**2 + 7 * x - 7, x**3 + 7 * x**2 - x + 5, x**3 + 7 * x**2 - 5 * x + 5, x**3 + 4 * x**2 - 3 * x + 7, x**3 + 8 * x**2 + 5 * x - 1, x**3 + 8 * x**2 - 2 * x + 6, x**3 + 6 * x**2 - 3 * x + 8, x**3 + 9 * x**2 + 6 * x - 8, x**3 + 15 * x**2 - 9 * x + 13, ) def display(T, p, radical, P, I, J): """Useful for inspection, when running test manually.""" print('=' * 20) print(T, p, radical) for Pi in P: print(f' ({Pi!r})') print("I: ", I) print("J: ", J) print(f'Equal: {I == J}') inspect = False for g in cases: T = Poly(g) rad = {} ZK, dK = round_two(T, radicals=rad) dT = T.discriminant() f_squared = dT // dK F = factorint(f_squared) for p in F: radical = rad.get(p) P = prime_decomp(p, T, dK=dK, ZK=ZK, radical=radical) I = prod(Pi**Pi.e for Pi in P) J = p * ZK if inspect: display(T, p, radical, P, I, J) assert I == J
def test_two_elt_rep(): ell = 7 T = Poly(cyclotomic_poly(ell)) ZK, dK = round_two(T) for p in [29, 13, 11, 5]: P = prime_decomp(p, T) for Pi in P: # We have Pi in two-element representation, and, because we are # looking at a cyclotomic field, this was computed by the "easy" # method that just factors T mod p. We will now convert this to # a set of Z-generators, then convert that back into a two-element # rep. The latter need not be identical to the two-elt rep we # already have, but it must have the same HNF. H = p * ZK + Pi.alpha * ZK gens = H.basis_element_pullbacks() # Note: we could supply f = Pi.f, but prefer to test behavior without it. b = _two_elt_rep(gens, ZK, p) if b != Pi.alpha: H2 = p * ZK + b * ZK assert H2 == H
def test_decomp_5(): # Here is our first test of the "hard case" of prime decomposition. # We work in a quadratic extension Q(sqrt(d)) where d is 1 mod 4, and # we consider the factorization of the rational prime 2, which divides # the index. # Theory says the form of p's factorization depends on the residue of # d mod 8, so we consider both cases, d = 1 mod 8 and d = 5 mod 8. for d in [-7, -3]: T = Poly(x**2 - d) rad = {} ZK, dK = round_two(T, radicals=rad) p = 2 P = prime_decomp(p, T, dK=dK, ZK=ZK, radical=rad.get(p)) if d % 8 == 1: assert len(P) == 2 assert all(P[i].e == 1 and P[i].f == 1 for i in range(2)) assert prod(Pi**Pi.e for Pi in P) == p * ZK else: assert d % 8 == 5 assert len(P) == 1 assert P[0].e == 1 assert P[0].f == 2 assert P[0].as_submodule() == p * ZK
def _do_round_two(self): from sympy.polys.numberfields.basis import round_two ZK, dK = round_two(self.ext.minpoly, radicals=self._nilradicals_mod_p) self._maximal_order = ZK self._discriminant = dK