class BianchiDistributionElement(ModuleElement): r""" This class represents elements in an overconvergent Bianchi coefficient module. INPUT: - ``parent`` - An overconvergent coefficient module. - ``val`` - The value that it needs to store (default: 0). It can be another BianchiDistributionElement, in which case the values are copied. It can also be a column vector (or something coercible to a column vector) which represents the values of the element applied to the polynomials `1`, `x`, `y`, `x^2`, `xy`, `y^2`, ... ,`y^n`. - ``check`` - boolean (default: True). If set to False, no checks are done and ``val`` is assumed to be the a column vector. """ def __init__(self, parent, val=0, check=True, normalize=False): """ Initialisation function. Takes as input a vector val, which should have length equal to the dimension of the space, which is the nth triangle number, where n is the depth. This corresponds to the ordered basis for distributions (namely, the dual basis to the basis 1, x, y, x^2, xy, ...). Input: - parent: BianchiDistributions object of depth n - val : vector of length n^2 encoding the moments of the distribution """ ## Parents/precision ModuleElement.__init__(self, parent) self._parent = parent self._depth = self._parent._depth self._dimension = self._parent._dimension ## check multiple possibilities for input if not check: self._moments = val else: ## is input already a distribution? if isinstance(val, self.__class__): ## if depth is the same, then keep this if val._parent._depth == parent._depth: self._moments = val._moments else: ## depths are different, take the minimum d = min([val.nrows(), parent.dimension()]) self._moments = val._moments.submatrix(0, 0, nrows=d) elif isinstance(val, int) or isinstance(val, Integer): ## Initialise distribution to be trivial, i.e. constant moment = input and rest = 0 self._moments = MatrixSpace(self._parent._R, self._dimension, 1)(0) self._moments[0, 0] = val ## input is a vector storing the moments elif isinstance(val, Vector_integer_dense) or isinstance( val, FreeModuleElement_generic_dense): self._moments = MatrixSpace(self._parent._R, self._dimension, 1)(0) for i, o in enumerate(val.list()): self._moments[i, 0] = o ## input is a list storing the moments elif isinstance(val, list): self._moments = MatrixSpace(self._parent._R, self._dimension, 1)(0) for i, o in enumerate(val): self._moments[i, 0] = o else: try: self._moments = Matrix(self._parent._R, self._depth, 1, val) except (TypeError, ValueError): self._moments = self._parent._R(val) * MatrixSpace( self._parent._R, self._dimension, 1)(1) def __hash__(self): return hash(self._moments) def moment(self, ij): """ Returns the (i,j)th moment mu(x^iy^j). EXAMPLES:: sage: from darmonpoints.ocbianchi import BianchiDistributions sage: D = BianchiDistributions(11,4) sage: mu = D.basis_vector((2,3)) sage: mu.moment((2,3)) 1 + O(11^3) sage: mu.moment((2,1)) O(11^3) """ if isinstance(ij, tuple): idx = self._parent.index(ij) else: idx = ij return self._parent._Rmod(self._moments[idx, 0]) def moments(self): """ Returns the moments (as a column vector). EXAMPLES:: sage: from darmonpoints.ocbianchi import BianchiDistributions sage: D = BianchiDistributions(11,2) sage: mu = D.basis_vector((1,1)) sage: mu.moments() [0] [0] [0] [1] """ return self._moments def __getitem__(self, ij): r""" """ return self.moment(ij) def __setitem__(self, ij, val): r""" Sets the value of ``self`` on the polynomial `x^iy^j` to ``val``. INPUT: - ``r`` - an integer. The power of `x`. - ``val`` - a value. """ if isinstance(ij, tuple): idx = self._parent.index(ij[0], ij[1]) else: idx = ij self._moments[idx, 0] = val def element(self): r""" The element ``self`` represents. """ tmp = self.matrix_rep() return [tmp[ii, 0] for ii in range(tmp.nrows())] def list(self): r""" The element ``self`` represents. """ return self.element() def matrix_rep(self, B=None): r""" Returns a matrix representation of ``self``. """ #Express the element in terms of the basis B if B is None: B = self._parent.basis() A = Matrix(self._parent._R, self._parent.dimension(), self._parent.dimension(), [[b._moments[ii, 0] for b in B] for ii in range(self._dimension)]) tmp = A.solve_right(self._moments) return tmp def _add_(self, y): r""" Add two elements. EXAMPLES:: sage: from darmonpoints.ocbianchi import BianchiDistributions sage: D = BianchiDistributions(11,4) sage: mu1 = D.basis_vector((2,3)) sage: mu2 = D.basis_vector((1,2)) sage: mu1 + mu2 X*Y^2 + X^2*Y^3 """ val = self._moments + y._moments return self.__class__(self._parent, val, check=False) def _sub_(self, y): r""" Subtract two elements. """ val = self._moments - y._moments return self.__class__(self._parent, val, check=False) def _neg_(self): return self.__class__(self._parent, -self._moments, check=False) def _rmul_(self, a): #assume that a is a scalar return self.__class__(self._parent, a * self._moments, check=False) def _repr_(self): r""" Returns the representation of self as a string. The monomial X^iY^j is the dual basis vector to the monomial x^iy^j in the ring of analytic functions. EXAMPLES:: sage: from darmonpoints.ocbianchi import BianchiDistributions sage: D = BianchiDistributions(11,4) sage: mu = D.basis_vector((2,1)) + D.basis_vector((1,0)) sage: mu X + X^2*Y sage: D.basis_vector((1,1)) + 2*D.basis_vector((2,1)) X*Y + 2*X^2*Y """ R = self.parent()._repr_R s = str( sum([ ZZ(self._moments[idx, 0]) * self._parent.monomial_from_index(idx, R) for idx in range(self._moments.nrows()) ])) return s def __cmp__(self, other): return cmp(self._moments, other._moments) def __nonzero__(self): return self._moments != 0 def evaluate_at_poly(self, P, R=None, depth=None): r""" Evaluate ``self`` at a polynomial. The polynomial can be defined over ZZ or Zp. By default, this function picks the ring R to be a ring that coerces to both the base ring of the polynomial and the Bianchi distribution. You can specify depth, but this currently does nothing at all. EXAMPLES:: sage: from darmonpoints.ocbianchi import BianchiDistributions sage: D = BianchiDistributions(11,4) sage: x,y = D.analytic_vars() sage: mu = D.basis_vector((2,1)) + D.basis_vector((1,0)) sage: mu(x^2*y) 1 sage: mu(y) 0 sage: mu(x^2*y + x) 2 """ p = self._parent._p ## Currently empty functionality if depth is None: depth = self._depth # Define the ring R, if not specified if R is None: try: R = pushout(P.parent().base_ring(), self.parent().base_ring()) except AttributeError: R = self.parent().base_ring() ## Attempt to coerce the input into a form we can evaluate P = self.parent().analytic_functions()(P) ## For each monomial x^iy^j in the polynomial, multip] ly the coefficient of X^iY^j (in mu) by the ## coefficient of x^iy^j (in f) and take the sum. This is our final value ## --> P.coefficients is a dictionary which has monomials as keys; we generate monomials using exponents. ## --> self._moments takes as input an index and spits out the cofficient. So generate the index from the exponent. coefficient_list = [] for polx in P.padded_list(self._depth): coefficient_list.extend(polx.padded_list(self._depth)) return ZZ((Matrix(coefficient_list) * self.moments())[0, 0]) def __call__(self, P): r""" Call function; evaluates the distribution at an analytic function. """ return self.evaluate_at_poly(P) def reduce_mod(self, N=None): r""" Reduces all the moments modulo N. """ if N is None: N = self.parent()._pN self._moments = self._moments.apply_map(lambda x: x % N) return self def max_filtration_step(self): r""" Computes the maximal step of the filtration in which mu lives. """ ## Take min of v_p(mu(x^i*y^j)) + i + j p = self._parent._p min_modified_moment = min( (o[0].valuation(p) + sum(tuple(self.parent().ij_from_pos(n))) for n, o in enumerate(self._moments))) ## Last filtration step in which this appears is step r, where r is the min such that this min/2 - r >= 0 ## ..... IN THE SUPERSINGULAR CASE! ## In the ordinary case, the min r is sufficient *without* dividing by 2!!! return min_modified_moment ## QQ(min_modified_moment/2).floor() def normalize(self, r=None): r""" Adjust the moments to the precision given by the filtration step where self belongs. """ if r is None: r = self.max_filtration_step() V = self._moments p = self._parent._p for n in xrange(self._moments.nrows()): k = r - sum(tuple(self.parent().ij_from_pos(n))) self._moments[n, 0] = self._moments[n, 0] % p**k return self def valuation(self): r""" Returns the same as max_filtration_step, defining an element to have valuation r if Filr^(r,r) is the maximal filtration step in which it lives. """ return self.max_filtration_step()
class OCVnElement(ModuleElement): r""" This class represents elements in an overconvergent coefficient module. INPUT: - ``parent`` - An overconvergent coefficient module. - ``val`` - The value that it needs to store (default: 0). It can be another OCVnElement, in which case the values are copied. It can also be a column vector (or something coercible to a column vector) which represents the values of the element applied to the polynomials `1`, `x`, `x^2`, ... ,`x^n`. - ``check`` - boolean (default: True). If set to False, no checks are done and ``val`` is assumed to be the a column vector. AUTHORS: - Cameron Franc (2012-02-20) - Marc Masdeu (2012-02-20) """ def __init__(self, parent, val=0, check=True, normalize=False): ModuleElement.__init__(self, parent) self._parent = parent self._depth = self._parent._depth if not check: self._val = val else: if isinstance(val, self.__class__): if val._parent._depth == parent._depth: self._val = val._val else: d = min([val._parent._depth, parent._depth]) self._val = val._val.submatrix(0, 0, nrows=d) elif isinstance(val, Vector_integer_dense) or isinstance( val, FreeModuleElement_generic_dense): self._val = MatrixSpace(self._parent._R, self._depth, 1)(0) for i, o in enumerate(val.list()): self._val[i, 0] = o else: try: self._val = Matrix(self._parent._R, self._depth, 1, val) except (TypeError, ValueError): self._val = self._parent._R(val) * MatrixSpace( self._parent._R, self._depth, 1)(1) self._moments = self._val def lift(self, p=None, M=None): return self def moment(self, i): return self._parent._Rmod(self._moments[i, 0]) def __getitem__(self, r): r""" Returns the value of ``self`` on the polynomial `x^r`. INPUT: - ``r`` - an integer. The power of `x`. EXAMPLES: """ return self._val[r, 0] def __setitem__(self, r, val): r""" Sets the value of ``self`` on the polynomial `x^r` to ``val``. INPUT: - ``r`` - an integer. The power of `x`. - ``val`` - a value. EXAMPLES: """ self._val[r, 0] = val def element(self): r""" The element ``self`` represents. """ tmp = self.matrix_rep() return [tmp[ii, 0] for ii in range(tmp.nrows())] def list(self): r""" The element ``self`` represents. """ return self.element() def matrix_rep(self, B=None): r""" Returns a matrix representation of ``self``. """ #Express the element in terms of the basis B if B is None: B = self._parent.basis() 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)]) tmp = A.solve_right(self._val) return tmp def _add_(self, y): r""" Add two elements. """ val = self._val + y._val return self.__class__(self._parent, val, check=False) def _sub_(self, y): r""" Subtract two elements. """ val = self._val - y._val return self.__class__(self._parent, val, check=False) def _div_(self, right): r""" Finds the scalar such that self = a * right (assuming that it exists) """ if self.is_zero(): return 0 else: a = None for u, v in zip(self._moments, right._moments): if u != 0: a = u / v break assert a is not None assert (self - a * right).is_zero(), 'Not a scalar multiple of right' return a def r_act_by(self, x): r""" Act on the right by a matrix. """ #assert(x.nrows()==2 and x.ncols()==2) #An element of GL2 return self._acted_upon_(x.adjugate(), False) def _acted_upon_(self, x, right_action): # Act by x on the left try: x = x.matrix() except AttributeError: pass if right_action: return self._acted_upon_(x.adjugate(), False) else: R = self._parent._R A = self._parent._get_powers(x) tmp = A * self._val return self.__class__(self._parent, tmp, check=False) def _neg_(self): return self.__class__(self._parent, -self._val, check=False) def _rmul_(self, a): #assume that a is a scalar return self.__class__(self._parent, self._parent._Rmod(a) * self._val, check=False) def _repr_(self): r""" Returns the representation of self as a string. """ 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 def __cmp__(self, other): return cmp(self._val, other._val) def __nonzero__(self): return self._val != 0 def evaluate_at_poly(self, P, R=None, depth=None): r""" Evaluate ``self`` at a polynomial """ p = self._parent._p if R is None: try: R = pushout(P.parent().base_ring(), self.parent().base_ring()) except AttributeError: R = self.parent().base_ring() if depth is None and hasattr(P, 'degree'): try: depth = min([P.degree() + 1, self._depth]) return sum(R(self._val[ii, 0]) * P[ii] for ii in xrange(depth)) except NotImplementedError: pass return R(self._val[0, 0]) * P else: return sum( R(self._val[ii, 0]) * P[ii] for ii in xrange(self._depth)) def valuation(self, l=None): r""" The `l`-adic valuation of ``self``. INPUT: a prime `l`. The default (None) uses the prime of the parent. """ if not self._parent.base_ring().is_exact(): if (not l is None and l != self._parent._Rmod.prime()): raise ValueError( "This function can only be called with the base prime") l = self._parent._Rmod.prime() return min( [self._val[ii, 0].valuation(l) for ii in range(self._depth)]) else: return min( [self._val[ii, 0].valuation(l) for ii in range(self._depth)]) def valuation_list(self, l=None): r""" The `l`-adic valuation of ``self``, as a list. INPUT: a prime `l`. The default (None) uses the prime of the parent. """ if not self._parent.base_ring().is_exact(): if (not l is None and l != self._parent._Rmod.prime()): raise ValueError( "This function can only be called with the base prime") l = self._parent._Rmod.prime() return [self._val[ii, 0].valuation(l) for ii in range(self._depth)] else: return [self._val[ii, 0].valuation(l) for ii in range(self._depth)] def reduce_mod(self, N=None): if N is None: N = self.parent()._pN self._val = self._val.apply_map(lambda x: x % N) return self
class OCVnElement(ModuleElement): r""" This class represents elements in an overconvergent coefficient module. INPUT: - ``parent`` - An overconvergent coefficient module. - ``val`` - The value that it needs to store (default: 0). It can be another OCVnElement, in which case the values are copied. It can also be a column vector (or something coercible to a column vector) which represents the values of the element applied to the polynomials `1`, `x`, `x^2`, ... ,`x^n`. - ``check`` - boolean (default: True). If set to False, no checks are done and ``val`` is assumed to be the a column vector. AUTHORS: - Cameron Franc (2012-02-20) - Marc Masdeu (2012-02-20) """ def __init__(self,parent,val = 0,check = True,normalize=False): ModuleElement.__init__(self,parent) self._parent = parent self._depth = self._parent._depth if not check: self._val = val else: if isinstance(val,self.__class__): if val._parent._depth == parent._depth: self._val = val._val else: d = min([val._parent._depth,parent._depth]) self._val = val._val.submatrix(0,0,nrows = d) elif isinstance(val, Vector_integer_dense) or isinstance(val, FreeModuleElement_generic_dense): self._val = MatrixSpace(self._parent._R, self._depth, 1)(0) for i,o in enumerate(val.list()): self._val[i,0] = o else: try: self._val = Matrix(self._parent._R,self._depth,1,val) except (TypeError, ValueError): self._val= self._parent._R(val) * MatrixSpace(self._parent._R,self._depth,1)(1) self._moments = self._val def lift(self, p=None,M=None): return self def moment(self, i): return self._parent._Rmod(self._moments[i,0]) def __getitem__(self,r): r""" Returns the value of ``self`` on the polynomial `x^r`. INPUT: - ``r`` - an integer. The power of `x`. EXAMPLES: """ return self._val[r,0] def __setitem__(self,r, val): r""" Sets the value of ``self`` on the polynomial `x^r` to ``val``. INPUT: - ``r`` - an integer. The power of `x`. - ``val`` - a value. EXAMPLES: """ self._val[r,0] = val def element(self): r""" The element ``self`` represents. """ tmp = self.matrix_rep() return [tmp[ii,0] for ii in range(tmp.nrows())] def list(self): r""" The element ``self`` represents. """ return self.element() def matrix_rep(self,B=None): r""" Returns a matrix representation of ``self``. """ #Express the element in terms of the basis B if B is None: B = self._parent.basis() 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)]) tmp=A.solve_right(self._val) return tmp def _add_(self,y): r""" Add two elements. """ val=self._val+y._val return self.__class__(self._parent,val, check = False) def _sub_(self,y): r""" Subtract two elements. """ val=self._val-y._val return self.__class__(self._parent,val, check = False) def r_act_by(self,x): r""" Act on the right by a matrix. """ #assert(x.nrows()==2 and x.ncols()==2) #An element of GL2 return self._acted_upon_(x.adjoint(), False) def _acted_upon_(self,x, right_action): # Act by x on the left try: x = x.matrix() except AttributeError: pass if right_action: return self._acted_upon_(x.adjoint(), False) else: R = self._parent._R A = self._parent._get_powers(x) tmp = A * self._val return self.__class__(self._parent, tmp, check = False) def _neg_(self): return self.__class__(self._parent,-self._val, check = False) def _rmul_(self,a): #assume that a is a scalar return self.__class__(self._parent,a*self._val, check = False) def _repr_(self): r""" Returns the representation of self as a string. """ 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 def __cmp__(self,other): return cmp(self._val,other._val) def __nonzero__(self): return self._val!=0 def evaluate_at_poly(self,P,R = None,depth = None): r""" Evaluate ``self`` at a polynomial """ p = self._parent._p if R is None: try: R = pushout(P.parent().base_ring(),self.parent().base_ring()) except AttributeError: R = self.parent().base_ring() if depth is None and hasattr(P,'degree'): try: depth = min([P.degree()+1,self._depth]) return sum(R(self._val[ii,0])*P[ii] for ii in xrange(depth)) except NotImplementedError: pass return R(self._val[0,0])*P else: return sum(R(self._val[ii,0])*P[ii] for ii in xrange(depth)) def valuation(self,l=None): r""" The `l`-adic valuation of ``self``. INPUT: a prime `l`. The default (None) uses the prime of the parent. """ if not self._parent.base_ring().is_exact(): if(not l is None and l!=self._parent._Rmod.prime()): raise ValueError, "This function can only be called with the base prime" l = self._parent._Rmod.prime() return min([self._val[ii,0].valuation(l) for ii in range(self._depth)]) else: return min([self._val[ii,0].valuation(l) for ii in range(self._depth)]) def reduce_mod(self, N = None): if N is None: N = self.parent()._pN self._val = self._val.apply_map(lambda x: x % N) return self
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)