Skip to content

Commit 6b61ac3

Browse files
Merge pull request #6772 from hgu2699/abvarfq-cyclic-isogeny
Add cyclic isogeny invariants, search options, and tests for AV/Fq Issue #6696
2 parents 8eb2275 + c459dd3 commit 6b61ac3

File tree

5 files changed

+151
-12
lines changed

5 files changed

+151
-12
lines changed

lmfdb/abvar/fq/isog_class.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ def __init__(self, dbdata):
8484
dbdata["hyp_count"] = None
8585
if "jacobian_count" not in dbdata:
8686
dbdata["jacobian_count"] = None
87+
# New invariants: cyclicity and noncyclic primes
88+
if "is_cyclic" not in dbdata:
89+
dbdata["is_cyclic"] = None
90+
if "noncyclic_primes" not in dbdata:
91+
dbdata["noncyclic_primes"] = []
8792
self.__dict__.update(dbdata)
8893

8994
@classmethod
@@ -232,9 +237,15 @@ def properties(self):
232237
("Primitive", "yes" if self.is_primitive else "no"),
233238
]
234239
if self.has_principal_polarization != 0:
235-
props += [("Principally polarizable", "yes" if self.has_principal_polarization == 1 else "no")]
240+
props += [(
241+
"Principally polarizable",
242+
"yes" if self.has_principal_polarization == 1 else "no",
243+
)]
236244
if self.has_jacobian != 0:
237-
props += [("Contains a Jacobian", "yes" if self.has_jacobian == 1 else "no")]
245+
props += [(
246+
"Contains a Jacobian",
247+
"yes" if self.has_jacobian == 1 else "no",
248+
)]
238249
return props
239250

240251
# at some point we were going to display the weil_numbers instead of the frobenius angles

lmfdb/abvar/fq/main.py

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from lmfdb.utils import redirect_no_cache
2424
from lmfdb.utils.search_columns import SearchColumns, SearchCol, MathCol, LinkCol, ProcessedCol, CheckCol, CheckMaybeCol
2525
from lmfdb.abvar.fq.download import AbvarFq_download
26+
from lmfdb.utils.search_parsing import parse_primes
2627

2728
logger = make_logger("abvarfq")
2829

@@ -399,8 +400,31 @@ def nbsp(knowl, label):
399400
jacobian = YesNoMaybeBox(
400401
"jacobian",
401402
label="Jacobian",
402-
knowl="ag.jacobian"
403+
knowl="ag.jacobian",
403404
)
405+
406+
# Cyclic group of points (advanced yes/no box)
407+
cyclic = YesNoBox(
408+
"cyclic",
409+
label="Cyclic group of points",
410+
knowl="av.fq.cyclic_group_points",
411+
advanced=True,
412+
)
413+
414+
# Non-cyclic primes with mode selector (include / exactly / subset)
415+
noncyclic_mode = SubsetBox(
416+
"noncyclic_primes_mode",
417+
advanced=True,
418+
)
419+
noncyclic_primes = TextBoxWithSelect(
420+
"noncyclic_primes",
421+
label="Non-cyclic primes",
422+
select_box=noncyclic_mode,
423+
knowl="av.fq.noncyclic_primes",
424+
example="2 or 2,3,5",
425+
advanced=True,
426+
)
427+
404428
uglabel = "Use %s in the following inputs" % display_knowl("av.decomposition", "Geometric decomposition")
405429
use_geom_decomp = CheckBox(
406430
"use_geom_decomp",
@@ -506,6 +530,7 @@ def short_label(d):
506530
[newton_polygon, abvar_point_count, curve_point_count, simple_factors],
507531
[newton_elevation, jac_cnt, hyp_cnt, twist_count, max_twist_degree],
508532
[angle_rank, angle_corank, geom_deg, p_corank, geom_squarefree],
533+
[cyclic, noncyclic_primes],
509534
use_geom_refine,
510535
[dim1, dim2, dim3, dim4, dim5],
511536
[dim1d, dim2d, dim3d, number_field, galois_group],
@@ -516,6 +541,7 @@ def short_label(d):
516541
[g, geom_simple],
517542
[initial_coefficients, polarizable],
518543
[p_rank, jacobian],
544+
[cyclic, noncyclic_primes],
519545
[p_corank, geom_squarefree],
520546
[jac_cnt, hyp_cnt],
521547
[angle_rank, angle_corank],
@@ -536,6 +562,7 @@ def short_label(d):
536562
[count],
537563
]
538564

565+
539566
def search_types(self, info):
540567
return self._search_again(info, [
541568
('List', 'List of isogeny classes'),
@@ -552,6 +579,7 @@ def common_parse(info, query):
552579
parse_bool(info, query, "primitive", qfield="is_primitive")
553580
parse_bool_unknown(info, query, "jacobian", qfield="has_jacobian")
554581
parse_bool_unknown(info, query, "polarizable", qfield="has_principal_polarization")
582+
parse_bool(info, query, "cyclic", qfield="is_cyclic")
555583
parse_ints(info, query, "p_rank")
556584
parse_ints(info, query, "p_corank", qfield="p_rank_deficit")
557585
parse_ints(info, query, "angle_rank")
@@ -561,6 +589,14 @@ def common_parse(info, query):
561589
parse_ints(info, query, "hyp_cnt", qfield="hyp_count", name="Number of Hyperelliptic Jacobians")
562590
parse_ints(info, query, "twist_count")
563591
parse_ints(info, query, "max_twist_degree")
592+
parse_primes(
593+
info,
594+
query,
595+
"noncyclic_primes",
596+
qfield="noncyclic_primes",
597+
mode=info.get("noncyclic_primes_mode"),
598+
)
599+
564600
parse_ints(info, query, "size")
565601
parse_newton_polygon(info, query, "newton_polygon", qfield="slopes")
566602
parse_string_start(info, query, "initial_coefficients", qfield="poly_str", initial_segment=["1"])
@@ -680,7 +716,7 @@ def extended_code(c):
680716
ProcessedCol("number_fields", "av.fq.number_field", "Number fields", lambda nfs: ", ".join(nf_display_knowl(nf, field_pretty(nf)) for nf in nfs), default=False),
681717
SearchCol("galois_groups_pretty", "nf.galois_group", "Galois groups", download_col="galois_groups", default=False),
682718
SearchCol("decomposition_display_search", "av.decomposition", "Isogeny factors", download_col="decompositionraw")],
683-
db_cols=["label", "g", "q", "poly", "p_rank", "p_rank_deficit", "is_simple", "is_geometrically_simple", "simple_distinct", "simple_multiplicities", "is_primitive", "primitive_models", "curve_count", "curve_counts", "abvar_count", "abvar_counts", "jacobian_count", "hyp_count", "number_fields", "galois_groups", "slopes", "newton_elevation", "twist_count", "max_twist_degree", "geometric_extension_degree", "angle_rank", "angle_corank", "is_supersingular", "has_principal_polarization", "has_jacobian"])
719+
db_cols=["label", "g", "q", "poly", "p_rank", "p_rank_deficit", "is_simple", "is_geometrically_simple", "simple_distinct", "simple_multiplicities", "is_primitive", "primitive_models", "curve_count", "curve_counts", "abvar_count", "abvar_counts", "jacobian_count", "hyp_count", "number_fields", "galois_groups", "slopes", "newton_elevation", "twist_count", "max_twist_degree", "geometric_extension_degree", "angle_rank", "angle_corank", "is_supersingular", "has_principal_polarization", "has_jacobian", "is_cyclic", "noncyclic_primes"])
684720

685721
def abvar_postprocess(res, info, query):
686722
gals = set()

lmfdb/abvar/fq/templates/show-abvarfq.html

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,27 @@ <h2>Invariants</h2>
3131
{% if cl.size is not none %}
3232
<tr><td>{{KNOWL('av.fq.isogeny_class_size',title='Isomorphism classes')}}:</td><td>&nbsp;&nbsp;{{cl.size}}</td></tr>
3333
{% endif %}
34+
{# --- NEW: cyclicity invariants --- #}
35+
{% if cl.is_cyclic is not none %}
36+
<tr>
37+
<td>{{ KNOWL('av.fq.cyclic_group_points', title='Cyclic group of points') }}:</td>
38+
<td>&nbsp;&nbsp;
39+
{% if cl.is_cyclic %}
40+
yes
41+
{% else %}
42+
no
43+
{% endif %}
44+
</td>
45+
</tr>
46+
{% endif %}
47+
48+
49+
{% if cl.noncyclic_primes %}
50+
<tr>
51+
<td>{{ KNOWL('av.fq.noncyclic_primes', title='Non-cyclic primes') }}:</td>
52+
<td>&nbsp;&nbsp;${{ cl.noncyclic_primes|join(', ') }}$</td>
53+
</tr>
54+
{% endif %}
3455
</table>
3556
</p>
3657

lmfdb/abvar/fq/test_av.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,12 @@ def test_download_curves(self):
157157

158158
page = self.tc.get('Variety/Abelian/Fq/download_curves/5.3.ac_e_ai_v_abl', follow_redirects=True)
159159
assert 'No curves for abelian variety isogeny class 5.3.ac_e_ai_v_abl' in page.get_data(as_text=True)
160+
161+
def test_cyclic_group_of_points_display(self):
162+
r"""
163+
Check that the cyclic group of points information is displayed
164+
on the isogeny-class page.
165+
"""
166+
page = self.tc.get("/Variety/Abelian/Fq/2/9/aj_bl").get_data(as_text=True)
167+
assert "Cyclic" in page
168+

lmfdb/abvar/fq/test_browse_page.py

Lines changed: 70 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ def test_index_page(self):
1212
"""
1313
homepage = self.tc.get("/Variety/Abelian/Fq/").get_data(as_text=True)
1414
assert "by dimension and base field" in homepage
15+
assert "Cyclic group of points" in homepage
1516

1617
def test_stats_page(self):
17-
self.check_args("/Variety/Abelian/Fq/stats","Abelian variety isogeny classes: Statistics")
18+
self.check_args("/Variety/Abelian/Fq/stats", "Abelian variety isogeny classes: Statistics")
1819

1920
# TODO test dynamic stats
2021

@@ -43,7 +44,10 @@ def test_lookup(self):
4344
r"""
4445
Check that Variety/Abelian/Fq/?jump works
4546
"""
46-
self.check_args("/Variety/Abelian/Fq/?jump=x^6-3*x^5%2B3*x^4-2*x^3%2B6*x^2-12*x%2B8", "3.2.ad_d_ac")
47+
self.check_args(
48+
"/Variety/Abelian/Fq/?jump=x^6-3*x^5%2B3*x^4-2*x^3%2B6*x^2-12*x%2B8",
49+
"3.2.ad_d_ac"
50+
)
4751

4852
# Various searches
4953
# Many things are checked twice: Once from main index/browse page, and once from the refining search page
@@ -148,7 +152,10 @@ def test_search_newton(self):
148152
# slope not a rational number
149153
self.check_args("/Variety/Abelian/Fq/?newton_polygon=t", "is not a valid input")
150154
# slopes are not increasing
151-
self.check_args("/Variety/Abelian/Fq/?start=&count=&newton_polygon=%5B1%2C1%2F2%2C0%5D", "Slopes must be increasing")
155+
self.check_args(
156+
"/Variety/Abelian/Fq/?start=&count=&newton_polygon=%5B1%2C1%2F2%2C0%5D",
157+
"Slopes must be increasing"
158+
)
152159

153160
def test_search_initcoeffs(self):
154161
r"""
@@ -157,7 +164,10 @@ def test_search_initcoeffs(self):
157164
self.check_args("/Variety/Abelian/Fq/?initial_coefficients=%5B1%2C-1%2C3%2C9%5D", "4.3.b_ab_d_j")
158165
self.check_args("/Variety/Abelian/Fq/?initial_coefficients=%5B1%2C-1%2C3%2C9%5D", "4.3.b_ab_d_j")
159166
# there should be only one match, if ranges were supported
160-
self.check_args("/Variety/Abelian/Fq/?angle_ranks=&initial_coefficients=%5B3%2C+9%2C+10%2C+87-100%5D", "Ranges not supported")
167+
self.check_args(
168+
"/Variety/Abelian/Fq/?angle_ranks=&initial_coefficients=%5B3%2C+9%2C+10%2C+87-100%5D",
169+
"Ranges not supported"
170+
)
161171

162172
def test_search_pointcountsav(self):
163173
r"""
@@ -187,8 +197,14 @@ def test_search_isogfactor(self):
187197
Check that we can search by decomposition into isogeny factors
188198
"""
189199
# [3.5.ah_y_ach,*]
190-
self.check_args("/Variety/Abelian/Fq/?simple_quantifier=include&simple_factors=3.5.ah_y_ach", "4.5.ak_by_agk_qb")
191-
self.check_args("/Variety/Abelian/Fq/?p_rank=4&dim1_factors=2&dim2_factors=2&dim1_distinct=1&dim2_distinct=1", "6.2.ag_p_aw_bh_acu_ey")
200+
self.check_args(
201+
"/Variety/Abelian/Fq/?simple_quantifier=include&simple_factors=3.5.ah_y_ach",
202+
"4.5.ak_by_agk_qb"
203+
)
204+
self.check_args(
205+
"/Variety/Abelian/Fq/?p_rank=4&dim1_factors=2&dim2_factors=2&dim1_distinct=1&dim2_distinct=1",
206+
"6.2.ag_p_aw_bh_acu_ey"
207+
)
192208
self.check_args("/Variety/Abelian/Fq/?dim1_factors=6&dim1_distinct=1", "5 matches")
193209

194210
def test_search_numberfield(self):
@@ -274,8 +290,54 @@ def test_search_combos(self):
274290
self.check_args("/Variety/Abelian/Fq/?p_rank=2&initial_coefficients=%5B1%2C-1%2C3%2C9%5D", "4.3.b_ab_d_j")
275291
self.check_args("/Variety/Abelian/Fq/?p_rank=2&initial_coefficients=%5B1%2C-1%2C3%2C9%5D", "4.3.b_ab_d_j")
276292
# initial coefficients and point counts of the abelian variety
277-
self.check_args("/Variety/Abelian/Fq/?initial_coefficients=%5B1%2C-1%2C3%2C9%5D&abvar_point_count=%5B75%2C7125%5D", "No matches")
278-
self.check_args("/Variety/Abelian/Fq/?initial_coefficients=%5B1%2C-1%2C3%2C9%5D&abvar_point_count=%5B75%2C7125%5D", "No matches")
293+
self.check_args(
294+
"/Variety/Abelian/Fq/?initial_coefficients=%5B1%2C-1%2C3%2C9%5D&abvar_point_count=%5B75%2C7125%5D",
295+
"No matches"
296+
)
297+
self.check_args(
298+
"/Variety/Abelian/Fq/?initial_coefficients=%5B1%2C-1%2C3%2C9%5D&abvar_point_count=%5B75%2C7125%5D",
299+
"No matches"
300+
)
279301
# Combining unknown fields on Jacobian and Principal polarization.
280302
self.check_args("/Variety/Abelian/Fq/?g=3&jacobian=no&polarizable=not_no", "3.2.a_a_ae")
281303
self.check_args("/Variety/Abelian/Fq/?g=3&jacobian=no&polarizable=yes", "3.2.a_ac_a")
304+
305+
def test_search_cyclic_group(self):
306+
r"""
307+
Check that we can restrict to cyclic or non-cyclic groups of points
308+
using the cyclic search parameter.
309+
"""
310+
# 1.2.ac has cyclic group of points (is_cyclic = True)
311+
self.check_args(
312+
"/Variety/Abelian/Fq/?cyclic=yes",
313+
">1.2.ac<",
314+
)
315+
316+
# 1.3.a has non-cyclic group of points (is_cyclic = False)
317+
self.check_args(
318+
"/Variety/Abelian/Fq/?cyclic=no",
319+
">1.3.a<",
320+
)
321+
322+
# And make sure they do not appear in the wrong list
323+
self.not_check_args(
324+
"/Variety/Abelian/Fq/?cyclic=yes",
325+
">1.3.a<",
326+
)
327+
self.not_check_args(
328+
"/Variety/Abelian/Fq/?cyclic=no",
329+
">1.2.ac<",
330+
)
331+
332+
def test_search_noncyclic_primes(self):
333+
r"""
334+
Check that the noncyclic_primes search parameter is accepted and
335+
finds classes that are non-cyclic at p = 2.
336+
"""
337+
self.check_args(
338+
"/Variety/Abelian/Fq/?noncyclic_primes=2",
339+
">1.3.a<",
340+
)
341+
342+
343+

0 commit comments

Comments
 (0)