From d825db50b7b42d9ab2d11c810111267e23014ebb Mon Sep 17 00:00:00 2001 From: AndrewVSutherland Date: Mon, 27 May 2024 15:23:30 -0400 Subject: [PATCH 1/5] add maximal/largest search option --- lmfdb/classical_modular_forms/main.py | 73 ++++++++++---------- lmfdb/classical_modular_forms/test_cmf.py | 23 ++++++ lmfdb/classical_modular_forms/test_cmf2.py | 2 +- lmfdb/classical_modular_forms/web_newform.py | 11 ++- 4 files changed, 68 insertions(+), 41 deletions(-) diff --git a/lmfdb/classical_modular_forms/main.py b/lmfdb/classical_modular_forms/main.py index 66f5be17dc..9aa081729b 100644 --- a/lmfdb/classical_modular_forms/main.py +++ b/lmfdb/classical_modular_forms/main.py @@ -780,10 +780,20 @@ 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_bool(info, query, 'is_maximal') 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 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): @@ -803,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): @@ -851,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), @@ -870,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, @@ -1555,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')], @@ -1601,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', @@ -1626,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', @@ -1686,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 93a77ab99f..fdbb5f0053 100644 --- a/lmfdb/classical_modular_forms/test_cmf.py +++ b/lmfdb/classical_modular_forms/test_cmf.py @@ -274,6 +274,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 22235f59b0..d14644040b 100644 --- a/lmfdb/classical_modular_forms/test_cmf2.py +++ b/lmfdb/classical_modular_forms/test_cmf2.py @@ -201,7 +201,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 06068fd9ba..dc9d5c88b4 100644 --- a/lmfdb/classical_modular_forms/web_newform.py +++ b/lmfdb/classical_modular_forms/web_newform.py @@ -8,7 +8,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 @@ -183,7 +183,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: @@ -1203,7 +1208,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') From e668e8b26e1b3cfd0f3b5c2282d3d71b1775b7f7 Mon Sep 17 00:00:00 2001 From: AndrewVSutherland Date: Mon, 27 May 2024 16:04:16 -0400 Subject: [PATCH 2/5] delint --- lmfdb/classical_modular_forms/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lmfdb/classical_modular_forms/main.py b/lmfdb/classical_modular_forms/main.py index 9aa081729b..ca5d0ea603 100644 --- a/lmfdb/classical_modular_forms/main.py +++ b/lmfdb/classical_modular_forms/main.py @@ -14,7 +14,7 @@ parse_noop, parse_equality_constraints, integer_options, parse_subset, search_wrap, display_float, factor_base_factorization_latex, flash_error, to_dict, comma, display_knowl, bigint_knowl, num2letters, - SearchArray, TextBox, TextBoxNoEg, SelectBox, TextBoxWithSelect, YesNoBox, + SearchArray, TextBox, SelectBox, TextBoxWithSelect, YesNoBox, DoubleSelectBox, RowSpacer, HiddenBox, SearchButtonWithSelect, SubsetBox, ParityMod, CountBox, SelectBoxNoEg, StatsDisplay, proportioners, totaler, integer_divisors, From 34342c253d67d448001a8c53594aca72c65470ec Mon Sep 17 00:00:00 2001 From: AndrewVSutherland Date: Mon, 27 May 2024 16:15:49 -0400 Subject: [PATCH 3/5] delint --- lmfdb/classical_modular_forms/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lmfdb/classical_modular_forms/main.py b/lmfdb/classical_modular_forms/main.py index ca5d0ea603..d31ddc8621 100644 --- a/lmfdb/classical_modular_forms/main.py +++ b/lmfdb/classical_modular_forms/main.py @@ -14,9 +14,9 @@ parse_noop, parse_equality_constraints, integer_options, parse_subset, search_wrap, display_float, factor_base_factorization_latex, flash_error, to_dict, comma, display_knowl, bigint_knowl, num2letters, - SearchArray, TextBox, SelectBox, TextBoxWithSelect, YesNoBox, + 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 From 70e284fd2c26acb7edbf8968a8b9e965ee7fc1c7 Mon Sep 17 00:00:00 2001 From: AndrewVSutherland Date: Mon, 27 May 2024 16:21:28 -0400 Subject: [PATCH 4/5] support projective_image_type searches for backward compatibility --- lmfdb/classical_modular_forms/main.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lmfdb/classical_modular_forms/main.py b/lmfdb/classical_modular_forms/main.py index d31ddc8621..c3834aaf0c 100644 --- a/lmfdb/classical_modular_forms/main.py +++ b/lmfdb/classical_modular_forms/main.py @@ -790,7 +790,9 @@ def newform_parse(info, query): parse_bool(info, query, 'is_maximal') parse_ints(info, query, 'hecke_ring_index') parse_ints(info, query, 'hecke_ring_generator_nbound') - if info.get('projective_image','').lower() in ["dn","dihedral"]: + 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) From a609797d12456f46a8ba4add6a99e993fcace686 Mon Sep 17 00:00:00 2001 From: AndrewVSutherland Date: Mon, 27 May 2024 16:29:04 -0400 Subject: [PATCH 5/5] tweak --- lmfdb/classical_modular_forms/main.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lmfdb/classical_modular_forms/main.py b/lmfdb/classical_modular_forms/main.py index c3834aaf0c..f4855e0c8d 100644 --- a/lmfdb/classical_modular_forms/main.py +++ b/lmfdb/classical_modular_forms/main.py @@ -787,7 +787,6 @@ def newform_parse(info, query): query['is_largest'] = True elif info['is_maximal_largest'] == 'notlargest': query['is_largest'] = False - parse_bool(info, query, 'is_maximal') parse_ints(info, query, 'hecke_ring_index') parse_ints(info, query, 'hecke_ring_generator_nbound') if 'projective_image_type' in info and not 'projective_image' in info: