Mercurial > btquotients
changeset 9:ef4bfc30518c
Added funcionality as Hecke Module and submodules. Changed from Shimura curve perspective to BTQuotient.
author | Marc Masdeu <masdeu@math.columbia.edu> |
---|---|
date | Sat, 23 Jul 2011 15:03:15 +0200 |
parents | 437554c02df4 |
children | df9b6b470cef |
files | btquotients.sage |
diffstat | 1 files changed, 606 insertions(+), 596 deletions(-) [+] |
line wrap: on
line diff
--- a/btquotients.sage Tue Jul 19 17:47:48 2011 +0200 +++ b/btquotients.sage Sat Jul 23 15:03:15 2011 +0200 @@ -18,23 +18,7 @@ from sage.rings.all import ZZ from sage.misc.latex import latex from sage.plot import plot -from sage.rings.arith import * -from sage.graphs.graph import Graph -from sage.algebras.quatalg.quaternion_algebra import QuaternionAlgebra -from sage.rings.padics.factory import Qp -from sage.rings.padics.factory import Zp -from sage.rings.finite_rings.integer_mod_ring import Zmod -from sage.rings.number_field.all import NumberField -from sage.quadratic_forms.quadratic_form import QuadraticForm -from sage.combinat.combinat import permutations -from sage.interfaces.magma import magma -from sage.rings.number_field.order import AbsoluteOrder -from sage.rings.all import Qq, monomials -from copy import copy -from sage.structure.unique_representation import UniqueRepresentation -from sage.modules.free_module import FreeModule_submodule_pid -from itertools import ifilter -from sage.functions.other import ceil +from sage.rings.padics.precision_error import PrecisionError r""" Initialized with an element x0 of GL2(ZZ), finds elements @@ -54,7 +38,7 @@ e=edge_list[label] t'=t*p**power (in the stabilizer of the standard edge) -INPUT:: Y: ShimuraCurveFiber object in which to work +INPUT:: Y: BTQuotient object in which to work x0: Something coercible into a matrix in GL2(ZZ) Note that we should allow elements in GL2(Qp), but for what we do it is enough to work with @@ -91,13 +75,17 @@ else: return -1 - def igamma(self): + def igamma(self,iota=None): Y=self._parent - if(self._igamma_prec>=Y._prec): + if iota is None: + iota=Y.iota + if(self._igamma_prec>=Y._prec): + return self._cached_igamma + self._igamma_prec=Y._prec + self._cached_igamma=iota(self.gamma) return self._cached_igamma - self._igamma_prec=Y._prec - self._cached_igamma=Y.iota(self.gamma) - return self._cached_igamma + else: + return iota(self.gamma) def t(self): Y=self._parent @@ -118,10 +106,16 @@ class BruhatTitsTree: def __init__(self,p): self._p=p - self._MZp=None + self._MQp=None + self._Mat_22=MatrixSpace(ZZ,2,2) + self._prec=-1 - def update_prec(self,ZZp): - self._MZp=MatrixSpace(ZZp,2,2) + def update_precision(self,prec): + self._prec=prec + self._MQp=MatrixSpace(Qp(self._p,prec),2,2) + + def increase_precision(self,amount=1): + self.update_precision(self._prec+amount) def target(self,M): return self.vertex(M) @@ -133,7 +127,7 @@ def edge(self,A): p=self._p - B=self._MZp(A) + B=self._MQp(A) v=min([B[i,j].valuation() for i in range(2) for j in range(2)]) if(v): B=p**(-v)*B @@ -141,16 +135,16 @@ b01=B[0,1].valuation() if (b00 <= b01): tmp=(B.determinant()).valuation()-b00 - B=Matrix(ZZ,2,2,[p**b00,0,(B[1,0]/B[0,0].unit_part()).residue(1+tmp),p**(tmp)]) + B=self._Mat_22([p**b00,0,(B[1,0]/B[0,0].unit_part()).residue(1+tmp),p**(tmp)]) else: tmp=(B.determinant()).valuation()-b01 - B=Matrix(ZZ,2,2,[0,p**b01,p**(tmp),(B[1,1]/B[0,1].unit_part()).residue(tmp)]) + B=self._Mat_22([0,p**b01,p**(tmp),(B[1,1]/B[0,1].unit_part()).residue(tmp)]) B.set_immutable() return(B) def vertex(self,A): p=self._p - B=self._MZp(A) + B=self._MQp(A) v=min([B[i,j].valuation() for i in range(2) for j in range(2)]) if(v): B=p**(-v)*B @@ -160,7 +154,7 @@ B.swap_columns(0,1) b00=b01 tmp=B.determinant().valuation()-b00 - B=Matrix(ZZ,2,2,[p**b00,0,(B[1,0]/B[0,0].unit_part()).residue(tmp),p**tmp]) + B=self._Mat_22([p**b00,0,(B[1,0]/B[0,0].unit_part()).residue(tmp),p**tmp]) B.set_immutable() return B @@ -173,8 +167,8 @@ try: return self._edges_leaving_origin except: p=self._p - self._edges_leaving_origin=[[Matrix(ZZ,2,2,[0,-1,p,0]),self.target([0,-1,p,0])]] - self._edges_leaving_origin.extend([[Matrix(ZZ,2,2,[p,i,0,1]),self.target([p,i,0,1])] for i in range(p)]) + self._edges_leaving_origin=[[self._Mat_22([0,-1,p,0]),self.target([0,-1,p,0])]] + self._edges_leaving_origin.extend([[self._Mat_22([p,i,0,1]),self.target([p,i,0,1])] for i in range(p)]) return self._edges_leaving_origin def leaving_edges(self,M): @@ -185,7 +179,6 @@ x.swap_columns(0,1) x.rescale_col(0,self._p) return self.edge(x) - # return self.edge(M*Matrix(ZZ,2,2,[0,1,self._p,0])) def entering_edges(self,M): return [[self.opposite(e[0]),M] for e in self.leaving_edges(M)] @@ -201,11 +194,11 @@ if(level<0): return [] if(level==0): - return [Matrix(ZZ,2,2,edge) for edge in edgelist] + return [self._Mat_22(edge) for edge in edgelist] else: newEgood=[] for edge in edgelist: - edge=Matrix(ZZ,2,2,edge) + edge=self._Mat_22(edge) origin=self.origin(edge) newE=self.leaving_edges(self.target(edge)) newEgood.extend([e[0] for e in newE if e[1]!=origin]) @@ -219,7 +212,7 @@ #Assume z belongs to some extension of QQp. p=self._p if(z.valuation()<0): - return self.vertex(Matrix(ZZ,2,2,[0,1,p,0])*self.whereis(1/(p*z))) + return self.vertex(self._Mat_22([0,1,p,0])*self.whereis(1/(p*z))) a=0 pn=1 val=z.valuation() @@ -234,12 +227,12 @@ if(len(L[n])>0): a+=pn*L[n][0] pn*=p - return self.vertex(Matrix(ZZ,2,2,[pn,a,0,1])) + return self.vertex(self._Mat_22([pn,a,0,1])) r""" - Structure that holds vertices in the quotient, as they are computed. At time of creation, it is given an owner (always ShimuraCurveFiber), a label (a non-negative integer) and a rep (an element of GL_2(ZZ) giving a basis for a lattice representing the vertex). + Structure that holds vertices in the quotient, as they are computed. At time of creation, it is given an owner (always BTQuotient), a label (a non-negative integer) and a rep (an element of GL_2(ZZ) giving a basis for a lattice representing the vertex). The lists leaving_edges and entering_edges store this information as it is known """ @@ -257,7 +250,7 @@ r""" - Structure that holds edges in the quotient, as they are computed. At time of creation, it is given an owner (always ShimuraCurveFiber), a label (a non-negative integer) and a rep (an element of GL_2(ZZ) which is chosen so that when acting on ZZp it takes it to the + Structure that holds edges in the quotient, as they are computed. At time of creation, it is given an owner (always BTQuotient), a label (a non-negative integer) and a rep (an element of GL_2(ZZ) which is chosen so that when acting on ZZp it takes it to the compact open ball corresponding to the ends containing the given edge in the Bruhat-Tits tree. @@ -280,27 +273,34 @@ self.valuation=valuation self.parity=valuation%2 -class ShimuraCurveFiber(SageObject,UniqueRepresentation): +class BTQuotient(SageObject,UniqueRepresentation): @staticmethod - def __classcall__(cls,X,p,backend='sage'): - return super(ShimuraCurveFiber,cls).__classcall__(cls,X,p,backend) + def __classcall__(cls,p,Nminus,Nplus=1,backend='magma'): + return super(BTQuotient,cls).__classcall__(cls,p,Nminus,Nplus,backend) - def __init__(self,X,p,backend): - #global magma - p=ZZ(p) + def __init__(self,p,Nminus,Nplus=1,backend='magma'): + Nminus=Integer(Nminus) + Nplus=Integer(Nplus) + p=Integer(p) if(not p.is_prime()): - raise ValueError, "must be a prime" - if(X._level%p!= 0): - raise ValueError, "p must divide the level X" + raise ValueError, "p must be a prime" + if (not is_squarefree(self._p*self._Nminus)): + raise ValueError, "level must be squarefree" + if(gcd(lev,Nplus)>1): + raise ValueError, "level and conductor must be coprime" - self._generic_fiber=X + if(len(Nminus.factor())%2!=1): + raise ValueError, "Nminus should be divisible by an odd number of primes" + self._pN=p self._p=p - self._Nminus=ZZ(X.level()/p) - self._Nplus=X._Nplus + self._Nminus=Nminus + self._Nplus=Nplus if(backend=='magma' or not self._Nminus.is_prime() or self._Nplus!=1 or self._p==2): - try: self._magma=magma - except: + try: + self._magma=magma + magmap=magma.p + except RuntimeError: raise NotImplementedError,'Sage does not know yet how to work with the kind of orders that you are trying to use. Try installing Magma first and set it up so that Sage can use it.' self._backend='magma' else: @@ -308,11 +308,6 @@ self._BT=BruhatTitsTree(p) self._prec=-1 - self._hecke_data=dict() - self._atkin_lehner_data=dict() - self._alpha1=dict() - self._beta1=dict() - self._CM_points=dict() self._cached_vertices=dict() self._cached_edges=dict() self._cached_paths=dict() @@ -320,69 +315,113 @@ self._cached_equivalent=dict() self._V=(QQ**4).ambient_module().change_ring(ZZ) - self._Xv=[Matrix(ZZ,2,2,[1,0,0,0]),Matrix(ZZ,2,2,[0,1,0,0]),Matrix(ZZ,2,2,[0,0,1,0]),Matrix(ZZ,2,2,[0,0,0,1])] - self._Xe=[Matrix(ZZ,2,2,[1,0,0,0]),Matrix(ZZ,2,2,[0,1,0,0]),Matrix(ZZ,2,2,[0,0,self._p,0]),Matrix(ZZ,2,2,[0,0,0,1])] - self._R4x4=MatrixSpace(ZZ,4,4) + self._Mat_44=MatrixSpace(ZZ,4,4) + self._Mat_22=MatrixSpace(ZZ,2,2) + self._Mat_41=MatrixSpace(ZZ,4,1) + self._mat_p001=self._Mat_22([self._p,0,0,1]) + self._Xv=[self._Mat_22([1,0,0,0]),self._Mat_22([0,1,0,0]),self._Mat_22([0,0,1,0]),self._Mat_22([0,0,0,1])] + self._Xe=[self._Mat_22([1,0,0,0]),self._Mat_22([0,1,0,0]),self._Mat_22([0,0,self._p,0]),self._Mat_22([0,0,0,1])] self._elements=set([]) def __getstate__(self): from sage.all import dumps odict=self.__dict__.copy() odict['_S']=self._S.sparse6_string() - del odict['_generic_fiber'] return odict def __setstate__(self,ndict): from sage.all import loads self.__dict__.update(ndict) - self._generic_fiber=ShimuraCurve(ndict['_p']*ndict['_Nminus'],ndict['_Nplus']) self._S=Graph(ndict['_S'],multiedges=True,weighted=True) def _repr_(self): - return "Fiber of Shimura curve of level %s and conductor %s at the prime %s"%(self._generic_fiber.level().factor(),self._Nplus.factor(),self._p) + return "Quotient of the Bruhat Tits tree of GL_2(QQ_%s) with discriminant %s and level %s at the prime %s"%(self.Nminus().factor(),self.Nplus().factor(),self.prime()) def _latex_(self): - return "X(%s,%s)\\otimes_{\\mathbb{Z}} \\mathbb{F}_{%s}"%(latex(self._generic_fiber.level().factor()),latex(self._Nplus.factor()),latex(self._p)) - - def generic_fiber(self): - return self._generic_fiber + return "X(%s,%s)\\otimes_{\\mathbb{Z}} \\mathbb{F}_{%s}"%(latex(self.level().factor()),latex(self.Nplus().factor()),latex(self.prime())) def get_vertex_list(self): try: return self._vertex_list except AttributeError: - self._compute() + self._compute_quotient() return self._vertex_list def get_edge_list(self): try: return self._edge_list except AttributeError: - self._compute() + self._compute_quotient() return self._edge_list + @cached_method def genus(self): - try: return self._genus - except AttributeError: - self._compute() - return self._genus + Nplus=self._Nplus + lev=self._p*self._Nminus + #Compute the genus using the genus formulas + e4=1 + e3=1 + mu=Nplus + for f in lev.factor(): + e4=e4*(1-kronecker_symbol(-4,Integer(f[0]))) + e3=e3*(1-kronecker_symbol(-3,Integer(f[0]))) + mu=mu*(Integer(f[0])-1) + for f in Nplus.factor(): + if (f[1]==1): + e4=e4*(1+kronecker_symbol(-4,Integer(f[0]))) + e3=e3*(1+kronecker_symbol(-3,Integer(f[0]))) + else: + if(kronecker_symbol(-4,Integer(f[0]))==1): + e4=2*e4 + else: + e4=0 + if(kronecker_symbol(-3,Integer(f[0]))==1): + e3=2*e3 + else: + e3=0 + mu=mu*(1+1/Integer(f[0])) + if(lev==1): + einf=sum([euler_phi(gcd(d,Integer(Nplus/d))) for d in Integer(Nplus).divisors()]) + else: + einf=0 + return 1+Integer(mu/12-e3/3-e4/4-einf/2) def Nplus(self): - return(self._Nplus) + return self._Nplus def Nminus(self): - return(self._Nminus) + return self._Nminus + + @cached_method + def level(self): + return self._Nminus*self._p + + def prime(self): + return self._p def get_graph(self): try: return self._S except AttributeError: - self._compute() + self._compute_quotient() return self._S def plot(self): S=self.get_graph() return S.plot(vertex_labels=True,edge_labels=True) + @cached_method def is_admissible(self,D): - return self.generic_fiber().is_admissible(D) + D=Integer(D).squarefree_part() + disc=D + if(D%4!=1): + disc*=4 + ff=self._level.factor() + for f in ff: + if kronecker_symbol(disc,f[0])!=-1: + return False + ff=self._Nplus.factor() + for f in ff: + if kronecker_symbol(disc,f[0])!=1: + return False + return True def _local_splitting_map(self,prec): I,J,K=self._local_splitting(prec) @@ -456,13 +495,16 @@ B=self.get_eichler_order_basis() return Matrix(Zmod(self._pN),4,4,[phi(B[kk,0])[ii,jj] for ii in range(2) for jj in range(2) for kk in range(4)]) + def increase_precision(self,amount=1): + self.get_Iota(self._prec+amount) + def get_Iota(self,prec=None): - if(prec==None or prec==self._prec): + if(prec is None or prec is self._prec): return self._Iota self._pN=self._p**prec self._R=Qp(self._p,prec=prec) - self._BT.update_prec(self._R) + self._BT.update_precision(prec) if(prec>self._prec): Iotamod=self._get_Iota0(prec) @@ -470,9 +512,17 @@ self._Iota=Matrix(self._R,4,4,[Iotamod[ii,jj] for ii in range(4) for jj in range(4)]) self._prec=prec - self._Iotainv=self._R4x4([self._Iotainv_lift[ii,jj]%self._pN for ii in range(4) for jj in range(4)]) + self._Iotainv=self._Mat_44([self._Iotainv_lift[ii,jj]%self._pN for ii in range(4) for jj in range(4)]) return self._Iota + def get_Iota_exact(self): + try: + return self._Iota_exact + except: + raise RuntimeError + + def iota_exact(self,g): + return Matrix(self._FF,2,2,self.get_Iota_exact()*g) def iota(self,g,prec=None): return Matrix(self._R,2,2,self.get_Iota(prec)*g) @@ -494,6 +544,12 @@ self._init_order() return self._O + def get_splitting_field(self): + try: return self._FF + except AttributeError: pass + self._init_order() + return self._FF + def get_eichler_order_basis(self): try: return self._B except AttributeError: pass @@ -512,25 +568,25 @@ self._init_order() return self._OM + @cached_method def get_units_of_order(self): - try: return self._O_units - except AttributeError: pass OM=self.get_eichler_order_quadmatrix() v=pari('qfminim(%s,2,0)'%(OM._pari_())) - n_units=ZZ(v[0].python()/2) + n_units=Integer(v[0].python()/2) v=pari('qfminim(%s,0,%s)'%((OM._pari_()),n_units)) - self._O_units=[] + O_units=[] for jj in range(n_units): vec=Matrix(ZZ,1,4,[v[2][ii,jj].python() for ii in range(4)]) - self._O_units.append(vec) - return self._O_units + O_units.append(vec) + return O_units + @cached_method def get_CM_points(self,O,prec=None,ignore_units=False): p=self._p - if(prec==None): + if prec is None: prec=self._prec if(not isinstance(O,AbsoluteOrder)): - disc=ZZ(O) + disc=Integer(O) R = QQ['x'] f = R([-disc, 0, 1]) K=NumberField(f, 'sq', check=False) @@ -555,10 +611,8 @@ all_elts0=[self._conv(v) for v in all_elts] if(not ignore_units): - #print 'Looking for units...' units=self._find_elements_in_order(p**(2*nn)) units=[[u[ii]/(p**nn) for ii in range(4)] for u in units] - #print 'Done.' units0=[self._conv(u) for u in units] else: units=[] @@ -604,23 +658,14 @@ The data is a tuple (acter,edge_images), with edge images being of type EdgeDecomposition """ - def Up_data(self): - try: return self._Up_data - except AttributeError: pass + @cached_method + def _get_Up_data(self): E=self.get_edge_list() - Up_data=[] vec_a=self._BT.subdivide([1],1) - for alpha in vec_a: - tmp=[alpha.inverse(),[]]#GraphActionData(alpha.inverse(),[]) - tmp[1].extend((EdgeDecomposition(self,e.rep*alpha) for e in E)) - tmp[1].extend((EdgeDecomposition(self,e.opposite.rep*alpha) for e in E)) - Up_data.append(tmp) - self._Up_data=Up_data - return self._Up_data + return [[alpha.inverse(),[EdgeDecomposition(self,e.rep*alpha) for e in E]+[EdgeDecomposition(self,e.opposite.rep*alpha) for e in E]] for alpha in vec_a] - def atkin_lehner_data(self,q): - try: return self._atkin_lehner_data[q] - except KeyError: pass + @cached_method + def _get_atkin_lehner_data(self,q): E=self.get_edge_list() B=self.get_eichler_order_basis() BB=self._BB @@ -628,23 +673,25 @@ nninc=-2 while(len(v)==0): nninc+=2 - print 'nninc =',nninc print 'Searching for norm', q*self._p**nninc v=self._find_elements_in_order(q*self._p**nninc) - beta1=v[0] - self._beta1[q]=Matrix(QQ,4,1,beta1) - x=self.iota(self._beta1[q]) - nn=ceil(x.determinant().valuation()) - T=[self._beta1[q],[EdgeDecomposition(self,x.adjoint()*e.rep,extrapow=nn) for e in E]] - self._atkin_lehner_data[q]=T + beta1=Matrix(QQ,4,1,v[0]) + success=False + while(not success): + try: + x=self.iota(beta1) + nn=ceil(x.determinant().valuation()) + T=[beta1,[EdgeDecomposition(self,x.adjoint()*e.rep,extrapow=nn) for e in E]] + success=True + except PrecisionError: + self.increase_precision(10) return T - def hecke_data(self,l): - try: return self._hecke_data[l] - except KeyError: pass + @cached_method + def _get_hecke_data(self,l): print 'Finding representatives...' E=self.get_edge_list() - if((self._p*self.Nminus()*self.Nplus())%l==0): + if((self.level()*self.Nplus())%l==0): Sset=[] else: Sset=[self._p] @@ -656,13 +703,12 @@ nninc=-2 while(len(v)==0): nninc+=2 - print 'nninc =',nninc print 'Searching for norm', l*self._p**nninc v=self._find_elements_in_order(l*self._p**nninc) alpha1=v[0] alpha0=self._conv(alpha1) - self._alpha1[l]=Matrix(QQ,4,1,alpha1) - alphamat=self.iota(self._alpha1[l]) + alpha=Matrix(QQ,4,1,alpha1) + alphamat=self.iota(alpha) A=self.get_quaternion_algebra() I=enumerate_words([self._conv(x) for x in list(self._elements)]) n_tests=0 @@ -680,22 +726,27 @@ break if(new): v1=BB*Matrix(QQ,4,1,v.coefficient_tuple()) - x=self.iota(v1)*alphamat - nn=ceil(x.determinant().valuation()) - T.append([v1,[EdgeDecomposition(self,x.adjoint()*e.rep,extrapow=nn) for e in E]]) + success=False + while(not success): + try: + x=self.iota(v1)*alphamat + nn=ceil(x.determinant().valuation()) + T.append([v1,[EdgeDecomposition(self,x.adjoint()*e.rep,extrapow=nn) for e in E]]) + success=True + except PrecisionError: + self.increase_precision(10) + alphamat=self.iota(alpha) T0.append(v0) - print len(T0) - self._hecke_data[l]=T print 'Done (used %s reps in total)'%(n_tests) - return T + return T,alpha def _find_equivalent_vertex(self,v0,V=None,comp=False,valuation=None): try: return self._cached_vertices[v0] except KeyError: pass - if(V==None): + if V is None: V=self._vertex_list - if(valuation==None): + if valuation is None: valuation=v0.determinant().valuation(self._p) parity=valuation%2 for v in filter(lambda v:v.parity==parity,V): @@ -709,10 +760,10 @@ try: return self._cached_edges[e0] except KeyError: pass - if(valuation==None): + if valuation is None: valuation=e0.determinant().valuation(self._p) parity=valuation%2 - if(E==None): + if E is None: E=self._edge_list if(parity==1): E=[e.opposite for e in E] @@ -724,46 +775,38 @@ return 0,None def _find_path_and_fold(self,v1): - try: - return self._cached_paths[v1] - except KeyError: - chain,v=self._find_path(v1) - while(len(chain)>0): - v0=chain.pop() - g,v=self._find_equivalent_vertex(v0,V=[e.target for e in v.leaving_edges]) - self._cached_paths[v1]=v - return v + try: return self._cached_paths[v1] + except KeyError: pass + chain,v=self._find_path(v1) + while(len(chain)>0): + v0=chain.pop() + g,v=self._find_equivalent_vertex(v0,V=[e.target for e in v.leaving_edges]) + self._cached_paths[v0]=v + return v def _find_path(self,v): vertex_with_hash=self._BT.vertex_with_hash - m=Matrix(ZZ,2,2,[self._p,0,0,1]) + m=self._mat_p001 new_v,h=vertex_with_hash(v) chain=[] while h[1]!=0 or h[0]<0: - try: - v=self._boundary[h] - return chain,v - except KeyError: pass + if self._boundary.has_key(h): + return chain,self._boundary[h] chain.append(new_v) new_v,h=vertex_with_hash(new_v*m) - try: - v=self._boundary[h] - return chain,v - except KeyError: pass + if self._boundary.has_key(h): + return chain,self._boundary[h] chain.append(new_v) a=h[0] while(a>0): a-=1 - new_v=Matrix(ZZ,2,2,[new_v[0,0]/self._p,0,0,1]) + if self._boundary.has_key((a,0)): + return chain,self._boundary[(a,0)] + new_v=self._Mat_22([new_v[0,0]/self._p,0,0,1]) new_v.set_immutable() - try: - v=self._boundary[(a,0)] - return chain,v - except KeyError: pass chain.append(new_v) raise RuntimeError - def _find_lattice(self,v1,v2,as_edges,m): if(as_edges): X=self._Xe @@ -773,13 +816,13 @@ if(m+1>self._prec): self.get_Iota(m+1) v1adj=v1.adjoint() - R=self._R4x4 + R=self._Mat_44 vecM=[v2*X[ii]*v1adj for ii in range(4)] M=(self._Iotainv*R([[vecM[ii][jj,kk] for ii in range(4) ] for jj in range(2) for kk in range(2)])).augment(R(self._pN)).transpose() M.echelonize() E = M.submatrix(0,0,4,4) - A=E*self._OM*E.transpose() - return E,A + OM=self.get_eichler_order_quadmatrix() + return E,E*OM*E.transpose() def _edge_stabilizer(self,e): p=self._p @@ -802,7 +845,7 @@ stabs=[] for jj in range(n_vecs): vec=Matrix(ZZ,1,4,list(mat.row(jj))) - try: nrd=ZZ((vec*A*vec.transpose())[0,0]/2) + try: nrd=Integer((vec*A*vec.transpose())[0,0]/2) except TypeError: continue if(nrd.is_power_of(p**2) and nrd.valuation(p)==twom): w=vec*E @@ -818,21 +861,19 @@ return self._cached_equivalent[(v1,v2,as_edges)] except KeyError: pass p=self._p - if(twom==None): - twom=(v1*v2).determinant().valuation(p) + twom=(v1*v1).determinant().valuation(p) if twom is None else twom if (twom%2!=0): self._cached_equivalent[(v1,v2,as_edges)]=0 return 0 E,A=self._find_lattice(v1,v2,as_edges,twom) - m=ZZ(twom/2) + m=Integer(twom/2) ## Using PARI to get the shortest vector in the lattice (via LLL) try: - #v=pari('qfminim(%s,0,2)'%(A._pari_())) v=pari('qfminim(%s,0,2,flag=0)'%(A._pari_())) except RuntimeError: print A raise Exception,'You might have found a PARI bug, please do what they ask and report it.' - try: nrd=ZZ(v[1].python()/2) + try: nrd=Integer(v[1].python()/2) except TypeError: print "This shouldn't happen...something seems wrong with PARI." if(nrd.valuation(p)==twom and nrd.is_power_of(p**2)): @@ -849,13 +890,31 @@ def _init_order(self): if(self._backend=='magma'): A=self._magma.QuaternionAlgebra(self._Nminus) + self._magma.eval('A:=QuaternionAlgebra(%s)'%(self._Nminus)) + self._magma.eval('O:=QuaternionOrder(A,%s)'%(self._Nplus)) g=A.gens() - #We store the order because we need to split it + # We store the order because we need to split it self._O=A.QuaternionOrder(self._Nplus) OBasis=self._O.Basis() self._A=QuaternionAlgebra((g[0]**2).sage(),(g[1]**2).sage()) v=[1]+self._A.gens() self._B=Matrix(self._A,4,1,[sum([OBasis[tt+1][rr+1].sage()*v[rr] for rr in range(4)]) for tt in range(4)]) + + # Now we do the exact splitting + self._magma.eval('f:=MatrixRepresentation(O)') + f=self._magma.function_call('MatrixRepresentation',args=[self._O],nvals=1) + self._FF=NumberField(f.Codomain().BaseRing().DefiningPolynomial().sage(),'a') + allmats=[] + for kk in range(4): + self._magma.eval('x:=f(O.%s)'%(kk+1)) + all_str=[] + for ii in range(2): + for jj in range(2): + self._magma.eval('v%s%s:=[Sage(z) : z in Eltseq(x[%s,%s])]'%(ii,jj,ii+1,jj+1)) + v=[self._FF(self._magma.eval('Sprint(v%s)'%tt)) for tt in ['00','01','10','11']] + allmats.append(Matrix(self._FF,2,2,v)) + #print [(x.determinant(),x.trace()) for x in allmats] + self._Iota_exact=Matrix(self._FF,4,4,[self._FF(allmats[kk][ii,jj]) for ii in range(2) for jj in range(2) for kk in range(4) ]) else: assert(self._backend=='sage') # Note that we can't work with non-maximal orders in sage @@ -871,22 +930,21 @@ # End of addition self._B=Matrix(self._A,4,1,[OBasis[tt] for tt in range(4)]) - self._OQuadForm=QuadraticForm(Matrix(ZZ,4,4,[(self._B[ii,0]*self._B[jj,0].conjugate()).reduced_trace() for ii in range(4) for jj in range(4)])) + self._OQuadForm=QuadraticForm(self._Mat_44([(self._B[ii,0]*self._B[jj,0].conjugate()).reduced_trace() for ii in range(4) for jj in range(4)])) self._OM=self._OQuadForm.matrix() self._BB=Matrix(QQ,4,4,[[self._B[ii,0][jj] for ii in range(4)] for jj in range(4)]).inverse() - def _compute(self): + def _compute_quotient(self): self.get_Iota(1) p=self._p num_verts=0 - V=[Vertex(self,num_verts,Matrix(ZZ,2,2,[1,0,0,1]),[],[],1,0)] + V=[Vertex(self,num_verts,self._Mat_22([1,0,0,1]),[],[],1,0)] E=[] v0=V[0] S=Graph(0,multiedges=True,weighted=True) edge_list=[] - vertex_list=[[v0],[]] - vertex_list_done=[] + vertex_list=[v0] boundary=dict({(0,0):v0}) self._num_edges=0 num_verts+=1 @@ -894,6 +952,7 @@ while len(V)>0: v=V.pop(0) E=self._BT.leaving_edges(v.rep) + #print 'V = %s, E = %s, G = %s (target = %s)'%(num_verts,self._num_edges,1+self._num_edges-num_verts,self.genus()) for e in E: new_edge=True edge_det=e[0].determinant() @@ -907,12 +966,12 @@ new_det=target.determinant() new_valuation=new_det.valuation(p) new_parity=new_valuation%2 - g1,v1=self._find_equivalent_vertex(target,vertex_list[new_parity],comp=True,valuation=new_valuation) + g1,v1=self._find_equivalent_vertex(target,vertex_list,comp=True,valuation=new_valuation) if(g1==0): #The vertex is also new v1=Vertex(self,num_verts,target,[],[],new_det,new_valuation) - vertex_list[new_parity].append(v1) + vertex_list.append(v1) num_verts+=1 S.add_vertex(v1.label) #Add the vertex to the list of pending vertices @@ -928,23 +987,22 @@ # Add the edge to the graph S.add_edge(v.label,v1.label,self._num_edges) - if(S.degree(v.label) == self._p+1): - vertex_list_done.append(v) - vertex_list[v.parity].remove(v) - if(S.degree(v1.label) == self._p+1): - vertex_list_done.append(v1) - vertex_list[v1.parity].remove(v1) - V.remove(v1) + # if(S.degree(v.label) == self._p+1): + # vertex_list_done.append(v) + # vertex_list[v.parity].remove(v) + # + # if(S.degree(v1.label) == self._p+1): + # vertex_list_done.append(v1) + # vertex_list[v1.parity].remove(v1) + # V.remove(v1) opp_det=opp.determinant() new_e_opp=Edge(self,self._num_edges,opp,v1,v,[],new_e,opp_det,opp_det.valuation(p)) new_e.opposite=new_e_opp - if(new_e.parity==0): - edge_list.append(new_e) - else: - edge_list.append(new_e_opp) + tmp=new_e if new_e.parity==0 else new_e_opp + edge_list.append(tmp) v.leaving_edges.append(new_e) v.entering_edges.append(new_e_opp) @@ -952,26 +1010,40 @@ v1.leaving_edges.append(new_e_opp) self._num_edges += 1 - genus=ZZ(1- len(vertex_list[0])-len(vertex_list[1])-len(vertex_list_done)+self._num_edges) - if(genus!=self.generic_fiber().genus()): + computed_genus=Integer(1- len(vertex_list)+self._num_edges) + if(computed_genus!=self.genus()): print 'You found a bug!' - print 'Computed genus =',genus - print 'Theoretical genus =',self.generic_fiber().genus() - self._genus=genus + print 'Computed genus =',computed_genus + print 'Theoretical genus =',self.genus() + raise RuntimeError self._edge_list=edge_list - self._vertex_list=sorted(vertex_list[0]+vertex_list[1]+vertex_list_done,key=lambda v:v.label) + self._vertex_list=vertex_list self._boundary=boundary self._S=S + @cached_method def _fixed_points(self,x,K): - a=x[0,0] - b=x[0,1] - c=x[1,0] - d=x[1,1] + a=x[0,0],b=x[0,1],c=x[1,0],d=x[1,1] t=x.trace() n=x.determinant() A=K(a-d) - D=our_sqrt(t**2-4*n,K) + D2=K(t**2-4*n) + if D2==0: + D=D2 + else: + # Compute the square root of D in a naive way + p=K.base_ring().prime() + z=K.gen() + for a,b in cartesian_product_iterator([range(p),range(p)]): + y0=a+b*z + if((y0**2-D2).valuation()>0): + break + y1=y0 + D=0 + while(D!=y1): + D=y1 + y1=(D**2+D2)/(2*D) + return [(A+D)/(2*c),(A-D)/(2*c)] def _mult_in_order(self,v): @@ -981,106 +1053,13 @@ def _conv(self,v): return ((Matrix(QQ,1,4,v)*self.get_eichler_order_basis())[0,0]) + @cached_method def _find_elements_in_order(self,n,t=None,primitive=False): - p=self._p OQuadForm=self.get_eichler_order_quadform() V=OQuadForm.vectors_by_length(n)[n] - if(primitive): - W=[] - for v in V: - if not all([v[ii]%p==0 for ii in range(4)]): - W.append(v) - else: - W=V - if(t==None): - return W - WW=[] - for v in W: - x=self._conv(v) - assert(x.reduced_norm()==n) - if(x.reduced_trace()==t): - WW.append(v) - return WW - - -class ShimuraCurve(SageObject,UniqueRepresentation): - def __init__(self,lev,Nplus=1): - lev=ZZ(lev) - Nplus=ZZ(Nplus) - if (not is_squarefree(lev)): - raise NotImplementedError, "level must be squarefree" - if(gcd(lev,Nplus)>1): - raise ValueError, "level and conductor must be coprime" - self._level=lev - self._Nplus=Nplus - - if(len(lev.factor())%2!=0): - raise ValueError, "level should be divisible by an even number of primes" - self._fibers=dict([]) + W=V if not primitive else filter(lambda v: any((vi%self._p!=0 for vi in v)),V) + return W if t is None else filter(lambda v:self._conv(v).reduced_trace()==t,W) - #Compute the genus using the genus formulas - e4=1 - e3=1 - mu=Nplus - for f in lev.factor(): - e4=e4*(1-kronecker_symbol(-4,ZZ(f[0]))) - e3=e3*(1-kronecker_symbol(-3,ZZ(f[0]))) - mu=mu*(ZZ(f[0])-1) - for f in Nplus.factor(): - if (f[1]==1): - e4=e4*(1+kronecker_symbol(-4,ZZ(f[0]))) - e3=e3*(1+kronecker_symbol(-3,ZZ(f[0]))) - else: - if(kronecker_symbol(-4,ZZ(f[0]))==1): - e4=2*e4 - else: - e4=0 - if(kronecker_symbol(-3,ZZ(f[0]))==1): - e3=2*e3 - else: - e3=0 - mu=mu*(1+1/ZZ(f[0])) - if(lev==1): - einf=sum([euler_phi(gcd(d,ZZ(Nplus/d))) for d in ZZ(Nplus).divisors()]) - else: - einf=0 - self._genus=1+ZZ(mu/12-e3/3-e4/4-einf/2) - - def _repr_(self): - return "Shimura Curve of level %s and conductor %s"%(self._level.factor(),self._Nplus.factor()) - - def _latex_(self): - return "X(%s,%s)"%(latex(self._level.factor()),latex(self._Nplus.factor())) - - def level(self): - return self._level - - def genus(self): - return self._genus - - def is_admissible(self,D): - D=ZZ(D).squarefree_part() - disc=D - if(D%4!=1): - disc*=4 - ff=self._level.factor() - for f in ff: - if kronecker_symbol(disc,f[0])!=-1: - return False - ff=self._Nplus.factor() - for f in ff: - if kronecker_symbol(disc,f[0])!=1: - return False - return True - - def __getitem__(self,key): - return self.special_fiber(key) - - def special_fiber(self,p,backend='sage'): - try: return self._fibers[p] - except KeyError: pass - self._fibers[p]=ShimuraCurveFiber(self,p,backend=backend) - return self._fibers[p] ######################################################################### # Copyright (C) 2011 Cameron Franc and Marc Masdeu @@ -1105,51 +1084,75 @@ from sage.quadratic_forms.quadratic_form import QuadraticForm from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.laurent_series_ring import LaurentSeriesRing +from sage.modular.hecke.all import (AmbientHeckeModule, HeckeSubmodule, HeckeModuleElement) +import sage.rings.arith as arith +import sage.modular.hecke.hecke_operator -########################### -### Automorphic code ### -########################### - -class HarmonicCocycle(ModuleElement): +class HarmonicCocycle(HeckeModuleElement): def __init__(self,_parent,vec=None): - ModuleElement.__init__(self,_parent) + HeckeModuleElement.__init__(self,_parent,vec) self._parent=_parent + if(isinstance(vec,HarmonicCocycle)): #The first argument is just a modular form that we copy as is - self._ZZp=vec._ZZp + self._R=vec._R self._nE=vec._nE self._wt=vec._wt - self._F=[OCVnElement(_parent,vec._F[ii],quick=True) for ii in range(self._nE)] - elif(isinstance(vec,pAutomorphicForm)): + self._F=[_parent._U.element_class(_parent._U,vec._F[ii],quick=True) for ii in range(self._nE)] + return + + try: vec=Sequence(vec) + except TypeError: pass + # When vec contains coordinates for the basis + if(isinstance(vec,(list,tuple)) and vec[0].parent() is self._parent._R): + try: + v=[self._parent._R(x) for x in vec] + if self._parent.dimension()!=len(v): + raise ValueError + self._R=self._parent._R + self._wt=_parent._k + self._nE=len(_parent._E) + tmp=list(self._parent.basis_matrix().transpose()*Matrix(self._R,self._parent.dimension(),1,v)) + self._F=[_parent._U.element_class(_parent._U,Matrix(self._R,self._wt-1,1,tmp[e*(self._wt-1):(e+1)*(self._wt-1)]),quick=True) for e in range(self._nE)] + return + except ValueError: + assert(0) + pass + if(isinstance(vec,pAutomorphicForm)): #need to put some assertions - self._ZZp=Qp(_parent._X._p,prec=_parent._prec) + self._R=Qp(_parent._X._p,prec=_parent._prec) self._nE=len(_parent._E) self._wt=_parent._k E=_parent._E - self._F=[OCVnElement(_parent._U,vec._F[ii]).l_act_by(E[ii].rep) for ii in range(self._nE)] + self._F=[_parent._U.element_class(_parent._U,vec._F[ii]).l_act_by(E[ii].rep) for ii in range(self._nE)] + return elif(isinstance(vec,dict)): assert(vec['p'])==_parent._X._p assert(vec['t'])==_parent._U._depth assert(vec['level'])==_parent._X._level assert(vec['k']==_parent._U.weight()+2) - self._ZZp=Qp(_parent._X._p,prec=_parent._prec) + self._R=_parent._U._R self._wt=_parent._k self._nE=len(_parent._E) self._F=[_parent._U(c) for c in mydata['value']] - elif(isinstance(vec,list) and len(vec)==len(_parent._E) and vec[0].parent() is _parent._U): - self._ZZp=Qp(_parent._X._p,prec=_parent._prec) + return + elif(isinstance(vec,(list,tuple)) and vec[0].parent() is _parent._U): + self._R=_parent._U._R self._wt=_parent._k self._nE=len(_parent._E) self._F=copy(vec) + return else: try: - veczp=_parent._U._ZZp(vec) - self._ZZp=Qp(_parent._X._p,prec=_parent._prec) + veczp=_parent._U._R(vec) + self._R=_parent._U._R self._wt=_parent._k self._nE=len(_parent._E) self._F=[_parent._U(veczp) for ii in range(self._nE)] + return except: + print vec raise ValueError def _add_(self,g): @@ -1167,10 +1170,8 @@ def _repr_(self): tmp='Harmonic cocycle on '+str(self._parent)+':\n' - tmp+=' e | f(e)' - tmp+='\n' for e in range(self._nE): - tmp+=' '+str(e)+' | '+str(self._F[e])+'\n' + tmp+=str(e)+' |--> '+str(self._F[e])+'\n' return tmp def __eq__(self,other): @@ -1188,12 +1189,19 @@ else: return min([self._F[e].valuation() for e in range(self._nE)]) + def list(self): + R=self._R + A=self._parent.basis_matrix().transpose() + B=Matrix(R,self._nE*(self._parent._k-1),1,[self._F[e]._val[ii,0] for e in range(self._nE) for ii in range(self._parent._k-1) ]) + res=(A.solve_right(B)).transpose() + return res.list() + #In HarmonicCocycle def evaluate(self,e1): X=self._parent._X p=X._p u=EdgeDecomposition(X,e1) - return (u.sign()*self._F[u.label]).l_act_by(u.igamma()*(p**(-u.power))) + return (u.sign()*self._F[u.label]).l_act_by(u.igamma(self.iota)*(p**(-u.power))) #In HarmonicCocycle def riemann_sum(self,f,center=1,level=0,E=None): @@ -1201,7 +1209,7 @@ R1.set_default_prec(self._parent._k-1) R2=PolynomialRing(f.base_ring(),'r2') - if(E==None): + if E is None: E=self._parent._X._BT.get_ball(center,level) else: E=self._parent._X._BT.subdivide(E,level) @@ -1213,6 +1221,7 @@ new=self.evaluate(e).l_act_by(e.inverse()).evaluate(exp) value+=new return value + def mod_form(self,z=None,level=0): return self.derivative(z,level,order=0) @@ -1225,7 +1234,7 @@ substitute=R.hom([x1,z],codomain=Rx) x,y=R.gens() center=self._parent._X._BT.whereis(z) - zbar=our_conj(z) + zbar=z.trace()-z f=R(1)/(x-y) k=self._parent._k V=[f] @@ -1233,82 +1242,58 @@ V=[v.derivative(y) for v in V]+[k/(y-zbar)*v for v in V] k+=2 return sum([self.riemann_sum(substitute(v),center,level) for v in V]) - if(z==None): + if(z is None): return F else: return F(z) - # In HarmonicCocycle - # So far only works for k=2 !! - def coleman(self,t1,t2,center=1,level=1,E=None): - if(center==None): - center=self._parent._X._BT.whereis(t1) - if(E==None): - E=self._parent._X._BT.get_ball(center,0) - EList=[] - for e in E: - EList.extend(self._parent._X._BT.subdivide([e],level)) - K=t1.parent() - R1=LaurentSeriesRing(K,'r1') - R1.set_default_prec(self._parent._U.weight()+1) - r1=R1.gen() - value=0 - ii=0 - for e in EList: - ii+=1 - #print ii,"/",len(EList) - b=e[0,1] - d=e[1,1] - y=(b-d*t1)/(b-d*t2) - exp=R1(our_log(y)) - new=(self.evaluate(e)).evaluate(exp) - value+=new - return value - - # In HarmonicCocycle - # So far only works for k=2 !! - def coleman_mult(self,t1,t2,center=1,level=1,E=None): - if(center==None): - center=self._parent._X._BT.whereis(t1) - if(E==None): - E=self._parent._X._BT.get_ball(center,0) - EList=[] - for e in E: - EList.extend(self._parent._X._BT.subdivide([e],level)) - - K=t1.parent() - R1=LaurentSeriesRing(K,'r1') - R1.set_default_prec(self._parent._U.weight()+1) - r1=R1.gen() - p=self._parent._X._p - value=1 - ii=0 - for e in EList: - ii+=1 - b=e[0,1] - d=e[1,1] - if(d!=0): - y=(b-d*t1)/(b-d*t2) - power=self.evaluate(e)._val[0,0].rational_reconstruction() - new=y**power - value*=new - return value - -class HarmonicCocycles(Module): +class HarmonicCocycles(AmbientHeckeModule): Element=HarmonicCocycle - def __init__(self,X,k,prec=100): + def __init__(self,X,k,prec=None,basis_matrix=None): self._k=k - self._prec=prec self._X=X - self._ZZp=Qp(self._X._p,prec=prec) - Module.__init__(self,base=self._ZZp) + if(prec is None): + self._is_exact=True + self._prec=None + try: + self._R=X.get_splitting_field() + except AttributeError: + raise ValueError, "It looks like you are not using Magma as backend...and still we don't know how to compute splittings in that case!" + self._U=OCVn(self._k-2,self._R) + else: + self._is_exact=False + self._prec=prec + self._R=Qp(self._X._p,prec=prec) + self._U=OCVn(self._k-2,self._R,self._k-1) + if basis_matrix is None: + if(self._k==2): + rank=self._X.genus() + else: + # Here we assume that the group has no torsion. This is false for small discriminants, and we should find a formula! + #rank=(self._X.genus()-1)*(self._k-1) + A=self.basis_matrix() + rank=A.nrows() + self.__rank=rank + else: + self.__matrix=basis_matrix + self.__matrix.set_immutable() + self.__rank=self.__matrix.nrows() + AmbientHeckeModule.__init__(self, self._R, self.__rank, self._X.prime()*self._X.Nplus()*self._X.Nminus(), weight=self._k) self._E=self._X.get_edge_list() self._V=self._X.get_vertex_list() - self._basis_matrix=None - self._U=OCVn(self._k-2,self._ZZp,self._k-1) self._populate_coercion_lists_() + def rank(self): + return self.__rank + + def submodule(self,v,check=False): + return HarmonicCocyclesSubmodule(self,v,dual=None,check=check) + #return HarmonicCocycles(X=self._X,k=self._k,prec=self._prec,basis_matrix=v.basis_matrix()*self.basis_matrix()) + + def is_simple(self): + return self.rank()==1 + def _repr_(self): s='Space of harmonic cocycles of weight '+str(self._k)+' on '+str(self._X) return s @@ -1324,49 +1309,59 @@ """ Can coerce from other HarmonicCocycles or from pAutomorphicForms """ - if(isinstance(S,HarmonicCocycles)): + if isinstance(S,(HarmonicCocycles,pAutomorphicForms)): if(S._k!=self._k): return False if(S._X!=self._X): return False return True - if(isinstance(S,pAutomorphicForms)): - if(S._n!=self._k-2): - return False - if(S._X!=self._X): - return False - return True + try: + tmp=tuple(S) + if len(tmp)==self.dimension(): + return True + except NotImplementedError,TypeError: + return False return False + def __cmp__(self,other): + try: + res=(self.base_ring()==other.base_ring() and self._X==other._X and self._k==other._k) + return res + except: + return False + def _element_constructor_(self,x): #Code how to coherce x into the space #Admissible values of x? - if(isinstance(x,HarmonicCocycle) or isinstance(x,pAutomorphicForm)): + if isinstance(x,(HarmonicCocycle,pAutomorphicForm)): return HarmonicCocycle(self,x) + return HarmonicCocycle(self,x) + + def is_exact(self): + return self._is_exact + + def free_module(self): + """ + Return the underlying free module. + """ + try: return self.__free_module + except AttributeError: pass + V = self.base_ring()**self.dimension() + self.__free_module = V + return V + + def character(self): + return lambda x:x def iota(self,g): - return self._X.iota(g,self._prec) - - def get_basis_matrix(self,extra_conditions=[]): - if (self._basis_matrix==None or len(extra_conditions)>0): - self._compute_basis(extra_conditions) - return self._basis_matrix + if self.is_exact(): + return self._X.iota_exact(g) + else: + return self._X.iota(g,self._prec) - def get_basis(self,extra_conditions=[]): - nE=len(self._E) - basis_matrix=self.get_basis_matrix(extra_conditions) - basis=[] - for ii in range(basis_matrix.nrows()): - r=list(basis_matrix.row(ii)) - basis.append(HarmonicCocycle(self,[OCVnElement(self._U,Matrix(self._ZZp,self._k-1,1,r[e*(self._k-1):(e+1)*(self._k-1)]),quick=True) for e in range(nE)])) - return basis - - def dimension(self,extra_conditions=[]): - if (self._basis_matrix==None or len(extra_conditions)>0): - self._compute_basis(extra_conditions) - return self._basis_matrix.nrows() - - def _compute_basis(self,extra_conditions): + def basis_matrix(self): + try: return self.__matrix + except AttributeError: pass nV=len(self._V) nE=len(self._E) stab_conds=[] @@ -1382,105 +1377,108 @@ except IndexError: pass n_stab_conds=len(stab_conds) - self._M=Matrix(self._ZZp,(nV+n_stab_conds+len(extra_conditions))*d,nE*d,0,sparse=True) + self._M=Matrix(self._R,(nV+n_stab_conds)*d,nE*d,0,sparse=True) for v in self._V: for e in filter(lambda e:e.parity==0,v.leaving_edges): - C=sum(self._U.l_matrix_representation_list([self.iota(x[0]) for x in e.links]),Matrix(self._ZZp,d,d,0)) + C=sum(self._U.l_matrix_representation_list([self.iota(x[0]) for x in e.links]),Matrix(self._R,d,d,0)) self._M.set_block(v.label*d,e.label*d,C) for e in filter(lambda e:e.parity==0,v.entering_edges): - C=sum(self._U.l_matrix_representation_list([self.iota(x[0]) for x in e.opposite.links]),Matrix(self._ZZp,d,d,0)) + C=sum(self._U.l_matrix_representation_list([self.iota(x[0]) for x in e.opposite.links]),Matrix(self._R,d,d,0)) self._M.set_block(v.label*d,e.opposite.label*d,C) for kk in range(n_stab_conds): v=stab_conds[kk] self._M.set_block((nV+kk)*d,v[0]*d,v[1]) - for kk in range(len(extra_conditions)): - v=extra_conditions[kk] - self._M.set_block((nV+n_stab_conds+kk)*d,v*d,Matrix(self._ZZp,d,d,1)) - x1=self._M.right_kernel().matrix() K=[c for c in x1.rows()] - for ii in range(len(K)): - s=min([t.valuation() for t in K[ii]]) - for jj in range(len(K[ii])): - K[ii][jj]=(p**(-s))*K[ii][jj] - self._basis_matrix=Matrix(self._ZZp,len(K),nE*d,K) + if not self.is_exact: + for ii in range(len(K)): + s=min([t.valuation() for t in K[ii]]) + for jj in range(len(K[ii])): + K[ii][jj]=(p**(-s))*K[ii][jj] + + self.__matrix=Matrix(self._R,len(K),nE*d,K) + self.__matrix.set_immutable() + return self.__matrix - def atkin_lehner(self,q): - q=ZZ(q) - lev=self._X.generic_fiber().level() - if(not q.is_prime() or lev%q!=0): - raise ValueError, "q must be a prime dividing %s"%lev.factor() - def T(f): - R=Qp(f._ZZp.prime(),prec=f._ZZp.precision_cap()) - Data=self._X.atkin_lehner_data(q) - iota=self.iota - p=self._X._p - betamat=iota(self._X._beta1[q]) - tmp=[OCVnElement(self._U,zero_matrix(self._ZZp,self._k-1,1),quick=True) for jj in range(len(self._E))] - d1=Data[1] - mga=iota(Data[0]) + def __apply_atkin_lehner(self,q,f): + R=self._R + Data=self._X._get_atkin_lehner_data(q) + iota=self.iota + p=self._X._p + tmp=[self._U.element_class(self._U,zero_matrix(self._R,self._k-1,1),quick=True) for jj in range(len(self._E))] + d1=Data[1] + mga=iota(Data[0]) + for jj in range(len(self._E)): + t=d1[jj] + tmp[jj]+=(t.sign()*f._F[t.label]).l_act_by(p**(-t.power)*mga*t.igamma(self.iota)) + return HarmonicCocycle(self,tmp) + + def __apply_hecke_operator(self,l,f): + R=self._R + HeckeData,alpha=self._X._get_hecke_data(l) + if(self.level()%l==0): + factor=QQ(l**(Integer((self._k-2)/2))/(l+1)) + else: + factor=QQ(l**(Integer((self._k-2)/2))) + iota=self.iota + p=self._X._p + alphamat=iota(alpha) + tmp=[self._U.element_class(self._U,zero_matrix(self._R,self._k-1,1),quick=True) for jj in range(len(self._E))] + for ii in range(len(HeckeData)): + d1=HeckeData[ii][1] + mga=iota(HeckeData[ii][0])*alphamat for jj in range(len(self._E)): t=d1[jj] - tmp[jj]+=(t.sign()*f._F[t.label]).l_act_by(p**(-t.power)*mga*t.igamma()) - return HarmonicCocycle(self,tmp) - return T + tmp[jj]+=(t.sign()*f._F[t.label]).l_act_by(p**(-t.power)*mga*t.igamma(self.iota)) + return HarmonicCocycle(self,[factor*x for x in tmp]) + + def _compute_atkin_lehner_matrix(self,d): + res=self.__compute_operator_matrix(lambda f:self.__apply_atkin_lehner(d,f)) + return res + + def _compute_hecke_matrix_prime(self,l): + res=self.__compute_operator_matrix(lambda f:self.__apply_hecke_operator(l,f)) + return res - def hecke_operator(self,l): - l=ZZ(l) - if(not l.is_prime()): - raise ValueError, "l must be prime" + def __compute_operator_matrix(self,T): + R=self._R + A=self.basis_matrix().transpose() + basis=self.basis() + B=zero_matrix(R,len(self._E)*(self._k-1),self.dimension()) + for rr in range(len(basis)): + g=T(basis[rr]) + B.set_block(0,rr,Matrix(R,len(self._E)*(self._k-1),1,[g._F[e]._val[ii,0] for e in range(len(self._E)) for ii in range(self._k-1) ])) + res=(A.solve_right(B)).transpose() + res.set_immutable() + return res - def T(f): - R=Qp(f._ZZp.prime(),prec=f._ZZp.precision_cap()) - HeckeData=self._X.hecke_data(l) - if(self._X.generic_fiber().level()%l==0): - factor=QQ(l**(ZZ((self._k-2)/2))/(l+1)) - else: - factor=QQ(l**(ZZ((self._k-2)/2))) - iota=self.iota - p=self._X._p - alphamat=iota(self._X._alpha1[l]) - tmp=[OCVnElement(self._U,zero_matrix(self._ZZp,self._k-1,1),quick=True) for jj in range(len(self._E))] - for ii in range(len(HeckeData)): - d1=HeckeData[ii][1] - mga=iota(HeckeData[ii][0])*alphamat - for jj in range(len(self._E)): - t=d1[jj] - tmp[jj]+=(t.sign()*f._F[t.label]).l_act_by(p**(-t.power)*mga*t.igamma()) - return HarmonicCocycle(self,[factor*x for x in tmp]) - return T +class HarmonicCocyclesSubmodule(sage.modular.hecke.submodule.HeckeSubmodule,HarmonicCocycles): + """ + A submodule of an ambient space of harmonic cocycles. + """ + def __init__(self, ambient_module, submodule, dual=None, check=False): + """ + ambient_module -- HarmonicCocycles + submodule -- a submodule of the ambient space. + dual_module -- (default: None) ignored + check -- (default: False) whether to check that the + submodule is Hecke equivariant + """ + A = ambient_module + sage.modular.hecke.submodule.HeckeSubmodule.__init__(self, A, submodule, check=check) + self.__rank=submodule.dimension() + #HarmonicCocycles.__init__(A,X=A._X,k=A._k,prec=A._prec,basis_matrix=submodule.basis_matrix()*A.basis_matrix()) - def operator_matrix(self,T): - R=Qp(self._ZZp.prime(),prec=self._ZZp.precision_cap()) - A=self.get_basis_matrix() - basis=self.get_basis() - tmp=[] - for f in basis: - g=T(f) - tmp.append([g._F[e]._val[ii,0] for e in range(len(self._E)) for ii in range(self._k-1) ]) - B=Matrix(R,len(basis),len(self._E)*(self._k-1),tmp) - return (A.solve_left(B)).transpose() - - def hecke_matrix(self,l): - return self.operator_matrix(self.hecke_operator(l)) + def rank(self): + return self.__rank - def hecke_polynomial(self,l): - v1=self.hecke_matrix(l).characteristic_polynomial().coeffs() - try: - v=[x.rational_reconstruction() for x in v1] - return PolynomialRing(QQ,'x')(v) - except: - raise Exception,"Need to use more precision for this computation..." + def _repr_(self): + return "Dimension %s subspace of %s"%(self.dimension(),self.ambient()) - -###################### -### New code that is really automorphic -###################### - class pAutomorphicForm(ModuleElement): def __init__(self,_parent,vec,quick=False): @@ -1488,34 +1486,34 @@ self._parent=_parent self._nE=2*len(_parent._E) # We record the values at the opposite edges if(quick): - self._ZZp=Qp(_parent._X._p,prec=_parent._prec) + self._R=Qp(_parent._X._p,prec=_parent._prec) self._F=[v for v in vec] else: if(isinstance(vec,pAutomorphicForm)): - self._ZZp=Qp(_parent._X._p,prec=_parent._prec) + self._R=Qp(_parent._X._p,prec=_parent._prec) self._F=[self._parent._U(vec._F[ii]) for ii in range(self._nE)] self._make_invariant() elif(isinstance(vec,HarmonicCocycle)): assert(_parent._U.weight()==vec._wt-2) - self._ZZp=Qp(_parent._X._p,prec=_parent._prec) + self._R=Qp(_parent._X._p,prec=_parent._prec) self._F=[] assert(2*len(vec._F)==self._nE) assert(isinstance(_parent._U,OCVn)) E=self._parent._E for ii in range(len(vec._F)): - self._F.append(_parent._U(OCVnElement(vec._parent._U,vec._F[ii]).l_act_by(E[ii].rep.inverse()))) + self._F.append(_parent._U(self._MElement(vec._parent._U,vec._F[ii]).l_act_by(E[ii].rep.inverse()))) for ii in range(len(vec._F)): - self._F.append(_parent._U((-1*(OCVnElement(vec._parent._U,self._F[ii])).r_act_by(Matrix(QQ,2,2,[0,-1/_parent._X._p,-1,0]))))) + self._F.append(_parent._U((-1*(self._MElement(vec._parent._U,self._F[ii])).r_act_by(Matrix(QQ,2,2,[0,-1/_parent._X._p,-1,0]))))) self._make_invariant() elif(isinstance(vec,list) and len(vec)==self._nE): - self._ZZp=Qp(_parent._X._p,prec=_parent._prec) + self._R=Qp(_parent._X._p,prec=_parent._prec) try: self._F=[self._parent._U(v) for v in vec] except: try: - veczp=_parent._U._ZZp(vec) + veczp=_parent._U._R(vec) self._parent=_parent self._F=[self._parent._U(veczp) for ii in range(self._nE)] except: @@ -1523,8 +1521,8 @@ assert(0) else: try: - self._ZZp=Qp(_parent._X._p,prec=_parent._prec) - veczp=_parent._U._ZZp(vec) + self._R=Qp(_parent._X._p,prec=_parent._prec) + veczp=_parent._U._R(vec) self._parent=_parent self._F=[self._parent._U(veczp) for ii in range(self._nE)] except: @@ -1539,8 +1537,8 @@ for ii in range(self._nE): Si=S[ii%lS] if(Si!=1): - x=OCVnElement(self._F[ii].parent(),self._F[ii]._val,quick=True) - self._F[ii]=OCVnElement(self._F[ii].parent(),0) + x=self._MElement(self._F[ii].parent(),self._F[ii]._val,quick=True) + self._F[ii]=self._MElement(self._F[ii].parent(),0) s=QQ(0) m=M[ii] for v in Si: @@ -1617,7 +1615,7 @@ tmp='p-adic automorphic form on '+str(self._parent)+':\n' tmp+=' e | c(e)' tmp+='\n' - for e in range(ZZ(self._nE/2)): + for e in range(Integer(self._nE/2)): tmp+=' '+str(e)+' | '+str(self._F[e])+'\n' return tmp @@ -1632,7 +1630,7 @@ def improve(self): MMM=self._parent - h2=MMM.Up(self,True) + h2=MMM.__apply_Up_operator(self,True) ii=0 current_val=0 old_val=-1 @@ -1641,7 +1639,7 @@ ii+=1 print ii,"th iteration, precision=",current_val self._F=[self._parent._U(c) for c in h2._F] - h2=MMM.Up(self,True) + h2=MMM.__apply_Up_operator(self,True) current_val=(h2-self).valuation() self._F=[self._parent._U(c) for c in h2._F] @@ -1685,7 +1683,7 @@ substitute=R.hom([x1,z],codomain=Rx) x,y=R.gens() center=self._parent._X._BT.whereis(z) - zbar=our_conj(z) + zbar=z.trace()-z f=R(1)/(x-y) k=self._parent._n+2 V=[f] @@ -1693,7 +1691,7 @@ V=[v.derivative(y) for v in V]+[k/(y-zbar)*v for v in V] k+=2 return sum([self.integrate(substitute(v),center,level,method) for v in V]) - if(z==None): + if(z is None): return F else: return F(z,level,method) @@ -1701,9 +1699,9 @@ def _exp_factor(self,t1,t2,center=1,E=None,delta=-1): K=t1.parent() - if(center==None): + if(center is None): center=self._parent._X._BT.whereis(t1) - if(E==None): + if(E is None): E=self._parent._X._BT.get_ball(center,level=0) R1=LaurentSeriesRing(t1.parent(),'r1') R1.set_default_prec(self._parent._U.weight()+1) @@ -1719,7 +1717,7 @@ num=b-d*t1 den=b-d*t2 y=num/den - power=ZZ(self.evaluate(e)._val[0,0].rational_reconstruction()) + power=Integer(self.evaluate(e)._val[0,0].rational_reconstruction()) new=K.teichmuller(y)**power value*=new return value @@ -1734,9 +1732,9 @@ x=R.gen() R1=LaurentSeriesRing(K,'r1') r1=R1.gen() - if(center==None): + if(center is None): center=self._parent._X._BT.whereis(t1) - if(E==None): + if(E is None): E=self._parent._X._BT.get_ball(center,level=0) value=0 ii=0 @@ -1745,7 +1743,6 @@ R1.set_default_prec(self._parent._U.weight()+1) for e in E: ii+=1 - #print ii,"/",len(E) b=e[0,1] d=e[1,1] y=(b-d*t1)/(b-d*t2) @@ -1756,7 +1753,6 @@ R1.set_default_prec(self._parent._U.dimension()) for e in E: ii+=1 - #print ii,"/",len(E) f=(x-t1)/(x-t2) y0=f(R1([e[0,1],e[0,0]])/R1([e[1,1],e[1,0]])) y0=p**(-y0(0).valuation())*y0 @@ -1781,31 +1777,31 @@ class pAutomorphicForms(Module): Element=pAutomorphicForm def __init__(self,X,U,prec=None,t=None,R=None,overconvergent=False): - if(R==None): + if(R is None): if(not isinstance(U,Integer)): - self._ZZp=U.base_ring() + self._R=U.base_ring() else: - if(prec==None): + if(prec is None): prec=100 - self._ZZp=Qp(X._p,prec) + self._R=Qp(X._p,prec) else: - self._ZZp=R + self._R=R #U is a CoefficientModuleSpace if(isinstance(U,Integer)): - if(t==None): + if(t is None): if(overconvergent): t=prec-U+1 else: t=0 - self._U=OCVn(U-2,self._ZZp,U-1+t) + self._U=OCVn(U-2,self._R,U-1+t) else: self._U=U self._X=X self._V=self._X.get_vertex_list() self._E=self._X.get_edge_list() - self._prec=self._ZZp.precision_cap() + self._prec=self._R.precision_cap() self._n=self._U.weight() - Module.__init__(self,base=self._ZZp) + Module.__init__(self,base=self._R) self._populate_coercion_lists_() def _repr_(self): @@ -1833,7 +1829,7 @@ def _element_constructor_(self,x): #Code how to coherce x into the space #Admissible values of x? - if(isinstance(x,(HarmonicCocycle,pAutomorphicForm))): + if isinstance(x,(HarmonicCocycle,pAutomorphicForm)): return pAutomorphicForm(self,x) def _an_element_(self): @@ -1842,27 +1838,22 @@ def iota(self,g): return self._X.iota(g,self._prec) - def Up(self,v=None,scale=False): - def T(f): - HeckeData=self._X.Up_data() - if(scale==False): - factor=(self._X._p)**(self._U.weight()/2) - else: - factor=1 - Tf=[] - for jj in range(2*len(self._E)): - tmp=self._U(0) - for d in HeckeData: - gg=d[0] # acter - u=d[1][jj] # edge_list[jj] - r=self._X._p**(-(u.power))*(u.t()*gg) - tmp+=f._F[u.label+u.parity*len(self._E)].r_act_by(r) - Tf.append(factor*tmp) - return pAutomorphicForm(self,Tf,quick=True) - if(v==None): - return T + def __apply_Up_operator(self,f,scale=False): + HeckeData=self._X._get_Up_data() + if(scale==False): + factor=(self._X._p)**(self._U.weight()/2) else: - return T(v) + factor=1 + Tf=[] + for jj in range(2*len(self._E)): + tmp=self._U(0) + for d in HeckeData: + gg=d[0] # acter + u=d[1][jj] # edge_list[jj] + r=self._X._p**(-(u.power))*(u.t()*gg) + tmp+=f._F[u.label+u.parity*len(self._E)].r_act_by(r) + Tf.append(factor*tmp) + return pAutomorphicForm(self,Tf,quick=True) ######################################################################### # Copyright (C) 2011 Cameron Franc and Marc Masdeu @@ -1886,82 +1877,97 @@ ModuleElement.__init__(self,_parent) self._parent=_parent self._n=self._parent._n - self._nhalf=ZZ(self._n/2) + self._nhalf=Integer(self._n/2) self._depth=self._parent._depth - if(quick): + if quick: self._val=copy(val) else: - if(isinstance(val,OCVnElement)): + if isinstance(val,self.__class__): d=min([val._parent._depth,_parent._depth]) assert(val._parent.weight()==_parent.weight()) - self._val=Matrix(self._parent._ZZp,self._depth,1,0) + self._val=Matrix(self._parent._R,self._depth,1,0) for ii in range(d): self._val[ii,0]=val._val[ii,0] else: try: - self._val=MatrixSpace(self._parent._ZZp,self._depth,1)(val) + self._val=MatrixSpace(self._parent._R,self._depth,1)(val) except: - self._val=val*ones_matrix(self._parent._ZZp,self._depth,1) + self._val=val*ones_matrix(self._parent._R,self._depth,1) def __getitem__(self,r): return self._val[r,0] + def element(self): + tmp=self.matrix_rep() + return [tmp[ii,0] for ii in range(tmp.nrows())] + + def list(self): + return self.element() + def matrix_rep(self,B=None): #Express the element in terms of the basis B - if(B==None): + if(B is None): B=self._parent.basis() - A=Matrix(self._parent._ZZp,self._parent.dimension(),self._parent.dimension(),[[b._val[ii,0] for b in B] for ii in range(self._depth)]) + A=Matrix(self._parent._R,self._parent.dimension(),self._parent.dimension(),[[b._val[ii,0] for b in B] for ii in range(self._depth)]) return A.solve_right(self._val) def _add_(self,y): - assert(self._depth==y._depth and (self._n==y._n or any[self._val==0,y._val==0])) val=self._val+y._val - return OCVnElement(self._parent,val,quick=True) + return self.__class__(self._parent,val,quick=True) def _sub_(self,y): - assert(self._depth==y._depth and (self._n==y._n or any[self._val==0,y._val==0])) val=self._val-y._val - return OCVnElement(self._parent,val,quick=True) + return self.__class__(self._parent,val,quick=True) def l_act_by(self,x): #assert(x.nrows()==2 and x.ncols()==2) #An element of GL2 - K=self._parent._ZZp return self._l_act_by(x[0,0],x[0,1],x[1,0],x[1,1],extrafactor=x.determinant()**(-self._nhalf)) def r_act_by(self,x): #assert(x.nrows()==2 and x.ncols()==2) #An element of GL2 - K=self._parent._ZZp return self._l_act_by(x[1,1],-x[0,1],-x[1,0],x[0,0],extrafactor=x.determinant()**(-self._nhalf)) def _l_act_by(self,a,b,c,d,extrafactor=1): - R=self._parent._ZZp - t=min([R(x).valuation() for x in [a,b,c,d] if x!=0]) - factor=R.prime()**(-t) + R=self._parent._R + if(self._parent.is_exact()): + factor=1 + else: + t=min([R(x).valuation() for x in [a,b,c,d] if x!=0]) + factor=R.prime()**(-t) try: x=self._parent._powers[(factor*a,factor*b,factor*c,factor*d)] - return OCVnElement(self._parent,extrafactor*factor**(-self._n)*x*self._val,quick=True) + return self.__class__(self._parent,extrafactor*factor**(-self._n)*x*self._val,quick=True) except KeyError: tmp=self._parent._get_powers_and_mult(factor*a,factor*b,factor*c,factor*d,extrafactor*factor**(-self._n),self._val) - return OCVnElement(self._parent,tmp,quick=True) + return self.__class__(self._parent,tmp,quick=True) def _rmul_(self,a): #assume that a is a scalar - return OCVnElement(self._parent,a*self._val,quick=True) + return self.__class__(self._parent,a*self._val,quick=True) def precision_absolute(self): #This needs to be thought more carefully... - return [self._val[ii,0].precision_absolute() for ii in range(self._depth)] + if not self._parent.is_exact(): + return [self._val[ii,0].precision_absolute() for ii in range(self._depth)] + else: + return oo def precision(self): #This needs to be thought more carefully... - return min([self._val[ii,0].precision_absolute() for ii in range(self._depth)]) + if not self._parent.is_exact(): + return min([self._val[ii,0].precision_absolute() for ii in range(self._depth)]) + else: + return oo def precision_relative(self): #This needs to be thought more carefully... - return [self._val[ii,0].precision_relative() for ii in range(self._depth)] + if not self._parent.is_exact(): + return [self._val[ii,0].precision_relative() for ii in range(self._depth)] + else: + return oo def _repr_(self): - R=PowerSeriesRing(self._parent._ZZp,default_prec=self._depth,name='z') + R=PowerSeriesRing(self._parent._R,default_prec=self._depth,name='z') z=R.gen() s=str(sum([R(self._val[ii,0]*z**ii) for ii in range(self._depth)])) return s @@ -1973,35 +1979,47 @@ return self._val!=0 def evaluate(self,P): - p=self._parent._ZZp.prime() + p=self._parent._R.prime() try: r=min([P.degree()+1,self._depth]) return sum([self._val[ii,0]*P[ii] for ii in range(r)]) except: return self._val[0,0]*P - def valuation(self): - return min([self._val[ii,0].valuation() for ii in range(self._depth)]) + def valuation(self,l=None): + if not self._parent.is_exact(): + if(l!=self._parent._R.prime()): + raise ValueError, "This function can only be called with the base prime" + return min([self._val[ii,0].valuation() for ii in range(self._depth)]) + else: + return min([self._val[ii,0].valuation(l) for ii in range(self._depth)]) -class OCVn(Module): + +class OCVn(Module,UniqueRepresentation): Element=OCVnElement - def __init__(self,n,ZZp,depth=None,basis=None): - Module.__init__(self,base=ZZp) - if(basis!=None): + def __init__(self,n,R,depth=None,basis=None): + Module.__init__(self,base=R) + if not basis is None: self._basis=copy(basis) self._n=n - self._ZZp=ZZp - self._ZZmod=Zmod(self._ZZp.prime()**(self._ZZp.precision_cap())) + self._R=R + self._is_exact=R.is_exact() + if self._is_exact: + self._Rmod=self._R + else: + self._Rmod=Zmod(self._R.prime()**(self._R.precision_cap())) - if(depth==None): + if depth is None: depth=n+1 + if depth != n+1: + if self._is_exact: raise ValueError, "Trying to construct an over-convergent module with exact coefficients, how do you store p-adics ??" self._depth=depth - self._PowerSeries=PowerSeriesRing(self._ZZmod,default_prec=self._depth,name='z') + self._PowerSeries=PowerSeriesRing(self._Rmod,default_prec=self._depth,name='z') self._powers=dict() self._populate_coercion_lists_() def _an_element_(self): - return OCVnElement(self,Matrix(self._ZZp,self._depth,1,range(1,self._depth+1)),quick=True) + return OCVnElement(self,Matrix(self._R,self._depth,1,range(1,self._depth+1)),quick=True) def _coerce_map_from_(self, S): """ @@ -2014,15 +2032,12 @@ #Admissible values of x? return OCVnElement(self,x) - def base(self): - return self._ZZp + def is_exact(self): + return self._is_exact def _get_powers_and_mult(self,a,b,c,d,lambd,vect): - p=self._ZZp.prime() #print a,b,c,d - ZZmod=self._ZZmod R=self._PowerSeries - z=R.gen() r=R([b,a]) s=R([d,c]) n=self._n @@ -2032,7 +2047,7 @@ for ii in range(n): rpows.append(r*rpows[ii]) spows.append(s*spows[ii]) - x=Matrix(ZZmod,n+1,n+1,0) + x=Matrix(self._Rmod,n+1,n+1,0) for ii in range(n+1): y=rpows[ii]*spows[n-ii] for jj in range(self._depth): @@ -2040,48 +2055,52 @@ else: ratio=r*(s**(-1)) y=s**n - x=Matrix(ZZmod,self._depth,self._depth,0) + x=Matrix(self._Rmod,self._depth,self._depth,0) for jj in range(self._depth): x[0,jj]=y[jj] for ii in range(1,self._depth): y*=ratio for jj in range(self._depth): x[ii,jj]=y[jj] - xnew=x.change_ring(self._ZZp) + if self._Rmod is self._R: + xnew=x + else: + xnew=x.change_ring(self._R) self._powers[(a,b,c,d)]=xnew - return self._ZZp(lambd)*xnew*vect + return self._R(lambd)*xnew*vect def _repr_(self): - s='Overconvergent coefficient module of weight n='+str(self._n)+' over the ring '+ str(self._ZZp)+' and depth '+str(self._depth) + s='Overconvergent coefficient module of weight n='+str(self._n)+' over the ring '+ str(self._R)+' and depth '+str(self._depth) return s def basis(self): try: return self._basis except: pass - self._basis=[OCVnElement(self,Matrix(self._ZZp,self._depth,1,{(jj,0):1},sparse=False),quick=True) for jj in range(self._depth)] + self._basis=[OCVnElement(self,Matrix(self._R,self._depth,1,{(jj,0):1},sparse=False),quick=True) for jj in range(self._depth)] return self._basis def base_ring(self): - return self._ZZp + return self._R def depth(self): return self._depth def dimension(self): return self._depth + def weight(self): return self._n def l_matrix_representation_list(self,v,B=None): - if(B==None): + if B is None: B=self.basis() return [self.l_matrix_representation(g,B) for g in v] def l_matrix_representation(self,g,B=None): - if(B==None): + if B is None: B=self.basis() A=[(b.l_act_by(g)).matrix_rep(B) for b in B] d=self.dimension() - return Matrix(self._ZZp,d,d,[A[jj][ii,0] for ii in range(d) for jj in range(d)]) + return Matrix(self._R,d,d,[A[jj][ii,0] for ii in range(d) for jj in range(d)]) ######################################################################### @@ -2095,12 +2114,6 @@ from sage.misc.mrange import cartesian_product_iterator from sage.rings.all import Qp -def our_conj(x): - return x.trace()-x - -def act_flt(g,x): - return (g[0,0]*x+g[0,1])/(g[1,0]*x+g[1,1]) - def getcoords(E,u,prec=20): q = E.parameter(prec=prec) un = u * q**(-(u.valuation()/q.valuation()).floor()) @@ -2125,9 +2138,6 @@ return (xx1,yy1) -def our_pow(x,y,K): - return K.teichmuller(x)*our_exp(y*our_log(x)) - def our_sqrt(x,K): if(x==0): return x @@ -2149,7 +2159,7 @@ def our_log(x,prec=None): K=x.parent() - if prec==None: + if prec is None: prec=K.precision_cap()+10 x0=x.unit_part() y=x0/K.teichmuller(x0)-1 @@ -2162,7 +2172,7 @@ def our_exp(x,prec=None): K=x.parent() - if prec==None: + if prec is None: prec=K.precision_cap()+10 tmp=K(1+x) xpow=x**2