def __init__(self,G,prec=500,verbose=None): r""" Creates an ambient space of Maass waveforms (i.e. there are apriori no members). INPUT: - `'G`` -- subgroup of the modular group - ``prec`` -- default working precision in bits EXAMPLES:: sage: S=MaassWaveForms(Gamma0(1)); S Space of Maass waveforms on the group G: Arithmetic Subgroup of PSL2(Z) with index 1. Given by: perm(S)=() perm(ST)=() Constructed from G=Modular Group SL(2,Z) """ from mysubgroup import MySubgroup if(not isinstance(G, MySubgroup)): if(isinstance(G,int) or isinstance(G,sage.rings.integer.Integer)): self._G=MySubgroup(Gamma0(G)) else: try: self._G=MySubgroup(G) except TypeError: raise TypeError,"Incorrect input!! Need subgroup of PSL2Z! Got :%s" %(G) else: self._G=G self.verbose=verbose self.prec=prec self._Weyl_law_const=self._Weyl_law_consts() maass_forms=dict() # list of members if(verbose==None): self._verbose=0 # output debugging stuff or not else: self._verbose=verbose self._symmetry=None
class MaassWaveForms (Parent): r""" Describes a space of Maass waveforms """ def __init__(self,G,prec=500,verbose=None): r""" Creates an ambient space of Maass waveforms (i.e. there are apriori no members). INPUT: - `'G`` -- subgroup of the modular group - ``prec`` -- default working precision in bits EXAMPLES:: sage: S=MaassWaveForms(Gamma0(1)); S Space of Maass waveforms on the group G: Arithmetic Subgroup of PSL2(Z) with index 1. Given by: perm(S)=() perm(ST)=() Constructed from G=Modular Group SL(2,Z) """ from mysubgroup import MySubgroup if(not isinstance(G, MySubgroup)): if(isinstance(G,int) or isinstance(G,sage.rings.integer.Integer)): self._G=MySubgroup(Gamma0(G)) else: try: self._G=MySubgroup(G) except TypeError: raise TypeError,"Incorrect input!! Need subgroup of PSL2Z! Got :%s" %(G) else: self._G=G self.verbose=verbose self.prec=prec self._Weyl_law_const=self._Weyl_law_consts() maass_forms=dict() # list of members if(verbose==None): self._verbose=0 # output debugging stuff or not else: self._verbose=verbose self._symmetry=None def _repr_(self): r""" Return the string representation of self. EXAMPLES:: sage: M=MaassWaveForms(MySubgroup(Gamma0(1)));M Space of Maass waveforms on the group G: Arithmetic Subgroup of PSL2(Z) with index 1. Given by: perm(S)=() perm(ST)=() Constructed from G=Modular Group SL(2,Z) """ s="Space of Maass waveforms on the group G:\n"+str(self._G) return s def __reduce__(self): r""" Used for pickling. """ return(MaassWaveForms,(self._G,self.prec,self.verbose)) def __cmp__(self,other): r""" Compare self to other """ if(self._G <> other._G or self.prec<>other.prec): return False else: return True def get_element_in_range(self,R1,R2,ST=None,neps=10): r""" Finds element of the space self with R in the interval R1 and R2 INPUT: - ``R1`` -- lower bound (real) - ``R1`` -- upper bound (real) """ if(ST <>None): ST0=ST else: ST0=self._ST l=self.split_interval(R1,R2) if(self._verbose>1): print "Split into intervals:" for [r1,r2,y] in l: print "[",r1,",",r2,"]:",y Rl=list() for [r1,r2,y] in l: [R,er]=find_single_ev(self,r1,r2,Yset=y,ST=ST0,neps=neps) Rl.append([R,er]) print "R=",R print "er=",er 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 Weyl_law_N(self,T,T1=None): r""" The counting function for this space. N(T)=#{disc. ev.<=T} INPUT: - ``T`` -- double EXAMPLES:: sage: M=MaassWaveForms(MySubgroup(Gamma0(1)) sage: M.Weyl_law_N(10) 0.572841337202191 """ (c1,c2,c3,c4,c5)=self._Weyl_law_const cc1=RR(c1); cc2=RR(c2); cc3=RR(c3); cc4=RR(c4); cc5=RR(c5) #print "c1,c2,c3,c4,c5=",cc1,cc2,cc3,cc4,cc5 t=sqrt(T*T+0.25) try: lnt=ln(t) except TypeError: lnt=mpmath.ln(t) #print "t,ln(t)=",t,lnt NT=cc1*t*t-cc2*t*lnt+cc3*t+cc4*t+cc5 if(T1<>None): t=sqrt(T1*T1+0.25) NT1=cc1*(T1*T1+0.25)-cc2*t*ln(t)+cc3*t+cc4*t+cc5 return RR(abs(NT1-NT)) else: return RR(NT) def next_eigenvalue(self,R): r""" An estimate of where the next eigenvlue will be, i.e. the smallest R1>R so that N(R1)-N(R)>=1 INPUT: - ``R`` -- real > 0 OUTPUT: - real > R EXAMPLES:: sage: M.next_eigenvalue(10.0) 12.2500000000000 """ #cdef nmax N=self.Weyl_law_N(R) try: for j in range(1,10000): R1=R+j*RR(j)/100.0 N1=self.Weyl_law_N(R1) if(N1-N >= 1.0): raise StopIteration() except StopIteration: return R1 else: raise ArithmeticError,"Could not find next eigenvalue! in interval: [%s,%s]" %(R,R1) def Weyl_law_Np(self,T,T1=None): r""" The derviative of the counting function for this space. N(T)=#{disc. ev.<=T} INPUT: - ``T`` -- double EXAMPLES:: sage: M=MaassWaweForms(MySubgroup(Gamma0(1)) sage: M.Weyl_law_Np(10) """ (c1,c2,c3,c4,c5)=self._Weyl_law_const cc1=RR(c1); cc2=RR(c2); cc3=RR(c3); cc4=RR(c4); cc5=RR(c5) #print "c1,c2,c3,c4,c5=",c1,c2,c3,c4,c5 NpT=2.0*cc1*T-cc2*(ln(T)+1.0)+cc3+cc4 return RR(NpT) #### Split an interv def split_interval(self,R1,R2): r""" Split an interval into pieces, each containing (on average) at most one eigenvalue as well as a 0<Y<Y0 s.t. K_IR(Y) has no zero here INPUT: - ''R1'' -- real - ''R2'' -- real OUPUT: - list of triplets (r1,r2,y) where [r1,r2] does not contain a zero of K_ir(y) EXAMPLES:: sage: M._next_kbes sage: l=M.split_interval(9.0,11.0) sage: print l[0],'\n',l[1],'\n',l[2],'\n',l[3] (9.00000000000000, 9.9203192604549457, 0.86169527676551638) (9.9203192704549465, 10.135716354265259, 0.86083358148875089) (10.13571636426526, 10.903681677771321, 0.86083358148875089) (10.903681687771321, 11.0000000000000, 0.85997274790726208) """ # It is enough to work with double precision base=mpmath.fp pi=base.pi # Locate all zeros of K_IR(Y0) first #def f(r): # ir=base.mpc(0 ,r) # return base.besselk(ir,Y0) # First we find the next zero # First split into intervals having at most one zero ivs=list() rnew=R1; rold=R1 while (rnew < R2): rnew=min(R2,self.next_eigenvalue(rold)) if( abs(rold-rnew)==0.0): print "ivs=",ivs exit iv=(rold,rnew) ivs.append(iv) rold=rnew # We now need to split these intervals into pieces with at most one zero of the K-Bessel function Y00=base.mpf(0.995)*base.sqrt(base.mpf(3))/base.mpf(2 *self._G._level) new_ivs=list() for (r1,r2) in ivs: print "r1,r2=",r1,r2 Y0=Y00; r11=r1 i=0 while(r11 < r2 and i<1000): t=self._next_kbessel_zero(r11,r2,Y0*pi);i=i+1 print "t=",t iv=(r11,t,Y0); new_ivs.append(iv) # must find Y0 s.t. |besselk(it,Y0)| is large enough Y1=Y0 k=base.besselk(base.mpc(0,t),Y1).real*mpmath.exp(t*0.5*base.pi) j=0 while(j<1000 and abs(k)<1e-3): Y1=Y1*0.999;j=j+1 k=base.besselk(base.mpc(0,t),Y1).real*mpmath.exp(t*0.5*base.pi) Y0=Y1 r11=t+1E-08 return new_ivs def _next_kbessel_zero(self,r1,r2,y): r""" The first zero after r1 i the interval [r1,r2] of K_ir(y),K_ir(2y) INPUT: - ´´r1´´ -- double - ´´r2´´ -- double - ´´y´´ -- double OUTPUT: - ''double'' EXAMPLES:: sage: M=MaassWaveForms(MySubgroup(Gamma0(1))) sage: Y0=0.995*sqrt(3.0)/2.0 sage: M._next_kbessel_zero(9.0,15.0,Y0) 9.9203192604549439 sage: M._next_kbessel_zero(9.921,15.0,Y0) 10.139781183668587 CAVEAT: The rootfinding algorithm is not very sophisticated and might miss roots """ base=mpmath.fp h=(r2-r1)/500.0 t1=-1.0; t2=-1.0 r0=base.mpf(r1) kd0=my_mpmath_kbes_diff_r(r0,y,base) #print "r0,y=",r0,y,kd0 while(t1<r1 and r0<r2): # Let us first find a R-value for which the derivative changed sign kd=my_mpmath_kbes_diff_r(r0,y,base) i=0 while(kd*kd0>0 and i<500 and r0<r2): i=i+1 r0=r0+h kd=my_mpmath_kbes_diff_r(r0,y,base) #print "r0,kd=",r0,kd #print "kd*kd0=",kd*kd0 #print "-r0,y,kd=",r0,y,kd #t1=base.findroot(lambda x : base.besselk(base.mpc(0,x),base.mpf(y),verbose=True).real,r0) try: t1=base.findroot(lambda x : my_mpmath_kbes(x,y,base),r0) except ValueError: t1=base.findroot(lambda x : my_mpmath_kbes(x,y,mpmath.mp),r0) r0=r0+h if(r0>=r2 or t1>=r2): t1=r2 r0=r1 kd0=my_mpmath_kbes_diff_r(r0,y,base) while(t2<r1 and r0<r2): kd=my_mpmath_kbes_diff_r(r0,y,base) i=0 while(kd*kd0>0 and i<500 and r0<r2): i=i+1 r0=r0+h kd=my_mpmath_kbes_diff_r(r0,y,base) try: t2=base.findroot(lambda x : my_mpmath_kbes(x,2*y,base),r0) except ValueError: t2=base.findroot(lambda x : my_mpmath_kbes(x,2*y,mpmath.mp),r0) #t2=base.findroot(lambda x : base.besselk(base.mpc(0,x),base.mpf(2*y),verbose=True).real,r0) r0=r0+h if(r0>=r2 or t2>=r2): t2=r2 #print "zero(besselk,y1,y2)(",r1,r2,")=",t1,t2 t=min(min(max(r1,t1),max(r1,t2)),r2) return t