def gritsenko_lift(self, f, k, is_integral=False): """ INPUT: - `f` -- the Fourier expansion of a Jacobi form as a dictionary """ N = self.level() frN = 4 * N p1list = self.precision()._p1list() divisor_dict = self._divisor_dict() ##TODO: Precalculate disc_coeffs or at least use a recursive definition disc_coeffs = dict() coeffs = dict() f_index = lambda d, b: ((d + b**2) // frN, b) for ( ((a, b, c), l), eps, disc ) in self.__precision._iter_positive_forms_with_content_and_discriminant( ): (_, bp, _) = apply_GL_to_form(p1list[l], (a, b, c)) try: coeffs[((a, b, c), l)] = disc_coeffs[(disc, bp, eps)] except KeyError: disc_coeffs[(disc, bp, eps)] = \ sum(t**(k-1) * f[ f_index(disc//t**2, (bp // t) % (2 * N)) ] for t in divisor_dict[eps]) if disc_coeffs[(disc, bp, eps)] != 0: coeffs[((a, b, c), l)] = disc_coeffs[(disc, bp, eps)] for ((a, b, c), l) in self.__precision.iter_indefinite_forms(): if l == 0: coeffs[((a, b, c), l)] = ( sigma(c, k - 1) * f[(0, 0)] if c != 0 else (Integer(-bernoulli(k) / Integer(2 * k) * f[(0, 0)]) if is_integral else -bernoulli(k) / Integer(2 * k) * f[(0, 0)])) else: coeffs[((a, b, c), l)] = ( sigma(c // self.level(), k - 1) * f[(0, 0)] if c != 0 else (Integer(-bernoulli(k) / Integer(2 * k) * f[(0, 0)]) if is_integral else -bernoulli(k) / Integer(2 * k) * f[(0, 0)])) return coeffs
def gritsenko_lift(self, f, k, is_integral = False) : """ INPUT: - `f` -- the Fourier expansion of a Jacobi form as a dictionary """ N = self.level() frN = 4 * N p1list = self.precision()._p1list() divisor_dict = self._divisor_dict() ##TODO: Precalculate disc_coeffs or at least use a recursive definition disc_coeffs = dict() coeffs = dict() f_index = lambda d,b : ((d + b**2)//frN, b) for (((a,b,c),l), eps, disc) in self.__precision._iter_positive_forms_with_content_and_discriminant() : (_,bp,_) = apply_GL_to_form(p1list[l], (a,b,c)) try : coeffs[((a,b,c),l)] = disc_coeffs[(disc, bp, eps)] except KeyError : disc_coeffs[(disc, bp, eps)] = \ sum( t**(k-1) * f[ f_index(disc//t**2, (bp // t) % (2 * N)) ] for t in divisor_dict[eps] ) if disc_coeffs[(disc, bp, eps)] != 0 : coeffs[((a,b,c),l)] = disc_coeffs[(disc, bp, eps)] for ((a,b,c), l) in self.__precision.iter_indefinite_forms() : if l == 0 : coeffs[((a,b,c),l)] = ( sigma(c, k-1) * f[(0,0)] if c != 0 else ( Integer(-bernoulli(k) / Integer(2 * k) * f[(0,0)]) if is_integral else -bernoulli(k) / Integer(2 * k) * f[(0,0)] ) ) else : coeffs[((a,b,c),l)] = ( sigma(c//self.level(), k-1) * f[(0,0)] if c != 0 else ( Integer(-bernoulli(k) / Integer(2 * k) * f[(0,0)]) if is_integral else -bernoulli(k) / Integer(2 * k) * f[(0,0)] ) ) return coeffs
def _compute_hecke_matrix(self, n): """ Compute the matrix of the Hecke operator T_n acting on self. NOTE: If self is a level 1 space, the much faster Victor Miller basis is used for this computation. EXAMPLES:: sage: M = ModularForms(11, 2) sage: M._compute_hecke_matrix(6) [ 2 0] [ 0 12] Check that :trac:`22780` is fixed:: sage: M = ModularForms(1, 12) sage: M._compute_hecke_matrix(2) [ -24 0] [ 0 2049] sage: ModularForms(1, 2).hecke_matrix(2) [] TESTS: The following Hecke matrix is 43x43 with very large integer entries. We test it indirectly by computing the product and the sum of its eigenvalues, and reducing these two integers modulo all the primes less than 100:: sage: M = ModularForms(1, 512) sage: t = M._compute_hecke_matrix(5) # long time (2s) sage: t[-1, -1] == 1 + 5^511 # long time (0s, depends on above) True sage: f = t.charpoly() # long time (4s) sage: [f[0]%p for p in prime_range(100)] # long time (0s, depends on above) [0, 0, 0, 0, 1, 9, 2, 7, 0, 0, 0, 0, 1, 12, 9, 16, 37, 0, 21, 11, 70, 22, 0, 58, 76] sage: [f[42]%p for p in prime_range(100)] # long time (0s, depends on above) [0, 0, 4, 0, 10, 4, 4, 8, 12, 1, 23, 13, 10, 27, 20, 13, 16, 59, 53, 41, 11, 13, 12, 6, 82] """ if self.level() == 1: k = self.weight() d = self.dimension() if d == 0: return matrix(self.base_ring(), 0, 0, []) from sage.modular.all import victor_miller_basis, hecke_operator_on_basis vmb = victor_miller_basis(k, prec=d * n + 1)[1:] Tcusp = hecke_operator_on_basis(vmb, n, k) return Tcusp.block_sum(matrix(self.base_ring(), 1, 1, [sigma(n, k - 1)])) else: return space.ModularFormsSpace._compute_hecke_matrix(self, n)
def _compute_hecke_matrix(self, n): """ Compute the matrix of the Hecke operator T_n acting on self. NOTE: If self is a level 1 space, the much faster Victor Miller basis is used for this computation. EXAMPLES:: sage: M = ModularForms(11, 2) sage: M._compute_hecke_matrix(6) [ 2 0] [ 0 12] Check that :trac:`22780` is fixed:: sage: M = ModularForms(1, 12) sage: M._compute_hecke_matrix(2) [ -24 0] [ 0 2049] sage: ModularForms(1, 2).hecke_matrix(2) [] TESTS: The following Hecke matrix is 43x43 with very large integer entries. We test it indirectly by computing the product and the sum of its eigenvalues, and reducing these two integers modulo all the primes less than 100:: sage: M = ModularForms(1, 512) sage: t = M._compute_hecke_matrix(5) # long time (2s) sage: t[-1, -1] == 1 + 5^511 # long time (0s, depends on above) True sage: f = t.charpoly() # long time (4s) sage: [f[0]%p for p in prime_range(100)] # long time (0s, depends on above) [0, 0, 0, 0, 1, 9, 2, 7, 0, 0, 0, 0, 1, 12, 9, 16, 37, 0, 21, 11, 70, 22, 0, 58, 76] sage: [f[42]%p for p in prime_range(100)] # long time (0s, depends on above) [0, 0, 4, 0, 10, 4, 4, 8, 12, 1, 23, 13, 10, 27, 20, 13, 16, 59, 53, 41, 11, 13, 12, 6, 82] """ if self.level() == 1: k = self.weight() d = self.dimension() if d == 0: return matrix(self.base_ring(), 0, 0, []) from sage.modular.all import victor_miller_basis, hecke_operator_on_basis vmb = victor_miller_basis(k, prec=d*n+1)[1:] Tcusp = hecke_operator_on_basis(vmb, n, k) return Tcusp.block_sum(matrix(self.base_ring(), 1, 1, [sigma(n, k-1)])) else: return space.ModularFormsSpace._compute_hecke_matrix(self, n)
def coeff(m): m = ZZ(m) if m < 0: return ZZ(0) elif m == 0: return ZZ(1) factor = -2 * k / QQ(bernoulli(k)) / lamk sum1 = sigma(m, k - 1) if M.divides(m): sum2 = (lamk - 1) * sigma(ZZ(m / M), k - 1) else: sum2 = ZZ(0) if (M == 1): sum3 = ZZ(0) else: if (m == 1): N = ZZ(1) else: N = ZZ(m / M**ZZ(m.valuation(M))) sum3 = -sigma(ZZ(N), k - 1) * ZZ(m / N)**(k - 1) / (lamk + 1) return factor * (sum1 + sum2 + sum3) * dval**m
def coeff(m): m = ZZ(m) if m < 0: return ZZ(0) elif m == 0: return ZZ(1) factor = -2*k / QQ(bernoulli(k)) / lamk sum1 = sigma(m, k-1) if M.divides(m): sum2 = (lamk-1) * sigma(ZZ(m/M), k-1) else: sum2 = ZZ(0) if (M == 1): sum3 = ZZ(0) else: if (m == 1): N = ZZ(1) else: N = ZZ(m / M**ZZ(m.valuation(M))) sum3 = -sigma(ZZ(N), k-1) * ZZ(m/N)**(k-1) / (lamk + 1) return factor * (sum1 + sum2 + sum3) * dval**m
def maass_form(self, f, g, k=None, is_integral=False): r""" Return the Siegel modular form `I(f,g)` (Notation as in [Sko]). INPUT: - `f` -- modular form of level `1` - `g` -- cusp form of level `1` and weight = ``weight of f + 2`` - ``is_integral`` -- ``True`` if the result is guaranteed to have integer coefficients """ ## we introduce an abbreviations if is_integral: PS = self.integral_power_series_ring() else: PS = self.power_series_ring() fismodular = isinstance(f, ModularFormElement) gismodular = isinstance(g, ModularFormElement) ## We only check the arguments if f and g are ModularFormElements. ## Otherwise we trust in the user if fismodular and gismodular: assert(f.weight() + 2 == g.weight() | (f==0) | (g==0)), \ "incorrect weights!" assert ( g.q_expansion(1) == 0), "second argument is not a cusp form" qexp_prec = self._get_maass_form_qexp_prec() if qexp_prec is None: # there are no forms below prec return dict() if fismodular: k = f.weight() if f == f.parent()(0): f = PS(0, qexp_prec) else: f = PS(f.qexp(qexp_prec), qexp_prec) elif f == 0: f = PS(0, qexp_prec) else: f = PS(f(qexp_prec), qexp_prec) if gismodular: k = g.weight() - 2 if g == g.parent()(0): g = PS(0, qexp_prec) else: g = PS(g.qexp(qexp_prec), qexp_prec) elif g == 0: g = PS(0, qexp_prec) else: g = PS(g(qexp_prec), qexp_prec) if k is None: raise ValueError("if neither f nor g are not ModularFormElements " + \ "you must pass k") fderiv = f.derivative().shift(1) f *= Integer(k / 2) gfderiv = g - fderiv ## Form A and B - the Jacobi forms used in [Sko]'s I map. ## This is not necessary if we multiply Ifg0 and Ifg1 by etapow # (A0,A1,B0,B1) = (a0*etapow, a1*etapow, b0*etapow, b1*etapow) ## Calculate the image of the pair of modular forms (f,g) under ## [Sko]'s isomorphism I: M_{k} \oplus S_{k+2} -> J_{k,1}. # Multiplication of big polynomials may take > 60 GB, so wie have # to do it in small parts; This is only implemented for integral # coefficients. r""" Create the Jacobi form I(f,g) as in [Sko]. It suffices to construct for all Jacobi forms phi only the part sum_{r=0,1;n} c_phi(r^2-4n) q^n zeta^r. When, in this code part, we speak of Jacobi form we only mean this part. We need to compute Ifg = \sum_{r=0,1; n} c(r^2-4n) q^n zeta^r up to 4n-r^2 <= Dtop, i.e. n < precision """ ## Create the Jacobi forms A=a*etapow and B=b*etapow in stages. ## Recall a = sum_{s != r mod 2} s^2*(-1)^r*q^((s^2+r^2-1)/4)*zeta^r ## b = sum_{s != r mod 2} (-1)^r*q^((s^2+r^2-1)/4)*zeta^r ## r, s run over ZZ but with opposite parities. ## For r=0, we need s odd, (s^2-1)/4 < precision, with s=2t+1 hence t^2+t < precision. ## For r=1, we need s even, s^2/4 < precision, with s=2t hence t^2 < precision. ## we use a slightly overestimated ab_prec ab_prec = isqrt(qexp_prec + 1) a1dict = dict() a0dict = dict() b1dict = dict() b0dict = dict() for t in range(1, ab_prec + 1): tmp = t**2 a1dict[tmp] = -8 * tmp b1dict[tmp] = -2 tmp += t a0dict[tmp] = 8 * tmp + 2 b0dict[tmp] = 2 b1dict[0] = -1 a0dict[0] = 2 b0dict[0] = 2 a1 = PS(a1dict) b1 = PS(b1dict) a0 = PS(a0dict) b0 = PS(b0dict) ## Finally: I(f,g) is given by the formula below: ## We multiply by etapow explicitly and save two multiplications # Ifg0 = k/2*f*A0 - fderiv*B0 + g*B0 + O(q^precision) # Ifg1 = k/2*f*A1 - fderiv*B1 + g*B1 + O(q^precision) Ifg0 = (self._eta_power() * (f * a0 + gfderiv * b0)).list() Ifg1 = (self._eta_power() * (f * a1 + gfderiv * b1)).list() if len(Ifg0) < qexp_prec: Ifg0 += [0] * (qexp_prec - len(Ifg0)) if len(Ifg1) < qexp_prec: Ifg1 += [0] * (qexp_prec - len(Ifg1)) ## For applying the Maass' lifting to genus 2 modular forms. ## we put the coefficients of Ifg into a dictionary Chi ## so that we can access the coefficient corresponding to ## discriminant D by going Chi[D]. Cphi = dict([(0, 0)]) for i in range(qexp_prec): Cphi[-4 * i] = Ifg0[i] Cphi[1 - 4 * i] = Ifg1[i] del Ifg0[:], Ifg1[:] """ Create the Maas lift F:= VI(f,g) as in [Sko]. """ ## The constant term is given by -Cphi[0]*B_{2k}/(4*k) ## (note in [Sko] this coeff has typos). ## For nonconstant terms, ## The Siegel coefficient of q^n * zeta^r * qdash^m is given ## by the formula \sum_{ a | gcd(n,r,m) } Cphi[D/a^2] where ## D = r^2-4*n*m is the discriminant. ## Hence in either case the coefficient ## is fully determined by the pair (D,gcd(n,r,m)). ## Put (D,t) -> \sum_{ a | t } Cphi[D/a^2] ## in a dictionary (hash table) maassc. maass_coeffs = dict() divisor_dict = self._divisor_dict() ## First calculate maass coefficients corresponding to strictly positive definite matrices: for disc in self._negative_fundamental_discriminants(): for s in range( 1, isqrt((-self.__precision.discriminant()) // disc) + 1): ## add (disc*s^2,t) as a hash key, for each t that divides s for t in divisor_dict[s]: maass_coeffs[(disc * s**2,t)] = \ sum(a**(k-1) * Cphi[disc * s**2 / a**2] for a in divisor_dict[t]) ## Compute the coefficients of the Siegel form $F$: siegel_coeffs = dict() for (n, r, m), g in self.__precision.iter_positive_forms_with_content(): siegel_coeffs[(n, r, m)] = maass_coeffs[(r**2 - 4 * m * n, g)] ## Secondly, deal with the singular part. ## Include the coeff corresponding to (0,0,0): ## maass_coeffs = {(0,0): -bernoulli(k)/(2*k)*Cphi[0]} siegel_coeffs[(0, 0, 0)] = -bernoulli(k) / (2 * k) * Cphi[0] if is_integral: siegel_coeffs[(0, 0, 0)] = Integer(siegel_coeffs[(0, 0, 0)]) ## Calculate the other discriminant-zero maass coefficients. ## Since sigma is quite cheap it is faster to estimate the bound and ## save the time for repeated calculation for i in range(1, self.__precision._indefinite_content_bound()): ## maass_coeffs[(0,i)] = sigma(i, k-1) * Cphi[0] siegel_coeffs[(0, 0, i)] = sigma(i, k - 1) * Cphi[0] return siegel_coeffs
def maass_form( self, f, g, k = None, is_integral = False) : r""" Return the Siegel modular form `I(f,g)` (Notation as in [Sko]). INPUT: - `f` -- modular form of level `1` - `g` -- cusp form of level `1` and weight = ``weight of f + 2`` - ``is_integral`` -- ``True`` if the result is garanteed to have integer coefficients """ ## we introduce an abbreviations if is_integral : PS = self.integral_power_series_ring() else : PS = self.power_series_ring() fismodular = isinstance(f, ModularFormElement) gismodular = isinstance(g, ModularFormElement) ## We only check the arguments if f and g are ModularFormElements. ## Otherwise we trust in the user if fismodular and gismodular : assert( f.weight() + 2 == g.weight() | (f==0) | (g==0)), \ "incorrect weights!" assert( g.q_expansion(1) == 0), "second argument is not a cusp form" qexp_prec = self._get_maass_form_qexp_prec() if qexp_prec is None : # there are no forms below prec return dict() if fismodular : k = f.weight() if f == f.parent()(0) : f = PS(0, qexp_prec) else : f = PS(f.qexp(qexp_prec), qexp_prec) elif f == 0 : f = PS(0, qexp_prec) else : f = PS(f(qexp_prec), qexp_prec) if gismodular : k = g.weight() - 2 if g == g.parent()(0) : g = PS(0, qexp_prec) else : g = PS(g.qexp(qexp_prec), qexp_prec) elif g == 0 : g = PS(0, qexp_prec) else : g = PS(g(qexp_prec), qexp_prec) if k is None : raise ValueError, "if neither f nor g are not ModularFormElements " + \ "you must pass k" fderiv = f.derivative().shift(1) f *= Integer(k/2) gfderiv = g - fderiv ## Form A and B - the Jacobi forms used in [Sko]'s I map. ## This is not necessary if we multiply Ifg0 and Ifg1 by etapow # (A0,A1,B0,B1) = (a0*etapow, a1*etapow, b0*etapow, b1*etapow) ## Calculate the image of the pair of modular forms (f,g) under ## [Sko]'s isomorphism I : M_{k} \oplus S_{k+2} -> J_{k,1}. # Multiplication of big polynomials may take > 60 GB, so wie have # to do it in small parts; This is only implemented for integral # coefficients. """ Create the Jacobi form I(f,g) as in [Sko]. It suffices to construct for all Jacobi forms phi only the part sum_{r=0,1;n} c_phi(r^2-4n) q^n zeta^r. When, in this code part, we speak of Jacobi form we only mean this part. We need to compute Ifg = \sum_{r=0,1; n} c(r^2-4n) q^n zeta^r up to 4n-r^2 <= Dtop, i.e. n < precision """ ## Create the Jacobi forms A=a*etapow and B=b*etapow in stages. ## Recall a = sum_{s != r mod 2} s^2*(-1)^r*q^((s^2+r^2-1)/4)*zeta^r ## b = sum_{s != r mod 2} (-1)^r*q^((s^2+r^2-1)/4)*zeta^r ## r, s run over ZZ but with opposite parities. ## For r=0, we need s odd, (s^2-1)/4 < precision, with s=2t+1 hence t^2+t < precision. ## For r=1, we need s even, s^2/4 < precision, with s=2t hence t^2 < precision. ## we use a slightly overestimated ab_prec ab_prec = isqrt(qexp_prec + 1) a1dict = dict(); a0dict = dict() b1dict = dict(); b0dict = dict() for t in xrange(1, ab_prec + 1) : tmp = t**2 a1dict[tmp] = -8*tmp b1dict[tmp] = -2 tmp += t a0dict[tmp] = 8*tmp + 2 b0dict[tmp] = 2 b1dict[0] = -1 a0dict[0] = 2; b0dict[0] = 2 a1 = PS(a1dict); b1 = PS(b1dict) a0 = PS(a0dict); b0 = PS(b0dict) ## Finally: I(f,g) is given by the formula below: ## We multiply by etapow explecitely and save two multiplications # Ifg0 = k/2*f*A0 - fderiv*B0 + g*B0 + O(q^precision) # Ifg1 = k/2*f*A1 - fderiv*B1 + g*B1 + O(q^precision) Ifg0 = (self._eta_power() * (f*a0 + gfderiv*b0)).list() Ifg1 = (self._eta_power() * (f*a1 + gfderiv*b1)).list() if len(Ifg0) < qexp_prec : Ifg0 += [0]*(qexp_prec - len(Ifg0)) if len(Ifg1) < qexp_prec : Ifg1 += [0]*(qexp_prec - len(Ifg1)) ## For applying the Maass' lifting to genus 2 modular forms. ## we put the coefficients of Ifg into a dictionary Chi ## so that we can access the coefficient corresponding to ## discriminant D by going Chi[D]. Cphi = dict([(0,0)]) for i in xrange(qexp_prec) : Cphi[-4*i] = Ifg0[i] Cphi[1-4*i] = Ifg1[i] del Ifg0[:], Ifg1[:] """ Create the Maas lift F := VI(f,g) as in [Sko]. """ ## The constant term is given by -Cphi[0]*B_{2k}/(4*k) ## (note in [Sko] this coeff has typos). ## For nonconstant terms, ## The Siegel coefficient of q^n * zeta^r * qdash^m is given ## by the formula \sum_{ a | gcd(n,r,m) } Cphi[D/a^2] where ## D = r^2-4*n*m is the discriminant. ## Hence in either case the coefficient ## is fully deterimined by the pair (D,gcd(n,r,m)). ## Put (D,t) -> \sum_{ a | t } Cphi[D/a^2] ## in a dictionary (hash table) maassc. maass_coeffs = dict() divisor_dict = self._divisor_dict() ## First calculate maass coefficients corresponding to strictly positive definite matrices: for disc in self._negative_fundamental_discriminants() : for s in xrange(1, isqrt((-self.__precision.discriminant()) // disc) + 1) : ## add (disc*s^2,t) as a hash key, for each t that divides s for t in divisor_dict[s] : maass_coeffs[(disc * s**2,t)] = \ sum( a**(k-1) * Cphi[disc * s**2 / a**2] for a in divisor_dict[t] ) ## Compute the coefficients of the Siegel form $F$: siegel_coeffs = dict() for (n,r,m), g in self.__precision.iter_positive_forms_with_content() : siegel_coeffs[(n,r,m)] = maass_coeffs[(r**2 - 4*m*n, g)] ## Secondly, deal with the singular part. ## Include the coeff corresponding to (0,0,0): ## maass_coeffs = {(0,0): -bernoulli(k)/(2*k)*Cphi[0]} siegel_coeffs[(0,0,0)] = -bernoulli(k)/(2*k)*Cphi[0] if is_integral : siegel_coeffs[(0,0,0)] = Integer(siegel_coeffs[(0,0,0)]) ## Calculate the other discriminant-zero maass coefficients. ## Since sigma is quite cheap it is faster to estimate the bound and ## save the time for repeated calculation for i in xrange(1, self.__precision._indefinite_content_bound()) : ## maass_coeffs[(0,i)] = sigma(i, k-1) * Cphi[0] siegel_coeffs[(0,0,i)] = sigma(i, k-1) * Cphi[0] return siegel_coeffs