Skip to content
Draft
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
5 changes: 5 additions & 0 deletions .github/workflows/matrix_includes.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,5 +108,10 @@
"files": "lmfdb/modular_curves/test_modular_curves.py",
"folders": "modular_curves",
"server": "devmirror"
},
{
"files": "lmfdb/classical_modular_forms/test_cmf_eisenstein.py",
"folders": "classical_modular_forms/eisenstein",
"server": "devmirror"
}
]
2 changes: 1 addition & 1 deletion .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ jobs:
- name: checking that we didn't miss any test files
shell: bash -l {0}
# If this fails you need to update the file list above and file count
run: test $(find lmfdb -name 'test_*.py' -or -name '*_test.py' | wc -l) -eq 44
run: test $(find lmfdb -name 'test_*.py' -or -name '*_test.py' | wc -l) -eq 45

- name: Config LMFDB to run tests against proddb
if: matrix.files != 'lint' && matrix.server == 'proddb'
Expand Down
2 changes: 1 addition & 1 deletion lmfdb/bianchi_modular_forms/web_BMF.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ def conv(ap):
bc_urls = [url_for("cmf.by_url_newform_label", level=cond, weight=2, char_orbit_label='a', hecke_orbit=iso)
for cond, iso, num in curve_bc_parts]
bc_labels = [".".join( [str(cond), str(2), 'a', iso] ) for cond,iso,_ in curve_bc_parts]
bc_exists = [db.mf_newforms.label_exists(lab) for lab in bc_labels]
bc_exists = [db.mf_newforms_eis.label_exists(lab) for lab in bc_labels]
self.bc_forms = [{'exists':ex, 'label':lab, 'url':url} for ex,lab,url in zip(bc_exists, bc_labels, bc_urls)]
if self.ec_status is None:
if self.bct or self.label in bmfs_with_no_curve:
Expand Down
10 changes: 5 additions & 5 deletions lmfdb/classical_modular_forms/cmf_test_pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def all_newforms(self, level, weight):
res = []
errors = []
n = 0
for nf in list(db.mf_newforms.search({'level':level,'weight':weight}, ['label', 'dim'])):
for nf in list(db.mf_newforms_eis.search({'level':level,'weight':weight}, ['label', 'dim'])):
n += 1
r = self.newform(nf['label'], nf['dim'])
res.append(r)
Expand All @@ -69,8 +69,8 @@ def all_newspaces(self, level, weight):
res = []
n = 0
url = '/ModularForm/GL2/Q/holomorphic/%d/%d/' % (level, weight)
newspaces = list(db.mf_newspaces.search({'level':level,'weight':weight, 'char_parity':-1 if bool(weight % 2) else 1}, ['label', 'dim']))
newforms = list(db.mf_newforms.search({'level':level,'weight':weight}, ['label', 'space_label', 'dim']))
newspaces = list(db.mf_newspaces_eis.search({'level':level,'weight':weight,'char_parity':-1 if bool(weight % 2) else 1,'is_cuspidal':True}, ['label', 'dim']))
newforms = list(db.mf_newforms_eis.search({'level':level,'weight':weight,'is_cuspidal':True}, ['label', 'space_label', 'dim']))
dim = gp("mfdim([%s,%s,-1],0)" % (level,weight))
if dim is None:
for ns in newspaces:
Expand Down Expand Up @@ -130,7 +130,7 @@ def all_newspaces(self, level, weight):
res.append((None, url))

#test wrong parity newspaces
for ns in list(db.mf_newspaces.search({'level':level,'weight':weight, 'char_parity':1 if bool(weight % 2) else -1}, ['label', 'dim'])):
for ns in list(db.mf_newspaces_eis.search({'level':level,'weight':weight, 'char_parity':1 if bool(weight % 2) else -1, 'is_cuspidal':True}, ['label', 'dim'])):
label = ns['label']
dim = ns['dim']
url = '/ModularForm/GL2/Q/holomorphic/' + label.replace('.','/') + '/'
Expand Down Expand Up @@ -159,7 +159,7 @@ def all_newspaces(self, level, weight):
def test_all(self):
todo = []
from lmfdb import db
maxNk2 = db.mf_newforms.max('Nk2')
maxNk2 = db.mf_newforms_eis.max('Nk2')
for Nk2 in range(1, maxNk2 + 1):
for N in ZZ(Nk2).divisors():
k = sqrt(Nk2/N)
Expand Down
106 changes: 82 additions & 24 deletions lmfdb/classical_modular_forms/download.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,30 @@
from ast import literal_eval
from sage.all import prime_range, previous_prime
from sage.all import QQ, ZZ, prime_range, previous_prime
from flask import url_for, redirect, abort
from lmfdb import db
from psycodict.encoding import Json
from lmfdb.utils import Downloader, flash_error
from lmfdb.characters.TinyConrey import ConreyCharacter
from lmfdb.classical_modular_forms.web_newform import WebNewform, valid_label
from lmfdb.classical_modular_forms.web_newform import (
WebNewform,
valid_label,
LABEL_EIS_RE,
EMB_LABEL_EIS_RE,
)
from lmfdb.classical_modular_forms.web_space import WebNewformSpace, WebGamma1Space


class CMF_download(Downloader):
table = db.mf_newforms
table = db.mf_newforms_eis
title = 'Classical modular forms'
data_format = ['N=level', 'k=weight', 'dim', 'analytic conductor', 'defining polynomial', 'number field label', 'CM discriminants', 'RM discriminants', 'first few traces - a2,a3,a5,a7']
columns = ['level', 'weight', 'dim', 'analytic_conductor', 'field_poly', 'nf_label', 'cm_discs', 'rm_discs', 'trace_display']

def _get_hecke_nf(self, label):
proj = ['ap', 'hecke_ring_rank', 'hecke_ring_power_basis','hecke_ring_numerators', 'hecke_ring_denominators', 'field_poly','hecke_ring_cyclotomic_generator', 'hecke_ring_character_values', 'maxp']
print(label)
data = db.mf_hecke_nf.lucky({'label':label}, proj)
print(data)
data = db.mf_hecke_nf_eis.lucky({'label':label}, proj)
if data is None:
f = db.mf_newforms.lookup(label,projection=["level","char_orbit_label","dim","traces"])
f = db.mf_newforms_eis.lookup(label,projection=["level","char_orbit_label","dim","traces"])
if f["dim"] == 1:
vals = ConreyCharacter(f["level"], db.char_dirichlet.lookup("%s.%s" % (f["level"],f["char_orbit_label"]),projection="first")).values_gens
vals = [[v[0],[1] if v[1] == 0 else [-1]] for v in vals]
Expand All @@ -37,11 +40,16 @@ def _get_hecke_nf(self, label):

def _get_traces(self, label):
if label.count('.') == 1:
traces = db.mf_gamma1.lookup(label, projection=['traces'])
traces = db.mf_gamma1_eis.lookup(label, projection=['traces'])
elif label.count('.') == 2:
traces = db.mf_newspaces.lookup(label, projection=['traces'])
traces = db.mf_newspaces_eis.lookup(label, projection=['traces'])
elif label.count('.') == 3:
traces = db.mf_newforms.lookup(label, projection=['traces'])
traces = db.mf_newforms_eis.lookup(label, projection=['traces'])
elif LABEL_EIS_RE.match(label):
traces = db.mf_newforms_eis.lookup(label, projection=['traces'])
elif EMB_LABEL_EIS_RE.match(label):
form_label = ".".join(label.split(".")[:5])
traces = db.mf_newforms_eis.lookup(form_label, projection=['traces'])
else:
return abort(404, "Invalid label: %s" % label)
if traces is None:
Expand Down Expand Up @@ -223,17 +231,17 @@ def download_multiple_traces(self, info, spaces=False):
lang = self.languages.get(lang, self.languages['sage'])
query = literal_eval(info.get('query', '{}'))
if spaces:
count = db.mf_newspaces.count(query)
count = db.mf_newspaces_eis.count(query)
else:
count = db.mf_newforms.count(query)
count = db.mf_newforms_eis.count(query)
limit = 1000
if count > limit:
flash_error("We limit downloads of traces to %s forms", limit)
return redirect(url_for('.index'))
if spaces:
res = list(db.mf_newspaces.search(query, projection=['label', 'traces']))
res = list(db.mf_newspaces_eis.search(query, projection=['label', 'traces']))
else:
res = list(db.mf_newforms.search(query, projection=['label', 'traces']))
res = list(db.mf_newforms_eis.search(query, projection=['label', 'traces']))
s = ""
c = lang.comment_prefix
s += c + ' Query "%s" returned %d %s.\n\n' % (str(info.get('query')), len(res), 'spaces' if spaces else 'forms')
Expand Down Expand Up @@ -288,10 +296,10 @@ def download_multiple_space_traces(self, info):
# return self._download_cc(label, lang, 'angles', '.angles', 'Satake angles')

def download_embedding(self, label, lang='text'):
return redirect(url_for("cmf.mf_hecke_cc", label=label), 301)
return redirect(url_for("cmf.mf_hecke_cc_eis", label=label), 301)

def download_newform(self, label, lang='text'):
data = db.mf_newforms.lookup(label)
data = db.mf_newforms_eis.lookup(label)
if data is None:
return abort(404, "Label not found: %s" % label)
form = WebNewform(data)
Expand All @@ -309,7 +317,7 @@ def download_code(self, label, lang):
Fullname = {'magma': 'Magma', 'sage': 'SageMath', 'pari': 'Pari/GP'}
if lang not in Fullname:
abort(404,"Invalid code language specified: " + lang)
data = db.mf_newforms.lookup(label)
data = db.mf_newforms_eis.lookup(label)
if data is None:
return abort(404, "Label not found: %s" % label)
form = WebNewform(data)
Expand Down Expand Up @@ -339,7 +347,7 @@ def download_code(self, label, lang):
return script

def download_newspace(self, label, lang='text'):
data = db.mf_newspaces.lookup(label)
data = db.mf_newspaces_eis.lookup(label)
if data is None:
return abort(404, "Label not found: %s" % label)
space = WebNewformSpace(data)
Expand Down Expand Up @@ -373,7 +381,7 @@ def download_spaces(self, info):
lang = self.languages.get(lang, self.languages['sage'])
query = literal_eval(info.get('query', '{}'))
proj = ['label', 'analytic_conductor', 'conrey_index', 'char_order']
spaces = list(db.mf_newspaces.search(query, projection=proj))
spaces = list(db.mf_newspaces_eis.search(query, projection=proj))
s = ""
c = lang.comment_prefix
s += c + ' Query "%s" returned %d spaces.\n\n' % (str(info.get('query')), len(spaces))
Expand Down Expand Up @@ -586,12 +594,16 @@ def _magma_MakeNewformModSym(self, newform, hecke_nf):
'// The default sign is -1. You can change this with the optional parameter "sign".'
]
self.explain += explain
if newform.is_cuspidal:
ms_sub = "CuspidalSubspace(ModularSymbols(chi,%d,sign))" % (k,)
else:
ms_sub = "EisensteinSubspace(ModularSymbols(chi,%d,sign))" % (k,)
return explain + [
"function MakeNewformModSym_%s( : sign := -1)" % (newform.label.replace(".","_"), ),
" R<x> := PolynomialRing(Rationals());",
" chi := MakeCharacter_%d_%s();" % (N, o),
" // SetVerbose(\"ModularSymbols\", true);",
" Snew := NewSubspace(CuspidalSubspace(ModularSymbols(chi,%d,sign)));" % (k, ),
" Snew := NewSubspace(%s);" % ms_sub,
" Vf := Kernel(%s,Snew);" % (cutters,),
" return Vf;",
"end function;",
Expand All @@ -614,6 +626,12 @@ def _magma_MakeNewformModFrm(self, newform, hecke_nf):
so in particular v[0] = 0 and v[1] = 1,
returns a string containing magma code to create the newform
as a representative q-expansion (type ModFrm) in magma.

Cuspidal forms use ``CuspidalSubspace`` and a truncated coefficient
vector starting with `a_0 = 0`. Eisenstein forms use
``EisensteinSubspace`` and include the stored constant term `a_0`
(rational, or a power-basis / cyclotomic row passed through
``ConvertToHeckeField``) so ``Solution`` matches Magma's basis.
"""
explain = [
'// To make the newform (type ModFrm), type "MakeNewformModFrm_%s();".' % (newform.label.replace(".", "_"), ),
Expand All @@ -622,18 +640,57 @@ def _magma_MakeNewformModFrm(self, newform, hecke_nf):
'// This guess is increased enough to uniquely determine the newform.'
]
self.explain += explain
return explain + [
if newform.is_cuspidal:
mf_sub = "CuspidalSubspace(ModularForms(chi, %d))" % (newform.weight,)
else:
mf_sub = "EisensteinSubspace(ModularForms(chi, %d))" % (newform.weight,)
magma = self.languages['magma']
fn_lines = [
'function MakeNewformModFrm_%s(:prec:=%d)' % (newform.label.replace(".","_"), newform.dim),
' chi := MakeCharacter_%d_%s();' % (newform.level, newform.char_orbit_label),
' f_vec := qexpCoeffs();',
' Kf := Universe(f_vec);',
]
if not newform.is_cuspidal:
a0_num = newform.qexp[0]
a0_den = getattr(newform, 'a0_denom', None)
if a0_den is None:
a0_den = ZZ(1)
# ``a0_num`` is either a rational (dim 1) or a coefficient in the
# Hecke field: power basis coordinates (list of integers) or
# cyclotomic pairs ``[[c,e], ...]`` matching ``ConvertToHeckeField``.
if isinstance(a0_num, (list, tuple)) and a0_num and isinstance(
a0_num[0], (list, tuple)):
raw_a0_rows = [a0_num]
elif isinstance(a0_num, (list, tuple)):
row = [QQ(c) / QQ(a0_den) for c in a0_num]
raw_a0_rows = [row]
else:
raw_a0_rows = None
if raw_a0_rows is not None:
fn_lines.append(
' ' + magma.assign('raw_eisenstein_a0', raw_a0_rows).rstrip('\n'))
fn_lines.append(
' eisenstein_a0 := ConvertToHeckeField(raw_eisenstein_a0 : '
'pass_field := true, Kf := Kf)[1];')
else:
a0_rat = QQ(a0_num) / QQ(a0_den)
fn_lines.append(
' ' + magma.assign('eisenstein_a0_rat', a0_rat).rstrip('\n'))
fn_lines.append(' eisenstein_a0 := Kf!eisenstein_a0_rat;')
trunc_vec_line = (
' trunc_vec := Vector(Kf, [eisenstein_a0] cat '
'[f_vec[i]: i in [1..prec]]);')
else:
trunc_vec_line = ' trunc_vec := Vector(Kf, [0] cat [f_vec[i]: i in [1..prec]]);'
fn_lines += [
' // SetVerbose("ModularForms", true);',
' // SetVerbose("ModularSymbols", true);',
' S := CuspidalSubspace(ModularForms(chi, %d));' % newform.weight,
' S := %s;' % mf_sub,
' S := BaseChange(S, Kf);',
' maxprec := NextPrime(%d) - 1;' % hecke_nf['maxp'],
' while true do',
' trunc_vec := Vector(Kf, [0] cat [f_vec[i]: i in [1..prec]]);',
trunc_vec_line,
# weight 1 does not have NewSpace functionality, and anyway that
# would be an extra possibly expensive linear algebra step
' B := Basis(S, prec + 1);',
Expand All @@ -649,9 +706,10 @@ def _magma_MakeNewformModFrm(self, newform, hecke_nf):
' end while;',
'end function;'
]
return explain + fn_lines

def download_newform_to_magma(self, label, lang='magma'):
data = db.mf_newforms.lookup(label)
data = db.mf_newforms_eis.lookup(label)
if data is None:
return abort(404, "Label not found: %s" % label)
newform = WebNewform(data)
Expand Down
Loading
Loading