Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions lmfdb/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,14 @@ def test_complete(self):
("nf_fields", {'degree': 2, 'r2': 1, 'regulator': {'$gte': 0, '$lte': 0.999}}, "number fields with degree 2, signature [0,1], regulator less than 1.00"),
("nf_fields", {'degree': 4, 'r2': 1, 'regulator': {'$gte': 0, '$lte': 0.5}}, "number fields with degree 4, signature [2,1], regulator less than 0.51"),
("nf_fields", {'degree': 7, 'r2': 1, 'regulator': {'$gte': 0, '$lte': 6}}, "number fields with degree 7, signature [5,1], regulator less than 6.10"),
# Quadratic fields unramified outside S have discriminant dividing 8*prod_{odd p in S} p (issue #7017)
("nf_fields", {'degree': 2, 'ramps': {'$containedin': [1009, 1013]}}, "number fields with degree 2, unramified outside {1009,1013}"),
# CM fields are totally imaginary, forcing the signature (here reducing to the class number bound)
("nf_fields", {'degree': 2, 'cm': True, 'class_number': 1}, "number fields with signature [0,1], class number at most 100 (except 98)"),
# No CM fields have odd degree, so the search is (vacuously) complete
("nf_fields", {'degree': 3, 'cm': True}, "number fields with incompatible conditions: CM and signature"),
# For imaginary quadratic fields the narrow class number equals the class number
("nf_fields", {'degree': 2, 'r2': 1, 'narrow_class_number': 1}, "number fields with signature [0,1], class number at most 100 (except 98)"),
("artin_reps", {'GaloisLabel': '6T6', 'Conductor': {'$gte': 1, '$lte': 20000}}, "Artin representations with group 6T6, and conductor at most 22497"),
("gps_groups", {'order': {'$gte': 300, '$lte': 500}}, "groups of order at most 2000 except orders larger than 500 that are multiples of 128"),
("gps_groups", {'perfect': True, 'order': {'$gte': 20000, '$lte': 40000}}, "perfect groups of order at most 50000"),
Expand All @@ -370,8 +378,12 @@ def test_complete(self):
("belyi_galmaps", {'deg': {'$gte': 2, '$lte': 4}}, "Belyi maps of degree at most 6"),
("lf_fields", {'p': 2, 'n': 16}, "p-adic fields of degree at most 23 and residue characteristic at most 199"),
("lf_fields", {'p': 3, 'e': 9, 'f': 2}, "p-adic fields of degree at most 23 and residue characteristic at most 199"),
# Discriminant exponent c = 0 means unramified (e = 1), so n = f (issue #7017)
("lf_fields", {'p': {'$gte': 1, '$lte': 100}, 'c': 0, 'f': {'$gte': 1, '$lte': 10}}, "p-adic fields of degree at most 23 and residue characteristic at most 199"),
("lf_families", {'p': 2, 'e': 4, 'f0': {'$gte': 1, '$lte': 2}, 'e0': 2, 'f': 2}, "families of p-adic extensions with absolute degree at most 47, base degree at most 15 and residue characteristic at most 199"),
("char_dirichlet", {'modulus': {'$gte': 40, '$lte': 100}}, "Dirichlet characters with modulus at most a million"),
# A primitive character has conductor equal to its modulus (issue #7017)
("char_dirichlet", {'conductor': {'$gte': 1, '$lte': 1000}, 'is_primitive': True}, "primitive Dirichlet characters with conductor at most a million"),
("hgm_families", {'degree': {'$gte': 4, '$lte': 6}}, "hypergeometric families with degree at most 7"),
("gps_transitive", {'n': 18, 'solv': 1}, "transitive groups of degree at most 47 (except 32)"),
("gps_transitive", {'n': 32, 'order': 384}, "transitive groups of degree 32 and order at most 511"),
Expand All @@ -394,6 +406,10 @@ def test_complete(self):
("bmf_forms", {'field_disc': {'$gte': -120, '$lte': -3}, 'level_norm': {'$gte': 1, '$lte': 4000}}),
("ec_nfcurves", {'field_label': '7.7.20134393.1', 'conductor_norm': {'$gte': 1, '$lte': 50}}),
("nf_fields", {'degree': 6, 'disc_abs': {'$gte': 1, '$lte': 20000000}}),
# Quadratic disc bound 8*1009*1013*100003 exceeds the degree-2 completeness bound
("nf_fields", {'degree': 2, 'ramps': {'$containedin': [1009, 1013, 100003]}}),
# CM constrains only the signature, not the discriminant, so degree 4 CM alone is not complete
("nf_fields", {'degree': 4, 'cm': True}),
("nf_fields", {'degree': 2, 'r2': 1, 'regulator': 1}),
("nf_fields", {'degree': 4, 'r2': 2, 'regulator': {'$gte': 0.962, '$lte': 0.963}}), # Infinitely many degree 4 CM fields with regulator 0.962423650119
("nf_fields", {'degree': 6, 'r2': 3, 'regulator': {'$gte': 2.101, '$lte': 2.102}}), # Infinitely many degree 6 CM fields with regulator 2.10181872849
Expand All @@ -404,8 +420,12 @@ def test_complete(self):
("av_fq_isog", {'g': 6, 'q': 3}),
("belyi_galmaps", {'deg': 8}),
("lf_fields", {'p': 2, 'n': 24}),
# c = 0 gives e = 1, but n = f = 30 still exceeds the degree bound
("lf_fields", {'p': 2, 'c': 0, 'f': 30}),
("lf_families", {'p': 2, 'e': 4, 'f0': {'$gte': 1, '$lte': 4}, 'e0': 2, 'f': 2}),
("char_dirichlet", {'modulus': {'$gte': 400000, '$lte': 3000000}}),
# Without primitivity, a conductor bound does not bound the modulus
("char_dirichlet", {'conductor': {'$gte': 1, '$lte': 1000}}),
("hgm_families", {'degree': 8}),
("gps_transitive", {'n': 32, 'solv': 1}),
("gps_st", {'rational': True, 'weight': 1, 'degree': 8}),
Expand Down
66 changes: 62 additions & 4 deletions lmfdb/utils/completeness.py
Original file line number Diff line number Diff line change
Expand Up @@ -1033,6 +1033,20 @@ def __call__(self, query):
query["char_order"] = 1


class UnramifiedFiller:
"""
Infers ramification data for p-adic field queries.

The discriminant exponent c of a p-adic field is 0 exactly when the field is
unramified, i.e. when the ramification index e is 1. So when c is known to be 0
we can fill in e = 1; combined with MulFiller("n", "e", "f") this also gives n = f.
"""

def __call__(self, query):
if "c" in query and IntegerSet(query["c"]).is_subset(IntegerSet(0)):
query["e"] = IntegerSet(query.get("e")).intersection(IntegerSet(1))


#################################
# Specific CompletenessCheckers #
#################################
Expand Down Expand Up @@ -2218,6 +2232,29 @@ def clear_S(self, n, S, nram, galt, reasons, update_galt=True):

return False

def clear_S_disc(self, n, S, r2opts, reasons):
"""
Prove completeness for fields unramified outside S using a discriminant bound.

If a degree n field is unramified outside a finite set of primes S, then its
absolute discriminant divides ``prod_{p in S} p^{e_p}``, where ``e_p`` is an upper
bound for the p-adic valuation of the discriminant of a degree n field. If this
product is within our discriminant completeness bound for every relevant signature,
then the search is complete.

Currently only implemented for n = 2: the discriminant of the quadratic field
Q(sqrt(m)) (m squarefree) is m or 4m, so it divides ``8 * prod_{odd p in S} p``,
with the factor of 8 present only when 2 lies in S (otherwise the field is
unramified at 2 and its discriminant is odd).
"""
if n != 2 or not r2opts or n >= len(self._maxD):
return False
M = prod(8 if p == 2 else p for p in S)
if all(M <= self._maxD[n][r2] for r2 in r2opts):
reasons.add("degree 2, unramified outside {%s}" % ",".join(str(p) for p in sorted(S)))
return True
return False

def galt(self, n, gal, isgal, cyc, ab, solv):
"""
Compute the possible degree n transitive Galois groups satisfying the given constraints.
Expand Down Expand Up @@ -2413,13 +2450,26 @@ def _one_n(self, db, query, reasons):
r2opts = [r2 for r2 in r2opts if r2 % 2 == 0]
elif sign == -1:
r2opts = [r2 for r2 in r2opts if r2 % 2 == 1]
if query.get("cm") is True:
# A CM field is a totally imaginary quadratic extension of a totally real
# field, so it has signature [0, n/2] (in particular its degree n is even).
r2opts = [r2 for r2 in r2opts if 2 * r2 == n]
if not r2opts:
reasons.add("incompatible conditions: CM and signature")
return True, None
if not r2opts:
reasons.add("incompatible conditions: signature and discriminant")
return True, None
if n == 2 and r2opts == [1]:
# Imaginary quadratic fields, where we can use Mark Watkins' paper (Class groups of imaginary quadratic fields) to guarantee completeness based on class number
h = query.get("class_number")
C = query.get("class_group")
# Imaginary quadratic fields have r_1 = 0, so the narrow class number/group
# coincides with the ordinary class number/group.
if h is None:
h = query.get("narrow_class_number")
if C is None:
C = query.get("narrow_class_group")
if isinstance(C, list) and h is None:
h = prod(C)
h = IntegerSet(h)
Expand Down Expand Up @@ -2511,8 +2561,11 @@ def _one_n(self, db, query, reasons):
return True, None
if S is not None and nram is not None:
nram = min([len(S), nram])
if S is not None and self.clear_S(n, S, nram, galt, reasons):
return True, caveat
if S is not None:
if self.clear_S(n, S, nram, galt, reasons):
return True, caveat
if self.clear_S_disc(n, S, r2opts, reasons):
return True, caveat

# Can also iterate over valid discriminants in a discriminant range
if D.restricted():
Expand Down Expand Up @@ -2904,7 +2957,7 @@ def __call__(self, db, query):
CompletenessChecker("nf_fields", [((), NFBound())])


CompletenessChecker("lf_fields", [(("n", "p"), Bound(23, 199), "p-adic fields of degree at most 23 and residue characteristic at most 199")], fill=[MulFiller("n", "e", "f")])
CompletenessChecker("lf_fields", [(("n", "p"), Bound(23, 199), "p-adic fields of degree at most 23 and residue characteristic at most 199")], fill=[UnramifiedFiller(), MulFiller("n", "e", "f")])


CompletenessChecker("lf_families", [(("n0", "n", "p"), Bound(1, 47, 199), "families of p-adic fields of degree at most 47 and residue characteristic at most 199"),
Expand All @@ -2917,7 +2970,12 @@ def __call__(self, db, query):
MulFiller("n_absolute", "e_absolute", "f_absolute")])


CompletenessChecker("char_dirichlet", [("modulus", Bound(1000000), "Dirichlet characters with modulus at most a million")])
CompletenessChecker("char_dirichlet", [
("modulus", Bound(1000000), "Dirichlet characters with modulus at most a million"),
# A primitive Dirichlet character has conductor equal to its modulus, so a bound on
# the conductor also bounds the modulus.
("conductor", Bound(1000000), "primitive Dirichlet characters with conductor at most a million",
None, lambda query: query.get("is_primitive") is True)])


CompletenessChecker("artin_reps", [(("GaloisLabel", "Conductor"), ArtinBound())])
Expand Down
Loading