class HarmonicCocycles(AmbientHeckeModule): Element=HarmonicCocycleElement r""" This object represents a space of Gamma invariant harmonic cocycles valued in a cofficient module. INPUT: - ``X`` - A BTQuotient object - ``k`` - integer - The weight. - ``prec`` - integer (Default: None). If specified, the precision for the coefficient module - ``basis_matrix`` - integer (Default: None) - ``base_field`` - (Default: None) EXAMPLES: :: AUTHORS: - Cameron Franc (2012-02-20) - Marc Masdeu """ def __init__(self,X,k,prec=None,basis_matrix=None,base_field=None): self._k=k self._X=X self._E=self._X.get_edge_list() self._V=self._X.get_vertex_list() if prec is None: self._prec=None if base_field is 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!" else: pol=X.get_splitting_field().defining_polynomial().factor()[0][0] self._R=base_field.extension(pol,pol.variable_name()).absolute_field(name='r') self._U=OCVn(self._k-2,self._R) else: self._prec=prec if base_field is None: self._R=Qp(self._X._p,prec=prec) else: self._R=base_field self._U=OCVn(self._k-2,self._R,self._k-1) self.__rank = self._X.dimension_harmonic_cocycles(self._k) if basis_matrix is not None: self.__matrix=basis_matrix self.__matrix.set_immutable() assert 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._populate_coercion_lists_() def base_extend(self,base_ring): r""" This function extends the base ring of the coefficient module. INPUT: - ``base_ring`` - a ring that has a coerce map from the current base ring EXAMPLES: :: """ if not base_ring.has_coerce_map_from(self.base_ring()): raise ValueError, "No coercion defined" else: return self.change_ring(base_ring) def change_ring(self, new_base_ring): r""" This function changes the base ring of the coefficient module. INPUT: - ``new_base_ring'' - a ring that has a coerce map from the current base ring EXAMPLES: :: """ if not new_base_ring.has_coerce_map_from(self.base_ring()): raise ValueError, "No coercion defined" else: return self.__class__(self._X,self._k,prec=self._prec,basis_matrix=self.basis_matrix().change_ring(base_ring),base_field=new_base_ring) def rank(self): r""" The rank (dimension) of ``self``. EXAMPLES: :: """ return self.__rank def submodule(self,v,check=False): r""" Return the submodule of ``self`` spanned by ``v``. EXAMPLES: """ return HarmonicCocyclesSubmodule(self,v,dual=None,check=check) def is_simple(self): r""" Whether ``self`` is irreducible. EXAMPLES: :: """ return self.rank()==1 def _repr_(self): r""" This returns the representation of self as a string. """ return 'Space of harmonic cocycles of weight %s on %s'%(self._k,self._X) def _latex_(self): r""" A LaTeX representation of ``self``. EXAMPLES: :: """ s='\\text{Space of harmonic cocycles of weight }'+latex(self._k)+'\\text{ on }'+latex(self._X) return s def _an_element_(self): r""" """ return self.basis()[0] def _coerce_map_from_(self, S): r""" Can coerce from other HarmonicCocycles or from pAutomorphicForms """ if isinstance(S,(HarmonicCocycles,pAutomorphicForms)): if(S._k!=self._k): return False if(S._X!=self._X): return False return True return False def __cmp__(self,other): r""" """ 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): r""" """ #Code how to coherce x into the space #Admissible values of x? if isinstance(x,HarmonicCocycleElement): return HarmonicCocycleElement(self,x) elif isinstance(x,pAutomorphicForm): tmp=[self._U.element_class(_parent._U,x._F[ii]).l_act_by(self._E[ii].rep) for ii in range(self._nE)] return HarmonicCocycleElement(self,tmp,from_values=True) else: return HarmonicCocycleElement(self,x) def free_module(self): r""" This function returns the underlying free module EXAMPLES: :: """ try: return self.__free_module except AttributeError: pass V = self.base_ring()**self.dimension() self.__free_module = V return V def character(self): r""" Only implemented the trivial character so far. EXAMPLES: """ return lambda x:x def embed_quaternion(self,g): r""" Embed the quaternion element ``g`` into the matrix algebra. EXAMPLES: :: """ return self._X.embed_quaternion(g,exact = self._R.is_exact(), prec = self._prec) def basis_matrix(self): r""" Returns a basis of ``self`` in matrix form. If the coefficient module `M` is of finite rank then the space of Gamma invariant `M` valued harmonic cocycles can be represented as a subspace of the finite rank space of all functions from the finitely many edges in the corresponding BTQuotient into `M`. This function computes this representation of the space of cocycles. OUTPUT: A basis matrix describing the cocycles in the spaced of all `M` valued Gamma invariant functions on the tree. EXAMPLES: :: sage: X = BTQuotient(3,19) sage: C = HarmonicCocycles(X,4,prec = 5) sage: B = C.basis() Traceback (most recent call last): ... RuntimeError: The computed dimension does not agree with the expectation. Consider increasing precision! We try increasing the precision: :: sage: C = HarmonicCocycles(X,4,prec = 20) sage: B = C.basis() sage: len(B) == X.dimension_harmonic_cocycles(4) True AUTHORS: - Cameron Franc (2012-02-20) - Marc Masdeu (2012-02-20) """ try: return self.__matrix except AttributeError: pass nV=len(self._V) nE=len(self._E) stab_conds=[] S=self._X.get_edge_stabs() p=self._X._p d=self._k-1 for e in self._E: try: g=filter(lambda g:g[2],S[e.label])[0] C=self._U.l_matrix_representation(self.embed_quaternion(g[0])) C-=self._U.l_matrix_representation(Matrix(QQ,2,2,p**g[1])) stab_conds.append([e.label,C]) except IndexError: pass n_stab_conds=len(stab_conds) 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(self.embed_quaternion(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(self.embed_quaternion(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]) x1=self._M.right_kernel().matrix() if x1.nrows() != self.rank(): raise RuntimeError, 'The computed dimension does not agree with the expectation. Consider increasing precision!' K=[c for c in x1.rows()] if not self._R.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 __apply_atkin_lehner(self,q,f): r""" This function applies an Atkin-Lehner involution to a harmonic cocycle INPUT: - ``q`` - an integer dividing the full level p*Nminus*Nplus - ``f`` - a harmonic cocycle OUTPUT: The harmonic cocycle obtained by hitting f with the Atkin-Lehner at q EXAMPLES: :: """ R=self._R Data=self._X._get_atkin_lehner_data(q) 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=self.embed_quaternion(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.embed_quaternion)) return HarmonicCocycleElement(self,tmp,from_values=True) def __apply_hecke_operator(self,l,f): r""" This function applies a Hecke operator to a harmonic cocycle. INPUT: - ``l`` - an integer - ``f`` - a harmonic cocycle OUTPUT: A harmonic cocycle which is the result of applying the lth Hecke operator to f EXAMPLES: :: """ 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))) p=self._X._p alphamat=self.embed_quaternion(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=self.embed_quaternion(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(self.embed_quaternion)) return HarmonicCocycleElement(self,[factor*x for x in tmp],from_values=True) def _compute_atkin_lehner_matrix(self,d): r""" When the underlying coefficient module is finite, this function computes the matrix of an Atkin-Lehner involution in the basis provided by the function basis_matrix INPUT: - ``d`` - an integer dividing p*Nminus*Nplus OUTPUT: The matrix of the AL-involution at d in the basis given by self.basis_matrix EXAMPLES: :: """ res=self.__compute_operator_matrix(lambda f:self.__apply_atkin_lehner(d,f)) return res def _compute_hecke_matrix_prime(self,l): r""" When the underlying coefficient module is finite, this function computes the matrix of a (prime) Hecke operator in the basis provided by the function basis_matrix INPUT: - ``l`` - an integer prime OUTPUT: The matrix of T_l acting on the cocycles in the basis given by self.basis_matrix EXAMPLES: :: """ res=self.__compute_operator_matrix(lambda f:self.__apply_hecke_operator(l,f)) return res def __compute_operator_matrix(self,T): r""" Compute the matrix of the operator ``T``. EXAMPLES: :: """ 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) ])) try: res=(A.solve_right(B)).transpose() res.set_immutable() return res except ValueError: print A print B raise ValueError
def apply_Up(self,c,group = None,scale = 1,parallelize = False,times = 0,progress_bar = False,method = 'naive', repslocal = None, Up_reps = None, steps = 1): r""" Apply the Up Hecke operator operator to ``c``. """ assert steps >= 1 V = self.coefficient_module() R = V.base_ring() gammas = self.group().gens() if Up_reps is None: Up_reps = self.S_arithgroup().get_Up_reps() if repslocal is None: try: prec = V.base_ring().precision_cap() except AttributeError: prec = None repslocal = self.get_Up_reps_local(prec) i = 0 if method == 'naive': assert times == 0 G = self.S_arithgroup() Gn = G.large_group() if self.use_shapiro(): if self.coefficient_module().trivial_action(): def calculate_Up_contribution(lst, c, i, j): return sum([c.evaluate_and_identity(tt) for sk, tt in lst]) else: def calculate_Up_contribution(lst, c, i, j): return sum([sk * c.evaluate_and_identity(tt) for sk, tt in lst]) input_vec = [] for j, gamma in enumerate(gammas): for i, xi in enumerate(G.coset_reps()): delta = Gn(G.get_coset_ti(set_immutable(xi * gamma.quaternion_rep))[0]) input_vec.append(([(sk, Gn.get_hecke_ti(g,delta)) for sk, g in zip(repslocal, Up_reps)], c, i, j)) vals = [[V.coefficient_module()(0,normalize=False) for xi in G.coset_reps()] for gamma in gammas] if parallelize: for inp, outp in parallel(calculate_Up_contribution)(input_vec): vals[inp[0][-1]][inp[0][-2]] += outp else: for inp in input_vec: outp = calculate_Up_contribution(*inp) vals[inp[-1]][inp[-2]] += outp ans = self([V(o) for o in vals]) else: Gpn = G.small_group() if self.trivial_action(): def calculate_Up_contribution(lst,c,num_gamma): return sum([c.evaluate(tt) for sk, tt in lst], V(0,normalize=False)) else: def calculate_Up_contribution(lst,c,num_gamma,pb_fraction=None): i = 0 ans = V(0, normalize=False) for sk, tt in lst: ans += sk * c.evaluate(tt) update_progress(i * pb_fraction, 'Up action') return ans input_vec = [] for j,gamma in enumerate(gammas): input_vec.append(([(sk, Gpn.get_hecke_ti(g,gamma)) for sk, g in zip(repslocal, Up_reps)], c, j)) vals = [V(0,normalize=False) for gamma in gammas] if parallelize: for inp,outp in parallel(calculate_Up_contribution)(input_vec): vals[inp[0][-1]] += outp else: for counter, inp in enumerate(input_vec): outp = calculate_Up_contribution(*inp, pb_fraction=float(1)/float(len(repslocal) * len(input_vec))) vals[inp[-1]] += outp ans = self(vals) if scale != 1: ans = scale * ans else: assert method == 'bigmatrix' verbose('Getting Up matrices...') try: N = len(V(0)._moments.list()) except AttributeError: N = 1 nreps = len(Up_reps) ngens = len(self.group().gens()) NN = ngens * N A = Matrix(ZZ,NN,NN,0) total_counter = ngens**2 counter = 0 iS = 0 for i,gi in enumerate(self.group().gens()): ti = [tuple(self.group().get_hecke_ti(sk,gi).word_rep) for sk in Up_reps] jS = 0 for ans in find_newans(self,repslocal,ti): A.set_block(iS,jS,ans) jS += N if progress_bar: counter +=1 update_progress(float(counter)/float(total_counter),'Up matrix') iS += N verbose('Computing 2^(%s)-th power of a %s x %s matrix'%(times,A.nrows(),A.ncols())) for i in range(times): A = A**2 if N != 0: A = A.apply_map(lambda x: x % self._pN) update_progress(float(i+1)/float(times),'Exponentiating matrix') verbose('Done computing 2^(%s)-th power'%times) if times > 0: scale_factor = ZZ(scale).powermod(2**times,self._pN) else: scale_factor = ZZ(scale) bvec = Matrix(R,NN,1,[o for b in c._val for o in b._moments.list()]) if scale_factor != 1: bvec = scale_factor * bvec valmat = A * bvec appr_module = V.approx_module(N) ans = self([V(appr_module(valmat.submatrix(row=i,nrows = N).list())) for i in xrange(0,valmat.nrows(),N)]) if steps <= 1: return ans else: return self.apply_Up(ans, group = group,scale = scale,parallelize = parallelize,times = times,progress_bar = progress_bar,method = method, repslocal = repslocal, steps = steps -1)