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 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 _log_StirlingNegativePowers_(var, precision): r""" Helper function to calculate the logarithm of Stirling's approximation formula from the negative powers of ``var`` on, i.e., it skips the summands `n \log n - n + (\log n)/2 + \log(2\pi)/2`. INPUT: - ``var`` -- a string for the variable name. - ``precision`` -- an integer specifying the number of exact summands. If this is negative, then the result is `0`. OUTPUT: An asymptotic expansion. TESTS:: sage: asymptotic_expansions._log_StirlingNegativePowers_( ....: 'm', precision=-1) 0 sage: asymptotic_expansions._log_StirlingNegativePowers_( ....: 'm', precision=0) O(m^(-1)) sage: asymptotic_expansions._log_StirlingNegativePowers_( ....: 'm', precision=3) 1/12*m^(-1) - 1/360*m^(-3) + 1/1260*m^(-5) + O(m^(-7)) sage: _.parent() Asymptotic Ring <m^ZZ> over Rational Field """ from asymptotic_ring import AsymptoticRing from sage.rings.rational_field import QQ A = AsymptoticRing(growth_group='{n}^ZZ'.format(n=var), coefficient_ring=QQ) if precision < 0: return A.zero() n = A.gen() from sage.arith.all import bernoulli from sage.arith.srange import srange result = sum((bernoulli(k) / k / (k-1) / n**(k-1) for k in srange(2, 2*precision + 2, 2)), A.zero()) return result + (1 / n**(2*precision + 1)).O()
def _log_StirlingNegativePowers_(var, precision): r""" Helper function to calculate the logarithm of Stirling's approximation formula from the negative powers of ``var`` on, i.e., it skips the summands `n \log n - n + (\log n)/2 + \log(2\pi)/2`. INPUT: - ``var`` -- a string for the variable name. - ``precision`` -- an integer specifying the number of exact summands. If this is negative, then the result is `0`. OUTPUT: An asymptotic expansion. TESTS:: sage: asymptotic_expansions._log_StirlingNegativePowers_( ....: 'm', precision=-1) 0 sage: asymptotic_expansions._log_StirlingNegativePowers_( ....: 'm', precision=0) O(m^(-1)) sage: asymptotic_expansions._log_StirlingNegativePowers_( ....: 'm', precision=3) 1/12*m^(-1) - 1/360*m^(-3) + 1/1260*m^(-5) + O(m^(-7)) sage: _.parent() Asymptotic Ring <m^ZZ> over Rational Field """ from asymptotic_ring import AsymptoticRing from sage.rings.rational_field import QQ A = AsymptoticRing(growth_group='{n}^ZZ'.format(n=var), coefficient_ring=QQ) if precision < 0: return A.zero() n = A.gen() from sage.arith.all import bernoulli from sage.arith.srange import srange result = sum((bernoulli(k) / k / (k - 1) / n**(k - 1) for k in srange(2, 2 * precision + 2, 2)), A.zero()) return result + (1 / n**(2 * precision + 1)).O()
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 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 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 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 [Iwa1972]_, 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({}): {}".format(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(4) 1/90*pi^4 sage: zeta__exact(-3) 1/120 sage: zeta__exact(0) -1/2 sage: zeta__exact(5) Traceback (most recent call last): ... TypeError: n must be a critical value (i.e. even > 0 or odd < 0) REFERENCES: - [Iwa1972]_ - [IR1990]_ - [Was1997]_ """ 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 QQ((-1, 2))
def eisenstein_series_qexp(k, prec = 10, K=QQ, var='q', normalization='linear'): r""" Return the `q`-expansion of the normalized weight `k` Eisenstein series on `{\rm SL}_2(\ZZ)` to precision prec in the ring `K`. Three normalizations are available, depending on the parameter ``normalization``; the default normalization is the one for which the linear coefficient is 1. INPUT: - ``k`` - an even positive integer - ``prec`` - (default: 10) a nonnegative integer - ``K`` - (default: `\QQ`) a ring - ``var`` - (default: ``'q'``) variable name to use for q-expansion - ``normalization`` - (default: ``'linear'``) normalization to use. If this is ``'linear'``, then the series will be normalized so that the linear term is 1. If it is ``'constant'``, the series will be normalized to have constant term 1. If it is ``'integral'``, then the series will be normalized to have integer coefficients and no common factor, and linear term that is positive. Note that ``'integral'`` will work over arbitrary base rings, while ``'linear'`` or ``'constant'`` will fail if the denominator (resp. numerator) of `B_k / 2k` is invertible. ALGORITHM: We know `E_k = \text{constant} + \sum_n \sigma_{k-1}(n) q^n`. So we compute all the `\sigma_{k-1}(n)` simultaneously, using the fact that `\sigma` is multiplicative. EXAMPLES:: sage: eisenstein_series_qexp(2,5) -1/24 + q + 3*q^2 + 4*q^3 + 7*q^4 + O(q^5) sage: eisenstein_series_qexp(2,0) O(q^0) sage: eisenstein_series_qexp(2,5,GF(7)) 2 + q + 3*q^2 + 4*q^3 + O(q^5) sage: eisenstein_series_qexp(2,5,GF(7),var='T') 2 + T + 3*T^2 + 4*T^3 + O(T^5) We illustrate the use of the ``normalization`` parameter:: sage: eisenstein_series_qexp(12, 5, normalization='integral') 691 + 65520*q + 134250480*q^2 + 11606736960*q^3 + 274945048560*q^4 + O(q^5) sage: eisenstein_series_qexp(12, 5, normalization='constant') 1 + 65520/691*q + 134250480/691*q^2 + 11606736960/691*q^3 + 274945048560/691*q^4 + O(q^5) sage: eisenstein_series_qexp(12, 5, normalization='linear') 691/65520 + q + 2049*q^2 + 177148*q^3 + 4196353*q^4 + O(q^5) sage: eisenstein_series_qexp(12, 50, K=GF(13), normalization="constant") 1 + O(q^50) TESTS: Test that :trac:`5102` is fixed:: sage: eisenstein_series_qexp(10, 30, GF(17)) 15 + q + 3*q^2 + 15*q^3 + 7*q^4 + 13*q^5 + 11*q^6 + 11*q^7 + 15*q^8 + 7*q^9 + 5*q^10 + 7*q^11 + 3*q^12 + 14*q^13 + 16*q^14 + 8*q^15 + 14*q^16 + q^17 + 4*q^18 + 3*q^19 + 6*q^20 + 12*q^21 + 4*q^22 + 12*q^23 + 4*q^24 + 4*q^25 + 8*q^26 + 14*q^27 + 9*q^28 + 6*q^29 + O(q^30) This shows that the bug reported at :trac:`8291` is fixed:: sage: eisenstein_series_qexp(26, 10, GF(13)) 7 + q + 3*q^2 + 4*q^3 + 7*q^4 + 6*q^5 + 12*q^6 + 8*q^7 + 2*q^8 + O(q^10) We check that the function behaves properly over finite-characteristic base rings:: sage: eisenstein_series_qexp(12, 5, K = Zmod(691), normalization="integral") 566*q + 236*q^2 + 286*q^3 + 194*q^4 + O(q^5) sage: eisenstein_series_qexp(12, 5, K = Zmod(691), normalization="constant") Traceback (most recent call last): ... ValueError: The numerator of -B_k/(2*k) (=691) must be invertible in the ring Ring of integers modulo 691 sage: eisenstein_series_qexp(12, 5, K = Zmod(691), normalization="linear") q + 667*q^2 + 252*q^3 + 601*q^4 + O(q^5) sage: eisenstein_series_qexp(12, 5, K = Zmod(2), normalization="integral") 1 + O(q^5) sage: eisenstein_series_qexp(12, 5, K = Zmod(2), normalization="constant") 1 + O(q^5) sage: eisenstein_series_qexp(12, 5, K = Zmod(2), normalization="linear") Traceback (most recent call last): ... ValueError: The denominator of -B_k/(2*k) (=65520) must be invertible in the ring Ring of integers modulo 2 AUTHORS: - William Stein: original implementation - Craig Citro (2007-06-01): rewrote for massive speedup - Martin Raum (2009-08-02): port to cython for speedup - David Loeffler (2010-04-07): work around an integer overflow when `k` is large - David Loeffler (2012-03-15): add options for alternative normalizations (motivated by :trac:`12043`) """ ## we use this to prevent computation if it would fail anyway. if k <= 0 or k % 2 == 1 : raise ValueError("k must be positive and even") a0 = - bernoulli(k) / (2*k) if normalization == 'linear': a0den = a0.denominator() try: a0fac = K(1/a0den) except ZeroDivisionError: raise ValueError("The denominator of -B_k/(2*k) (=%s) must be invertible in the ring %s"%(a0den, K)) elif normalization == 'constant': a0num = a0.numerator() try: a0fac = K(1/a0num) except ZeroDivisionError: raise ValueError("The numerator of -B_k/(2*k) (=%s) must be invertible in the ring %s"%(a0num, K)) elif normalization == 'integral': a0fac = None else: raise ValueError("Normalization (=%s) must be one of 'linear', 'constant', 'integral'" % normalization) R = PowerSeriesRing(K, var) if K == QQ and normalization == 'linear': ls = Ek_ZZ(k, prec) # The following is *dramatically* faster than doing the more natural # "R(ls)" would be: E = ZZ[var](ls, prec=prec, check=False).change_ring(QQ) if len(ls)>0: E._unsafe_mutate(0, a0) return R(E, prec) # The following is an older slower alternative to the above three lines: #return a0fac*R(eisenstein_series_poly(k, prec).list(), prec=prec, check=False) else: # This used to work with check=False, but that can only be regarded as # an improbable lucky miracle. Enabling checking is a noticeable speed # regression; the morally right fix would be to expose FLINT's # fmpz_poly_to_nmod_poly command (at least for word-sized N). if a0fac is not None: return a0fac*R(eisenstein_series_poly(k, prec).list(), prec=prec, check=True) else: return R(eisenstein_series_poly(k, prec).list(), prec=prec, check=True)
def eisenstein_series_lseries(weight, prec=53, max_imaginary_part=0, max_asymp_coeffs=40): r""" Return the L-series of the weight `2k` Eisenstein series on `\mathrm{SL}_2(\ZZ)`. This actually returns an interface to Tim Dokchitser's program for computing with the L-series of the Eisenstein series INPUT: - ``weight`` - even integer - ``prec`` - integer (bits precision) - ``max_imaginary_part`` - real number - ``max_asymp_coeffs`` - integer OUTPUT: The L-series of the Eisenstein series. EXAMPLES: We compute with the L-series of `E_{16}` and then `E_{20}`:: sage: L = eisenstein_series_lseries(16) sage: L(1) -0.291657724743874 sage: L = eisenstein_series_lseries(20) sage: L(2) -5.02355351645998 Now with higher precision:: sage: L = eisenstein_series_lseries(20, prec=200) sage: L(2) -5.0235535164599797471968418348135050804419155747868718371029 """ f = eisenstein_series_qexp(weight,prec) from sage.lfunctions.all import Dokchitser from sage.symbolic.constants import pi key = (prec, max_imaginary_part, max_asymp_coeffs) j = weight L = Dokchitser(conductor = 1, gammaV = [0,1], weight = j, eps = (-1)**Integer(j/2), poles = [j], # Using a string for residues is a hack but it works well # since this will make PARI/GP compute sqrt(pi) with the # right precision. residues = '[sqrt(Pi)*(%s)]'%((-1)**Integer(j/2)*bernoulli(j)/j), prec = prec) s = 'coeff = %s;'%f.list() L.init_coeffs('coeff[k+1]',pari_precode = s, max_imaginary_part=max_imaginary_part, max_asymp_coeffs=max_asymp_coeffs) L.check_functional_equation() L.rename('L-series associated to the weight %s Eisenstein series %s on SL_2(Z)'%(j,f)) return L
def eisenstein_series_lseries(weight, prec=53, max_imaginary_part=0, max_asymp_coeffs=40): r""" Return the L-series of the weight `2k` Eisenstein series on `\mathrm{SL}_2(\ZZ)`. This actually returns an interface to Tim Dokchitser's program for computing with the L-series of the Eisenstein series INPUT: - ``weight`` - even integer - ``prec`` - integer (bits precision) - ``max_imaginary_part`` - real number - ``max_asymp_coeffs`` - integer OUTPUT: The L-series of the Eisenstein series. EXAMPLES: We compute with the L-series of `E_{16}` and then `E_{20}`:: sage: L = eisenstein_series_lseries(16) sage: L(1) -0.291657724743874 sage: L = eisenstein_series_lseries(20) sage: L(2) -5.02355351645998 Now with higher precision:: sage: L = eisenstein_series_lseries(20, prec=200) sage: L(2) -5.0235535164599797471968418348135050804419155747868718371029 """ f = eisenstein_series_qexp(weight,prec) from sage.lfunctions.all import Dokchitser j = weight L = Dokchitser(conductor = 1, gammaV = [0,1], weight = j, eps = (-1)**Integer(j/2), poles = [j], # Using a string for residues is a hack but it works well # since this will make PARI/GP compute sqrt(pi) with the # right precision. residues = '[sqrt(Pi)*(%s)]'%((-1)**Integer(j/2)*bernoulli(j)/j), prec = prec) s = 'coeff = %s;'%f.list() L.init_coeffs('coeff[k+1]',pari_precode = s, max_imaginary_part=max_imaginary_part, max_asymp_coeffs=max_asymp_coeffs) L.check_functional_equation() L.rename('L-series associated to the weight %s Eisenstein series %s on SL_2(Z)'%(j,f)) return L
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 (2s on sage.math, 2014) 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 (2s on sage.math, 2014) 3/2 TESTS:: sage: [1] + [Q.siegel_product(ZZ(a)) for a in range(1,11)] == Q.theta_series(11).list() # long time (2s on sage.math, 2014) 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 = " + str(d) + "\n") ## Product of "bad" places to omit S = 2 * d * u ## DIAGNOSTIC verbose("siegel_product Break 1. \n") verbose(" u = " + str(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; ## DIAGNOSTIC 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; #*/ ## DIAGNOSTIC #cout << endl; #cout << " generic factor = " << genericfactor << endl; #cout << " omit = " << omit << endl; #cout << " include = " << include << endl; #cout << endl; ## DIAGNOSTIC #// 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 HarmonicNumber(var, precision=None, skip_constant_summand=False): r""" Return the asymptotic expansion of a harmonic number. INPUT: - ``var`` -- a string for the variable name. - ``precision`` -- (default: ``None``) an integer. If ``None``, then the default precision of the asymptotic ring is used. - ``skip_constant_summand`` -- (default: ``False``) a boolean. If set, then the constant summand ``euler_gamma`` is left out. As a consequence, the coefficient ring of the output changes from ``Symbolic Constants Subring`` (if ``False``) to ``Rational Field`` (if ``True``). OUTPUT: An asymptotic expansion. EXAMPLES:: sage: asymptotic_expansions.HarmonicNumber('n', precision=5) log(n) + euler_gamma + 1/2*n^(-1) - 1/12*n^(-2) + 1/120*n^(-4) + O(n^(-6)) TESTS:: sage: ex = asymptotic_expansions.HarmonicNumber('n', precision=5) sage: n = ex.parent().gen() sage: ex.compare_with_values(n, # rel tol 1e-6 ....: lambda x: sum(1/k for k in srange(1, x+1)), [5, 10, 20]) [(5, 0.0038125360?), (10, 0.00392733?), (20, 0.0039579?)] sage: asymptotic_expansions.HarmonicNumber('n') log(n) + euler_gamma + 1/2*n^(-1) - 1/12*n^(-2) + 1/120*n^(-4) - 1/252*n^(-6) + 1/240*n^(-8) - 1/132*n^(-10) + 691/32760*n^(-12) - 1/12*n^(-14) + 3617/8160*n^(-16) - 43867/14364*n^(-18) + 174611/6600*n^(-20) - 77683/276*n^(-22) + 236364091/65520*n^(-24) - 657931/12*n^(-26) + 3392780147/3480*n^(-28) - 1723168255201/85932*n^(-30) + 7709321041217/16320*n^(-32) - 151628697551/12*n^(-34) + O(n^(-36)) sage: _.parent() Asymptotic Ring <n^ZZ * log(n)^ZZ> over Symbolic Constants Subring :: sage: asymptotic_expansions.HarmonicNumber( ....: 'n', precision=5, skip_constant_summand=True) log(n) + 1/2*n^(-1) - 1/12*n^(-2) + 1/120*n^(-4) + O(n^(-6)) sage: _.parent() Asymptotic Ring <n^ZZ * log(n)^ZZ> over Rational Field sage: for p in range(5): ....: print asymptotic_expansions.HarmonicNumber( ....: 'n', precision=p) O(log(n)) log(n) + O(1) log(n) + euler_gamma + O(n^(-1)) log(n) + euler_gamma + 1/2*n^(-1) + O(n^(-2)) log(n) + euler_gamma + 1/2*n^(-1) - 1/12*n^(-2) + O(n^(-4)) sage: asymptotic_expansions.HarmonicNumber('m', precision=5) log(m) + euler_gamma + 1/2*m^(-1) - 1/12*m^(-2) + 1/120*m^(-4) + O(m^(-6)) """ if not skip_constant_summand: from sage.symbolic.ring import SR coefficient_ring = SR.subring(no_variables=True) else: from sage.rings.rational_field import QQ coefficient_ring = QQ from asymptotic_ring import AsymptoticRing A = AsymptoticRing(growth_group='{n}^ZZ * log({n})^ZZ'.format(n=var), coefficient_ring=coefficient_ring) n = A.gen() if precision is None: precision = A.default_prec from sage.functions.log import log result = A.zero() if precision >= 1: result += log(n) if precision >= 2 and not skip_constant_summand: from sage.symbolic.constants import euler_gamma result += coefficient_ring(euler_gamma) if precision >= 3: result += 1 / (2 * n) from sage.arith.srange import srange from sage.arith.all import bernoulli for k in srange(2, 2 * precision - 4, 2): result += -bernoulli(k) / k / n**k if precision < 1: result += (log(n)).O() elif precision == 1: result += A(1).O() elif precision == 2: result += (1 / n).O() else: result += (1 / n**(2 * precision - 4)).O() return result
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 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 HarmonicNumber(var, precision=None, skip_constant_summand=False): r""" Return the asymptotic expansion of a harmonic number. INPUT: - ``var`` -- a string for the variable name. - ``precision`` -- (default: ``None``) an integer. If ``None``, then the default precision of the asymptotic ring is used. - ``skip_constant_summand`` -- (default: ``False``) a boolean. If set, then the constant summand ``euler_gamma`` is left out. As a consequence, the coefficient ring of the output changes from ``Symbolic Constants Subring`` (if ``False``) to ``Rational Field`` (if ``True``). OUTPUT: An asymptotic expansion. EXAMPLES:: sage: asymptotic_expansions.HarmonicNumber('n', precision=5) log(n) + euler_gamma + 1/2*n^(-1) - 1/12*n^(-2) + 1/120*n^(-4) + O(n^(-6)) TESTS:: sage: ex = asymptotic_expansions.HarmonicNumber('n', precision=5) sage: n = ex.parent().gen() sage: ex.compare_with_values(n, # rel tol 1e-6 ....: lambda x: sum(1/k for k in srange(1, x+1)), [5, 10, 20]) [(5, 0.0038125360?), (10, 0.00392733?), (20, 0.0039579?)] sage: asymptotic_expansions.HarmonicNumber('n') log(n) + euler_gamma + 1/2*n^(-1) - 1/12*n^(-2) + 1/120*n^(-4) - 1/252*n^(-6) + 1/240*n^(-8) - 1/132*n^(-10) + 691/32760*n^(-12) - 1/12*n^(-14) + 3617/8160*n^(-16) - 43867/14364*n^(-18) + 174611/6600*n^(-20) - 77683/276*n^(-22) + 236364091/65520*n^(-24) - 657931/12*n^(-26) + 3392780147/3480*n^(-28) - 1723168255201/85932*n^(-30) + 7709321041217/16320*n^(-32) - 151628697551/12*n^(-34) + O(n^(-36)) sage: _.parent() Asymptotic Ring <n^ZZ * log(n)^ZZ> over Symbolic Constants Subring :: sage: asymptotic_expansions.HarmonicNumber( ....: 'n', precision=5, skip_constant_summand=True) log(n) + 1/2*n^(-1) - 1/12*n^(-2) + 1/120*n^(-4) + O(n^(-6)) sage: _.parent() Asymptotic Ring <n^ZZ * log(n)^ZZ> over Rational Field sage: for p in range(5): ....: print asymptotic_expansions.HarmonicNumber( ....: 'n', precision=p) O(log(n)) log(n) + O(1) log(n) + euler_gamma + O(n^(-1)) log(n) + euler_gamma + 1/2*n^(-1) + O(n^(-2)) log(n) + euler_gamma + 1/2*n^(-1) - 1/12*n^(-2) + O(n^(-4)) sage: asymptotic_expansions.HarmonicNumber('m', precision=5) log(m) + euler_gamma + 1/2*m^(-1) - 1/12*m^(-2) + 1/120*m^(-4) + O(m^(-6)) """ if not skip_constant_summand: from sage.symbolic.ring import SR coefficient_ring = SR.subring(no_variables=True) else: from sage.rings.rational_field import QQ coefficient_ring = QQ from asymptotic_ring import AsymptoticRing A = AsymptoticRing(growth_group='{n}^ZZ * log({n})^ZZ'.format(n=var), coefficient_ring=coefficient_ring) n = A.gen() if precision is None: precision = A.default_prec from sage.functions.log import log result = A.zero() if precision >= 1: result += log(n) if precision >= 2 and not skip_constant_summand: from sage.symbolic.constants import euler_gamma result += coefficient_ring(euler_gamma) if precision >= 3: result += 1 / (2 * n) from sage.arith.srange import srange from sage.arith.all import bernoulli for k in srange(2, 2*precision - 4, 2): result += -bernoulli(k) / k / n**k if precision < 1: result += (log(n)).O() elif precision == 1: result += A(1).O() elif precision == 2: result += (1 / n).O() else: result += (1 / n**(2*precision - 4)).O() return result
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 (2s on sage.math, 2014) 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 (2s on sage.math, 2014) 3/2 TESTS:: sage: [1] + [Q.siegel_product(ZZ(a)) for a in range(1,11)] == Q.theta_series(11).list() # long time (2s on sage.math, 2014) 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 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 [Iwa1972]_, 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({}): {}".format(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(4) 1/90*pi^4 sage: zeta__exact(-3) 1/120 sage: zeta__exact(0) -1/2 sage: zeta__exact(5) Traceback (most recent call last): ... TypeError: n must be a critical value (i.e. even > 0 or odd < 0) REFERENCES: - [Iwa1972]_ - [IR1990]_ - [Was1997]_ """ 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 QQ((-1, 2))