Skip to content
Open
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
9 changes: 9 additions & 0 deletions lmfdb/abvar/fq/download.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,14 @@ def download_curves(self, label, lang='text'):
lang=lang,
title='Curves in abelian variety isogeny class %s,' % (label))

def get_projection(self, info, table, columns):
proj = super().get_projection(info, table, columns)
# The curves arrays are large, so only fetch them when the Curves column is
# actually included in the download (see LMFDB#6975).
if ("curves" in table.search_cols and "curves" not in proj
and any(col.name == "curves" and col.default(info) for col in columns.columns)):
proj = proj + ["curves"]
return proj

def postprocess(self, rec, info, query):
return AbvarFq_isoclass(rec)
31 changes: 27 additions & 4 deletions lmfdb/abvar/fq/isog_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,30 @@ def validate_label(label):
raise ValueError("the final part must be of the form c1_c2_..._cg, with each ci consisting of lower case letters")


def formatted_curve_equation(cv):
"""
TeXify a curve equation as stored in the curves column of av_fq_isog,
appending "=0" if the stored string is a bare polynomial.
"""
cv = teXify_pol(cv)
if "=" not in cv:
cv = cv + "=0"
return cv


def curves_display_search(curves):
"""
A compact rendering of the curves column for search results pages;
the full list is available on the homepage and in downloads.
"""
if not curves:
return ""
disp = "$%s$" % formatted_curve_equation(curves[0])
if len(curves) > 1:
disp += " and %s more" % (len(curves) - 1)
return disp


class AbvarFq_isoclass():
"""
Class for an isogeny class of abelian varieties over a finite field
Expand All @@ -80,6 +104,8 @@ def __init__(self, dbdata):
dbdata["hyp_count"] = None
if "jacobian_count" not in dbdata:
dbdata["jacobian_count"] = None
if "curves" not in dbdata:
dbdata["curves"] = None
# New invariants: cyclicity and noncyclic primes
if "is_cyclic" not in dbdata:
dbdata["is_cyclic"] = None
Expand Down Expand Up @@ -466,10 +492,7 @@ def twist_display(self, show_all):

def curve_display(self):
def show_curve(cv):
cv = teXify_pol(cv)
if "=" not in cv:
cv = cv + "=0"
return " <li>$%s$</li>\n" % cv
return " <li>$%s$</li>\n" % formatted_curve_equation(cv)
if hasattr(self, "curves") and self.curves:
s = "\n<ul>\n"
cutoff = 20 if len(self.curves) > 30 else len(self.curves)
Expand Down
15 changes: 14 additions & 1 deletion lmfdb/abvar/fq/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from lmfdb.api import datapage
from . import abvarfq_page
from .search_parsing import parse_nf_string, parse_galgrp
from .isog_class import validate_label, AbvarFq_isoclass
from .isog_class import validate_label, AbvarFq_isoclass, curves_display_search
from .stats import AbvarFqStats
from lmfdb.number_fields.web_number_field import nf_display_knowl, field_pretty
from lmfdb.utils import redirect_no_cache
Expand Down Expand Up @@ -706,6 +706,8 @@ def extended_code(c):
MathCol("abvar_counts", "ag.fq.point_counts", r"$\mathbb{F}_{q^k}$ points on variety", short_title="Fq^k points on variety", default=False),
MathCol("jacobian_count", "av.jacobian_count", "Jacobians", default=False),
MathCol("hyp_count", "av.hyperelliptic_count", "Hyperelliptic Jacobians", default=False),
ProcessedCol("curves", "ag.jacobian", "Curves", curves_display_search, default=False,
download_desc="Equations of curves whose Jacobians are in the isogeny class. For a base field GF(p^r) with r > 1, the variable a denotes a generator of the base field over its prime field. The list may be empty or missing if no such curves are known."),
MathCol("twist_count", "av.twist", "Num. twists", default=False),
MathCol("max_twist_degree", "av.twist", "Max. twist degree", default=False),
MathCol("geometric_extension_degree", "av.endomorphism_field", "End. degree", default=False),
Expand All @@ -714,6 +716,14 @@ def extended_code(c):
SearchCol("decomposition_display_search", "av.decomposition", "Isogeny factors", download_col="decompositionraw")],
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"])

# The curves column holds potentially long lists of equations (up to ~6000 per row, and
# 2+ MB per page of search results), so - unlike the other columns - it is left out of the
# fixed db_cols projection above and only fetched from the database when the column is
# actually displayed or included in a download. See LMFDB#6975.
def curves_requested(info):
"""Whether the (heavy, default-off) curves column is included in the current display/download."""
return any(col.name == "curves" and col.default(info) for col in abvar_columns.columns)

def abvar_postprocess(res, info, query):
gals = set()
for A in res:
Expand All @@ -740,6 +750,9 @@ def abvar_postprocess(res, info, query):
)
def abelian_variety_search(info, query):
common_parse(info, query)
if curves_requested(info):
# Fetch the (heavy) curves arrays only when the Curves column is displayed (LMFDB#6975).
query["__projection__"] = abvar_columns.db_cols + ["curves"]

@count_wrap(
template="abvarfq-count-results.html",
Expand Down
18 changes: 18 additions & 0 deletions lmfdb/abvar/fq/test_av.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,24 @@ def test_download_curves(self):
page = self.tc.get('Variety/Abelian/Fq/download_curves/5.3.ac_e_ai_v_abl', follow_redirects=True)
assert 'No curves for abelian variety isogeny class 5.3.ac_e_ai_v_abl' in page.get_data(as_text=True)

def test_curves_search_column(self):
r"""
Test the curves column on search results pages, including downloads.
"""
# The column is available in the column dropdown
page = self.tc.get("/Variety/Abelian/Fq/?q=4&g=1").get_data(as_text=True)
assert '<option value="curves">' in page
# A single curve is displayed as is, longer lists are truncated
page = self.tc.get("/Variety/Abelian/Fq/?q=4&g=1&showcol=curves").get_data(as_text=True)
assert "$y^2+y=x^3+a$" in page
assert "$y^2+a y=x^3$ and 1 more" in page
# Search downloads contain the full lists
data = self.tc.get("/Variety/Abelian/Fq/?q=4&g=1&showcol=curves&Submit=sage&download=1&query=%7B%27q%27%3A+4%2C+%27g%27%3A+1%7D").get_data(as_text=True)
assert '["y^2+a*y=x^3", "y^2+(a+1)*y=x^3"]' in data
# Classes for which curves have not been computed display an empty cell
page = self.tc.get("/Variety/Abelian/Fq/?q=729&g=2&showcol=curves").get_data(as_text=True)
assert "2.729.aee_gmg" in page

def test_cyclic_group_of_points_display(self):
r"""
Check that the cyclic group of points information is displayed
Expand Down
21 changes: 15 additions & 6 deletions lmfdb/utils/downloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,20 @@ def modify_query(self, info, query):
"""
pass

def get_projection(self, info, table, columns):
"""
This is a hook for downloaders to determine the database projection based on info.

By default it fetches the columns in ``columns.db_cols`` that are present in the
search table. It is overridden when a heavy column should only be fetched when it
is actually included in the download (see abelian varieties over Fq, LMFDB#6975).
"""
# It's fairly common to add virtual columns in postprocessing that are then used in MultiProcessedCols.
# These virtual columns are often only used in display code and won't be present in the database, so we just strip them out
if isinstance(columns.db_cols, list):
return [col for col in columns.db_cols if col in table.search_cols]
return columns.db_cols # some tables use 1 for project-to-all

def get_sort(self, info, query):
"""
This determines the sort order requested from the database.
Expand Down Expand Up @@ -705,12 +719,7 @@ def __call__(self, info):

# Determine which columns will be fetched from the database
columns = info["columns"]
# It's fairly common to add virtual columns in postprocessing that are then used in MultiProcessedCols.
# These virtual columns are often only used in display code and won't be present in the database, so we just strip them out
if isinstance(columns.db_cols, list):
proj = [col for col in columns.db_cols if col in table.search_cols]
else:
proj = columns.db_cols # some tables use 1 for project-to-all
proj = self.get_projection(info, table, columns)

# Extract the query and modify it
try:
Expand Down
Loading