class WebNewForm_computing_class(WebNewForm_class): r""" Class for representing a (cuspidal) newform on the web. TODO: Include the computed data in the original database so we won't have to compute here at all. """ def __init__(self, N=1, k=2, chi=1, label='', prec=10, bitprec=53, display_bprec=26,parent=None, data=None,compute=False, verbose=-1,get_from_db=True): r""" Init self as form with given label in S_k(N,chi) """ super(WebNewForm_computing_class,self).__init__(N,k,chi,label,prec,bitprec,display_bprec,get_from_db=False) print "d=",self.__dict__ wmf_logger.debug("WebNewForm with N,k,chi,label={0}".format( (N,k,chi,label))) self._as_factor = None self._prec_needed_for_lfunctions = None self.set_parent() self.set_newform_number() self.compute_additional_properties() #self.insert_into_db() def compute_additional_properties(self): r""" Compute everything we need. """ wmf_logger.debug("Update ap's") self._get_aps() wmf_logger.debug("compute q-expansion") prec = self._prec_needed_for_lfunctions() self.set_q_expansion_embeddings(prec=prec) wmf_logger.debug("as polynomial") if self._N == 1: self.as_polynomial_in_E4_and_E6() wmf_logger.debug("compute twist info") self.twist_info() wmf_logger.debug("compute CM-values") self.compute_cm_values_numeric() wmf_logger.debug("Get Atkin-Lehner evs") self.get_atkin_lehner_eigenvalues() wmf_logger.debug("compute Satake parameters") self.set_is_CM() wmf_logger.debug("compute Satake parameters") self.compute_satake_parameters_numeric() self.set_dimensions() self.coefficient_field() self.get_base_ring() #c = self.coefficients(self.prec(),insert_in_db=False) self.insert_into_db() ## Functions related to storing / fetching data from database ## def insert_into_db(self): r""" Insert a dictionary of data for self into the database collection WebNewforms.files """ wmf_logger.debug("inserting self into db! name={0}".format(self._name)) C = connect_to_modularforms_db('WebNewforms.files') fs = get_files_from_gridfs('WebNewforms') s = {'name':self._name,'version':float(self._version)} rec = C.find_one(s) if rec: id = rec.get('_id') else: id = None if id<>None: wmf_logger.debug("Removing self from db with id={0}".format(id)) fs.delete(id) fname = "webnewform-{0:0>5}-{1:0>3}-{2:0>3}-{3}".format(self._N,self._k,self._chi,self._label) d = self.to_dict() d.pop('_ap',None) d.pop('_character',None) d.pop('_as_factor',None) id = fs.put(dumps(d),filename=fname,N=int(self._N),k=int(self._k),chi=int(self._chi),label=self._label,name=self._name,version=float(self._version)) wmf_logger.debug("inserted :{0}".format(id)) ## Internal functions ## def set_parent(self): from wmf.web_modform_space_computing import WebModFormSpace_computing_class,WebModFormSpace_computing if not isinstance(self._parent,WebModFormSpace_computing_class): if self._verbose > 0: emf_logger.debug("compute parent! label={0}".format(label)) self._parent = WebModFormSpace_computing(self._N, self._k,self._chi) def set_newform_number(self): r""" Find the index of self in the Galois decomposition list. """ if self._newform_number is None: C = connect_to_modularforms_db('Newform_factors.files') res = C.find({'N':int(self.level()),'k':int(self.weight()), 'cchi':int(self.chi())}) num_orbits = res.count() print "num_orbits=",num_orbits for i in range(num_orbits): if orbit_label(i)==self.label(): self._newform_number = i break if self._newform_number is None: raise ValueError,"Newform with this label is not in the space!" def as_factor(self): r""" Return self as a newform factor """ if self._as_factor is None: C = connect_to_modularforms_db('Newform_factors.files') s = {'N':int(self.level()),'k':int(self.weight()), 'cchi':int(self.chi()),'newform':int(self.newform_number())} res = C.find_one(s) print res if not res is None: fid = res['_id'] fs = get_files_from_gridfs('Newform_factors') self._as_factor = loads(fs.get(fid).read()) if self._as_factor is None: raise ValueError,"Newform matching {0} can not be found".format(s) return self._as_factor def get_base_ring(self): r""" The base ring of self, that is, the field of values of the character of self. """ if self._base_ring is None: self._base_ring = self.as_factor().base_ring() return self._base_ring def relative_degree(self): r""" Degree of the field of coefficient relative to its base ring. """ if self._relative_degree is None: self._relative_degree = self.coefficient_field().absolute_degree()/self.base_ring().absolute_degree() return self._relative_degree def set_dimensions(self): r""" The dimension of this galois orbit is not necessarily equal to the degree of the number field, when we have a character.... We therefore need this routine to distinguish between the two cases... """ if self._dimension is None: self._dimension = self.as_factor().dimension() def _prec_needed_for_lfunctions(self): r""" Calculates the number of coefficients needed for the L-function main page (formula taken from their pages) """ if self._prec_needed_for_lfunctions is None: self._prec_needed_for_lfunctions = 22 + int(RR(5)*RR(self._k)*RR(self._N).sqrt()) return self._prec_needed_for_lfunctions def set_q_expansion_embeddings(self, prec=10, bitprec=53,format='numeric',display_bprec=26,insert_in_db=True): r""" Compute all embeddings of self into C which are in the same space as self. Return 0 if we didn't compute anything new, otherwise return 1. """ wmf_logger.debug("computing embeddings of q-expansions : has {0} embedded coeffs. Want : {1} with bitprec={2}".format(len(self._embeddings),prec,bitprec)) if display_bprec > bitprec: display_bprec = bitprec ## First check if we have sufficient data if self._embeddings['prec'] >= prec or self._embeddings['bitprec'] >= bitprec: return 0 ## We should already have sufficient data. ## Else we compute new embeddings. CF = ComplexField(bitprec) # First wee if we need higher precision, in which case we reset all coefficients: if self._embeddings['bitprec'] < bitprec: self._embeddings['values']=[] self._embeddings['latex']=[] self._embeddings['prec']=0 # See if we have need of more coefficients nstart = len(self._embeddings) wmf_logger.debug("Should have {0} embeddings".format(self._embeddings['prec'])) wmf_logger.debug("Computing new stuff !") for n in range(self._embeddings['prec'],prec): try: cn = self.coefficient(n) except IndexError: break if hasattr(cn, 'complex_embeddings'): cn_emb = cn.complex_embeddings(bitprec) else: cn_emb = [ CF(cn) for i in range(deg) ] self._embeddings['values'].append(cn_emb) self._embeddings['prec'] = len(self._embeddings['values']) # See if we also need to recompute the latex strings if display_bprec > self._embeddings['bitprec']: self._embeddings['latex'] = [] ## Have to redo these numc = len(self._embeddings['latex']) for n in range(numc,prec): cn_emb = [] for x in self._embeddings['values'][n]: t = my_complex_latex(x,display_bprec) cn_emb_latex.append(t) self._embeddidngs['latex'].append(cn_emb) wmf_logger.debug("has embeddings_latex:{0}".format(nstart)) return 1 def get_atkin_lehner_eigenvalues(self): r""" Get the Atkin-Lehner eigenvalues from database if they exist. """ if not ((self.character().is_trivial() or self.character().order() == 2) and not self._atkin_lehner_eigenvalues is None): return None C = connect_to_modularforms_db('Atkin-Lehner.files') fs = get_files_from_gridfs('Atkin-Lehner') s = {'N':int(self.level()),'k':int(self.weight()), 'cchi':int(self.chi()),'newform':int(self.newform_number())} res = C.find_one(s) self._atkin_lehner_eigenvalues = {} if not res is None: alid = res['_id'] al = loads(fs.get(alid).read()) for d in prime_divisors(self.level()): self._atkin_lehner_eigenvalues[d] = 1 if al[d]=='+' else -1 else: # We compute them A = self.as_factor() for p in prime_divisors(self.level()): self._atkin_lehner_eigenvalues[p]= int(A.atkin_lehner_operator(p).matrix()[0,0]) def set_twist_info(self, prec=10,insert_in_db=True): r""" Try to find forms of lower level which get twisted into self. OUTPUT: -''[t,l]'' -- tuple of a Bool t and a list l. The list l contains all tuples of forms which twists to the given form. The actual minimal one is the first element of this list. t is set to True if self is minimal and False otherwise EXAMPLES:: """ if(len(self._twist_info) > 0): return self._twist_info N = self.level() k = self.weight() if(is_squarefree(ZZ(N))): self._twist_info = [True, None ] return [True, None] # We need to check all square factors of N twist_candidates = list() KF = self.base_ring() # check how many Hecke eigenvalues we need to check max_nump = self._number_of_hecke_eigenvalues_to_check() maxp = max(primes_first_n(max_nump)) for d in divisors(N): if(d == 1): continue # we look at all d such that d^2 divdes N if(not ZZ(d ** 2).divides(ZZ(N))): continue D = DirichletGroup(d) # check possible candidates to twist into f # g in S_k(M,chi) wit M=N/d^2 M = ZZ(N / d ** 2) if(self._verbose > 0): wmf_logger.debug("Checking level {0}".format(M)) for xig in range(euler_phi(M)): (t, glist) = _get_newform(M,k, xig) if(not t): return glist for g in glist: if(self._verbose > 1): wmf_logger.debug("Comparing to function {0}".format(g)) KG = g.base_ring() # we now see if twisting of g by xi in D gives us f for xi in D: try: for p in primes_first_n(max_nump): if(ZZ(p).divides(ZZ(N))): continue bf = self.as_factor().q_eigenform(maxp + 1, names='x')[p] bg = g.q_expansion(maxp + 1)[p] if(bf == 0 and bg == 0): continue elif(bf == 0 and bg != 0 or bg == 0 and bf != 0): raise StopIteration() if(ZZ(p).divides(xi.conductor())): raise ArithmeticError("") xip = xi(p) # make a preliminary check that the base rings match with respect to being # real or not try: QQ(xip) XF = QQ if(KF != QQ or KG != QQ): raise StopIteration except TypeError: # we have a non-rational (i.e. complex) value of the character XF = xip.parent() if((KF.absolute_degree() == 1 or KF.is_totally_real()) and (KG.absolute_degre() == 1 or KG.is_totally_real())): raise StopIteration ## it is diffcult to compare elements from diferent rings in general but we make some checcks # is it possible to see if there is a larger ring which everything can be # coerced into? ok = False try: a = KF(bg / xip) b = KF(bf) ok = True if(a != b): raise StopIteration() except TypeError: pass try: a = KG(bg) b = KG(xip * bf) ok = True if(a != b): raise StopIteration() except TypeError: pass if(not ok): # we could coerce and the coefficients were equal return "Could not compare against possible candidates!" # otherwise if we are here we are ok and found a candidate twist_candidates.append([M, g.q_expansion(prec), xi]) except StopIteration: # they are not equal pass wmf_logger.debug("Candidates=v{0}".format(twist_candidates)) self._twist_info = (False, twist_candidates) if(len(twist_candidates) == 0): self._twist_info = [True, None] else: self._twist_info = [False, twist_candidates] return self._twist_info def set_is_CM(self,insert_in_db=True): r""" Checks if f has complex multiplication and if it has then it returns the character. OUTPUT: -''[t,x]'' -- string saying whether f is CM or not and if it is, the corresponding character EXAMPLES:: """ if(len(self._is_CM) > 0): return self._is_CM max_nump = self._number_of_hecke_eigenvalues_to_check() # E,v = self._f.compact_system_of_eigenvalues(max_nump+1) try: coeffs = self.coefficients(range(max_nump + 1),insert_in_db=insert_in_db) except IndexError: return None,None nz = coeffs.count(0) # number of zero coefficients nnz = len(coeffs) - nz # number of non-zero coefficients if(nz == 0): self._is_CM = [False, 0] return self._is_CM # probaly checking too many for D in range(3, ceil(QQ(max_nump) / QQ(2))): try: for x in DirichletGroup(D): if(x.order() != 2): continue # we know that for CM we need x(p) = -1 => c(p)=0 # (for p not dividing N) if(x.values().count(-1) > nz): raise StopIteration() # do not have CM with this char for p in prime_range(max_nump + 1): if(x(p) == -1 and coeffs[p] != 0): raise StopIteration() # do not have CM with this char # if we are here we have CM with x. self._is_CM = [True, x] return self._is_CM except StopIteration: pass self._is_CM = [False, 0] return self._is_CM def as_polynomial_in_E4_and_E6(self,insert_in_db=True): r""" If self is on the full modular group writes self as a polynomial in E_4 and E_6. OUTPUT: -''X'' -- vector (x_1,...,x_n) with f = Sum_{i=0}^{k/6} x_(n-i) E_6^i * E_4^{k/4-i} i.e. x_i is the coefficient of E_6^(k/6-i)* """ if(self.level() != 1): raise NotImplementedError("Only implemented for SL(2,Z). Need more generators in general.") if(self._as_polynomial_in_E4_and_E6 is not None and self._as_polynomial_in_E4_and_E6 != ''): return self._as_polynomial_in_E4_and_E6 d = self._parent.dimension_modular_forms() # dimension of space of modular forms k = self.weight() K = self.base_ring() l = list() # for n in range(d+1): # l.append(self._f.q_expansion(d+2)[n]) # v=vector(l) # (self._f.coefficients(d+1)) v = vector(self.coefficients(range(d),insert_in_db=insert_in_db)) d = dimension_modular_forms(1, k) lv = len(v) if(lv < d): raise ArithmeticError("not enough Fourier coeffs") e4 = EisensteinForms(1, 4).basis()[0].q_expansion(lv + 2) e6 = EisensteinForms(1, 6).basis()[0].q_expansion(lv + 2) m = Matrix(K, lv, d) lima = floor(k / 6) # lima=k\6; if((lima - (k / 2)) % 2 == 1): lima = lima - 1 poldeg = lima col = 0 monomials = dict() while(lima >= 0): deg6 = ZZ(lima) deg4 = (ZZ((ZZ(k / 2) - 3 * lima) / 2)) e6p = (e6 ** deg6) e4p = (e4 ** deg4) monomials[col] = [deg4, deg6] eis = e6p * e4p for i in range(1, lv + 1): m[i - 1, col] = eis.coefficients()[i - 1] lima = lima - 2 col = col + 1 if (col != d): raise ArithmeticError("bug dimension") # return [m,v] if self._verbose > 0: wmf_logger.debug("m={0}".format(m, type(m))) wmf_logger.debug("v={0}".format(v, type(v))) try: X = m.solve_right(v) except: return "" self._as_polynomial_in_E4_and_E6 = [poldeg, monomials, X] return [poldeg, monomials, X] def exact_cm_at_i_level_1(self, N=10,insert_in_db=True): r""" Use formula by Zagier (taken from pari implementation by H. Cohen) to compute the geodesic expansion of self at i and evaluate the constant term. INPUT: -''N'' -- integer, the length of the expansion to use. """ try: [poldeg, monomials, X] = self.as_polynomial_in_E4_and_E6() except: return "" k = self.weight() tab = dict() QQ['x'] tab[0] = 0 * x ** 0 tab[1] = X[0] * x ** poldeg for ix in range(1, len(X)): tab[1] = tab[1] + QQ(X[ix]) * x ** monomials[ix][1] for n in range(1, N + 1): tmp = -QQ(k + 2 * n - 2) / QQ(12) * x * tab[n] + (x ** 2 - QQ(1)) / QQ(2) * ((tab[ n]).derivative()) tab[n + 1] = tmp - QQ((n - 1) * (n + k - 2)) / QQ(144) * tab[n - 1] res = 0 for n in range(1, N + 1): term = (tab[n](x=0)) * 12 ** (floor(QQ(n - 1) / QQ(2))) * x ** (n - 1) / factorial(n - 1) res = res + term return res def print_as_polynomial_in_E4_and_E6(self): r""" """ if(self.level() != 1): return "" try: [poldeg, monomials, X] = self.as_polynomial_in_E4_and_E6() except ValueError: return "" s = "" e4 = "E_{4}" e6 = "E_{6}" dens = map(denominator, X) g = gcd(dens) s = "\\frac{1}{" + str(g) + "}\left(" for n in range(len(X)): c = X[n] * g if(c == -1): s = s + "-" elif(c != 1): s = s + str(c) if(n > 0 and c > 0): s = s + "+" d4 = monomials[n][0] d6 = monomials[n][1] if(d6 > 0): s = s + e6 + "^{" + str(d6) + "}" if(d4 > 0): s = s + e4 + "^{" + str(d4) + "}" s = s + "\\right)" return "\(" + s + "\)" def compute_cm_values_numeric(self,digits=12,insert_in_db=True): r""" Compute CM-values numerically. """ if isinstance(self._cm_values,dict) and self._cm_values <> {}: return self._cm_values # the points we want are i and rho. More can be added later... bits = ceil(int(digits) * int(4)) CF = ComplexField(bits) RF = ComplexField(bits) eps = RF(10 ** - (digits + 1)) if(self._verbose > 1): wmf_logger.debug("eps={0}".format(eps)) K = self.base_ring() # recall that degree = self.degree() cm_vals = dict() rho = CyclotomicField(3).gen() zi = CyclotomicField(4).gen() points = [rho, zi] maxprec = 1000 # max size of q-expansion minprec = 10 # max size of q-expansion for tau in points: q = CF(exp(2 * pi * I * tau)) fexp = dict() cm_vals[tau] = dict() if(tau == I and self.level() == -1): # cv= #"Exact(soon...)" #_cohen_exact_formula(k) for h in range(degree): cm_vals[tau][h] = cv continue if K.absolute_degree()==1: v1 = CF(0) v2 = CF(1) try: for prec in range(minprec, maxprec, 10): if(self._verbose > 1): wmf_logger.debug("prec={0}".format(prec)) v2 = self.as_factor().q_eigenform(prec).truncate(prec)(q) err = abs(v2 - v1) if(self._verbose > 1): wmf_logger.debug("err={0}".format(err)) if(err < eps): raise StopIteration() v1 = v2 cm_vals[tau][0] = None except StopIteration: cm_vals[tau][0] = v2 else: v1 = dict() v2 = dict() err = dict() for h in range(degree): v1[h] = 1 v2[h] = 0 try: for prec in range(minprec, maxprec, 10): if(self._verbose > 1): wmf_logger.debug("prec={0}".format(prec)) c = self.coefficients(range(prec),insert_in_db=insert_in_db) for h in range(degree): fexp[h] = list() v2[h] = 0 for n in range(prec): cn = c[n] if hasattr(cn, 'complex_embeddings'): cc = cn.complex_embeddings(CF.prec())[h] else: cc = CF(cn) v2[h] = v2[h] + cc * q ** n err[h] = abs(v2[h] - v1[h]) if(self._verbose > 1): wmf_logger.debug("v1[{0}]={1}".format(h,v1[h])) wmf_logger.debug("v2[{0}]={1}".format(h,v2[h])) wmf_logger.debug("err[{0}]={2}".format(h,err[h])) if(max(err.values()) < eps): raise StopIteration() v1[h] = v2[h] except StopIteration: pass for h in range(degree): if(err[h] < eps): cm_vals[tau][h] = v2[h] else: cm_vals[tau][h] = None self._cm_values = cm_vals def compute_satake_parameters_numeric(self, prec=10, bits=53,insert_in_db=True): r""" Compute the Satake parameters and return an html-table. We only do satake parameters for primes p primitive to the level. By defintion the S. parameters are given as the roots of X^2 - c(p)X + chi(p)*p^(k-1) if (p,N)=1 INPUT: -''prec'' -- compute parameters for p <=prec -''bits'' -- do real embedings intoi field of bits precision """ if self.character().order()>2: ## We only implement this for trival or quadratic characters. ## Otherwise there is difficulty to figure out what the embeddings mean... return K = self.coefficient_field() degree = self.degree() RF = RealField(bits) CF = ComplexField(bits) ps = prime_range(prec) self._satake['ps'] = [] alphas = dict() thetas = dict() aps = list() tps = list() k = self.weight() for j in range(degree): alphas[j] = dict() thetas[j] = dict() for j in xrange(len(ps)): p = ps[j] try: ap = self.coefficient(p) except IndexError: break # Remove bad primes if p.divides(self.level()): continue self._satake['ps'].append(p) chip = self.character().value(p) wmf_logger.debug("p={0}".format(p)) wmf_logger.debug("chip={0} of type={1}".format(chip,type(chip))) if hasattr(chip,'complex_embeddings'): wmf_logger.debug("embeddings(chip)={0}".format(chip.complex_embeddings())) wmf_logger.debug("ap={0}".format(ap)) wmf_logger.debug("K={0}".format(K)) # ap=self._f.coefficients(ZZ(prec))[p] if K.absolute_degree()==1: f1 = QQ(4 * chip * p ** (k - 1) - ap ** 2) alpha_p = (QQ(ap) + I * f1.sqrt()) / QQ(2) ab = RF(p ** ((k - 1) / 2)) norm_alpha = alpha_p / ab t_p = CF(norm_alpha).argument() thetas[0][p] = t_p alphas[0][p] = (alpha_p / ab).n(bits) else: for jj in range(degree): app = ap.complex_embeddings(bits)[jj] wmf_logger.debug("chip={0}".format(chip)) wmf_logger.debug("app={0}".format(app)) wmf_logger.debug("jj={0}".format(jj)) if not hasattr(chip,'complex_embeddings'): f1 = (4 * CF(chip) * p ** (k - 1) - app ** 2) else: f1 = (4 * chip.complex_embeddings(bits)[jj] * p ** (k - 1) - app ** 2) alpha_p = (app + I * abs(f1).sqrt()) # ab=RF(/RF(2))) # alpha_p=alpha_p/RealField(bits)(2) wmf_logger.debug("f1={0}".format(f1)) alpha_p = alpha_p / RF(2) wmf_logger.debug("alpha_p={0}".format(alpha_p)) t_p = CF(alpha_p).argument() # tps.append(t_p) # aps.append(alpha_p) alphas[jj][p] = alpha_p thetas[jj][p] = t_p self._satake['alphas'] = alphas self._satake['thetas'] = thetas self._satake['alphas_latex'] = dict() self._satake['thetas_latex'] = dict() for j in self._satake['alphas'].keys(): self._satake['alphas_latex'][j] = dict() for p in self._satake['alphas'][j].keys(): s = latex(self._satake['alphas'][j][p]) self._satake['alphas_latex'][j][p] = s for j in self._satake['thetas'].keys(): self._satake['thetas_latex'][j] = dict() for p in self._satake['thetas'][j].keys(): s = latex(self._satake['thetas'][j][p]) self._satake['thetas_latex'][j][p] = s wmf_logger.debug("satake=".format(self._satake)) return self._satake def _number_of_hecke_eigenvalues_to_check(self): r""" Compute the number of Hecke eigenvalues (at primes) we need to check to identify twists of our given form with characters of conductor dividing the level. """ ## initial bound bd = self.as_factor().sturm_bound() # we do not check primes dividing the level bd = bd + len(divisors(self.level())) return bd def twist_by(self, x): r""" twist self by a primitive Dirichlet character x """ # xx = x.primitive() assert x.is_primitive() q = x.conductor() # what level will the twist live on? level = self.level() qq = self.character().conductor() new_level = lcm(self.level(), lcm(q * q, q * qq)) D = DirichletGroup(new_level) new_x = D(self.character()) * D(x) * D(x) ix = D.list().index(new_x) # the correct space NS = WebModFormSpace(self._k, new_level, ix, self._prec) # have to find whih form wee want NS.galois_decomposition() M = NS.sturm_bound() + len(divisors(new_level)) C = self.coefficients(range(M)) for label in NS._galois_orbits_labels: wmf_logger.debug("label={0}".format(label)) FT = NS.f(label) CT = FT.f.coefficients(M) wmf_logger.debug("{0}".format(CT)) K = FT.f.hecke_eigenvalue_field() try: for n in range(2, M): if(new_level % n + 1 == 0): continue wmf_logger.debug("n={0}".format(n)) ct = CT[n] c = K(x(n)) * K(C[n]) wmf_logger.debug("{0} {1}".format(ct, c)) if ct != c: raise StopIteration() except StopIteration: pass else: wmf_logger.debug("Twist of f={0}".format(FT)) return FT
def set_parent(self): from wmf.web_modform_space_computing import WebModFormSpace_computing_class,WebModFormSpace_computing if not isinstance(self._parent,WebModFormSpace_computing_class): if self._verbose > 0: emf_logger.debug("compute parent! label={0}".format(label)) self._parent = WebModFormSpace_computing(self._N, self._k,self._chi)