// Derived from SciPy's special/cephes/zeta.c // https://github.com/scipy/scipy/blob/master/scipy/special/cephes/zeta.c // Made freely available by Stephen L. Moshier without support or guarantee. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Copyright ©1984, ©1987 by Stephen L. Moshier // Portions Copyright ©2016 The Gonum Authors. All rights reserved. package cephes import "math" // zetaCoegs are the expansion coefficients for Euler-Maclaurin summation // formula: // \frac{(2k)!}{B_{2k}} // where // B_{2k} // are Bernoulli numbers. var zetaCoefs = [...]float64{ 12.0, -720.0, 30240.0, -1209600.0, 47900160.0, -1.307674368e12 / 691, 7.47242496e10, -1.067062284288e16 / 3617, 5.109094217170944e18 / 43867, -8.028576626982912e20 / 174611, 1.5511210043330985984e23 / 854513, -1.6938241367317436694528e27 / 236364091, } // Zeta computes the Riemann zeta function of two arguments. // Zeta(x,q) = \sum_{k=0}^{\infty} (k+q)^{-x} // Note that Zeta returns +Inf if x is 1 and will panic if x is less than 1, // q is either zero or a negative integer, or q is negative and x is not an // integer. // // Note that: // zeta(x,1) = zetac(x) + 1 func Zeta(x, q float64) float64 { // REFERENCE: Gradshteyn, I. S., and I. M. Ryzhik, Tables of Integrals, Series, // and Products, p. 1073; Academic Press, 1980. if x == 1 { return math.Inf(1) } if x < 1 { panic(badParamOutOfBounds) } if q <= 0 { if q == math.Floor(q) { panic(badParamFunctionSingularity) } if x != math.Floor(x) { panic(badParamOutOfBounds) // Because q^-x not defined } } // Asymptotic expansion: http://dlmf.nist.gov/25.11#E43 if q > 1e8 { return (1/(x-1) + 1/(2*q)) * math.Pow(q, 1-x) } // The Euler-Maclaurin summation formula is used to obtain the expansion: // Zeta(x,q) = \sum_{k=1}^n (k+q)^{-x} + \frac{(n+q)^{1-x}}{x-1} - \frac{1}{2(n+q)^x} + \sum_{j=1}^{\infty} \frac{B_{2j}x(x+1)...(x+2j)}{(2j)! (n+q)^{x+2j+1}} // where // B_{2j} // are Bernoulli numbers. // Permit negative q but continue sum until n+q > 9. This case should be // handled by a reflection formula. If q<0 and x is an integer, there is a // relation to the polyGamma function. s := math.Pow(q, -x) a := q i := 0 b := 0.0 for i < 9 || a <= 9 { i++ a += 1.0 b = math.Pow(a, -x) s += b if math.Abs(b/s) < machEp { return s } } w := a s += b * w / (x - 1) s -= 0.5 * b a = 1.0 k := 0.0 for _, coef := range zetaCoefs { a *= x + k b /= w t := a * b / coef s = s + t t = math.Abs(t / s) if t < machEp { return s } k += 1.0 a *= x + k b /= w k += 1.0 } return s }