def set_info_for_navigation(info, is_set, sbar): r""" Set information for the navigation page. """ (friends, lifts) = sbar ## We always print the list of weights info['initial_list_of_weights'] = print_list_of_weights() # ajax_more2(print_list_of_weights,{'kstart':[0,10,25,50],'klen':[15,15,15]},text=['<<','>>']) ## And the list of characters if we know the level. if(is_set['level']): s = "<option value=" + str(0) + ">Trivial character</option>" D = DirichletGroup(info['level']) if(is_set['weight'] and is_even(info['weight'])): if(is_fundamental_discriminant(info['level'])): x = kronecker_character(info['level']) xi = D.list().index(x) s = s + "<option value=" + str(xi) + ">Kronecker character</option>" for x in D: if(is_set['weight'] and is_even(info['weight']) and x.is_odd()): continue if(is_set['weight'] and is_odd(info['weight']) and x.is_even()): continue xi = D.list().index(x) # s=s+"<option value="+str(xi)+">\(\chi_{"+str(xi)+"}\)</option>" s = s + "<option value=" + str(xi) + ">" + str(xi) + "</option>" info['list_of_characters'] = s friends.append(('L-function', '/Lfunction/ModularForm/GL2/Q/holomorphic/')) lifts.append(('Half-Integral Weight Forms', '/ModularForm/Mp2/Q')) lifts.append(('Siegel Modular Forms', '/ModularForm/GSp4/Q')) return (info, lifts)
def central_character(self): r""" Return the central character of this representation. This is the restriction to `\QQ_p^\times` of the unique smooth character `\omega` of `\mathbf{A}^\times / \QQ^\times` such that `\omega(\varpi_\ell) = \ell^j \varepsilon(\ell)` for all primes `\ell \nmid Np`, where `\varpi_\ell` is a uniformiser at `\ell`, `\varepsilon` is the Nebentypus character of the newform `f`, and `j` is the twist factor (see the documentation for :func:`~LocalComponent`). EXAMPLES:: sage: LocalComponent(Newform('27a'), 3).central_character() Character of Q_3*, of level 0, mapping 3 |--> 1 sage: LocalComponent(Newforms(Gamma1(5), 5, names='c')[0], 5).central_character() Character of Q_5*, of level 1, mapping 2 |--> c0 + 1, 5 |--> 125 sage: LocalComponent(Newforms(DirichletGroup(24)([1, -1,-1]), 3, names='a')[0], 2).central_character() Character of Q_2*, of level 3, mapping 7 |--> 1, 5 |--> -1, 2 |--> -2 """ from sage.rings.arith import crt chi = self.newform().character() f = self.prime() ** self.conductor() N = self.newform().level() // f G = DirichletGroup(f, self.coefficient_field()) chip = G([chi(crt(ZZ(x), 1, f, N)) for x in G.unit_gens()]).primitive_character() a = crt(1, self.prime(), f, N) if chip.conductor() == 1: return SmoothCharacterGroupQp(self.prime(), self.coefficient_field()).character(0, [chi(a) * self.prime()**self.twist_factor()]) else: return SmoothCharacterGroupQp(self.prime(), self.coefficient_field()).character(chip.conductor().valuation(self.prime()), list((~chip).values_on_gens()) + [chi(a) * self.prime()**self.twist_factor()])
def dc_calc_gauss(modulus, number): arg = request.args.get("val", []) if not arg: return flask.abort(404) try: from sage.modular.dirichlet import DirichletGroup chi = DirichletGroup(modulus)[number] gauss_sum_numerical = chi.gauss_sum_numerical(100, int(arg)) return "\(%s\)" % (latex(gauss_sum_numerical)) except Exception, e: return "<span style='color:red;'>ERROR: %s</span>" % e
def dc_calc_kloosterman(modulus, number): arg = request.args.get("val", []) if not arg: return flask.abort(404) try: arg = map(int, arg.split(",")) from sage.modular.dirichlet import DirichletGroup chi = DirichletGroup(modulus)[number] kloosterman_sum_numerical = chi.kloosterman_sum_numerical(100, arg[0], arg[1]) return "\(%s\)" % (latex(kloosterman_sum_numerical)) except Exception, e: return "<span style='color:red;'>ERROR: %s</span>" % e
def dc_calc_jacobi(modulus, number): arg = request.args.get("val", []) if not arg: return flask.abort(404) try: arg = map(int, arg.split(".")) mod = arg[0] num = arg[1] from sage.modular.dirichlet import DirichletGroup chi = DirichletGroup(modulus)[number] psi = DirichletGroup(mod)[num] jacobi_sum = chi.jacobi_sum(psi) return "\(%s\)" % (latex(jacobi_sum)) except Exception, e: return "<span style='color:red;'>ERROR: %s</span>" % e
def print_list_of_characters(level=1, weight=2): r""" Prints a list of characters compatible with the weight and level. """ emf_logger.debug("print_list_of_chars") D = DirichletGroup(level) res = list() for j in range(len(D.list())): if D.list()[j].is_even() and is_even(weight): res.append(j) if D.list()[j].is_odd() and is_odd(weight): res.append(j) s = "" for j in res: s += "\(\chi_{" + str(j) + "}\)" return s
def render_elliptic_modular_form_space(level=None, weight=None, character=None, label=None, **kwds): r""" Render the webpage for a elliptic modular forms space. """ emf_logger.debug("In render_elliptic_modular_form_space kwds: {0}".format(kwds)) emf_logger.debug("Input: level={0},weight={1},character={2},label={3}".format(level, weight, character, label)) info = to_dict(kwds) info["level"] = level info["weight"] = weight info["character"] = character # if kwds.has_key('character') and kwds['character']=='*': # return render_elliptic_modular_form_space_list_chars(level,weight) if character == 0: dimtbl = DimensionTable() else: dimtbl = DimensionTable(1) if not dimtbl.is_in_db(level, weight, character): emf_logger.debug("Data not available") if character == 0: d = dimension_new_cusp_forms(level, weight) else: D = DirichletGroup(level) x = D.galois_orbits(reps_only=True)[character] d = dimension_new_cusp_forms(x, weight) if d > 0: return render_template("not_available.html") else: info["is_empty"] = True return render_template("emf_space.html", **info) emf_logger.debug("Created dimension table in render_elliptic_modular_form_space") info = set_info_for_modular_form_space(**info) emf_logger.debug("keys={0}".format(info.keys())) if "download" in kwds and "error" not in kwds: return send_file(info["tempfile"], as_attachment=True, attachment_filename=info["filename"]) if "dimension_newspace" in kwds and kwds["dimension_newspace"] == 1: # if there is only one orbit we list it emf_logger.debug("Dimension of newforms is one!") info["label"] = "a" return redirect(url_for("emf.render_elliptic_modular_forms", **info)) info["title"] = "Newforms of weight %s on \(\Gamma_{0}(%s)\)" % (weight, level) bread = [(EMF_TOP, url_for("emf.render_elliptic_modular_forms"))] bread.append(("Level %s" % level, url_for("emf.render_elliptic_modular_forms", level=level))) bread.append(("Weight %s" % weight, url_for("emf.render_elliptic_modular_forms", level=level, weight=weight))) # emf_logger.debug("friends={0}".format(friends)) info["bread"] = bread return render_template("emf_space.html", **info)
def _Weyl_law_consts(self): r""" Compute constants for the Weyl law on self._G OUTPUT: - tuple of real numbers EXAMPLES:: sage: M=MaassWaveForms(MySubgroup(Gamma0(1))) sage: M._Weyl_law_consts() (0, 2/pi, (log(pi) - log(2) + 2)/pi, 0, -2) """ import mpmath pi=mpmath.fp.pi ix=Integer(self._G.index()) nc=Integer(len(self._G.cusps())) if(self._G.is_congruence()): lvl=Integer(self._G.level()) else: lvl=0 n2=Integer(self._G.nu2()) n3=Integer(self._G.nu3()) c1=ix/Integer(12) c2=Integer(2)*nc/pi c3=nc*(Integer(2)-ln(Integer(2))+ln(pi))/pi if(lvl<>0): A=1 for q in divisors(lvl): num_prim_dc=0 DG=DirichletGroup(q) for chi in DG.list(): if(chi.is_primitive()): num_prim_dc=num_prim_dc+1 for m in divisors(lvl): if(lvl % (m*q) == 0 and m % q ==0 ): fak=(q*lvl)/gcd(m,lvl/m) A=A*Integer(fak)**num_prim_dc c4=-ln(A)/pi else: c4=Integer(0) # constant term c5=-ix/144+n2/8+n3*2/9-nc/4-1 return (c1,c2,c3,c4,c5)
def __init__(self, parent, k, chi=None): r""" Create a locally algebraic weight-character. EXAMPLES:: sage: pAdicWeightSpace(29)(13, DirichletGroup(29, Qp(29)).0) (13, 29, [2 + 2*29 + ... + O(29^20)]) """ WeightCharacter.__init__(self, parent) k = ZZ(k) self._k = k if chi is None: chi = trivial_character(self._p, QQ) n = ZZ(chi.conductor()) if n == 1: n = self._p if not n.is_power_of(self._p): raise ValueError, "Character must have %s-power conductor" % p self._chi = DirichletGroup(n, chi.base_ring())(chi)
def set_table(info, is_set, make_link=True): # level_min,level_max,weight=2,chi=0,make_link=True): r""" make a bunch of html tables with information about spaces of modular forms with parameters in the given ranges. Should use database in the future... """ D = 0 rowlen = 10 # split into rows of this length... rowlen0 = rowlen rowlen1 = rowlen characters = dict() if('level_min' in info): level_min = int(info['level_min']) else: level_min = 1 if('level_max' in info): level_max = int(info['level_max']) else: level_max = 50 if (level_max - level_min + 1) < rowlen: rowlen0 = level_max - level_min + 1 if(info['list_chars'] != '0'): char1 = 1 else: char1 = 0 if(is_set['weight']): weight = int(info['weight']) else: weight = 2 ## setup the table # print "char11=",char1 tbl = dict() if(char1 == 1): tbl['header'] = 'Dimension of \( S_{' + str(weight) + '}(N,\chi_{n})\)' else: tbl['header'] = 'Dimension of \( S_{' + str(weight) + '}(N)\)' tbl['headersv'] = list() tbl['headersh'] = list() tbl['corner_label'] = "" tbl['data'] = list() tbl['data_format'] = 'html' tbl['class'] = "dimension_table" tbl['atts'] = "border=\"0\" class=\"data_table\"" num_rows = ceil(QQ(level_max - level_min + 1) / QQ(rowlen0)) print "num_rows=", num_rows for i in range(1, rowlen0 + 1): tbl['headersh'].append(i + level_min - 1) for r in range(num_rows): tbl['headersv'].append(r * rowlen0) print "level_min=", level_min print "level_max=", level_max print "char=", char1 for r in range(num_rows): row = list() for k in range(1, rowlen0 + 1): row.append("") # print "row nr. ",r for k in range(1, rowlen0 + 1): N = level_min - 1 + r * rowlen0 + k s = "<a name=\"#" + str(N) + "\"></a>" # print "col ",k,"=",N if(N > level_max or N < 1): continue if(char1 == 0): d = dimension_cusp_forms(N, weight) print "d=", d if(make_link): url = "?weight=" + str(weight) + "&level=" + str(N) + "&character=0" row.append(s + "<a target=\"mainWindow\" href=\"" + url + "\">" + str(d) + "</a>") else: row.append(s + str(d)) # print "dim(",N,weight,")=",d else: D = DirichletGroup(N) print "D=", D s = "<a name=\"#" + str(N) + "\"></a>" small_tbl = dict() # small_tbl['header']='Dimension of \( S_{'+str(weight)+'}(N)\)' small_tbl['headersv'] = ['\( d \)'] small_tbl['headersh'] = list() small_tbl['corner_label'] = "\( n \)" small_tbl['data'] = list() small_tbl['atts'] = "border=\"1\" padding=\"1\"" small_tbl['data_format'] = 'html' row1 = list() # num_small_rows = ceil(QQ(level_max) / QQ(rowlen)) ii = 0 for chi in range(0, len(D.list())): x = D[chi] S = CuspForms(x, weight) d = S.dimension() if(d == 0): continue small_tbl['headersh'].append(chi) if(make_link): url = "?weight=" + str(weight) + "&level=" + str(N) + "&character=" + str(chi) row1.append("<a target=\"mainWindow\" href=\"" + url + "\">" + str(d) + "</a>") else: row1.append(d) ii = ii + 1 print "d=", d if(ii > rowlen1 and len(row1) > 0): ## we make a new table since we may not have regularly dstributed labels # print "Break line! Make new table!" small_tbl['data'].append(row1) s = s + html_table(small_tbl) small_tbl['headersh'] = list() small_tbl['data'] = list() row1 = list() ii = 0 if(len(row1) > 0): small_tbl['data'].append(row1) if(len(row1) > 0 or len(small_tbl['data']) > 0): # print "small_tbl=",small_tbl ss = html_table(small_tbl) # print "ss=",ss s = s + ss # s=s+"\( \chi_{"+str(chi)+"}\) :"+str(d) # print N,k,chi,d # print s else: s = "All spaces are zero-dimensional!" row.append(s) print "row=", row tbl['data'].append(row) s = html_table(tbl) s = s + "\n <br> \(N=" + str(rowlen0) + "\cdot row+col\)" print "Whole table=", s ## ugly solution. but we have latex in the data fields... ss = re.sub('texttt', '', s) info['popup_table'] = ss # info['sidebar']=set_sidebar([navigation,parents,siblings,friends,lifts]) return info
def line(N): G = DirichletGroup(N) return [(_, G[_].is_primitive()) for _ in range(len(G))]
def dimension_eis(self, k=2, eps=None, algorithm="CohenOesterle"): r""" Return the dimension of the space of Eisenstein series forms for self, or the dimension of the subspace corresponding to the given character if one is supplied. INPUT: - ``k`` - an integer (default: 2), the weight. - ``eps`` - either None or a Dirichlet character modulo N, where N is the level of this group. If this is None, then the dimension of the whole space is returned; otherwise, the dimension of the subspace of Eisenstein series of character eps. - ``algorithm`` -- either "CohenOesterle" (the default) or "Quer". This specifies the method to use in the case of nontrivial character: either the Cohen--Oesterle formula as described in Stein's book, or by Möbius inversion using the subgroups GammaH (a method due to Jordi Quer). AUTHORS: - William Stein - Cohen--Oesterle algorithm - Jordi Quer - algorithm based on GammaH subgroups - David Loeffler (2009) - code refactoring EXAMPLES: The following two computations use different algorithms:: sage: [Gamma1(36).dimension_eis(1,eps) for eps in DirichletGroup(36)] [0, 4, 3, 0, 0, 2, 6, 0, 0, 2, 3, 0] sage: [Gamma1(36).dimension_eis(1,eps,algorithm="Quer") for eps in DirichletGroup(36)] [0, 4, 3, 0, 0, 2, 6, 0, 0, 2, 3, 0] So do these:: sage: [Gamma1(48).dimension_eis(3,eps) for eps in DirichletGroup(48)] [0, 12, 0, 4, 0, 8, 0, 4, 12, 0, 4, 0, 8, 0, 4, 0] sage: [Gamma1(48).dimension_eis(3,eps,algorithm="Quer") for eps in DirichletGroup(48)] [0, 12, 0, 4, 0, 8, 0, 4, 12, 0, 4, 0, 8, 0, 4, 0] """ from .all import Gamma0 # first deal with special cases if eps is None: return GammaH_class.dimension_eis(self, k) N = self.level() K = eps.base_ring() eps = DirichletGroup(N, K)(eps) if eps.is_trivial(): return Gamma0(N).dimension_eis(k) # Note case of k = 0 and trivial character already dealt with separately, so k <= 0 here is valid: if (k <= 0) or ((k % 2) == 1 and eps.is_even()) or ((k % 2) == 0 and eps.is_odd()): return ZZ(0) if algorithm == "Quer": n = eps.order() dim = ZZ(0) for d in n.divisors(): G = GammaH_constructor(N, (eps**d).kernel()) dim = dim + moebius(d) * G.dimension_eis(k) return dim // phi(n) elif algorithm == "CohenOesterle": from sage.modular.dims import CohenOesterle j = 2 - k # We use the Cohen-Oesterle formula in a subtle way to # compute dim M_k(N,eps) (see Ch. 6 of William Stein's book on # computing with modular forms). alpha = -ZZ( K(Gamma0(N).index() * (j - 1) / ZZ(12)) + CohenOesterle(eps, j)) if k == 1: return alpha else: return alpha - self.dimension_cusp_forms(k, eps) else: #algorithm not in ["CohenOesterle", "Quer"]: raise ValueError("Unrecognised algorithm in dimension_eis")
def dimension_new_cusp_forms(self, k=2, eps=None, p=0, algorithm="CohenOesterle"): r""" Dimension of the new subspace (or `p`-new subspace) of cusp forms of weight `k` and character `\varepsilon`. INPUT: - ``k`` - an integer (default: 2) - ``eps`` - a Dirichlet character - ``p`` - a prime (default: 0); just the `p`-new subspace if given - ``algorithm`` - either "CohenOesterle" (the default) or "Quer". This specifies the method to use in the case of nontrivial character: either the Cohen--Oesterle formula as described in Stein's book, or by Möbius inversion using the subgroups GammaH (a method due to Jordi Quer). EXAMPLES:: sage: G = DirichletGroup(9) sage: eps = G.0^3 sage: eps.conductor() 3 sage: [Gamma1(9).dimension_new_cusp_forms(k, eps) for k in [2..10]] [0, 0, 0, 2, 0, 2, 0, 2, 0] sage: [Gamma1(9).dimension_cusp_forms(k, eps) for k in [2..10]] [0, 0, 0, 2, 0, 4, 0, 6, 0] sage: [Gamma1(9).dimension_new_cusp_forms(k, eps, 3) for k in [2..10]] [0, 0, 0, 2, 0, 2, 0, 2, 0] Double check using modular symbols (independent calculation):: sage: [ModularSymbols(eps,k,sign=1).cuspidal_subspace().new_subspace().dimension() for k in [2..10]] [0, 0, 0, 2, 0, 2, 0, 2, 0] sage: [ModularSymbols(eps,k,sign=1).cuspidal_subspace().new_subspace(3).dimension() for k in [2..10]] [0, 0, 0, 2, 0, 2, 0, 2, 0] Another example at level 33:: sage: G = DirichletGroup(33) sage: eps = G.1 sage: eps.conductor() 11 sage: [Gamma1(33).dimension_new_cusp_forms(k, G.1) for k in [2..4]] [0, 4, 0] sage: [Gamma1(33).dimension_new_cusp_forms(k, G.1, algorithm="Quer") for k in [2..4]] [0, 4, 0] sage: [Gamma1(33).dimension_new_cusp_forms(k, G.1^2) for k in [2..4]] [2, 0, 6] sage: [Gamma1(33).dimension_new_cusp_forms(k, G.1^2, p=3) for k in [2..4]] [2, 0, 6] """ if eps is None: return GammaH_class.dimension_new_cusp_forms(self, k, p) N = self.level() eps = DirichletGroup(N, eps.base_ring())(eps) if eps.is_trivial(): from .all import Gamma0 return Gamma0(N).dimension_new_cusp_forms(k, p) from .congroup_gammaH import mumu if p == 0 or N % p != 0 or eps.conductor().valuation(p) == N.valuation( p): D = [eps.conductor() * d for d in divisors(N // eps.conductor())] return sum([ Gamma1_constructor(M).dimension_cusp_forms( k, eps.restrict(M), algorithm) * mumu(N // M) for M in D ]) eps_p = eps.restrict(N // p) old = Gamma1_constructor(N // p).dimension_cusp_forms( k, eps_p, algorithm) return self.dimension_cusp_forms(k, eps, algorithm) - 2 * old
def multiple_of_order(self, maxp=None, proof=True): """ Return a multiple of the order. INPUT: - ``proof`` -- a boolean (default: True) The computation of the rational torsion order of J1(p) is conjectural and will only be used if proof=False. See Section 6.2.3 of [CES2003]_. EXAMPLES:: sage: J = J1(11); J Abelian variety J1(11) of dimension 1 sage: J.rational_torsion_subgroup().multiple_of_order() 5 sage: J = J0(17) sage: J.rational_torsion_subgroup().order() 4 This is an example where proof=False leads to a better bound and better performance. :: sage: J = J1(23) sage: J.rational_torsion_subgroup().multiple_of_order() # long time (2s) 9406793 sage: J.rational_torsion_subgroup().multiple_of_order(proof=False) 408991 """ try: if proof: return self._multiple_of_order else: return self._multiple_of_order_proof_false except AttributeError: pass A = self.abelian_variety() N = A.level() if A.dimension() == 0: self._multiple_of_order = ZZ(1) self._multiple_of_order_proof_false = self._multiple_of_order return self._multiple_of_order # return the order of the cuspidal subgroup in the J0(p) case if A.is_J0() and N.is_prime(): self._multiple_of_order = QQ((A.level()-1)/12).numerator() self._multiple_of_order_proof_false = self._multiple_of_order return self._multiple_of_order # The elliptic curve case if A.dimension() == 1: self._multiple_of_order = A.elliptic_curve().torsion_order() self._multiple_of_order_proof_false = self._multiple_of_order return self._multiple_of_order # The conjectural J1(p) case if not proof and A.is_J1() and N.is_prime(): epsilons = [epsilon for epsilon in DirichletGroup(N) if not epsilon.is_trivial() and epsilon.is_even()] bernoullis = [epsilon.bernoulli(2) for epsilon in epsilons] self._multiple_of_order_proof_false = ZZ(N/(2**(N-3))*prod(bernoullis)) return self._multiple_of_order_proof_false # The Gamma0 and Gamma1 case if all((is_Gamma0(G) or is_Gamma1(G) for G in A.groups())): self._multiple_of_order = self.multiple_of_order_using_frobp() return self._multiple_of_order # Unhandled case raise NotImplementedError("No implemented algorithm")
def make_table_of_spaces_fixed_level(level=1, character=0, weight_block=0, **kwds): r""" """ wlen = 15 print "make table: ", level, character, weight_block w_start = wlen * weight_block w_stop = wlen * (weight_block + 1) s = "<table><thead></thead><tbody>\n" s += "<tr><td>Weight \(k\):</td>" dims = dict() links = dict() character = int(character) x = trivial_character(level) if character > 0: D = DirichletGroup(level).list() x = D[int(character)] if x.is_even() and is_odd(w_start): w_start = w_start + 1 w_stop = w_stop + 1 if x.is_odd() and is_even(w_start): w_start = w_start + 1 w_stop = w_stop + 1 weights = list() for weight in range(w_start, w_start + 2 * wlen, 2): weights.append(weight) for weight in weights: s += "<td> %s </td>" % weight s += "</tr><tr>" if character > 0: s += "<td>Dimension:" # of \(S^{\\textrm{new}}_{k}(%s),\chi_{%s}\):" % (level,character) else: s += "<td>Dimension:" # of \(S^{\\textrm{new}}_{k}(%s)\):" % (level) for weight in weights: if character > 0: dims[weight] = dimension_new_cusp_forms(x, weight) else: dims[weight] = dimension_new_cusp_forms(level, weight) s += "<td> %s </td>" % dims[weight] j = 0 # we display ony even weight if the character is even print "w_start=", w_start print "w_stop=", w_stop for weight in weights: if weight not in dims: continue if dims[weight] > 0: url = url_for('emf.render_elliptic_modular_forms', level=level, weight=weight) if character > 0: lab = "\(S^{\\textrm{}}_{%s}(%s,\chi_{%s})\)" % (weight, level, character) else: # lab = " \(S_{%s}(%s)\)" %(weight,level) lab = " S<sup><small></small></sup><sub><small>%s</small></sub>(%s)" % ( weight, level) links[ weight] = "<a style=\"display:inline\" href=\"%s\">%s</a>" % ( url, lab) else: links[weight] = "" j += 1 if j >= wlen: exit l = max(map(len_as_printed, map(str, links))) * 10.0 s += "</tr><tr>" s += "<td>Link to space:</td>" for weight in weights: if weight in links: s += "<td width=\"%s\">%s</td>" % (l + 50, links[weight]) s += "</tr></tbody></table>" # print s return s
def dimension_of_ordinary_subspace(self, p=None, cusp=False): """ If ``cusp`` is ``True``, return dimension of cuspidal ordinary subspace. This does a weight 2 computation with sage's ModularSymbols. EXAMPLES:: sage: M = OverconvergentModularSymbols(11, 0, sign=-1, p=3, prec_cap=4, base=ZpCA(3, 8)) sage: M.dimension_of_ordinary_subspace() 2 sage: M.dimension_of_ordinary_subspace(cusp=True) 2 sage: M = OverconvergentModularSymbols(11, 0, sign=1, p=3, prec_cap=4, base=ZpCA(3, 8)) sage: M.dimension_of_ordinary_subspace(cusp=True) 2 sage: M.dimension_of_ordinary_subspace() 4 sage: M = OverconvergentModularSymbols(11, 0, sign=0, p=3, prec_cap=4, base=ZpCA(3, 8)) sage: M.dimension_of_ordinary_subspace() 6 sage: M.dimension_of_ordinary_subspace(cusp=True) 4 sage: M = OverconvergentModularSymbols(11, 0, sign=1, p=11, prec_cap=4, base=ZpCA(11, 8)) sage: M.dimension_of_ordinary_subspace(cusp=True) 1 sage: M.dimension_of_ordinary_subspace() 2 sage: M = OverconvergentModularSymbols(11, 2, sign=1, p=11, prec_cap=4, base=ZpCA(11, 8)) sage: M.dimension_of_ordinary_subspace(cusp=True) 0 sage: M.dimension_of_ordinary_subspace() 1 sage: M = OverconvergentModularSymbols(11, 10, sign=1, p=11, prec_cap=4, base=ZpCA(11, 8)) sage: M.dimension_of_ordinary_subspace(cusp=True) 1 sage: M.dimension_of_ordinary_subspace() 2 An example with odd weight and hence non-trivial character:: sage: K = Qp(11, 6) sage: DG = DirichletGroup(11, K) sage: chi = DG([K(378703)]) sage: MM = FamiliesOfOMS(chi, 1, p=11, prec_cap=[4, 4], base_coeffs=ZpCA(11, 4), sign=-1) sage: MM.dimension_of_ordinary_subspace() 1 """ try: p = self.prime() except AttributeError: if p is None: raise ValueError("If self doesn't have a prime, must specify p.") try: return self._ord_dim_dict[(p, cusp)] except AttributeError: self._ord_dim_dict = {} except KeyError: pass from sage.modular.dirichlet import DirichletGroup from sage.rings.finite_rings.constructor import GF try: chi = self.character() except AttributeError: chi = DirichletGroup(self.level(), GF(p))[0] if chi is None: chi = DirichletGroup(self.level(), GF(p))[0] from sage.modular.modsym.modsym import ModularSymbols r = self.weight() % (p-1) if chi.is_trivial(): N = chi.modulus() if N % p != 0: N *= p else: e = N.valuation(p) N.divide_knowing_divisible_by(p ** (e-1)) chi = DirichletGroup(N, GF(p))[0] elif chi.modulus() % p != 0: chi = DirichletGroup(chi.modulus() * p, GF(p))(chi) DG = DirichletGroup(chi.modulus(), GF(p)) if r == 0: from sage.modular.arithgroup.congroup_gamma0 import Gamma0_constructor as Gamma0 verbose("in dim: %s, %s, %s"%(self.sign(), chi, p)) M = ModularSymbols(DG(chi), 2, self.sign(), GF(p)) else: psi = [GF(p)(u) ** r for u in DG.unit_gens()] #mod p Teichmuller^r psi = DG(psi) M = ModularSymbols(DG(chi) * psi, 2, self.sign(), GF(p)) if cusp: M = M.cuspidal_subspace() hecke_poly = M.hecke_polynomial(p) verbose("in dim: %s"%(hecke_poly)) x = hecke_poly.parent().gen() d = hecke_poly.degree() - hecke_poly.ord(x) self._ord_dim_dict[(p, cusp)] = d return d
def possible_orders(self, proof=True): """ Return the possible orders of this torsion subgroup. Outside of special cases, this is done by computing a divisor and multiple of the order. INPUT: - ``proof`` -- a boolean (default: True) OUTPUT: - an array of positive integers The computation of the rational torsion order of J1(p) is conjectural and will only be used if proof=False. See Section 6.2.3 of [CES2003]_. EXAMPLES:: sage: J0(11).rational_torsion_subgroup().possible_orders() [5] sage: J0(33).rational_torsion_subgroup().possible_orders() [100, 200] sage: J1(13).rational_torsion_subgroup().possible_orders() [19] sage: J1(16).rational_torsion_subgroup().possible_orders() [1, 2, 4, 5, 10, 20] """ try: if proof: return self._possible_orders else: return self._possible_orders_proof_false except AttributeError: pass A = self.abelian_variety() N = A.level() # return the order of the cuspidal subgroup in the J0(p) case if A.is_J0() and N.is_prime(): self._possible_orders = [QQ((A.level()-1)/12).numerator()] self._possible_orders_proof_false = self._possible_orders return self._possible_orders # the elliptic curve case if A.dimension() == 1: self._possible_orders = [A.elliptic_curve().torsion_order()] self._possible_orders_proof_false = self._possible_orders return self._possible_orders # the conjectural J1(p) case if not proof and A.is_J1() and N.is_prime(): epsilons = [epsilon for epsilon in DirichletGroup(N) if not epsilon.is_trivial() and epsilon.is_even()] bernoullis = [epsilon.bernoulli(2) for epsilon in epsilons] self._possible_orders_proof_false = [ZZ(N/(2**(N-3))*prod(bernoullis))] return self._possible_orders_proof_false u = self.multiple_of_order() l = self.divisor_of_order() assert u % l == 0 O = [l * d for d in divisors(u//l)] self._possible_orders = O if u == l: self._possible_orders_proof_false = O return O
def hecke_operator_on_basis(B, n, k, eps=None, already_echelonized=False): r""" Given a basis `B` of `q`-expansions for a space of modular forms with character `\varepsilon` to precision at least `\#B\cdot n+1`, this function computes the matrix of `T_n` relative to `B`. .. note:: If the elements of B are not known to sufficient precision, this function will report that the vectors are linearly dependent (since they are to the specified precision). INPUT: - ``B`` - list of q-expansions - ``n`` - an integer >= 1 - ``k`` - an integer - ``eps`` - Dirichlet character - ``already_echelonized`` -- bool (default: False); if True, use that the basis is already in Echelon form, which saves a lot of time. EXAMPLES:: sage: sage.modular.modform.constructor.ModularForms_clear_cache() sage: ModularForms(1,12).q_expansion_basis() [ q - 24*q^2 + 252*q^3 - 1472*q^4 + 4830*q^5 + O(q^6), 1 + 65520/691*q + 134250480/691*q^2 + 11606736960/691*q^3 + 274945048560/691*q^4 + 3199218815520/691*q^5 + O(q^6) ] sage: hecke_operator_on_basis(ModularForms(1,12).q_expansion_basis(), 3, 12) Traceback (most recent call last): ... ValueError: The given basis vectors must be linearly independent. sage: hecke_operator_on_basis(ModularForms(1,12).q_expansion_basis(30), 3, 12) [ 252 0] [ 0 177148] TESTS: This shows that the problem with finite fields reported at trac #8281 is solved:: sage: bas_mod5 = [f.change_ring(GF(5)) for f in victor_miller_basis(12, 20)] sage: hecke_operator_on_basis(bas_mod5, 2, 12) [4 0] [0 1] This shows that empty input is handled sensibly (trac #12202):: sage: x = hecke_operator_on_basis([], 3, 12); x [] sage: x.parent() Full MatrixSpace of 0 by 0 dense matrices over Cyclotomic Field of order 1 and degree 1 sage: y = hecke_operator_on_basis([], 3, 12, eps=DirichletGroup(13).0^2); y [] sage: y.parent() Full MatrixSpace of 0 by 0 dense matrices over Cyclotomic Field of order 12 and degree 4 """ if not isinstance(B, (list, tuple)): raise TypeError, "B (=%s) must be a list or tuple" % B if len(B) == 0: if eps is None: R = CyclotomicField(1) else: R = eps.base_ring() return MatrixSpace(R, 0)(0) f = B[0] R = f.base_ring() if eps is None: eps = DirichletGroup(1, R).gen(0) all_powerseries = True for x in B: if not is_PowerSeries(x): all_powerseries = False if not all_powerseries: raise TypeError, "each element of B must be a power series" n = Integer(n) k = Integer(k) prec = (f.prec() - 1) // n A = R**prec V = A.span_of_basis([g.padded_list(prec) for g in B], already_echelonized=already_echelonized) return _hecke_operator_on_basis(B, V, n, k, eps)
class AlgebraicWeight(WeightCharacter): r""" A point in weight space corresponding to a locally algebraic character, of the form `x \mapsto \chi(x) x^k` where `k` is an integer and `\chi` is a Dirichlet character modulo `p^n` for some `n`. TESTS:: sage: w = pAdicWeightSpace(23)(12, DirichletGroup(23, QQ).0) # exact sage: w == loads(dumps(w)) True sage: w = pAdicWeightSpace(23)(12, DirichletGroup(23, Qp(23)).0) # inexact sage: w == loads(dumps(w)) True sage: w is loads(dumps(w)) # elements are not globally unique False """ def __init__(self, parent, k, chi=None): r""" Create a locally algebraic weight-character. EXAMPLES:: sage: pAdicWeightSpace(29)(13, DirichletGroup(29, Qp(29)).0) (13, 29, [2 + 2*29 + ... + O(29^20)]) """ WeightCharacter.__init__(self, parent) k = ZZ(k) self._k = k if chi is None: chi = trivial_character(self._p, QQ) n = ZZ(chi.conductor()) if n == 1: n = self._p if not n.is_power_of(self._p): raise ValueError, "Character must have %s-power conductor" % p self._chi = DirichletGroup(n, chi.base_ring())(chi) def __call__(self, x): r""" Evaluate this character at an element of `\ZZ_p^\times`. EXAMPLES: Exact answers are returned when this is possible:: sage: kappa = pAdicWeightSpace(29)(13, DirichletGroup(29, QQ).0) sage: kappa(1) 1 sage: kappa(0) 0 sage: kappa(12) -106993205379072 sage: kappa(-1) -1 sage: kappa(13 + 4*29 + 11*29^2 + O(29^3)) 9 + 21*29 + 27*29^2 + O(29^3) When the character chi is defined over a p-adic field, the results returned are inexact:: sage: kappa = pAdicWeightSpace(29)(13, DirichletGroup(29, Qp(29)).0^14) sage: kappa(1) 1 + O(29^20) sage: kappa(0) 0 sage: kappa(12) 17 + 11*29 + 7*29^2 + 4*29^3 + 5*29^4 + 2*29^5 + 13*29^6 + 3*29^7 + 18*29^8 + 21*29^9 + 28*29^10 + 28*29^11 + 28*29^12 + 28*29^13 + 28*29^14 + 28*29^15 + 28*29^16 + 28*29^17 + 28*29^18 + 28*29^19 + O(29^20) sage: kappa(12) == -106993205379072 True sage: kappa(-1) == -1 True sage: kappa(13 + 4*29 + 11*29^2 + O(29^3)) 9 + 21*29 + 27*29^2 + O(29^3) """ if isinstance(x, pAdicGenericElement): if x.parent().prime() != self._p: raise TypeError, "x must be an integer or a %s-adic integer" % self._p if self._p**(x.precision_absolute()) < self._chi.conductor(): raise Exception, "Precision too low" xint = x.lift() else: xint = x if (xint % self._p == 0): return 0 return self._chi(xint) * x**self._k def k(self): r""" If this character is `x \mapsto x^k \chi(x)` for an integer `k` and a Dirichlet character `\chi`, return `k`. EXAMPLE:: sage: kappa = pAdicWeightSpace(29)(13, DirichletGroup(29, Qp(29)).0^14) sage: kappa.k() 13 """ return self._k def chi(self): r""" If this character is `x \mapsto x^k \chi(x)` for an integer `k` and a Dirichlet character `\chi`, return `\chi`. EXAMPLE:: sage: kappa = pAdicWeightSpace(29)(13, DirichletGroup(29, Qp(29)).0^14) sage: kappa.chi() Dirichlet character modulo 29 of conductor 29 mapping 2 |--> 28 + 28*29 + 28*29^2 + ... + O(29^20) """ return self._chi def _repr_(self): r""" String representation of self. EXAMPLES:: sage: pAdicWeightSpace(17)(2)._repr_() '2' sage: pAdicWeightSpace(17)(2, DirichletGroup(17, QQ).0)._repr_() '(2, 17, [-1])' sage: pAdicWeightSpace(17)(2, DirichletGroup(17, QQ).0^2)._repr_() '2' """ if self._chi.is_trivial(): return "%s" % self._k else: return "(%s, %s, %s)" % (self._k, self._chi.modulus(), self._chi._repr_short_()) def teichmuller_type(self): r""" Return the Teichmuller type of this weight-character `\kappa`, which is the unique `t \in \ZZ/(p-1)\ZZ` such that `\kappa(\mu) = \mu^t` for \mu a `(p-1)`-st root of 1. For `p = 2` this doesn't make sense, but we still want the Teichmuller type to correspond to the index of the component of weight space in which `\kappa` lies, so we return 1 if `\kappa` is odd and 0 otherwise. EXAMPLE:: sage: pAdicWeightSpace(11)(2, DirichletGroup(11,QQ).0).teichmuller_type() 7 sage: pAdicWeightSpace(29)(13, DirichletGroup(29, Qp(29)).0).teichmuller_type() 14 sage: pAdicWeightSpace(2)(3, DirichletGroup(4,QQ).0).teichmuller_type() 0 """ # Special case p == 2 if self._p == 2: if self.is_even(): return IntegerModRing(2)(0) else: return IntegerModRing(2)(1) m = IntegerModRing(self._p).multiplicative_generator() x = [y for y in IntegerModRing(self._chi.modulus()) if y == m and y**(self._p - 1) == 1] if len(x) != 1: raise ArithmeticError x = x[0] f = IntegerModRing(self._p)(self._chi(x)).log(m) return IntegerModRing(self._p - 1)(self._k + f) def Lvalue(self): r""" Return the value of the p-adic L-function of `\QQ` evaluated at this weight-character. If the character is `x \mapsto x^k \chi(x)` where `k > 0` and `\chi` has conductor a power of `p`, this is an element of the number field generated by the values of `\chi`, equal to the value of the complex L-function `L(1-k, \chi)`. If `\chi` is trivial, it is equal to `(1 - p^{k-1})\zeta(1-k)`. At present this is not implemented in any other cases, except the trivial character (for which the value is `\infty`). TODO: Implement this more generally using the Amice transform machinery in sage/schemes/elliptic_curves/padic_lseries.py, which should clearly be factored out into a separate class. EXAMPLES:: sage: pAdicWeightSpace(7)(4).Lvalue() == (1 - 7^3)*zeta__exact(-3) True sage: pAdicWeightSpace(7)(5, DirichletGroup(7, Qp(7)).0^4).Lvalue() 0 sage: pAdicWeightSpace(7)(6, DirichletGroup(7, Qp(7)).0^4).Lvalue() 1 + 2*7 + 7^2 + 3*7^3 + 3*7^5 + 4*7^6 + 2*7^7 + 5*7^8 + 2*7^9 + 3*7^10 + 6*7^11 + 2*7^12 + 3*7^13 + 5*7^14 + 6*7^15 + 5*7^16 + 3*7^17 + 6*7^18 + O(7^19) """ if self._k > 0: return -self._chi.bernoulli(self._k)/self._k if self.is_trivial(): return Infinity else: raise NotImplementedError, "Don't know how to compute value of this L-function"
class AlgebraicWeight(WeightCharacter): r""" A point in weight space corresponding to a locally algebraic character, of the form `x \mapsto \chi(x) x^k` where `k` is an integer and `\chi` is a Dirichlet character modulo `p^n` for some `n`. TESTS:: sage: w = pAdicWeightSpace(23)(12, DirichletGroup(23, QQ).0) # exact sage: w == loads(dumps(w)) True sage: w = pAdicWeightSpace(23)(12, DirichletGroup(23, Qp(23)).0) # inexact sage: w == loads(dumps(w)) True sage: w is loads(dumps(w)) # elements are not globally unique False """ def __init__(self, parent, k, chi=None): r""" Create a locally algebraic weight-character. EXAMPLES:: sage: pAdicWeightSpace(29)(13, DirichletGroup(29, Qp(29)).0) (13, 29, [2 + 2*29 + ... + O(29^20)]) """ WeightCharacter.__init__(self, parent) k = ZZ(k) self._k = k if chi is None: chi = trivial_character(self._p, QQ) n = ZZ(chi.conductor()) if n == 1: n = self._p if not n.is_power_of(self._p): raise ValueError("Character must have %s-power conductor" % p) self._chi = DirichletGroup(n, chi.base_ring())(chi) def __call__(self, x): r""" Evaluate this character at an element of `\ZZ_p^\times`. EXAMPLES: Exact answers are returned when this is possible:: sage: kappa = pAdicWeightSpace(29)(13, DirichletGroup(29, QQ).0) sage: kappa(1) 1 sage: kappa(0) 0 sage: kappa(12) -106993205379072 sage: kappa(-1) -1 sage: kappa(13 + 4*29 + 11*29^2 + O(29^3)) 9 + 21*29 + 27*29^2 + O(29^3) When the character chi is defined over a p-adic field, the results returned are inexact:: sage: kappa = pAdicWeightSpace(29)(13, DirichletGroup(29, Qp(29)).0^14) sage: kappa(1) 1 + O(29^20) sage: kappa(0) 0 sage: kappa(12) 17 + 11*29 + 7*29^2 + 4*29^3 + 5*29^4 + 2*29^5 + 13*29^6 + 3*29^7 + 18*29^8 + 21*29^9 + 28*29^10 + 28*29^11 + 28*29^12 + 28*29^13 + 28*29^14 + 28*29^15 + 28*29^16 + 28*29^17 + 28*29^18 + 28*29^19 + O(29^20) sage: kappa(12) == -106993205379072 True sage: kappa(-1) == -1 True sage: kappa(13 + 4*29 + 11*29^2 + O(29^3)) 9 + 21*29 + 27*29^2 + O(29^3) """ if isinstance(x, pAdicGenericElement): if x.parent().prime() != self._p: raise TypeError("x must be an integer or a %s-adic integer" % self._p) if self._p**(x.precision_absolute()) < self._chi.conductor(): raise PrecisionError("Precision too low") xint = x.lift() else: xint = x if (xint % self._p == 0): return 0 return self._chi(xint) * x**self._k def k(self): r""" If this character is `x \mapsto x^k \chi(x)` for an integer `k` and a Dirichlet character `\chi`, return `k`. EXAMPLES:: sage: kappa = pAdicWeightSpace(29)(13, DirichletGroup(29, Qp(29)).0^14) sage: kappa.k() 13 """ return self._k def chi(self): r""" If this character is `x \mapsto x^k \chi(x)` for an integer `k` and a Dirichlet character `\chi`, return `\chi`. EXAMPLES:: sage: kappa = pAdicWeightSpace(29)(13, DirichletGroup(29, Qp(29)).0^14) sage: kappa.chi() Dirichlet character modulo 29 of conductor 29 mapping 2 |--> 28 + 28*29 + 28*29^2 + ... + O(29^20) """ return self._chi def __hash__(self): r""" TESTS:: sage: w = pAdicWeightSpace(23)(12, DirichletGroup(23, QQ).0) sage: hash(w) 2363715643371367891 # 64-bit -1456525869 # 32-bit """ if self._chi.is_trivial(): return hash(self._k) else: return hash((self._k, self._chi.modulus(), self._chi)) def _repr_(self): r""" String representation of self. EXAMPLES:: sage: pAdicWeightSpace(17)(2)._repr_() '2' sage: pAdicWeightSpace(17)(2, DirichletGroup(17, QQ).0)._repr_() '(2, 17, [-1])' sage: pAdicWeightSpace(17)(2, DirichletGroup(17, QQ).0^2)._repr_() '2' """ if self._chi.is_trivial(): return "%s" % self._k else: return "(%s, %s, %s)" % (self._k, self._chi.modulus(), self._chi._repr_short_()) def teichmuller_type(self): r""" Return the Teichmuller type of this weight-character `\kappa`, which is the unique `t \in \ZZ/(p-1)\ZZ` such that `\kappa(\mu) = \mu^t` for \mu a `(p-1)`-st root of 1. For `p = 2` this doesn't make sense, but we still want the Teichmuller type to correspond to the index of the component of weight space in which `\kappa` lies, so we return 1 if `\kappa` is odd and 0 otherwise. EXAMPLES:: sage: pAdicWeightSpace(11)(2, DirichletGroup(11,QQ).0).teichmuller_type() 7 sage: pAdicWeightSpace(29)(13, DirichletGroup(29, Qp(29)).0).teichmuller_type() 14 sage: pAdicWeightSpace(2)(3, DirichletGroup(4,QQ).0).teichmuller_type() 0 """ # Special case p == 2 if self._p == 2: if self.is_even(): return IntegerModRing(2)(0) else: return IntegerModRing(2)(1) m = IntegerModRing(self._p).multiplicative_generator() x = [ y for y in IntegerModRing(self._chi.modulus()) if y == m and y**(self._p - 1) == 1 ] if len(x) != 1: raise ArithmeticError x = x[0] f = IntegerModRing(self._p)(self._chi(x)).log(m) return IntegerModRing(self._p - 1)(self._k + f) def Lvalue(self): r""" Return the value of the p-adic L-function of `\QQ` evaluated at this weight-character. If the character is `x \mapsto x^k \chi(x)` where `k > 0` and `\chi` has conductor a power of `p`, this is an element of the number field generated by the values of `\chi`, equal to the value of the complex L-function `L(1-k, \chi)`. If `\chi` is trivial, it is equal to `(1 - p^{k-1})\zeta(1-k)`. At present this is not implemented in any other cases, except the trivial character (for which the value is `\infty`). TODO: Implement this more generally using the Amice transform machinery in sage/schemes/elliptic_curves/padic_lseries.py, which should clearly be factored out into a separate class. EXAMPLES:: sage: pAdicWeightSpace(7)(4).Lvalue() == (1 - 7^3)*zeta__exact(-3) True sage: pAdicWeightSpace(7)(5, DirichletGroup(7, Qp(7)).0^4).Lvalue() 0 sage: pAdicWeightSpace(7)(6, DirichletGroup(7, Qp(7)).0^4).Lvalue() 1 + 2*7 + 7^2 + 3*7^3 + 3*7^5 + 4*7^6 + 2*7^7 + 5*7^8 + 2*7^9 + 3*7^10 + 6*7^11 + 2*7^12 + 3*7^13 + 5*7^14 + 6*7^15 + 5*7^16 + 3*7^17 + 6*7^18 + O(7^19) """ if self._k > 0: return -self._chi.bernoulli(self._k) / self._k if self.is_trivial(): return Infinity else: raise NotImplementedError( "Don't know how to compute value of this L-function")
def dimension_cusp_forms(self, k=2, eps=None, algorithm="CohenOesterle"): r""" Return the dimension of the space of cusp forms for self, or the dimension of the subspace corresponding to the given character if one is supplied. INPUT: - ``k`` - an integer (default: 2), the weight. - ``eps`` - either None or a Dirichlet character modulo N, where N is the level of this group. If this is None, then the dimension of the whole space is returned; otherwise, the dimension of the subspace of forms of character eps. - ``algorithm`` -- either "CohenOesterle" (the default) or "Quer". This specifies the method to use in the case of nontrivial character: either the Cohen--Oesterle formula as described in Stein's book, or by Moebius inversion using the subgroups GammaH (a method due to Jordi Quer). EXAMPLES: We compute the same dimension in two different ways :: sage: K = CyclotomicField(3) sage: eps = DirichletGroup(7*43,K).0^2 sage: G = Gamma1(7*43) Via Cohen--Oesterle: :: sage: Gamma1(7*43).dimension_cusp_forms(2, eps) 28 Via Quer's method: :: sage: Gamma1(7*43).dimension_cusp_forms(2, eps, algorithm="Quer") 28 Some more examples: :: sage: G.<eps> = DirichletGroup(9) sage: [Gamma1(9).dimension_cusp_forms(k, eps) for k in [1..10]] [0, 0, 1, 0, 3, 0, 5, 0, 7, 0] sage: [Gamma1(9).dimension_cusp_forms(k, eps^2) for k in [1..10]] [0, 0, 0, 2, 0, 4, 0, 6, 0, 8] """ from all import Gamma0 # first deal with special cases if eps is None: return GammaH_class.dimension_cusp_forms(self, k) N = self.level() if eps.base_ring().characteristic() != 0: raise ValueError eps = DirichletGroup(N, eps.base_ring())(eps) if eps.is_trivial(): return Gamma0(N).dimension_cusp_forms(k) if (k <= 0) or ((k % 2) == 1 and eps.is_even()) or ((k % 2) == 0 and eps.is_odd()): return ZZ(0) if k == 1: try: n = self.dimension_cusp_forms(1) if n == 0: return ZZ(0) else: # never happens at present raise NotImplementedError, "Computations of dimensions of spaces of weight 1 cusp forms not implemented at present" except NotImplementedError: raise # now the main part if algorithm == "Quer": n = eps.order() dim = ZZ(0) for d in n.divisors(): G = GammaH_constructor(N, (eps**d).kernel()) dim = dim + moebius(d) * G.dimension_cusp_forms(k) return dim // phi(n) elif algorithm == "CohenOesterle": K = eps.base_ring() from sage.modular.dims import CohenOesterle from all import Gamma0 return ZZ( K(Gamma0(N).index() * (k - 1) / ZZ(12)) + CohenOesterle(eps, k)) else: #algorithm not in ["CohenOesterle", "Quer"]: raise ValueError, "Unrecognised algorithm in dimension_cusp_forms"
def make_table_of_dimensions(level_start=1, level_stop=50, weight_start=1, weight_stop=24, char=0, **kwds): r""" make an html table with information about spaces of modular forms with parameters in the given ranges. using a fixed character. Should use database in the future... """ D = 0 rowlen = 15 # split into rows of this length... rowlen0 = rowlen rowlen1 = rowlen characters = dict() level = 'N' weight = 'k' print "char=", char if level_start == level_stop: level = level_start count_min = weight_start count_max = weight_stop if (weight_stop - weight_start + 1) < rowlen: rowlen0 = weight_stop - weight_start + 1 if weight_start == weight_stop: weight = weight_start count_min = level_start count_max = level_stop if (level_stop - level_start + 1) < rowlen: rowlen0 = level_stop - level_start + 1 # else: # return "" tbl = dict() if (char == 0): tbl['header'] = '' # Dimension of \( S_{'+str(weight)+'}('+str(level)+',\chi_{n})\)' charst = "" else: # s = 'Dimension of \( S_{'+str(weight)+'}('+str(level)+')\)' # s += ' (trivial character)' charst = ",\chi_{%s}" % char tbl['header'] = '' tbl['headersv'] = list() tbl['headersh'] = list() if weight == 'k': tbl['corner_label'] = "weight \(k\):" else: tbl['corner_label'] = "level \(N\):" tbl['data'] = list() tbl['data_format'] = 'html' tbl['class'] = "dimension_table" tbl['atts'] = "border=\"1\" class=\"nt_data\" padding=\"25\" width=\"100%\"" num_rows = ceil(QQ(count_max - count_min + 1) / QQ(rowlen0)) print "num_rows=", num_rows for i in range(1, rowlen0 + 1): tbl['headersh'].append(i + count_min - 1) if level_start == level_stop: st = "Dimension of \(S_{k}(%s%s) \):" % (level, charst) tbl['headersv'] = [st] else: st = "Dimension of \(S_{%s}(N%s) \):" % (weight, charst) tbl['headersv'] = [st] tbl['headersv'].append('Link to space:') # make a dummy table first # num_rows = (num_rows-1)*2 for r in range(num_rows * 2): row = [] for k in range(1, rowlen0 + 1): row.append("") tbl['data'].append(row) tbl['data_format'] = dict() for k in range(0, rowlen0): tbl['data_format'][k] = 'html' print "nu_rows=", len(tbl['data']) print "num_cols=", rowlen0 print "num_cols=", [len(r) for r in tbl['data']] for r in range(num_rows): for k in range(0, rowlen0): cnt = count_min + r * rowlen0 + k if level_start == level_stop: weight = cnt else: level = cnt url = url_for('emf.render_elliptic_modular_forms', level=level, weight=weight) if (cnt > count_max or cnt < count_min): tbl['data'][2 * r][k] = "" continue # s="<a name=\"#%s,%s\"></a>" % (level,weight) if (char == 0): d = dimension_cusp_forms(level, weight) else: x = DirichletGroup(level)[char] d = dimension_cusp_forms(x, weight) tbl['data'][2 * r][k] = str(d) if d > 0: s = "\(S_{%s}(%s)\)" % (weight, level) ss = "<a href=\"" + url + "\">" + s + "</a>" tbl['data'][2 * r + 1][k] = ss # else: # tbl['data'][2*r+1][k]="\(\emptyset\)" # ss = make_table_of_characters(level,weight) # tbl['data'][2*r+1][k]=ss # tbl['data'][r][k]=s # print "row=",row # tbl['data'][r]=row # print "tbl=",tbl s = html_table(tbl) # s=s+"\n <br> \(N="+str(rowlen0)+"\cdot row+col\)" # print "SS=",s return s
def dimension_of_ordinary_subspace(self, p=None, cusp=False): """ If ``cusp`` is ``True``, return dimension of cuspidal ordinary subspace. This does a weight 2 computation with sage's ModularSymbols. EXAMPLES:: sage: M = OverconvergentModularSymbols(11, 0, sign=-1, p=3, prec_cap=4, base=ZpCA(3, 8)) sage: M.dimension_of_ordinary_subspace() 2 sage: M.dimension_of_ordinary_subspace(cusp=True) 2 sage: M = OverconvergentModularSymbols(11, 0, sign=1, p=3, prec_cap=4, base=ZpCA(3, 8)) sage: M.dimension_of_ordinary_subspace(cusp=True) 2 sage: M.dimension_of_ordinary_subspace() 4 sage: M = OverconvergentModularSymbols(11, 0, sign=0, p=3, prec_cap=4, base=ZpCA(3, 8)) sage: M.dimension_of_ordinary_subspace() 6 sage: M.dimension_of_ordinary_subspace(cusp=True) 4 sage: M = OverconvergentModularSymbols(11, 0, sign=1, p=11, prec_cap=4, base=ZpCA(11, 8)) sage: M.dimension_of_ordinary_subspace(cusp=True) 1 sage: M.dimension_of_ordinary_subspace() 2 sage: M = OverconvergentModularSymbols(11, 2, sign=1, p=11, prec_cap=4, base=ZpCA(11, 8)) sage: M.dimension_of_ordinary_subspace(cusp=True) 0 sage: M.dimension_of_ordinary_subspace() 1 sage: M = OverconvergentModularSymbols(11, 10, sign=1, p=11, prec_cap=4, base=ZpCA(11, 8)) sage: M.dimension_of_ordinary_subspace(cusp=True) 1 sage: M.dimension_of_ordinary_subspace() 2 An example with odd weight and hence non-trivial character:: sage: K = Qp(11, 6) sage: DG = DirichletGroup(11, K) sage: chi = DG([K(378703)]) sage: MM = FamiliesOfOMS(chi, 1, p=11, prec_cap=[4, 4], base_coeffs=ZpCA(11, 4), sign=-1) sage: MM.dimension_of_ordinary_subspace() 1 """ try: p = self.prime() except AttributeError: if p is None: raise ValueError( "If self doesn't have a prime, must specify p.") try: return self._ord_dim_dict[(p, cusp)] except AttributeError: self._ord_dim_dict = {} except KeyError: pass from sage.modular.dirichlet import DirichletGroup from sage.rings.finite_rings.constructor import GF try: chi = self.character() except AttributeError: chi = DirichletGroup(self.level(), GF(p))[0] if chi is None: chi = DirichletGroup(self.level(), GF(p))[0] from sage.modular.modsym.modsym import ModularSymbols r = self.weight() % (p - 1) if chi.is_trivial(): N = chi.modulus() if N % p != 0: N *= p else: e = N.valuation(p) N.divide_knowing_divisible_by(p**(e - 1)) chi = DirichletGroup(N, GF(p))[0] elif chi.modulus() % p != 0: chi = DirichletGroup(chi.modulus() * p, GF(p))(chi) DG = DirichletGroup(chi.modulus(), GF(p)) if r == 0: from sage.modular.arithgroup.congroup_gamma0 import Gamma0_constructor as Gamma0 verbose("in dim: %s, %s, %s" % (self.sign(), chi, p)) M = ModularSymbols(DG(chi), 2, self.sign(), GF(p)) else: psi = [GF(p)(u)**r for u in DG.unit_gens()] #mod p Teichmuller^r psi = DG(psi) M = ModularSymbols(DG(chi) * psi, 2, self.sign(), GF(p)) if cusp: M = M.cuspidal_subspace() hecke_poly = M.hecke_polynomial(p) verbose("in dim: %s" % (hecke_poly)) x = hecke_poly.parent().gen() d = hecke_poly.degree() - hecke_poly.ord(x) self._ord_dim_dict[(p, cusp)] = d return d
def dimension_eis(self, k=2, eps=None, algorithm="CohenOesterle"): r""" Return the dimension of the space of Eisenstein series forms for self, or the dimension of the subspace corresponding to the given character if one is supplied. INPUT: - ``k`` - an integer (default: 2), the weight. - ``eps`` - either None or a Dirichlet character modulo N, where N is the level of this group. If this is None, then the dimension of the whole space is returned; otherwise, the dimension of the subspace of Eisenstein series of character eps. - ``algorithm`` -- either "CohenOesterle" (the default) or "Quer". This specifies the method to use in the case of nontrivial character: either the Cohen--Oesterle formula as described in Stein's book, or by Moebius inversion using the subgroups GammaH (a method due to Jordi Quer). AUTHORS: - William Stein - Cohen--Oesterle algorithm - Jordi Quer - algorithm based on GammaH subgroups - David Loeffler (2009) - code refactoring EXAMPLES: The following two computations use different algorithms: :: sage: [Gamma1(36).dimension_eis(1,eps) for eps in DirichletGroup(36)] [0, 4, 3, 0, 0, 2, 6, 0, 0, 2, 3, 0] sage: [Gamma1(36).dimension_eis(1,eps,algorithm="Quer") for eps in DirichletGroup(36)] [0, 4, 3, 0, 0, 2, 6, 0, 0, 2, 3, 0] So do these: :: sage: [Gamma1(48).dimension_eis(3,eps) for eps in DirichletGroup(48)] [0, 12, 0, 4, 0, 8, 0, 4, 12, 0, 4, 0, 8, 0, 4, 0] sage: [Gamma1(48).dimension_eis(3,eps,algorithm="Quer") for eps in DirichletGroup(48)] [0, 12, 0, 4, 0, 8, 0, 4, 12, 0, 4, 0, 8, 0, 4, 0] """ from all import Gamma0 # first deal with special cases if eps is None: return GammaH_class.dimension_eis(self, k) N = self.level() eps = DirichletGroup(N)(eps) if eps.is_trivial(): return Gamma0(N).dimension_eis(k) # Note case of k = 0 and trivial character already dealt with separately, so k <= 0 here is valid: if (k <= 0) or ((k % 2) == 1 and eps.is_even()) or ((k%2) == 0 and eps.is_odd()): return ZZ(0) if algorithm == "Quer": n = eps.order() dim = ZZ(0) for d in n.divisors(): G = GammaH_constructor(N,(eps**d).kernel()) dim = dim + moebius(d)*G.dimension_eis(k) return dim//phi(n) elif algorithm == "CohenOesterle": from sage.modular.dims import CohenOesterle K = eps.base_ring() j = 2-k # We use the Cohen-Oesterle formula in a subtle way to # compute dim M_k(N,eps) (see Ch. 6 of William Stein's book on # computing with modular forms). alpha = -ZZ( K(Gamma0(N).index()*(j-1)/ZZ(12)) + CohenOesterle(eps,j) ) if k == 1: return alpha else: return alpha - self.dimension_cusp_forms(k, eps) else: #algorithm not in ["CohenOesterle", "Quer"]: raise ValueError, "Unrecognised algorithm in dimension_eis"
def half_integral_weight_modform_basis(chi, k, prec): r""" A basis for the space of weight `k/2` forms with character `\chi`. The modulus of `\chi` must be divisible by `16` and `k` must be odd and `>1`. INPUT: - ``chi`` - a Dirichlet character with modulus divisible by 16 - ``k`` - an odd integer = 1 - ``prec`` - a positive integer OUTPUT: a list of power series .. warning:: 1. This code is very slow because it requests computation of a basis of modular forms for integral weight spaces, and that computation is still very slow. 2. If you give an input prec that is too small, then the output list of power series may be larger than the dimension of the space of half-integral forms. EXAMPLES: We compute some half-integral weight forms of level 16\*7 :: sage: half_integral_weight_modform_basis(DirichletGroup(16*7).0^2,3,30) [q - 2*q^2 - q^9 + 2*q^14 + 6*q^18 - 2*q^21 - 4*q^22 - q^25 + O(q^30), q^2 - q^14 - 3*q^18 + 2*q^22 + O(q^30), q^4 - q^8 - q^16 + q^28 + O(q^30), q^7 - 2*q^15 + O(q^30)] The following illustrates that choosing too low of a precision can give an incorrect answer. :: sage: half_integral_weight_modform_basis(DirichletGroup(16*7).0^2,3,20) [q - 2*q^2 - q^9 + 2*q^14 + 6*q^18 + O(q^20), q^2 - q^14 - 3*q^18 + O(q^20), q^4 - 2*q^8 + 2*q^12 - 4*q^16 + O(q^20), q^7 - 2*q^8 + 4*q^12 - 2*q^15 - 6*q^16 + O(q^20), q^8 - 2*q^12 + 3*q^16 + O(q^20)] We compute some spaces of low level and the first few possible weights. :: sage: half_integral_weight_modform_basis(DirichletGroup(16,QQ).1, 3, 10) [] sage: half_integral_weight_modform_basis(DirichletGroup(16,QQ).1, 5, 10) [q - 2*q^3 - 2*q^5 + 4*q^7 - q^9 + O(q^10)] sage: half_integral_weight_modform_basis(DirichletGroup(16,QQ).1, 7, 10) [q - 2*q^2 + 4*q^3 + 4*q^4 - 10*q^5 - 16*q^7 + 19*q^9 + O(q^10), q^2 - 2*q^3 - 2*q^4 + 4*q^5 + 4*q^7 - 8*q^9 + O(q^10), q^3 - 2*q^5 - 2*q^7 + 4*q^9 + O(q^10)] sage: half_integral_weight_modform_basis(DirichletGroup(16,QQ).1, 9, 10) [q - 2*q^2 + 4*q^3 - 8*q^4 + 14*q^5 + 16*q^6 - 40*q^7 + 16*q^8 - 57*q^9 + O(q^10), q^2 - 2*q^3 + 4*q^4 - 8*q^5 - 8*q^6 + 20*q^7 - 8*q^8 + 32*q^9 + O(q^10), q^3 - 2*q^4 + 4*q^5 + 4*q^6 - 10*q^7 - 16*q^9 + O(q^10), q^4 - 2*q^5 - 2*q^6 + 4*q^7 + 4*q^9 + O(q^10), q^5 - 2*q^7 - 2*q^9 + O(q^10)] This example once raised an error (see trac #5792). :: sage: half_integral_weight_modform_basis(trivial_character(16),9,10) [q - 2*q^2 + 4*q^3 - 8*q^4 + 4*q^6 - 16*q^7 + 48*q^8 - 15*q^9 + O(q^10), q^2 - 2*q^3 + 4*q^4 - 2*q^6 + 8*q^7 - 24*q^8 + O(q^10), q^3 - 2*q^4 - 4*q^7 + 12*q^8 + O(q^10), q^4 - 6*q^8 + O(q^10)] ALGORITHM: Basmaji (page 55 of his Essen thesis, "Ein Algorithmus zur Berechnung von Hecke-Operatoren und Anwendungen auf modulare Kurven", http://wstein.org/scans/papers/basmaji/). Let `S = S_{k+1}(\epsilon)` be the space of cusp forms of even integer weight `k+1` and character `\varepsilon = \chi \psi^{(k+1)/2}`, where `\psi` is the nontrivial mod-4 Dirichlet character. Let `U` be the subspace of `S \times S` of elements `(a,b)` such that `\Theta_2 a = \Theta_3 b`. Then `U` is isomorphic to `S_{k/2}(\chi)` via the map `(a,b) \mapsto a/\Theta_3`. """ if chi.modulus() % 16: raise ValueError, "the character must have modulus divisible by 16" if not k % 2: raise ValueError, "k (=%s) must be odd" % k if k < 3: raise ValueError, "k (=%s) must be at least 3" % k chi = chi.minimize_base_ring() psi = chi.parent()(DirichletGroup(4, chi.base_ring()).gen()) eps = chi * psi**((k + 1) // 2) eps = eps.minimize_base_ring() M = constructor.ModularForms(eps, (k + 1) // 2) C = M.cuspidal_subspace() B = C.basis() # This computation of S below -- of course --dominates the whole function. #from sage.misc.all import cputime #tm = cputime() #print "Computing basis..." S = [f.q_expansion(prec) for f in B] #print "Time to compute basis", cputime(tm) T2 = theta2_qexp(prec) T3 = theta_qexp(prec) n = len(S) MS = MatrixSpace(M.base_ring(), 2 * n, prec) A = copy(MS.zero_matrix()) for i in range(n): T2f = T2 * S[i] T3f = T3 * S[i] for j in range(prec): A[i, j] = T2f[j] A[n + i, j] = -T3f[j] B = A.kernel().basis() a_vec = [sum([b[i] * S[i] for i in range(n)]) for b in B] if len(a_vec) == 0: return [] R = a_vec[0].parent() t3 = R(T3) return [R(a) / t3 for a in a_vec]
def dimension_new_cusp_forms(self, k=2, eps=None, p=0, algorithm="CohenOesterle"): r""" Dimension of the new subspace (or `p`-new subspace) of cusp forms of weight `k` and character `\varepsilon`. INPUT: - ``k`` - an integer (default: 2) - ``eps`` - a Dirichlet character - ``p`` - a prime (default: 0); just the `p`-new subspace if given - ``algorithm`` - either "CohenOesterle" (the default) or "Quer". This specifies the method to use in the case of nontrivial character: either the Cohen--Oesterle formula as described in Stein's book, or by Moebius inversion using the subgroups GammaH (a method due to Jordi Quer). EXAMPLES:: sage: G = DirichletGroup(9) sage: eps = G.0^3 sage: eps.conductor() 3 sage: [Gamma1(9).dimension_new_cusp_forms(k, eps) for k in [2..10]] [0, 0, 0, 2, 0, 2, 0, 2, 0] sage: [Gamma1(9).dimension_cusp_forms(k, eps) for k in [2..10]] [0, 0, 0, 2, 0, 4, 0, 6, 0] sage: [Gamma1(9).dimension_new_cusp_forms(k, eps, 3) for k in [2..10]] [0, 0, 0, 2, 0, 2, 0, 2, 0] Double check using modular symbols (independent calculation):: sage: [ModularSymbols(eps,k,sign=1).cuspidal_subspace().new_subspace().dimension() for k in [2..10]] [0, 0, 0, 2, 0, 2, 0, 2, 0] sage: [ModularSymbols(eps,k,sign=1).cuspidal_subspace().new_subspace(3).dimension() for k in [2..10]] [0, 0, 0, 2, 0, 2, 0, 2, 0] Another example at level 33:: sage: G = DirichletGroup(33) sage: eps = G.1 sage: eps.conductor() 11 sage: [Gamma1(33).dimension_new_cusp_forms(k, G.1) for k in [2..4]] [0, 4, 0] sage: [Gamma1(33).dimension_new_cusp_forms(k, G.1, algorithm="Quer") for k in [2..4]] [0, 4, 0] sage: [Gamma1(33).dimension_new_cusp_forms(k, G.1^2) for k in [2..4]] [2, 0, 6] sage: [Gamma1(33).dimension_new_cusp_forms(k, G.1^2, p=3) for k in [2..4]] [2, 0, 6] """ if eps == None: return GammaH_class.dimension_new_cusp_forms(self, k, p) N = self.level() eps = DirichletGroup(N)(eps) from all import Gamma0 if eps.is_trivial(): return Gamma0(N).dimension_new_cusp_forms(k, p) from congroup_gammaH import mumu if p == 0 or N%p != 0 or eps.conductor().valuation(p) == N.valuation(p): D = [eps.conductor()*d for d in divisors(N//eps.conductor())] return sum([Gamma1_constructor(M).dimension_cusp_forms(k, eps.restrict(M), algorithm)*mumu(N//M) for M in D]) eps_p = eps.restrict(N//p) old = Gamma1_constructor(N//p).dimension_cusp_forms(k, eps_p, algorithm) return self.dimension_cusp_forms(k, eps, algorithm) - 2*old
def divisor_of_order(self): """ Return a divisor of the order of this torsion subgroup of a modular abelian variety. OUTPUT: A divisor of this torsion subgroup. EXAMPLES:: sage: t = J0(37)[1].rational_torsion_subgroup() sage: t.divisor_of_order() 3 sage: J = J1(19) sage: J.rational_torsion_subgroup().divisor_of_order() 4383 sage: J = J0(45) sage: J.rational_cusp_subgroup().order() 32 sage: J.rational_cuspidal_subgroup().order() 64 sage: J.rational_torsion_subgroup().divisor_of_order() 64 """ try: return self._divisor_of_order except AttributeError: pass A = self.abelian_variety() N = A.level() if A.dimension() == 0: self._divisor_of_order = ZZ(1) return self._divisor_of_order # return the order of the cuspidal subgroup in the J0(p) case if A.is_J0() and N.is_prime(): self._divisor_of_order = QQ((A.level()-1)/12).numerator() return self._divisor_of_order # The elliptic curve case if A.dimension() == 1: self._divisor_of_order = A.elliptic_curve().torsion_order() return self._divisor_of_order # The J1(p) case if A.is_J1() and N.is_prime(): epsilons = [epsilon for epsilon in DirichletGroup(N) if not epsilon.is_trivial() and epsilon.is_even()] bernoullis = [epsilon.bernoulli(2) for epsilon in epsilons] self._divisor_of_order = ZZ(N/(2**(N-3))*prod(bernoullis)) return self._divisor_of_order # The Gamma0 case if all(is_Gamma0(G) for G in A.groups()): self._divisor_of_order = A.rational_cuspidal_subgroup().order() return self._divisor_of_order # Unhandled case self._divisor_of_order = ZZ(1) return self._divisor_of_order
def dimension_cusp_forms(self, k=2, eps=None, algorithm="CohenOesterle"): r""" Return the dimension of the space of cusp forms for self, or the dimension of the subspace corresponding to the given character if one is supplied. INPUT: - ``k`` - an integer (default: 2), the weight. - ``eps`` - either None or a Dirichlet character modulo N, where N is the level of this group. If this is None, then the dimension of the whole space is returned; otherwise, the dimension of the subspace of forms of character eps. - ``algorithm`` -- either "CohenOesterle" (the default) or "Quer". This specifies the method to use in the case of nontrivial character: either the Cohen--Oesterle formula as described in Stein's book, or by Möbius inversion using the subgroups GammaH (a method due to Jordi Quer). Ignored for weight 1. EXAMPLES: We compute the same dimension in two different ways :: sage: K = CyclotomicField(3) sage: eps = DirichletGroup(7*43,K).0^2 sage: G = Gamma1(7*43) Via Cohen--Oesterle:: sage: Gamma1(7*43).dimension_cusp_forms(2, eps) 28 Via Quer's method:: sage: Gamma1(7*43).dimension_cusp_forms(2, eps, algorithm="Quer") 28 Some more examples:: sage: G.<eps> = DirichletGroup(9) sage: [Gamma1(9).dimension_cusp_forms(k, eps) for k in [1..10]] [0, 0, 1, 0, 3, 0, 5, 0, 7, 0] sage: [Gamma1(9).dimension_cusp_forms(k, eps^2) for k in [1..10]] [0, 0, 0, 2, 0, 4, 0, 6, 0, 8] In weight 1, we can sometimes rule out cusp forms existing via Riemann-Roch, but if this does not work, we trigger computation of the cusp forms space via Schaeffer's algorithm:: sage: chi = [u for u in DirichletGroup(40) if u(-1) == -1 and u(21) == 1][0] sage: Gamma1(40).dimension_cusp_forms(1, chi) 0 sage: G = DirichletGroup(57); chi = (G.0) * (G.1)^6 sage: Gamma1(57).dimension_cusp_forms(1, chi) 1 """ from .all import Gamma0 # first deal with special cases if eps is None: return GammaH_class.dimension_cusp_forms(self, k) N = self.level() K = eps.base_ring() eps = DirichletGroup(N, K)(eps) if K.characteristic() != 0: raise NotImplementedError( 'dimension_cusp_forms() is only implemented for rings of characteristic 0' ) if eps.is_trivial(): return Gamma0(N).dimension_cusp_forms(k) if (k <= 0) or ((k % 2) == 1 and eps.is_even()) or ((k % 2) == 0 and eps.is_odd()): return ZZ(0) if k == 1: from sage.modular.modform.weight1 import dimension_wt1_cusp_forms return dimension_wt1_cusp_forms(eps) # now the main part if algorithm == "Quer": n = eps.order() dim = ZZ(0) for d in n.divisors(): G = GammaH_constructor(N, (eps**d).kernel()) dim = dim + moebius(d) * G.dimension_cusp_forms(k) return dim // phi(n) elif algorithm == "CohenOesterle": from sage.modular.dims import CohenOesterle return ZZ( K(Gamma0(N).index() * (k - 1) / ZZ(12)) + CohenOesterle(eps, k)) else: #algorithm not in ["CohenOesterle", "Quer"]: raise ValueError("Unrecognised algorithm in dimension_cusp_forms")
def hecke_operator_on_qexp(f, n, k, eps=None, prec=None, check=True, _return_list=False): r""" Given the `q`-expansion `f` of a modular form with character `\varepsilon`, this function computes the image of `f` under the Hecke operator `T_{n,k}` of weight `k`. EXAMPLES:: sage: M = ModularForms(1,12) sage: hecke_operator_on_qexp(M.basis()[0], 3, 12) 252*q - 6048*q^2 + 63504*q^3 - 370944*q^4 + O(q^5) sage: hecke_operator_on_qexp(M.basis()[0], 1, 12, prec=7) q - 24*q^2 + 252*q^3 - 1472*q^4 + 4830*q^5 - 6048*q^6 + O(q^7) sage: hecke_operator_on_qexp(M.basis()[0], 1, 12) q - 24*q^2 + 252*q^3 - 1472*q^4 + 4830*q^5 - 6048*q^6 - 16744*q^7 + 84480*q^8 - 113643*q^9 - 115920*q^10 + 534612*q^11 - 370944*q^12 - 577738*q^13 + O(q^14) sage: M.prec(20) 20 sage: hecke_operator_on_qexp(M.basis()[0], 3, 12) 252*q - 6048*q^2 + 63504*q^3 - 370944*q^4 + 1217160*q^5 - 1524096*q^6 + O(q^7) sage: hecke_operator_on_qexp(M.basis()[0], 1, 12) q - 24*q^2 + 252*q^3 - 1472*q^4 + 4830*q^5 - 6048*q^6 - 16744*q^7 + 84480*q^8 - 113643*q^9 - 115920*q^10 + 534612*q^11 - 370944*q^12 - 577738*q^13 + 401856*q^14 + 1217160*q^15 + 987136*q^16 - 6905934*q^17 + 2727432*q^18 + 10661420*q^19 - 7109760*q^20 + O(q^21) sage: (hecke_operator_on_qexp(M.basis()[0], 1, 12)*252).add_bigoh(7) 252*q - 6048*q^2 + 63504*q^3 - 370944*q^4 + 1217160*q^5 - 1524096*q^6 + O(q^7) sage: hecke_operator_on_qexp(M.basis()[0], 6, 12) -6048*q + 145152*q^2 - 1524096*q^3 + O(q^4) An example on a formal power series:: sage: R.<q> = QQ[[]] sage: f = q + q^2 + q^3 + q^7 + O(q^8) sage: hecke_operator_on_qexp(f, 3, 12) q + O(q^3) sage: hecke_operator_on_qexp(delta_qexp(24), 3, 12).prec() 8 sage: hecke_operator_on_qexp(delta_qexp(25), 3, 12).prec() 9 An example of computing `T_{p,k}` in characteristic `p`:: sage: p = 199 sage: fp = delta_qexp(prec=p^2+1, K=GF(p)) sage: tfp = hecke_operator_on_qexp(fp, p, 12) sage: tfp == fp[p] * fp True sage: tf = hecke_operator_on_qexp(delta_qexp(prec=p^2+1), p, 12).change_ring(GF(p)) sage: tfp == tf True """ if eps is None: # Need to have base_ring=ZZ to work over finite fields, since # ZZ can coerce to GF(p), but QQ can't. eps = DirichletGroup(1, base_ring=ZZ).gen(0) if check: if not (is_PowerSeries(f) or is_ModularFormElement(f)): raise TypeError, "f (=%s) must be a power series or modular form" % f if not is_DirichletCharacter(eps): raise TypeError, "eps (=%s) must be a Dirichlet character" % eps k = Integer(k) n = Integer(n) v = [] if prec is None: if is_ModularFormElement(f): # always want at least three coefficients, but not too many, unless # requested pr = max(f.prec(), f.parent().prec(), (n + 1) * 3) pr = min(pr, 100 * (n + 1)) prec = pr // n + 1 else: prec = (f.prec() / ZZ(n)).ceil() if prec == Infinity: prec = f.parent().default_prec() // n + 1 if f.prec() < prec: f._compute_q_expansion(prec) p = Integer(f.base_ring().characteristic()) if k != 1 and p.is_prime() and n.is_power_of(p): # if computing T_{p^a} in characteristic p, use the simpler (and faster) # formula v = [f[m * n] for m in range(prec)] else: l = k - 1 for m in range(prec): am = sum([eps(d) * d**l * f[m*n//(d*d)] for \ d in divisors(gcd(n, m)) if (m*n) % (d*d) == 0]) v.append(am) if _return_list: return v if is_ModularFormElement(f): R = f.parent()._q_expansion_ring() else: R = f.parent() return R(v, prec)
def set_table( info, is_set, make_link=True): # level_min,level_max,weight=2,chi=0,make_link=True): r""" make a bunch of html tables with information about spaces of modular forms with parameters in the given ranges. Should use database in the future... """ D = 0 rowlen = 10 # split into rows of this length... rowlen0 = rowlen rowlen1 = rowlen characters = dict() if ('level_min' in info): level_min = int(info['level_min']) else: level_min = 1 if ('level_max' in info): level_max = int(info['level_max']) else: level_max = 50 if (level_max - level_min + 1) < rowlen: rowlen0 = level_max - level_min + 1 if (info['list_chars'] != '0'): char1 = 1 else: char1 = 0 if (is_set['weight']): weight = int(info['weight']) else: weight = 2 ## setup the table # print "char11=",char1 tbl = dict() if (char1 == 1): tbl['header'] = 'Dimension of \( S_{' + str(weight) + '}(N,\chi_{n})\)' else: tbl['header'] = 'Dimension of \( S_{' + str(weight) + '}(N)\)' tbl['headersv'] = list() tbl['headersh'] = list() tbl['corner_label'] = "" tbl['data'] = list() tbl['data_format'] = 'html' tbl['class'] = "dimension_table" tbl['atts'] = "border=\"0\" class=\"data_table\"" num_rows = ceil(QQ(level_max - level_min + 1) / QQ(rowlen0)) print "num_rows=", num_rows for i in range(1, rowlen0 + 1): tbl['headersh'].append(i + level_min - 1) for r in range(num_rows): tbl['headersv'].append(r * rowlen0) print "level_min=", level_min print "level_max=", level_max print "char=", char1 for r in range(num_rows): row = list() for k in range(1, rowlen0 + 1): row.append("") # print "row nr. ",r for k in range(1, rowlen0 + 1): N = level_min - 1 + r * rowlen0 + k s = "<a name=\"#" + str(N) + "\"></a>" # print "col ",k,"=",N if (N > level_max or N < 1): continue if (char1 == 0): d = dimension_cusp_forms(N, weight) print "d=", d if (make_link): url = "?weight=" + str(weight) + "&level=" + str( N) + "&character=0" row.append(s + "<a target=\"mainWindow\" href=\"" + url + "\">" + str(d) + "</a>") else: row.append(s + str(d)) # print "dim(",N,weight,")=",d else: D = DirichletGroup(N) print "D=", D s = "<a name=\"#" + str(N) + "\"></a>" small_tbl = dict() # small_tbl['header']='Dimension of \( S_{'+str(weight)+'}(N)\)' small_tbl['headersv'] = ['\( d \)'] small_tbl['headersh'] = list() small_tbl['corner_label'] = "\( n \)" small_tbl['data'] = list() small_tbl['atts'] = "border=\"1\" padding=\"1\"" small_tbl['data_format'] = 'html' row1 = list() # num_small_rows = ceil(QQ(level_max) / QQ(rowlen)) ii = 0 for chi in range(0, len(D.list())): x = D[chi] S = CuspForms(x, weight) d = S.dimension() if (d == 0): continue small_tbl['headersh'].append(chi) if (make_link): url = "?weight=" + str(weight) + "&level=" + str( N) + "&character=" + str(chi) row1.append("<a target=\"mainWindow\" href=\"" + url + "\">" + str(d) + "</a>") else: row1.append(d) ii = ii + 1 print "d=", d if (ii > rowlen1 and len(row1) > 0): ## we make a new table since we may not have regularly dstributed labels # print "Break line! Make new table!" small_tbl['data'].append(row1) s = s + html_table(small_tbl) small_tbl['headersh'] = list() small_tbl['data'] = list() row1 = list() ii = 0 if (len(row1) > 0): small_tbl['data'].append(row1) if (len(row1) > 0 or len(small_tbl['data']) > 0): # print "small_tbl=",small_tbl ss = html_table(small_tbl) # print "ss=",ss s = s + ss # s=s+"\( \chi_{"+str(chi)+"}\) :"+str(d) # print N,k,chi,d # print s else: s = "All spaces are zero-dimensional!" row.append(s) print "row=", row tbl['data'].append(row) s = html_table(tbl) s = s + "\n <br> \(N=" + str(rowlen0) + "\cdot row+col\)" print "Whole table=", s ## ugly solution. but we have latex in the data fields... ss = re.sub('texttt', '', s) info['popup_table'] = ss # info['sidebar']=set_sidebar([navigation,parents,siblings,friends,lifts]) return info
def dimension_cusp_forms(self, k=2, eps=None, algorithm="CohenOesterle"): r""" Return the dimension of the space of cusp forms for self, or the dimension of the subspace corresponding to the given character if one is supplied. INPUT: - ``k`` - an integer (default: 2), the weight. - ``eps`` - either None or a Dirichlet character modulo N, where N is the level of this group. If this is None, then the dimension of the whole space is returned; otherwise, the dimension of the subspace of forms of character eps. - ``algorithm`` -- either "CohenOesterle" (the default) or "Quer". This specifies the method to use in the case of nontrivial character: either the Cohen--Oesterle formula as described in Stein's book, or by Moebius inversion using the subgroups GammaH (a method due to Jordi Quer). EXAMPLES: We compute the same dimension in two different ways :: sage: K = CyclotomicField(3) sage: eps = DirichletGroup(7*43,K).0^2 sage: G = Gamma1(7*43) Via Cohen--Oesterle: :: sage: Gamma1(7*43).dimension_cusp_forms(2, eps) 28 Via Quer's method: :: sage: Gamma1(7*43).dimension_cusp_forms(2, eps, algorithm="Quer") 28 Some more examples: :: sage: G.<eps> = DirichletGroup(9) sage: [Gamma1(9).dimension_cusp_forms(k, eps) for k in [1..10]] [0, 0, 1, 0, 3, 0, 5, 0, 7, 0] sage: [Gamma1(9).dimension_cusp_forms(k, eps^2) for k in [1..10]] [0, 0, 0, 2, 0, 4, 0, 6, 0, 8] """ from all import Gamma0 # first deal with special cases if eps is None: return GammaH_class.dimension_cusp_forms(self, k) N = self.level() if eps.base_ring().characteristic() != 0: raise ValueError eps = DirichletGroup(N, eps.base_ring())(eps) if eps.is_trivial(): return Gamma0(N).dimension_cusp_forms(k) if (k <= 0) or ((k % 2) == 1 and eps.is_even()) or ((k%2) == 0 and eps.is_odd()): return ZZ(0) if k == 1: try: n = self.dimension_cusp_forms(1) if n == 0: return ZZ(0) else: # never happens at present raise NotImplementedError, "Computations of dimensions of spaces of weight 1 cusp forms not implemented at present" except NotImplementedError: raise # now the main part if algorithm == "Quer": n = eps.order() dim = ZZ(0) for d in n.divisors(): G = GammaH_constructor(N,(eps**d).kernel()) dim = dim + moebius(d)*G.dimension_cusp_forms(k) return dim//phi(n) elif algorithm == "CohenOesterle": K = eps.base_ring() from sage.modular.dims import CohenOesterle from all import Gamma0 return ZZ( K(Gamma0(N).index() * (k-1)/ZZ(12)) + CohenOesterle(eps,k) ) else: #algorithm not in ["CohenOesterle", "Quer"]: raise ValueError, "Unrecognised algorithm in dimension_cusp_forms"
def product_space(chi, k, weights=False, base_ring=None, verbose=False): r""" Computes all eisenstein series, and products of pairs of eisenstein series of lower weight, lying in the space of modular forms of weight $k$ and nebentypus $\chi$. INPUT: - chi - Dirichlet character, the nebentypus of the target space - k - an integer, the weight of the target space OUTPUT: - a matrix of coefficients of q-expansions, which are the products of Eisenstein series in M_k(chi). WARNING: It is only for principal chi that we know that the resulting space is the whole space of modular forms. """ if weights == False: weights = srange(1, k / 2 + 1) weight_dict = {} weight_dict[-1] = [w for w in weights if w % 2] # Odd weights weight_dict[1] = [w for w in weights if not w % 2] # Even weights try: N = chi.modulus() except AttributeError: if chi.parent() == ZZ: N = chi chi = DirichletGroup(N)[0] Id = DirichletGroup(1)[0] if chi(-1) != (-1)**k: raise ValueError('chi(-1)!=(-1)^k') sturm = ModularForms(N, k).sturm_bound() + 1 if N > 1: target_dim = dimension_modular_forms(chi, k) else: target_dim = dimension_modular_forms(1, k) D = DirichletGroup(N) # product_space should ideally be called over number fields. Over complex # numbers the exact linear algebra solutions might not exist. if base_ring == None: base_ring = CyclotomicField(euler_phi(N)) Q = PowerSeriesRing(base_ring, 'q') q = Q.gen() d = len(D) prim_chars = [phi.primitive_character() for phi in D] divs = divisors(N) products = Matrix(base_ring, []) indexlist = [] rank = 0 if verbose: print(D) print('Sturm bound', sturm) #TODO: target_dim needs refinment in the case of weight 2. print('Target dimension', target_dim) for i in srange(0, d): # First character phi = prim_chars[i] M1 = phi.conductor() for j in srange(0, d): # Second character psi = prim_chars[j] M2 = psi.conductor() if not M1 * M2 in divs: continue parity = psi(-1) * phi(-1) for t1 in divs: if not M1 * M2 * t1 in divs: continue #TODO: THE NEXT CONDITION NEEDS TO BE CORRECTED. THIS IS JUST A TEST if phi.bar() == psi and not ( k == 2): #and i==0 and j==0 and t1==1): E = eisenstein_series_at_inf(phi, psi, k, sturm, t1, base_ring).padded_list() try: products.T.solve_right(vector(base_ring, E)) except ValueError: products = Matrix(products.rows() + [E]) indexlist.append([k, i, j, t1]) rank += 1 if verbose: print('Added ', [k, i, j, t1]) print('Rank is now', rank) if rank == target_dim: return products, indexlist for t in divs: if not M1 * M2 * t1 * t in divs: continue for t2 in divs: if not M1 * M2 * t1 * t2 * t in divs: continue for l in weight_dict[parity]: if l == 1 and phi.is_odd(): continue if i == 0 and j == 0 and (l == 2 or l == k - 2): continue #TODO: THE NEXT CONDITION NEEDS TO BE REMOVED. THIS IS JUST A TEST if l == 2 or l == k - 2: continue E1 = eisenstein_series_at_inf( phi, psi, l, sturm, t1 * t, base_ring) E2 = eisenstein_series_at_inf( phi**(-1), psi**(-1), k - l, sturm, t2 * t, base_ring) #If chi is non-principal this needs to be changed to be something like chi*phi^(-1) instead of phi^(-1) E = (E1 * E2 + O(q**sturm)).padded_list() try: products.T.solve_right(vector(base_ring, E)) except ValueError: products = Matrix(products.rows() + [E]) indexlist.append([l, k - l, i, j, t1, t2, t]) rank += 1 if verbose: print('Added ', [l, k - l, i, j, t1, t2, t]) print('Rank', rank) if rank == target_dim: return products, indexlist return products, indexlist