Source code for sympy.functions.special.zeta_functions

""" Riemann zeta and related function. """
from __future__ import print_function, division

from sympy.core import Function, S, sympify, pi, I
from sympy.core.compatibility import range
from sympy.core.function import ArgumentIndexError
from sympy.functions.combinatorial.numbers import bernoulli, factorial, harmonic
from sympy.functions.elementary.exponential import log, exp_polar
from sympy.functions.elementary.miscellaneous import sqrt

###############################################################################
###################### LERCH TRANSCENDENT #####################################
###############################################################################


[docs]class lerchphi(Function): r""" Lerch transcendent (Lerch phi function). For :math:`\operatorname{Re}(a) > 0`, `|z| < 1` and `s \in \mathbb{C}`, the Lerch transcendent is defined as .. math :: \Phi(z, s, a) = \sum_{n=0}^\infty \frac{z^n}{(n + a)^s}, where the standard branch of the argument is used for :math:`n + a`, and by analytic continuation for other values of the parameters. A commonly used related function is the Lerch zeta function, defined by .. math:: L(q, s, a) = \Phi(e^{2\pi i q}, s, a). **Analytic Continuation and Branching Behavior** It can be shown that .. math:: \Phi(z, s, a) = z\Phi(z, s, a+1) + a^{-s}. This provides the analytic continuation to `\operatorname{Re}(a) \le 0`. Assume now `\operatorname{Re}(a) > 0`. The integral representation .. math:: \Phi_0(z, s, a) = \int_0^\infty \frac{t^{s-1} e^{-at}}{1 - ze^{-t}} \frac{\mathrm{d}t}{\Gamma(s)} provides an analytic continuation to :math:`\mathbb{C} - [1, \infty)`. Finally, for :math:`x \in (1, \infty)` we find .. math:: \lim_{\epsilon \to 0^+} \Phi_0(x + i\epsilon, s, a) -\lim_{\epsilon \to 0^+} \Phi_0(x - i\epsilon, s, a) = \frac{2\pi i \log^{s-1}{x}}{x^a \Gamma(s)}, using the standard branch for both :math:`\log{x}` and :math:`\log{\log{x}}` (a branch of :math:`\log{\log{x}}` is needed to evaluate :math:`\log{x}^{s-1}`). This concludes the analytic continuation. The Lerch transcendent is thus branched at :math:`z \in \{0, 1, \infty\}` and :math:`a \in \mathbb{Z}_{\le 0}`. For fixed :math:`z, a` outside these branch points, it is an entire function of :math:`s`. See Also ======== polylog, zeta References ========== .. [1] Bateman, H.; Erdelyi, A. (1953), Higher Transcendental Functions, Vol. I, New York: McGraw-Hill. Section 1.11. .. [2] http://dlmf.nist.gov/25.14 .. [3] https://en.wikipedia.org/wiki/Lerch_transcendent Examples ======== The Lerch transcendent is a fairly general function, for this reason it does not automatically evaluate to simpler functions. Use expand_func() to achieve this. If :math:`z=1`, the Lerch transcendent reduces to the Hurwitz zeta function: >>> from sympy import lerchphi, expand_func >>> from sympy.abc import z, s, a >>> expand_func(lerchphi(1, s, a)) zeta(s, a) More generally, if :math:`z` is a root of unity, the Lerch transcendent reduces to a sum of Hurwitz zeta functions: >>> expand_func(lerchphi(-1, s, a)) 2**(-s)*zeta(s, a/2) - 2**(-s)*zeta(s, a/2 + 1/2) If :math:`a=1`, the Lerch transcendent reduces to the polylogarithm: >>> expand_func(lerchphi(z, s, 1)) polylog(s, z)/z More generally, if :math:`a` is rational, the Lerch transcendent reduces to a sum of polylogarithms: >>> from sympy import S >>> expand_func(lerchphi(z, s, S(1)/2)) 2**(s - 1)*(polylog(s, sqrt(z))/sqrt(z) - polylog(s, sqrt(z)*exp_polar(I*pi))/sqrt(z)) >>> expand_func(lerchphi(z, s, S(3)/2)) -2**s/z + 2**(s - 1)*(polylog(s, sqrt(z))/sqrt(z) - polylog(s, sqrt(z)*exp_polar(I*pi))/sqrt(z))/z The derivatives with respect to :math:`z` and :math:`a` can be computed in closed form: >>> lerchphi(z, s, a).diff(z) (-a*lerchphi(z, s, a) + lerchphi(z, s - 1, a))/z >>> lerchphi(z, s, a).diff(a) -s*lerchphi(z, s + 1, a) """ def _eval_expand_func(self, **hints): from sympy import exp, I, floor, Add, Poly, Dummy, exp_polar, unpolarify z, s, a = self.args if z == 1: return zeta(s, a) if s.is_Integer and s <= 0: t = Dummy('t') p = Poly((t + a)**(-s), t) start = 1/(1 - t) res = S(0) for c in reversed(p.all_coeffs()): res += c*start start = t*start.diff(t) return res.subs(t, z) if a.is_Rational: # See section 18 of # Kelly B. Roach. Hypergeometric Function Representations. # In: Proceedings of the 1997 International Symposium on Symbolic and # Algebraic Computation, pages 205-211, New York, 1997. ACM. # TODO should something be polarified here? add = S(0) mul = S(1) # First reduce a to the interaval (0, 1] if a > 1: n = floor(a) if n == a: n -= 1 a -= n mul = z**(-n) add = Add(*[-z**(k - n)/(a + k)**s for k in range(n)]) elif a <= 0: n = floor(-a) + 1 a += n mul = z**n add = Add(*[z**(n - 1 - k)/(a - k - 1)**s for k in range(n)]) m, n = S([a.p, a.q]) zet = exp_polar(2*pi*I/n) root = z**(1/n) return add + mul*n**(s - 1)*Add( *[polylog(s, zet**k*root)._eval_expand_func(**hints) / (unpolarify(zet)**k*root)**m for k in range(n)]) # TODO use minpoly instead of ad-hoc methods when issue 5888 is fixed if isinstance(z, exp) and (z.args[0]/(pi*I)).is_Rational or z in [-1, I, -I]: # TODO reference? if z == -1: p, q = S([1, 2]) elif z == I: p, q = S([1, 4]) elif z == -I: p, q = S([-1, 4]) else: arg = z.args[0]/(2*pi*I) p, q = S([arg.p, arg.q]) return Add(*[exp(2*pi*I*k*p/q)/q**s*zeta(s, (k + a)/q) for k in range(q)]) return lerchphi(z, s, a) def fdiff(self, argindex=1): z, s, a = self.args if argindex == 3: return -s*lerchphi(z, s + 1, a) elif argindex == 1: return (lerchphi(z, s - 1, a) - a*lerchphi(z, s, a))/z else: raise ArgumentIndexError def _eval_rewrite_helper(self, z, s, a, target): res = self._eval_expand_func() if res.has(target): return res else: return self def _eval_rewrite_as_zeta(self, z, s, a, **kwargs): return self._eval_rewrite_helper(z, s, a, zeta) def _eval_rewrite_as_polylog(self, z, s, a, **kwargs): return self._eval_rewrite_helper(z, s, a, polylog)
############################################################################### ###################### POLYLOGARITHM ########################################## ###############################################################################
[docs]class polylog(Function): r""" Polylogarithm function. For :math:`|z| < 1` and :math:`s \in \mathbb{C}`, the polylogarithm is defined by .. math:: \operatorname{Li}_s(z) = \sum_{n=1}^\infty \frac{z^n}{n^s}, where the standard branch of the argument is used for :math:`n`. It admits an analytic continuation which is branched at :math:`z=1` (notably not on the sheet of initial definition), :math:`z=0` and :math:`z=\infty`. The name polylogarithm comes from the fact that for :math:`s=1`, the polylogarithm is related to the ordinary logarithm (see examples), and that .. math:: \operatorname{Li}_{s+1}(z) = \int_0^z \frac{\operatorname{Li}_s(t)}{t} \mathrm{d}t. The polylogarithm is a special case of the Lerch transcendent: .. math:: \operatorname{Li}_{s}(z) = z \Phi(z, s, 1) See Also ======== zeta, lerchphi Examples ======== For :math:`z \in \{0, 1, -1\}`, the polylogarithm is automatically expressed using other functions: >>> from sympy import polylog >>> from sympy.abc import s >>> polylog(s, 0) 0 >>> polylog(s, 1) zeta(s) >>> polylog(s, -1) -dirichlet_eta(s) If :math:`s` is a negative integer, :math:`0` or :math:`1`, the polylogarithm can be expressed using elementary functions. This can be done using expand_func(): >>> from sympy import expand_func >>> from sympy.abc import z >>> expand_func(polylog(1, z)) -log(1 - z) >>> expand_func(polylog(0, z)) z/(1 - z) The derivative with respect to :math:`z` can be computed in closed form: >>> polylog(s, z).diff(z) polylog(s - 1, z)/z The polylogarithm can be expressed in terms of the lerch transcendent: >>> from sympy import lerchphi >>> polylog(s, z).rewrite(lerchphi) z*lerchphi(z, s, 1) """ @classmethod def eval(cls, s, z): s, z = sympify((s, z)) if z == 1: return zeta(s) elif z == -1: return -dirichlet_eta(s) elif z == 0: return S.Zero elif s == 2: if z == S.Half: return pi**2/12 - log(2)**2/2 elif z == 2: return pi**2/4 - I*pi*log(2) elif z == -(sqrt(5) - 1)/2: return -pi**2/15 + log((sqrt(5)-1)/2)**2/2 elif z == -(sqrt(5) + 1)/2: return -pi**2/10 - log((sqrt(5)+1)/2)**2 elif z == (3 - sqrt(5))/2: return pi**2/15 - log((sqrt(5)-1)/2)**2 elif z == (sqrt(5) - 1)/2: return pi**2/10 - log((sqrt(5)-1)/2)**2 # For s = 0 or -1 use explicit formulas to evaluate, but # automatically expanding polylog(1, z) to -log(1-z) seems undesirable # for summation methods based on hypergeometric functions elif s == 0: return z/(1 - z) elif s == -1: return z/(1 - z)**2 # polylog is branched, but not over the unit disk from sympy.functions.elementary.complexes import (Abs, unpolarify, polar_lift) if z.has(exp_polar, polar_lift) and (Abs(z) <= S.One) == True: return cls(s, unpolarify(z)) def fdiff(self, argindex=1): s, z = self.args if argindex == 2: return polylog(s - 1, z)/z raise ArgumentIndexError def _eval_rewrite_as_lerchphi(self, s, z, **kwargs): return z*lerchphi(z, s, 1) def _eval_expand_func(self, **hints): from sympy import log, expand_mul, Dummy s, z = self.args if s == 1: return -log(1 - z) if s.is_Integer and s <= 0: u = Dummy('u') start = u/(1 - u) for _ in range(-s): start = u*start.diff(u) return expand_mul(start).subs(u, z) return polylog(s, z)
############################################################################### ###################### HURWITZ GENERALIZED ZETA FUNCTION ###################### ###############################################################################
[docs]class zeta(Function): r""" Hurwitz zeta function (or Riemann zeta function). For `\operatorname{Re}(a) > 0` and `\operatorname{Re}(s) > 1`, this function is defined as .. math:: \zeta(s, a) = \sum_{n=0}^\infty \frac{1}{(n + a)^s}, where the standard choice of argument for :math:`n + a` is used. For fixed :math:`a` with `\operatorname{Re}(a) > 0` the Hurwitz zeta function admits a meromorphic continuation to all of :math:`\mathbb{C}`, it is an unbranched function with a simple pole at :math:`s = 1`. Analytic continuation to other :math:`a` is possible under some circumstances, but this is not typically done. The Hurwitz zeta function is a special case of the Lerch transcendent: .. math:: \zeta(s, a) = \Phi(1, s, a). This formula defines an analytic continuation for all possible values of :math:`s` and :math:`a` (also `\operatorname{Re}(a) < 0`), see the documentation of :class:`lerchphi` for a description of the branching behavior. If no value is passed for :math:`a`, by this function assumes a default value of :math:`a = 1`, yielding the Riemann zeta function. See Also ======== dirichlet_eta, lerchphi, polylog References ========== .. [1] http://dlmf.nist.gov/25.11 .. [2] https://en.wikipedia.org/wiki/Hurwitz_zeta_function Examples ======== For :math:`a = 1` the Hurwitz zeta function reduces to the famous Riemann zeta function: .. math:: \zeta(s, 1) = \zeta(s) = \sum_{n=1}^\infty \frac{1}{n^s}. >>> from sympy import zeta >>> from sympy.abc import s >>> zeta(s, 1) zeta(s) >>> zeta(s) zeta(s) The Riemann zeta function can also be expressed using the Dirichlet eta function: >>> from sympy import dirichlet_eta >>> zeta(s).rewrite(dirichlet_eta) dirichlet_eta(s)/(1 - 2**(1 - s)) The Riemann zeta function at positive even integer and negative odd integer values is related to the Bernoulli numbers: >>> zeta(2) pi**2/6 >>> zeta(4) pi**4/90 >>> zeta(-1) -1/12 The specific formulae are: .. math:: \zeta(2n) = (-1)^{n+1} \frac{B_{2n} (2\pi)^{2n}}{2(2n)!} .. math:: \zeta(-n) = -\frac{B_{n+1}}{n+1} At negative even integers the Riemann zeta function is zero: >>> zeta(-4) 0 No closed-form expressions are known at positive odd integers, but numerical evaluation is possible: >>> zeta(3).n() 1.20205690315959 The derivative of :math:`\zeta(s, a)` with respect to :math:`a` is easily computed: >>> from sympy.abc import a >>> zeta(s, a).diff(a) -s*zeta(s + 1, a) However the derivative with respect to :math:`s` has no useful closed form expression: >>> zeta(s, a).diff(s) Derivative(zeta(s, a), s) The Hurwitz zeta function can be expressed in terms of the Lerch transcendent, :class:`sympy.functions.special.lerchphi`: >>> from sympy import lerchphi >>> zeta(s, a).rewrite(lerchphi) lerchphi(1, s, a) """ @classmethod def eval(cls, z, a_=None): if a_ is None: z, a = list(map(sympify, (z, 1))) else: z, a = list(map(sympify, (z, a_))) if a.is_Number: if a is S.NaN: return S.NaN elif a is S.One and a_ is not None: return cls(z) # TODO Should a == 0 return S.NaN as well? if z.is_Number: if z is S.NaN: return S.NaN elif z is S.Infinity: return S.One elif z is S.Zero: return S.Half - a elif z is S.One: return S.ComplexInfinity if z.is_integer: if a.is_Integer: if z.is_negative: zeta = (-1)**z * bernoulli(-z + 1)/(-z + 1) elif z.is_even and z.is_positive: B, F = bernoulli(z), factorial(z) zeta = ((-1)**(z/2+1) * 2**(z - 1) * B * pi**z) / F else: return if a.is_negative: return zeta + harmonic(abs(a), z) else: return zeta - harmonic(a - 1, z) def _eval_rewrite_as_dirichlet_eta(self, s, a=1, **kwargs): if a != 1: return self s = self.args[0] return dirichlet_eta(s)/(1 - 2**(1 - s)) def _eval_rewrite_as_lerchphi(self, s, a=1, **kwargs): return lerchphi(1, s, a) def _eval_is_finite(self): arg_is_one = (self.args[0] - 1).is_zero if arg_is_one is not None: return not arg_is_one def fdiff(self, argindex=1): if len(self.args) == 2: s, a = self.args else: s, a = self.args + (1,) if argindex == 2: return -s*zeta(s + 1, a) else: raise ArgumentIndexError
[docs]class dirichlet_eta(Function): r""" Dirichlet eta function. For `\operatorname{Re}(s) > 0`, this function is defined as .. math:: \eta(s) = \sum_{n=1}^\infty \frac{(-1)^{n-1}}{n^s}. It admits a unique analytic continuation to all of :math:`\mathbb{C}`. It is an entire, unbranched function. See Also ======== zeta References ========== .. [1] https://en.wikipedia.org/wiki/Dirichlet_eta_function Examples ======== The Dirichlet eta function is closely related to the Riemann zeta function: >>> from sympy import dirichlet_eta, zeta >>> from sympy.abc import s >>> dirichlet_eta(s).rewrite(zeta) (1 - 2**(1 - s))*zeta(s) """ @classmethod def eval(cls, s): if s == 1: return log(2) z = zeta(s) if not z.has(zeta): return (1 - 2**(1 - s))*z def _eval_rewrite_as_zeta(self, s, **kwargs): return (1 - 2**(1 - s)) * zeta(s)
[docs]class stieltjes(Function): r"""Represents Stieltjes constants, :math:`\gamma_{k}` that occur in Laurent Series expansion of the Riemann zeta function. Examples ======== >>> from sympy import stieltjes >>> from sympy.abc import n, m >>> stieltjes(n) stieltjes(n) zero'th stieltjes constant >>> stieltjes(0) EulerGamma >>> stieltjes(0, 1) EulerGamma For generalized stieltjes constants >>> stieltjes(n, m) stieltjes(n, m) Constants are only defined for integers >= 0 >>> stieltjes(-1) zoo References ========== .. [1] https://en.wikipedia.org/wiki/Stieltjes_constants """ @classmethod def eval(cls, n, a=None): n = sympify(n) if a is not None: a = sympify(a) if a is S.NaN: return S.NaN if a.is_Integer and a.is_nonpositive: return S.ComplexInfinity if n.is_Number: if n is S.NaN: return S.NaN elif n < 0: return S.ComplexInfinity elif not n.is_Integer: return S.ComplexInfinity elif n == 0 and a in [None, 1]: return S.EulerGamma