diff --git a/lmfdb/classical_modular_forms/main.py b/lmfdb/classical_modular_forms/main.py index b18b388c18..f8fce2f518 100644 --- a/lmfdb/classical_modular_forms/main.py +++ b/lmfdb/classical_modular_forms/main.py @@ -15,7 +15,7 @@ flash_error, to_dict, comma, display_knowl, bigint_knowl, num2letters, SearchArray, TextBox, TextBoxNoEg, SelectBox, TextBoxWithSelect, YesNoBox, DoubleSelectBox, RowSpacer, HiddenBox, SearchButtonWithSelect, - SubsetBox, ParityMod, CountBox, SelectBoxNoEg, + SubsetBox, ParityMod, CountBox, StatsDisplay, proportioners, totaler, integer_divisors, redirect_no_cache) from psycodict.utils import range_formatter @@ -779,10 +779,21 @@ def newform_parse(info, query): parse_noop(info, query, 'atkin_lehner_string') parse_ints(info, query, 'fricke_eigenval') parse_bool(info, query, 'is_self_dual') + if info.get('is_maximal_largest'): + if info['is_maximal_largest'] == 'maximal': + query['is_maximal'] = True + elif info['is_maximal_largest'] == 'largest': + query['is_largest'] = True + elif info['is_maximal_largest'] == 'notlargest': + query['is_largest'] = False parse_ints(info, query, 'hecke_ring_index') parse_ints(info, query, 'hecke_ring_generator_nbound') - parse_noop(info, query, 'projective_image', func=str.upper) - parse_noop(info, query, 'projective_image_type') + if 'projective_image_type' in info and not 'projective_image' in info: + query['projective_image_type'] = info['projective_image_type'] + elif info.get('projective_image','').lower() in ["dn","dihedral"]: + query["projective_image_type"] = "Dn" + else: + parse_noop(info, query, 'projective_image', func=str.upper) parse_ints(info, query, 'artin_degree', name="Artin degree") def newspace_parse(info, query): @@ -802,7 +813,7 @@ def newspace_parse(info, query): if info['search_type'] != 'SpaceDimensions': parse_ints(info, query, 'num_forms', name='Number of newforms') if 'num_forms' not in query and info.get('all_spaces') != 'yes': - # Don't show spaces that only include dimension data but no newforms (Nk2 > 4000, nontrivial character) + # Don't show spaces that only include dimension data but no newforms query['num_forms'] = {'$exists':True} def _trace_col(i): @@ -850,6 +861,8 @@ def _AL_col(i, p): download_col="rm_discs"), CheckCol("is_self_dual", "cmf.selfdual", "Self-dual", default=False), CheckCol("is_twist_minimal", "cmf.twist_minimal", "Twist minimal", default=False), + CheckCol("is_largest", "cmf.maximal", "Largest", default=False), + CheckCol("is_maximal", "cmf.maximal", "Maximal", default=False), LinkCol("minimal_twist", "cmf.minimal_twist", "Minimal twist", url_for_label, default=False), MathCol("inner_twist_count", "cmf.inner_twist_count", "Inner twists", default=False), MathCol("analytic_rank", "cmf.analytic_rank", "Rank*", default=False), @@ -869,7 +882,7 @@ def _AL_col(i, p): MultiProcessedCol("qexp", "cmf.q-expansion", "$q$-expansion", ["label", "qexp_display"], lambda label, disp: fr'\({disp}\)' if disp else "", download_col="qexp_display")], - ['analytic_conductor', 'analytic_rank', 'atkin_lehner_eigenvals', 'char_conductor', 'char_orbit_label', 'char_order', 'cm_discs', 'dim', 'relative_dim', 'field_disc_factorization', 'field_poly', 'field_poly_is_real_cyclotomic', 'field_poly_root_of_unity', 'fricke_eigenval', 'hecke_ring_index_factorization', 'inner_twist_count', 'is_cm', 'is_rm', 'is_self_dual', 'is_twist_minimal', 'label', 'level', 'minimal_twist', 'nf_label', 'prim_orbit_index', 'projective_image', 'qexp_display', 'rm_discs', 'sato_tate_group', 'trace_display', 'weight'], + ['analytic_conductor', 'analytic_rank', 'atkin_lehner_eigenvals', 'char_conductor', 'char_orbit_label', 'char_order', 'cm_discs', 'dim', 'relative_dim', 'field_disc_factorization', 'field_poly', 'field_poly_is_real_cyclotomic', 'field_poly_root_of_unity', 'fricke_eigenval', 'hecke_ring_index_factorization', 'inner_twist_count', 'is_cm', 'is_largest', 'is_maximal', 'is_rm', 'is_self_dual', 'is_twist_minimal', 'label', 'level', 'minimal_twist', 'nf_label', 'prim_orbit_index', 'projective_image', 'qexp_display', 'rm_discs', 'sato_tate_group', 'trace_display', 'weight'], tr_class=["middle bottomlined", ""]) @search_wrap(table=db.mf_newforms, @@ -1554,13 +1567,6 @@ def __init__(self): example='1-10', example_span='1-10') - Nk2 = TextBox( - name='Nk2', - knowl='cmf.nk2', - label=r'\(Nk^2\)', - example='40-100', - example_span='40-100') - cm = SelectBox( name='cm', options=[('', 'any CM'), ('yes', 'has CM'), ('no', 'no CM')], @@ -1600,6 +1606,12 @@ def __init__(self): knowl='cmf.selfdual', label='Is self-dual') + is_maximal_largest = SelectBox( + name='is_maximal_largest', + knowl='cmf.maximal', + label='Is maximal/largest', + options=[('',''),('maximal','maximal'),('largest','largest'),('notlargest','not largest')]) + coefficient_ring_index = TextBox( name='hecke_ring_index', label='Coefficient ring index', @@ -1625,19 +1637,8 @@ def __init__(self): name='projective_image', label='Projective image', knowl='cmf.projective_image', - example='D15', - example_span='weight 1 only') - - projective_image_type = SelectBoxNoEg( - name='projective_image_type', - knowl='cmf.projective_image', - label='Projective image type', - options=[('', ''), - ('Dn', 'Dn'), - ('A4', 'A4'), - ('S4', 'S4'), - ('A5','A5')], - example_span='weight 1 only') + example='Dn', + example_span='A5, D7, or Dn; weight 1 only') num_newforms = TextBox( name='num_forms', @@ -1685,29 +1686,28 @@ def __init__(self): [level, weight], [level_primes, character], [char_order, char_primitive], - [dim, analytic_rank], - [analytic_conductor, Nk2], + [dim, is_maximal_largest], + [analytic_conductor, analytic_rank], [coefficient_field, is_self_dual], - [inner_twist_count, is_twist_minimal], - [self_twist_discs, self_twist], [coefficient_ring_index, hecke_ring_generator_nbound], - [projective_image, projective_image_type], - [results]] + [self_twist_discs, self_twist], + [inner_twist_count, is_twist_minimal], + [results, projective_image]] self.refine_array = [ [level, weight, analytic_conductor, analytic_rank, dim], - [level_primes, character, char_primitive, char_order, coefficient_field], - [self_twist, self_twist_discs, inner_twist_count, is_twist_minimal, is_self_dual], - [Nk2, coefficient_ring_index, hecke_ring_generator_nbound, projective_image, projective_image_type]] + [level_primes, character, char_primitive, char_order, is_maximal_largest], + [coefficient_field, self_twist, self_twist_discs, inner_twist_count, is_self_dual], + [coefficient_ring_index, hecke_ring_generator_nbound, is_twist_minimal, projective_image]] self.space_array = [ - [level, weight, analytic_conductor, Nk2, dim], - [level_primes, character, char_primitive, char_order, num_newforms] + [level, weight, analytic_conductor, dim, num_newforms], + [level_primes, character, char_primitive, char_order] ] self.sd_array = [ - [level, weight, analytic_conductor, Nk2, hdim], - [level_primes, character, char_primitive, char_order, hnum_newforms] + [level, weight, analytic_conductor, hdim, hnum_newforms], + [level_primes, character, char_primitive, char_order] ] self.traces_array = [ diff --git a/lmfdb/classical_modular_forms/test_cmf.py b/lmfdb/classical_modular_forms/test_cmf.py index 5df030e5d6..575f658d1c 100644 --- a/lmfdb/classical_modular_forms/test_cmf.py +++ b/lmfdb/classical_modular_forms/test_cmf.py @@ -273,6 +273,29 @@ def test_convert_conreylabels(self): page = self.tc.get('/ModularForm/GL2/Q/holomorphic/38/9/d/a/%d/%d/' % (c, e),follow_redirects=True) assert "Newform orbit 38.9.d.a" in page.get_data(as_text=True) + def test_maximal(self): + page = self.tc.get("/ModularForm/GL2/Q/holomorphic/?level=1234&weight=2") + assert '15 matches' in page.get_data(as_text=True) + assert '1234.2.a.h' in page.get_data(as_text=True) + assert '1234.2.a.i' in page.get_data(as_text=True) + assert '1234.2.b.c' in page.get_data(as_text=True) + page = self.tc.get("/ModularForm/GL2/Q/holomorphic/?level=1234&weight=2&is_maximal_largest=maximal") + assert 'unique match' in page.get_data(as_text=True) + assert not '1234.2.a.h' in page.get_data(as_text=True) + assert '1234.2.a.i' in page.get_data(as_text=True) + assert not '1234.2.b.c' in page.get_data(as_text=True) + page = self.tc.get("/ModularForm/GL2/Q/holomorphic/?level=1234&weight=2&is_maximal_largest=largest") + assert '5 matches' in page.get_data(as_text=True) + assert '1234.2.a.h' in page.get_data(as_text=True) + assert '1234.2.a.i' in page.get_data(as_text=True) + assert not '1234.2.b.c' in page.get_data(as_text=True) + page = self.tc.get("/ModularForm/GL2/Q/holomorphic/?level=1234&weight=2&is_maximal_largest=notlargest") + assert '10 matches' in page.get_data(as_text=True) + assert not '1234.2.a.h' in page.get_data(as_text=True) + assert not '1234.2.a.i' in page.get_data(as_text=True) + assert '1234.2.b.c' in page.get_data(as_text=True) + + def test_dim_table(self): page = self.tc.get("/ModularForm/GL2/Q/holomorphic/?weight=12&level=23&search_type=Dimensions", follow_redirects=True) assert 'Dimension search results' in page.get_data(as_text=True) diff --git a/lmfdb/classical_modular_forms/test_cmf2.py b/lmfdb/classical_modular_forms/test_cmf2.py index 5e3031f31e..2d1096fa40 100644 --- a/lmfdb/classical_modular_forms/test_cmf2.py +++ b/lmfdb/classical_modular_forms/test_cmf2.py @@ -200,7 +200,7 @@ def test_trivial_searches(self): ('level=900-1000&weight=1-&projective_image=D2', ['Results (26 matches)', r"\sqrt{-1}", r"\sqrt{-995}", r"\sqrt{137}"] )]: - for s in Subsets(['has_self_twist=yes', 'has_self_twist=cm', 'has_self_twist=rm', 'projective_image_type=Dn','dim=1-4']): + for s in Subsets(['has_self_twist=yes', 'has_self_twist=cm', 'has_self_twist=rm', 'projective_image=Dn','dim=1-4']): s = '&'.join(['/ModularForm/GL2/Q/holomorphic/?search_type=List', begin[0]] + list(s)) page = self.tc.get(s, follow_redirects=True) for elt in begin[1]: diff --git a/lmfdb/classical_modular_forms/web_newform.py b/lmfdb/classical_modular_forms/web_newform.py index c3f880c2a3..34ef20c81b 100644 --- a/lmfdb/classical_modular_forms/web_newform.py +++ b/lmfdb/classical_modular_forms/web_newform.py @@ -7,7 +7,7 @@ from flask import url_for from lmfdb.characters.TinyConrey import get_sage_genvalues, ConreyCharacter -from sage.all import (prime_range, latex, QQ, PolynomialRing, prime_pi, gcd, +from sage.all import (prime_range, latex, QQ, PolynomialRing, prime_pi, gcd, previous_prime, CDF, ZZ, CBF, cached_method, vector, lcm, RR, lazy_attribute) from sage.databases.cremona import cremona_letter_code, class_to_int @@ -182,7 +182,12 @@ def __init__(self, data, space=None, all_m=False, all_n=False, embedding_label=N self.hecke_ring_cyclotomic_generator = None # in case there is no data in mf_hecke_nf if self.embedding_label is None: hecke_cols = ['hecke_ring_numerators', 'hecke_ring_denominators', 'hecke_ring_inverse_numerators', 'hecke_ring_inverse_denominators', 'hecke_ring_cyclotomic_generator', 'hecke_ring_character_values', 'hecke_ring_power_basis', 'maxp'] - eigenvals = db.mf_hecke_nf.lucky({'hecke_orbit_code': self.hecke_orbit_code}, ['an'] + hecke_cols) + if self.dim == 1: + # avoid using mf_hecke_nf when the dimension is 1 + vals = ConreyCharacter(self.level, db.char_dirichlet.lookup("%s.%s"%(self.level,self.char_orbit_label),projection="first")).values_gens + eigenvals = { 'hecke_ring_cyclotomic_generator': 0, 'hecke_ring_character_values': vals, 'hecke_ring_power_basis': True, 'maxp': previous_prime(len(self.traces)+1), 'an': self.traces } + else: + eigenvals = db.mf_hecke_nf.lucky({'hecke_orbit_code': self.hecke_orbit_code}, ['an'] + hecke_cols) if eigenvals and eigenvals.get('an'): self.has_exact_qexp = True for attr in hecke_cols: @@ -1202,7 +1207,7 @@ def to_list(data): return [] out = [0]*(max(e for _, e in data) + 1) for c, e in data: - out[e] = c + out[e] += c return out coeffs = [to_list(data) for data in self.qexp[:prec]] return raw_typeset_qexp(coeffs, superscript=True, var=self._zeta_print, final_rawvar='z')