def zeta__exact(n): r""" Returns the exact value of the Riemann Zeta function The argument must be a critical value, namely either positive even or negative odd. See for example [Iwasawa]_, p13, Special value of `\zeta(2k)` EXAMPLES: Let us test the accuracy for negative special values:: sage: RR = RealField(100) sage: for i in range(1,10): ... print "zeta(" + str(1-2*i) + "): ", RR(zeta__exact(1-2*i)) - zeta(RR(1-2*i)) zeta(-1): 0.00000000000000000000000000000 zeta(-3): 0.00000000000000000000000000000 zeta(-5): 0.00000000000000000000000000000 zeta(-7): 0.00000000000000000000000000000 zeta(-9): 0.00000000000000000000000000000 zeta(-11): 0.00000000000000000000000000000 zeta(-13): 0.00000000000000000000000000000 zeta(-15): 0.00000000000000000000000000000 zeta(-17): 0.00000000000000000000000000000 Let us test the accuracy for positive special values:: sage: all(abs(RR(zeta__exact(2*i))-zeta(RR(2*i))) < 10**(-28) for i in range(1,10)) True TESTS:: sage: zeta__exact(5) Traceback (most recent call last): ... TypeError: n must be a critical value (i.e. even > 0 or odd < 0) REFERENCES: .. [Iwasawa] Iwasawa, *Lectures on p-adic L-functions* .. [IreRos] Ireland and Rosen, *A Classical Introduction to Modern Number Theory* .. [WashCyc] Washington, *Cyclotomic Fields* """ if n < 0: return bernoulli(1 - n) / (n - 1) elif n > 1: if (n % 2 == 0): return ZZ(-1)**(n / 2 + 1) * ZZ(2)**( n - 1) * pi**n * bernoulli(n) / factorial(n) else: raise TypeError( "n must be a critical value (i.e. even > 0 or odd < 0)") elif n == 1: return infinity elif n == 0: return -1 / 2
def zeta__exact(n): r""" Returns the exact value of the Riemann Zeta function The argument must be a critical value, namely either positive even or negative odd. See for example [Iwasawa]_, p13, Special value of `\zeta(2k)` EXAMPLES: Let us test the accuracy for negative special values:: sage: RR = RealField(100) sage: for i in range(1,10): ... print "zeta(" + str(1-2*i) + "): ", RR(zeta__exact(1-2*i)) - zeta(RR(1-2*i)) zeta(-1): 0.00000000000000000000000000000 zeta(-3): 0.00000000000000000000000000000 zeta(-5): 0.00000000000000000000000000000 zeta(-7): 0.00000000000000000000000000000 zeta(-9): 0.00000000000000000000000000000 zeta(-11): 0.00000000000000000000000000000 zeta(-13): 0.00000000000000000000000000000 zeta(-15): 0.00000000000000000000000000000 zeta(-17): 0.00000000000000000000000000000 Let us test the accuracy for positive special values:: sage: all(abs(RR(zeta__exact(2*i))-zeta(RR(2*i))) < 10**(-28) for i in range(1,10)) True TESTS:: sage: zeta__exact(5) Traceback (most recent call last): ... TypeError: n must be a critical value (i.e. even > 0 or odd < 0) REFERENCES: .. [Iwasawa] Iwasawa, *Lectures on p-adic L-functions* .. [IreRos] Ireland and Rosen, *A Classical Introduction to Modern Number Theory* .. [WashCyc] Washington, *Cyclotomic Fields* """ if n < 0: return bernoulli(1-n)/(n-1) elif n > 1: if (n % 2 == 0): return ZZ(-1)**(n/2 + 1) * ZZ(2)**(n-1) * pi**n * bernoulli(n) / factorial(n) else: raise TypeError("n must be a critical value (i.e. even > 0 or odd < 0)") elif n==1: return infinity elif n==0: return -1/2
def zeta__exact(n): r""" Returns the exact value of the Riemann Zeta function References: - Iwasawa's "Lectures on p-adic L-functions", p13, "Special value of `\zeta(2k)`" - Ireland and Rosen's "A Classical Introduction to Modern Number Theory" - Washington's "Cyclotomic Fields" EXAMPLES:: sage: ## Testing the accuracy of the negative special values sage: RR = RealField(100) sage: for i in range(1,10): ... print "zeta(" + str(1-2*i) + "): ", RR(zeta__exact(1-2*i)) - zeta(RR(1-2*i)) zeta(-1): 0.00000000000000000000000000000 zeta(-3): 0.00000000000000000000000000000 zeta(-5): 0.00000000000000000000000000000 zeta(-7): 0.00000000000000000000000000000 zeta(-9): 0.00000000000000000000000000000 zeta(-11): 0.00000000000000000000000000000 zeta(-13): 0.00000000000000000000000000000 zeta(-15): 0.00000000000000000000000000000 zeta(-17): 0.00000000000000000000000000000 sage: RR = RealField(100) sage: for i in range(1,10): ... print "zeta(" + str(1-2*i) + "): ", RR(zeta__exact(1-2*i)) - zeta(RR(1-2*i)) zeta(-1): 0.00000000000000000000000000000 zeta(-3): 0.00000000000000000000000000000 zeta(-5): 0.00000000000000000000000000000 zeta(-7): 0.00000000000000000000000000000 zeta(-9): 0.00000000000000000000000000000 zeta(-11): 0.00000000000000000000000000000 zeta(-13): 0.00000000000000000000000000000 zeta(-15): 0.00000000000000000000000000000 zeta(-17): 0.00000000000000000000000000000 """ if n < 0: k = 1 - n return -bernoulli(k) / k elif n > 1: if (n % 2 == 0): return ZZ(-1)**(n / 2 + 1) * ZZ(2)**( n - 1) * pi**n * bernoulli(n) / factorial(n) else: raise TypeError, "n must be a critical value! (I.e. even > 0 or odd < 0.)" elif n == 1: return infinity elif n == 0: return -1 / 2
def zeta__exact(n): r""" Returns the exact value of the Riemann Zeta function References: - Iwasawa's "Lectures on p-adic L-functions", p13, "Special value of `\zeta(2k)`" - Ireland and Rosen's "A Classical Introduction to Modern Number Theory" - Washington's "Cyclotomic Fields" EXAMPLES:: sage: ## Testing the accuracy of the negative special values sage: RR = RealField(100) sage: for i in range(1,10): ... print "zeta(" + str(1-2*i) + "): ", RR(zeta__exact(1-2*i)) - zeta(RR(1-2*i)) zeta(-1): 0.00000000000000000000000000000 zeta(-3): 0.00000000000000000000000000000 zeta(-5): 0.00000000000000000000000000000 zeta(-7): 0.00000000000000000000000000000 zeta(-9): 0.00000000000000000000000000000 zeta(-11): 0.00000000000000000000000000000 zeta(-13): 0.00000000000000000000000000000 zeta(-15): 0.00000000000000000000000000000 zeta(-17): 0.00000000000000000000000000000 sage: RR = RealField(100) sage: for i in range(1,10): ... print "zeta(" + str(1-2*i) + "): ", RR(zeta__exact(1-2*i)) - zeta(RR(1-2*i)) zeta(-1): 0.00000000000000000000000000000 zeta(-3): 0.00000000000000000000000000000 zeta(-5): 0.00000000000000000000000000000 zeta(-7): 0.00000000000000000000000000000 zeta(-9): 0.00000000000000000000000000000 zeta(-11): 0.00000000000000000000000000000 zeta(-13): 0.00000000000000000000000000000 zeta(-15): 0.00000000000000000000000000000 zeta(-17): 0.00000000000000000000000000000 """ if n<0: k = 1-n return -bernoulli(k)/k elif n>1: if (n % 2 == 0): return ZZ(-1)**(n/2 + 1) * ZZ(2)**(n-1) * pi**n * bernoulli(n) / factorial(n) else: raise TypeError, "n must be a critical value! (I.e. even > 0 or odd < 0.)" elif n==1: return infinity elif n==0: return -1/2
def gritsenko_lift_subspace(N, weight, precision): """ Return a list of data, which can be used by the Fourier expansion generator, for the space of Gritsenko lifts. INPUT: - `N` -- the level of the paramodular group - ``weight`` -- the weight of the space, that we consider - ``precision`` -- A precision class NOTE: The lifts returned have to have integral Fourier coefficients. """ jf = JacobiFormsD1NN(QQ, JacobiFormD1NN_Gamma( N, weight), (4 * N**2 + precision._enveloping_discriminant_bound() - 1) // (4 * N) + 1) return Sequence([ gritsenko_lift_fourier_expansion( g if i != 0 else (bernoulli(weight) / (2 * weight)).denominator() * g, precision, True) for (i, g) in enumerate(jf.gens()) ], universe=ParamodularFormD2FourierExpansionRing(ZZ, N), immutable=True, check=False)
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 gritsenko_lift_subspace(N, weight, precision) : """ Return a list of data, which can be used by the Fourier expansion generator, for the space of Gritsenko lifts. INPUT: - `N` -- the level of the paramodular group - ``weight`` -- the weight of the space, that we consider - ``precision`` -- A precision class NOTE: The lifts returned have to have integral Fourier coefficients. """ jf = JacobiFormsD1NN( QQ, JacobiFormD1NN_Gamma(N, weight), (4 * N**2 + precision._enveloping_discriminant_bound() - 1)//(4 * N) + 1) return Sequence( [ gritsenko_lift_fourier_expansion( g if i != 0 else (bernoulli(weight) / (2 * weight)).denominator() * g, precision, True ) for (i,g) in enumerate(jf.gens()) ], universe = ParamodularFormD2FourierExpansionRing(ZZ, N), immutable = True, check = False )
def __maass_lifts(self, k, precision, return_value) : r""" Return the Fourier expansion of all Maass forms of weight `k`. """ result = [] if k < 4 or k % 2 != 0 : return [] mf = ModularForms(1,k).echelon_basis() cf = ModularForms(1,k + 2).echelon_basis()[1:] integrality_factor = 2*k * bernoulli(k).denominator() for c in [(integrality_factor * mf[0],0)] \ + [ (f,0) for f in mf[1:] ] + [ (0,g) for g in cf ] : if return_value == "lifts" : result.append(SiegelModularFormG2MaassLift(c[0],c[1], precision, True)) else : result.append(c) return result
def __maass_lifts(self, k, precision, return_value): r""" Return the Fourier expansion of all Maass forms of weight `k`. """ result = [] if k < 4 or k % 2 != 0: return [] mf = ModularForms(1, k).echelon_basis() cf = ModularForms(1, k + 2).echelon_basis()[1:] integrality_factor = 2 * k * bernoulli(k).denominator() for c in [(integrality_factor * mf[0],0)] \ + [ (f,0) for f in mf[1:] ] + [ (0,g) for g in cf ] : if return_value == "lifts": result.append( SiegelModularFormG2MaassLift(c[0], c[1], precision, True)) else: result.append(c) return result
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 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
def eta_char(i,p,k,j,M): v=[0 for j in range(0,i)]+[binomial(j,i)*bernoulli(j-i) for j in range(i,M)] return dist_char(p,k,j,v)
def additive_lift(self, forms, weight, with_character = False, is_integral = False) : """ Borcherds additive lift to hermitian modular forms of degree `2`. This coinsides with Gritsenko's arithmetic lift after using the theta decomposition. INPUT: - ``forms`` -- A list of functions accepting an integer and returning a q-expansion. - ``weight`` -- A positive integer; The weight of the lift. - ``with_character`` -- A boolean (default: ``False``); Whether the lift has nontrivial character. - ``is_integral`` -- A boolean (default: ``False``); If ``True`` use rings of integral q-expansions over `\Z`. ALGORITHME: We use the explicite formulas in [D]. TESTS:: sage: from hermitianmodularforms.hermitianmodularformd2_fegenerators import HermitianModularFormD2AdditiveLift sage: HermitianModularFormD2AdditiveLift(4, [1,0,0], -3, 4).coefficients() {(2, 3, 2, 2): 720, (1, 1, 1, 1): 27, (1, 0, 0, 2): 270, (3, 3, 3, 3): 2943, (2, 1, 1, 3): 2592, (0, 0, 0, 2): 9, (2, 2, 2, 2): 675, (2, 3, 2, 3): 2160, (1, 1, 1, 2): 216, (3, 0, 0, 3): 8496, (2, 0, 0, 3): 2214, (1, 0, 0, 3): 720, (2, 1, 1, 2): 1080, (0, 0, 0, 1): 1, (3, 3, 2, 3): 4590, (3, 1, 1, 3): 4590, (1, 1, 1, 3): 459, (2, 0, 0, 2): 1512, (1, 0, 0, 1): 72, (0, 0, 0, 0): 1/240, (3, 4, 3, 3): 2808, (0, 0, 0, 3): 28, (3, 2, 2, 3): 4752, (2, 2, 2, 3): 1350} sage: HermitianModularFormD2AdditiveLift(4, [0,1,0], -3, 6).coefficients() {(2, 3, 2, 2): -19680, (1, 1, 1, 1): -45, (1, 0, 0, 2): -3690, (3, 3, 3, 3): -306225, (2, 1, 1, 3): -250560, (0, 0, 0, 2): 33, (2, 2, 2, 2): -13005, (2, 3, 2, 3): -153504, (1, 1, 1, 2): -1872, (3, 0, 0, 3): -1652640, (2, 0, 0, 3): -295290, (1, 0, 0, 3): -19680, (2, 1, 1, 2): -43920, (0, 0, 0, 1): 1, (3, 3, 2, 3): -948330, (3, 1, 1, 3): -1285290, (1, 1, 1, 3): -11565, (2, 0, 0, 2): -65520, (1, 0, 0, 1): -240, (0, 0, 0, 0): -1/504, (3, 4, 3, 3): -451152, (0, 0, 0, 3): 244, (3, 2, 2, 3): -839520, (2, 2, 2, 3): -108090} """ if with_character and self.__D % 4 != 0 : raise ValueError( "Characters are only possible for even discriminants." ) ## This will be needed if characters are implemented if with_character : if (Integer(self.__D / 4) % 4) in [-2,2] : alpha = (-self.__D / 4, 1/2) else : alpha = (-self.__D / 8, 1/2) #minv = 1/2 if with_character else 1 R = self.power_series_ring() q = R.gen(0) (vv_expfactor, vv_basis) = self._additive_lift_vector_valued_basis() vvform = dict((self._reduce_vector_valued_index(k), R(0)) for (k,_) in self._semireduced_vector_valued_indices_with_discriminant_offset(1)) for (f,b) in zip(forms, vv_basis) : ## We have to apply the scaling of exponents to the form f = R( f(self._qexp_precision()) ).add_bigoh(self._qexp_precision()) \ .subs({q : q**vv_expfactor}) if not f.is_zero() : for (k,e) in b.iteritems() : vvform[k] = vvform[k] + e * f ## the T = matrix(2,[*, t / 2, \bar t / 2, *] th fourier coefficients of the lift ## only depends on (- 4 * D * det(T), eps = gcd(T), \theta \cong t / eps) ## if m != 1 we consider 2*T maass_coeffs = dict() ## TODO: use divisor dictionaries if not with_character : ## The factor for the exponent of the basis of vector valued forms ## and the factor D in the formula for the discriminant are combined ## here vv_expfactor = vv_expfactor // (- self.__D) for eps in self._iterator_content() : for (theta, offset) in self._semireduced_vector_valued_indices_with_discriminant_offset(eps) : for disc in self._iterator_discriminant(eps, offset) : maass_coeffs[(disc, eps, theta)] = \ sum( a**(weight-1) * vvform[self._reduce_vector_valued_index((theta[0]//a, theta[1]//a))][vv_expfactor * disc // a**2] for a in divisors(eps)) else : ## The factor for the exponent of the basis of vector valued forms ## and the factor D in the formula for the discriminant are combined ## here vv_expfactor = (2 * vv_expfactor) // (- self.__D) if self.__D // 4 % 2 == 0 : for eps in self._iterator_content() : for (theta, offset) in self._semireduced_vector_valued_indices_with_discriminant_offset(eps) : for disc in self._iter_discriminant(eps, offset) : maass_coeffs[(disc, eps, theta)] = \ sum( a**(weight-1) * (1 if (theta[0] + theta[1] - 1) % 4 == 0 else -1) * vvform[self._reduce_vector_valued_index((theta[0]//a, theta[1]//a))][vv_expfactor * disc // a**2] for a in divisors(eps)) else : for eps in self._iterator_content() : for (theta, offset) in self._semireduced_vector_valued_indices_with_discriminant_offset(eps) : for disc in self._iter_discriminant(eps, offset) : maass_coeffs[(disc, eps, theta)] = \ sum( a**(weight-1) * (1 if (theta[1] - 1) % 4 == 0 else -1) * vvform[self._reduce_vector_valued_index((theta[0]//a, theta[1]//a))][vv_expfactor * disc // a**2] for a in divisors(eps) ) lift_coeffs = dict() ## TODO: Check whether this is correct. Add the character as an argument. for ((a,b1,b2,c), eps, disc) in self.precision().iter_positive_forms_for_character_with_content_and_discriminant(for_character = with_character) : (theta1, theta2) = self._reduce_vector_valued_index((b1/eps, b2/eps)) theta = (eps * theta1, eps * theta2) try: lift_coeffs[(a,b1,b2,c)] = maass_coeffs[(disc, eps, theta)] except : raise RuntimeError(str((a,b1,b2,c)) + " ; " + str((disc, eps, theta))) # Eisenstein component for (_,_,_,c) in self.precision().iter_semidefinite_forms_for_character(for_character = with_character) : if c != 0 : lift_coeffs[(0,0,0,c)] = vvform[(0,0)][0] * sigma(c, weight - 1) lift_coeffs[(0,0,0,0)] = - vvform[(0,0)][0] * bernoulli(weight) / Integer(2 * weight) if is_integral : lift_coeffs[(0,0,0,0)] = ZZ(lift_coeffs[(0,0,0,0)]) return lift_coeffs
def eta(i,p,k,M): """helper function in solving the difference equation -- see Lemma 4.4 of [PS]""" v=[0 for j in range(0,i)]+[binomial(j,i)*bernoulli(j-i) for j in range(i,M)] return dist(p,k,vector(v))
def siegel_product(self, u): """ Computes the infinite product of local densities of the quadratic form for the number `u`. EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1]) sage: Q.theta_series(11) 1 + 8*q + 24*q^2 + 32*q^3 + 24*q^4 + 48*q^5 + 96*q^6 + 64*q^7 + 24*q^8 + 104*q^9 + 144*q^10 + O(q^11) sage: Q.siegel_product(1) 8 sage: Q.siegel_product(2) ## This one is wrong -- expect 24, and the higher powers of 2 don't work... =( 24 sage: Q.siegel_product(3) 32 sage: Q.siegel_product(5) 48 sage: Q.siegel_product(6) 96 sage: Q.siegel_product(7) 64 sage: Q.siegel_product(9) 104 sage: Q.local_density(2,1) 1 sage: M = 4; len([v for v in mrange([M,M,M,M]) if Q(v) % M == 1]) / M^3 1 sage: M = 16; len([v for v in mrange([M,M,M,M]) if Q(v) % M == 1]) / M^3 # long time (41s on sage.math, 2011) 1 sage: Q.local_density(2,2) 3/2 sage: M = 4; len([v for v in mrange([M,M,M,M]) if Q(v) % M == 2]) / M^3 3/2 sage: M = 16; len([v for v in mrange([M,M,M,M]) if Q(v) % M == 2]) / M^3 # long time (41s on sage.math, 2011) 3/2 TESTS:: sage: [1] + [Q.siegel_product(ZZ(a)) for a in range(1,11)] == Q.theta_series(11).list() True """ ## Protect u (since it fails often if it's an just an int!) u = ZZ(u) n = self.dim() d = self.det() ## ??? Warning: This is a factor of 2^n larger than it should be! ## DIAGNOSTIC verbose("n = " + str(n)) verbose("d = " + str(d)) verbose("In siegel_product: d = ", d, "\n"); ## Product of "bad" places to omit S = 2 * d * u ## DIAGNOSTIC verbose("siegel_product Break 1. \n") verbose(" u = ", u, "\n") ## Make the odd generic factors if ((n % 2) == 1): m = (n-1) / 2 d1 = fundamental_discriminant(((-1)**m) * 2*d * u) ## Replaced d by 2d here to compensate for the determinant f = abs(d1) ## gaining an odd power of 2 by using the matrix of 2Q instead ## of the matrix of Q. ## --> Old d1 = CoreDiscriminant((mpz_class(-1)^m) * d * u); ## Make the ratio of factorials factor: [(2m)! / m!] * prod_{i=1}^m (2*i-1) factor1 = 1 for i in range(1, m+1): factor1 *= 2*i - 1 for i in range(m+1, 2*m + 1): factor1 *= i genericfactor = factor1 * ((u / f) ** m) \ * QQ(sqrt((2 ** n) * f) / (u * d)) \ * abs(QuadraticBernoulliNumber(m, d1) / bernoulli(2*m)) ## DIAGNOSTIC verbose("siegel_product Break 2. \n") ## Make the even generic factor if ((n % 2) == 0): m = n / 2 d1 = fundamental_discriminant(((-1)**m) * d) f = abs(d1) ## DIAGNOSTIC #cout << " mpz_class(-1)^m = " << (mpz_class(-1)^m) << " and d = " << d << endl; #cout << " f = " << f << " and d1 = " << d1 << endl; genericfactor = m / QQ(sqrt(f*d)) \ * ((u/2) ** (m-1)) * (f ** m) \ / abs(QuadraticBernoulliNumber(m, d1)) \ * (2 ** m) ## This last factor compensates for using the matrix of 2*Q ##return genericfactor ## Omit the generic factors in S and compute them separately omit = 1 include = 1 S_divisors = prime_divisors(S) ## DIAGNOSTIC #cout << "\n S is " << S << endl; #cout << " The Prime divisors of S are :"; #PrintV(S_divisors); for p in S_divisors: Q_normal = self.local_normal_form(p) ## DIAGNOSTIC verbose(" p = " + str(p) + " and its Kronecker symbol (d1/p) = (" + str(d1) + "/" + str(p) + ") is " + str(kronecker_symbol(d1, p)) + "\n") omit *= 1 / (1 - (kronecker_symbol(d1, p) / (p**m))) ## DIAGNOSTIC verbose(" omit = " + str(omit) + "\n") verbose(" Q_normal is \n" + str(Q_normal) + "\n") verbose(" Q_normal = \n" + str(Q_normal)) verbose(" p = " + str(p) + "\n") verbose(" u = " +str(u) + "\n") verbose(" include = " + str(include) + "\n") include *= Q_normal.local_density(p, u) ## DIAGNOSTIC #cout << " Including the p = " << p << " factor: " << local_density(Q_normal, p, u) << endl; ## DIAGNSOTIC verbose(" --- Exiting loop \n") #// **************** Important ******************* #// Additional fix (only included for n=4) to deal #// with the power of 2 introduced at the real place #// by working with Q instead of 2*Q. This needs to #// be done for all other n as well... #/* #if (n==4) # genericfactor = 4 * genericfactor; #*/ ## DIAGNSOTIC #cout << endl; #cout << " generic factor = " << genericfactor << endl; #cout << " omit = " << omit << endl; #cout << " include = " << include << endl; #cout << endl; ## DIAGNSOTIC #// cout << "siegel_product Break 3. " << endl; ## Return the final factor (and divide by 2 if n=2) if (n == 2): return (genericfactor * omit * include / 2) else: return (genericfactor * omit * include)
def siegel_product(self, u): """ Computes the infinite product of local densities of the quadratic form for the number `u`. EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1]) sage: Q.theta_series(11) 1 + 8*q + 24*q^2 + 32*q^3 + 24*q^4 + 48*q^5 + 96*q^6 + 64*q^7 + 24*q^8 + 104*q^9 + 144*q^10 + O(q^11) sage: Q.siegel_product(1) 8 sage: Q.siegel_product(2) ## This one is wrong -- expect 24, and the higher powers of 2 don't work... =( 24 sage: Q.siegel_product(3) 32 sage: Q.siegel_product(5) 48 sage: Q.siegel_product(6) 96 sage: Q.siegel_product(7) 64 sage: Q.siegel_product(9) 104 sage: Q.local_density(2,1) 1 sage: M = 4; len([v for v in mrange([M,M,M,M]) if Q(v) % M == 1]) / M^3 1 sage: M = 16; len([v for v in mrange([M,M,M,M]) if Q(v) % M == 1]) / M^3 # long time (41s on sage.math, 2011) 1 sage: Q.local_density(2,2) 3/2 sage: M = 4; len([v for v in mrange([M,M,M,M]) if Q(v) % M == 2]) / M^3 3/2 sage: M = 16; len([v for v in mrange([M,M,M,M]) if Q(v) % M == 2]) / M^3 # long time (41s on sage.math, 2011) 3/2 TESTS:: sage: [1] + [Q.siegel_product(ZZ(a)) for a in range(1,11)] == Q.theta_series(11).list() True """ ## Protect u (since it fails often if it's an just an int!) u = ZZ(u) n = self.dim() d = self.det( ) ## ??? Warning: This is a factor of 2^n larger than it should be! ## DIAGNOSTIC verbose("n = " + str(n)) verbose("d = " + str(d)) verbose("In siegel_product: d = ", d, "\n") ## Product of "bad" places to omit S = 2 * d * u ## DIAGNOSTIC verbose("siegel_product Break 1. \n") verbose(" u = ", u, "\n") ## Make the odd generic factors if ((n % 2) == 1): m = (n - 1) / 2 d1 = fundamental_discriminant( ((-1)**m) * 2 * d * u) ## Replaced d by 2d here to compensate for the determinant f = abs( d1) ## gaining an odd power of 2 by using the matrix of 2Q instead ## of the matrix of Q. ## --> Old d1 = CoreDiscriminant((mpz_class(-1)^m) * d * u); ## Make the ratio of factorials factor: [(2m)! / m!] * prod_{i=1}^m (2*i-1) factor1 = 1 for i in range(1, m + 1): factor1 *= 2 * i - 1 for i in range(m + 1, 2 * m + 1): factor1 *= i genericfactor = factor1 * ((u / f) ** m) \ * QQ(sqrt((2 ** n) * f) / (u * d)) \ * abs(QuadraticBernoulliNumber(m, d1) / bernoulli(2*m)) ## DIAGNOSTIC verbose("siegel_product Break 2. \n") ## Make the even generic factor if ((n % 2) == 0): m = n / 2 d1 = fundamental_discriminant(((-1)**m) * d) f = abs(d1) ## DIAGNOSTIC #cout << " mpz_class(-1)^m = " << (mpz_class(-1)^m) << " and d = " << d << endl; #cout << " f = " << f << " and d1 = " << d1 << endl; genericfactor = m / QQ(sqrt(f*d)) \ * ((u/2) ** (m-1)) * (f ** m) \ / abs(QuadraticBernoulliNumber(m, d1)) \ * (2 ** m) ## This last factor compensates for using the matrix of 2*Q ##return genericfactor ## Omit the generic factors in S and compute them separately omit = 1 include = 1 S_divisors = prime_divisors(S) ## DIAGNOSTIC #cout << "\n S is " << S << endl; #cout << " The Prime divisors of S are :"; #PrintV(S_divisors); for p in S_divisors: Q_normal = self.local_normal_form(p) ## DIAGNOSTIC verbose(" p = " + str(p) + " and its Kronecker symbol (d1/p) = (" + str(d1) + "/" + str(p) + ") is " + str(kronecker_symbol(d1, p)) + "\n") omit *= 1 / (1 - (kronecker_symbol(d1, p) / (p**m))) ## DIAGNOSTIC verbose(" omit = " + str(omit) + "\n") verbose(" Q_normal is \n" + str(Q_normal) + "\n") verbose(" Q_normal = \n" + str(Q_normal)) verbose(" p = " + str(p) + "\n") verbose(" u = " + str(u) + "\n") verbose(" include = " + str(include) + "\n") include *= Q_normal.local_density(p, u) ## DIAGNOSTIC #cout << " Including the p = " << p << " factor: " << local_density(Q_normal, p, u) << endl; ## DIAGNSOTIC verbose(" --- Exiting loop \n") #// **************** Important ******************* #// Additional fix (only included for n=4) to deal #// with the power of 2 introduced at the real place #// by working with Q instead of 2*Q. This needs to #// be done for all other n as well... #/* #if (n==4) # genericfactor = 4 * genericfactor; #*/ ## DIAGNSOTIC #cout << endl; #cout << " generic factor = " << genericfactor << endl; #cout << " omit = " << omit << endl; #cout << " include = " << include << endl; #cout << endl; ## DIAGNSOTIC #// cout << "siegel_product Break 3. " << endl; ## Return the final factor (and divide by 2 if n=2) if (n == 2): return (genericfactor * omit * include / 2) else: return (genericfactor * omit * include)
def solve_diff_eqn(self): r""" Solves the difference equation. See Theorem 4.5 and Lemma 4.4 of [PS]. INPUT: - ``self`` - an overconvergent distribution `\mu` of absolute precision `M` OUTPUT: - an overconvergent distribution `\nu` of absolute precision `M - \lfloor\log_p(M)\rfloor - 1` such that .. math:: \nu|\Delta = \mu,\text{ where }\Delta=\begin{pmatrix}1&1\\0&1 \end{pmatrix} - 1. EXAMPLES:: sage: D = OverconvergentDistributions(0,7,base=ZpCA(7,5)) sage: D10 = D.change_precision(10) sage: mu10 = D10((O(7^10), 4 + 6*7 + 5*7^3 + 2*7^4 + 5*7^5 + O(7^9), 5 + 7^3 + 5*7^4 + 6*7^5 + 7^6 + 6*7^7 + O(7^8), 2 + 7 + 6*7^2 + 6*7^4 + 7^5 + 7^6 + O(7^7), 3*7 + 4*7^2 + 4*7^3 + 3*7^4 + 3*7^5 + O(7^6), 5 + 3*7 + 2*7^2 + 7^3 + 3*7^4 + O(7^5), 1 + 7^2 + 7^3 + O(7^4), 6*7 + 6*7^2 + O(7^3), 2 + 3*7 + O(7^2), 1 + O(7))) sage: nu10 = mu10.solve_diff_eqn() sage: MS = OverconvergentModularSymbols(14, coefficients=D) sage: MR = MS.source() sage: Id = MR.gens()[0] sage: nu10 * MR.gammas[Id] - nu10 - mu10 7^8 * () sage: D = OverconvergentDistributions(0,7,base=Qp(7,5)) sage: D10 = D.change_precision(10) sage: mu10 = D10((O(7^10), 4 + 6*7 + 5*7^3 + 2*7^4 + 5*7^5 + O(7^9), 5 + 7^3 + 5*7^4 + 6*7^5 + 7^6 + 6*7^7 + O(7^8), 2 + 7 + 6*7^2 + 6*7^4 + 7^5 + 7^6 + O(7^7), 3*7 + 4*7^2 + 4*7^3 + 3*7^4 + 3*7^5 + O(7^6), 5 + 3*7 + 2*7^2 + 7^3 + 3*7^4 + O(7^5), 1 + 7^2 + 7^3 + O(7^4), 6*7 + 6*7^2 + O(7^3), 2 + 3*7 + O(7^2), 1 + O(7))) sage: nu10 = mu10.solve_diff_eqn() sage: MS = OverconvergentModularSymbols(14, coefficients=D); sage: MR = MS.source(); sage: Id = MR.gens()[0] sage: nu10 * MR.gammas[Id] - nu10 - mu10 7^8 * () sage: R = ZpCA(5, 5); D = OverconvergentDistributions(0,base=R); sage: nu = D((R(O(5^5)), R(5 + 3*5^2 + 4*5^3 + O(5^4)), R(5 + O(5^3)), R(2*5 + O(5^2), 2 + O(5)))); sage: nu.solve_diff_eqn() 5 * (1 + 3*5 + O(5^2), O(5)) Check input of relative precision 2:: sage: from sage.modular.pollack_stevens.coeffmod_OMS_element import CoeffMod_OMS_element sage: R = ZpCA(3, 9) sage: D = OverconvergentDistributions(0, base=R, prec_cap=4) sage: V = D.approx_module(2) sage: nu = CoeffMod_OMS_element(V([R(0, 9), R(2*3^2 + 2*3^4 + 2*3^7 + 3^8 + O(3^9))]), D, ordp=0, check=False) sage: mu = nu.solve_diff_eqn() sage: mu 3 * () """ #RH: see tests.sage for randomized verification that this function works correctly p = self.parent().prime() abs_prec = ZZ(self.precision_absolute()) if self.is_zero(): mu = self.parent()(0) mu.ordp = abs_prec - abs_prec.exact_log(p) - 1 return mu if self._unscaled_moment(0) != 0: raise ValueError("Distribution must have total measure 0 to be in image of difference operator.") M = ZZ(len(self._moments)) ## RP: This should never happen -- the distribution must be 0 at this point if M==1 if M == 1: return self.parent()(0) if M == 2: if p == 2: raise ValueError("Not enough accuracy to return anything") else: out_prec = abs_prec - abs_prec.exact_log(p) - 1 if self.ordp >= out_prec: mu = self.parent()(0) mu.ordp = out_prec return mu mu = self.parent()(self._unscaled_moment(1)) mu.ordp = self.ordp return mu R = self.parent().base_ring() K = R.fraction_field() bern = [bernoulli(i) for i in range(0,M-1,2)] minhalf = ~K(-2) #bernoulli(1) # bernoulli(1) = -1/2; the only nonzero odd bernoulli number v = [minhalf * self.moment(m) for m in range(M-1)] #(m choose m-1) * B_1 * mu[m]/m for m in range(1,M): scalar = K(self.moment(m)) * (~K(m)) for j in range(m-1,M-1,2): v[j] += binomial(j,m-1) * bern[(j-m+1)//2] * scalar ordp = min(a.valuation() for a in v) #Is this correct in ramified extensions of QQp? verbose("abs_prec: %s, ordp: %s"%(abs_prec, ordp), level=2) if ordp != 0: new_M = abs_prec - 1 - (abs_prec).exact_log(p) - ordp verbose("new_M: %s"%(new_M), level=2) V = self.parent().approx_module(new_M) v = V([R(v[i] >> ordp) for i in range(new_M)]) else: new_M = abs_prec - abs_prec.exact_log(p) - 1 verbose("new_M: %s"%(new_M), level=2) V = self.parent().approx_module(new_M) v = V([R(v[i]) for i in range(new_M)]) v[new_M-1] = v[new_M-1].add_bigoh(1) #To force normalize to deal with this properly. May not be necessary any more. mu = CoeffMod_OMS_element(v, self.parent(), ordp=ordp, check=False) verbose("mu.ordp: %s, mu._moments: %s"%(mu.ordp, mu._moments), level=2) return mu.normalize()
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
def additive_lift(self, forms, weight, with_character=False, is_integral=False): """ Borcherds additive lift to hermitian modular forms of degree `2`. This coinsides with Gritsenko's arithmetic lift after using the theta decomposition. INPUT: - ``forms`` -- A list of functions accepting an integer and returning a q-expansion. - ``weight`` -- A positive integer; The weight of the lift. - ``with_character`` -- A boolean (default: ``False``); Whether the lift has nontrivial character. - ``is_integral`` -- A boolean (default: ``False``); If ``True`` use rings of integral q-expansions over `\Z`. ALGORITHME: We use the explicite formulas in [D]. TESTS:: sage: from hermitianmodularforms.hermitianmodularformd2_fegenerators import HermitianModularFormD2AdditiveLift sage: HermitianModularFormD2AdditiveLift(4, [1,0,0], -3, 4).coefficients() {(2, 3, 2, 2): 720, (1, 1, 1, 1): 27, (1, 0, 0, 2): 270, (3, 3, 3, 3): 2943, (2, 1, 1, 3): 2592, (0, 0, 0, 2): 9, (2, 2, 2, 2): 675, (2, 3, 2, 3): 2160, (1, 1, 1, 2): 216, (3, 0, 0, 3): 8496, (2, 0, 0, 3): 2214, (1, 0, 0, 3): 720, (2, 1, 1, 2): 1080, (0, 0, 0, 1): 1, (3, 3, 2, 3): 4590, (3, 1, 1, 3): 4590, (1, 1, 1, 3): 459, (2, 0, 0, 2): 1512, (1, 0, 0, 1): 72, (0, 0, 0, 0): 1/240, (3, 4, 3, 3): 2808, (0, 0, 0, 3): 28, (3, 2, 2, 3): 4752, (2, 2, 2, 3): 1350} sage: HermitianModularFormD2AdditiveLift(4, [0,1,0], -3, 6).coefficients() {(2, 3, 2, 2): -19680, (1, 1, 1, 1): -45, (1, 0, 0, 2): -3690, (3, 3, 3, 3): -306225, (2, 1, 1, 3): -250560, (0, 0, 0, 2): 33, (2, 2, 2, 2): -13005, (2, 3, 2, 3): -153504, (1, 1, 1, 2): -1872, (3, 0, 0, 3): -1652640, (2, 0, 0, 3): -295290, (1, 0, 0, 3): -19680, (2, 1, 1, 2): -43920, (0, 0, 0, 1): 1, (3, 3, 2, 3): -948330, (3, 1, 1, 3): -1285290, (1, 1, 1, 3): -11565, (2, 0, 0, 2): -65520, (1, 0, 0, 1): -240, (0, 0, 0, 0): -1/504, (3, 4, 3, 3): -451152, (0, 0, 0, 3): 244, (3, 2, 2, 3): -839520, (2, 2, 2, 3): -108090} """ if with_character and self.__D % 4 != 0: raise ValueError( "Characters are only possible for even discriminants.") ## This will be needed if characters are implemented if with_character: if (Integer(self.__D / 4) % 4) in [-2, 2]: alpha = (-self.__D / 4, 1 / 2) else: alpha = (-self.__D / 8, 1 / 2) #minv = 1/2 if with_character else 1 R = self.power_series_ring() q = R.gen(0) (vv_expfactor, vv_basis) = self._additive_lift_vector_valued_basis() vvform = dict( (self._reduce_vector_valued_index(k), R(0)) for (k, _) in self. _semireduced_vector_valued_indices_with_discriminant_offset(1)) for (f, b) in zip(forms, vv_basis): ## We have to apply the scaling of exponents to the form f = R( f(self._qexp_precision()) ).add_bigoh(self._qexp_precision()) \ .subs({q : q**vv_expfactor}) if not f.is_zero(): for (k, e) in b.iteritems(): vvform[k] = vvform[k] + e * f ## the T = matrix(2,[*, t / 2, \bar t / 2, *] th fourier coefficients of the lift ## only depends on (- 4 * D * det(T), eps = gcd(T), \theta \cong t / eps) ## if m != 1 we consider 2*T maass_coeffs = dict() ## TODO: use divisor dictionaries if not with_character: ## The factor for the exponent of the basis of vector valued forms ## and the factor D in the formula for the discriminant are combined ## here vv_expfactor = vv_expfactor // (-self.__D) for eps in self._iterator_content(): for ( theta, offset ) in self._semireduced_vector_valued_indices_with_discriminant_offset( eps): for disc in self._iterator_discriminant(eps, offset): maass_coeffs[(disc, eps, theta)] = \ sum( a**(weight-1) * vvform[self._reduce_vector_valued_index((theta[0]//a, theta[1]//a))][vv_expfactor * disc // a**2] for a in divisors(eps)) else: ## The factor for the exponent of the basis of vector valued forms ## and the factor D in the formula for the discriminant are combined ## here vv_expfactor = (2 * vv_expfactor) // (-self.__D) if self.__D // 4 % 2 == 0: for eps in self._iterator_content(): for ( theta, offset ) in self._semireduced_vector_valued_indices_with_discriminant_offset( eps): for disc in self._iter_discriminant(eps, offset): maass_coeffs[(disc, eps, theta)] = \ sum( a**(weight-1) * (1 if (theta[0] + theta[1] - 1) % 4 == 0 else -1) * vvform[self._reduce_vector_valued_index((theta[0]//a, theta[1]//a))][vv_expfactor * disc // a**2] for a in divisors(eps)) else: for eps in self._iterator_content(): for ( theta, offset ) in self._semireduced_vector_valued_indices_with_discriminant_offset( eps): for disc in self._iter_discriminant(eps, offset): maass_coeffs[(disc, eps, theta)] = \ sum( a**(weight-1) * (1 if (theta[1] - 1) % 4 == 0 else -1) * vvform[self._reduce_vector_valued_index((theta[0]//a, theta[1]//a))][vv_expfactor * disc // a**2] for a in divisors(eps) ) lift_coeffs = dict() ## TODO: Check whether this is correct. Add the character as an argument. for ((a, b1, b2, c), eps, disc) in self.precision( ).iter_positive_forms_for_character_with_content_and_discriminant( for_character=with_character): (theta1, theta2) = self._reduce_vector_valued_index( (b1 / eps, b2 / eps)) theta = (eps * theta1, eps * theta2) try: lift_coeffs[(a, b1, b2, c)] = maass_coeffs[(disc, eps, theta)] except: raise RuntimeError( str((a, b1, b2, c)) + " ; " + str((disc, eps, theta))) # Eisenstein component for (_, _, _, c) in self.precision().iter_semidefinite_forms_for_character( for_character=with_character): if c != 0: lift_coeffs[(0, 0, 0, c)] = vvform[(0, 0)][0] * sigma(c, weight - 1) lift_coeffs[( 0, 0, 0, 0)] = -vvform[(0, 0)][0] * bernoulli(weight) / Integer(2 * weight) if is_integral: lift_coeffs[(0, 0, 0, 0)] = ZZ(lift_coeffs[(0, 0, 0, 0)]) return lift_coeffs