test_zeta.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. import scipy
  2. import scipy.special as sc
  3. import sys
  4. import numpy as np
  5. import pytest
  6. from numpy.testing import assert_equal, assert_allclose
  7. def test_zeta():
  8. assert_allclose(sc.zeta(2,2), np.pi**2/6 - 1, rtol=1e-12)
  9. def test_zetac():
  10. # Expected values in the following were computed using Wolfram
  11. # Alpha's `Zeta[x] - 1`
  12. x = [-2.1, 0.8, 0.9999, 9, 50, 75]
  13. desired = [
  14. -0.9972705002153750,
  15. -5.437538415895550,
  16. -10000.42279161673,
  17. 0.002008392826082214,
  18. 8.881784210930816e-16,
  19. 2.646977960169853e-23,
  20. ]
  21. assert_allclose(sc.zetac(x), desired, rtol=1e-12)
  22. def test_zetac_special_cases():
  23. assert sc.zetac(np.inf) == 0
  24. assert np.isnan(sc.zetac(-np.inf))
  25. assert sc.zetac(0) == -1.5
  26. assert sc.zetac(1.0) == np.inf
  27. assert_equal(sc.zetac([-2, -50, -100]), -1)
  28. def test_riemann_zeta_special_cases():
  29. assert np.isnan(sc.zeta(np.nan))
  30. assert sc.zeta(np.inf) == 1
  31. assert sc.zeta(0) == -0.5
  32. # Riemann zeta is zero add negative even integers.
  33. assert_equal(sc.zeta([-2, -4, -6, -8, -10]), 0)
  34. assert_allclose(sc.zeta(2), np.pi**2/6, rtol=1e-12)
  35. assert_allclose(sc.zeta(4), np.pi**4/90, rtol=1e-12)
  36. def test_riemann_zeta_avoid_overflow():
  37. s = -260.00000000001
  38. desired = -5.6966307844402683127e+297 # Computed with Mpmath
  39. assert_allclose(sc.zeta(s), desired, atol=0, rtol=5e-14)
  40. @pytest.mark.parametrize(
  41. "z, desired, rtol",
  42. [
  43. ## Test cases taken from mpmath with the script:
  44. # import numpy as np
  45. # import scipy.stats as stats
  46. # from mpmath import mp
  47. # # seed = np.random.SeedSequence().entropy
  48. # seed = 154689806791763421822480125722191067828
  49. # rng = np.random.default_rng(seed)
  50. # default_rtol = 1e-13
  51. # # A small point in each quadrant outside of the critical strip
  52. # cases = []
  53. # for x_sign, y_sign in [1, 1], [1, -1], [-1, 1], [-1, -1]:
  54. # x = x_sign * rng.uniform(2, 8)
  55. # y = y_sign * rng.uniform(2, 8)
  56. # z = x + y*1j
  57. # reference = complex(mp.zeta(z))
  58. # cases.append((z, reference, default_rtol))
  59. # # Moderately large imaginary part in each quadrant outside of critical strip
  60. # for x_sign, y_sign in [1, 1], [1, -1], [-1, 1], [-1, -1]:
  61. # x = x_sign * rng.uniform(2, 8)
  62. # y = y_sign * rng.uniform(50, 80)
  63. # z = x + y*1j
  64. # reference = complex(mp.zeta(z))
  65. # cases.append((z, reference, default_rtol))
  66. # # points in critical strip
  67. # x = rng.uniform(0.0, 1.0, size=5)
  68. # y = np.exp(rng.uniform(0, 5, size=5))
  69. # z = x + y*1j
  70. # for t in z:
  71. # reference = complex(mp.zeta(t))
  72. # cases.append((complex(t), reference, default_rtol))
  73. # z = x - y*1j
  74. # for t in z:
  75. # reference = complex(mp.zeta(t))
  76. # cases.append((complex(t), reference, default_rtol))
  77. # # Near small trivial zeros
  78. # x = np.array([-2, -4, -6, -8])
  79. # y = np.array([1e-15, -1e-15])
  80. # x, y = np.meshgrid(x, y)
  81. # x, y = x.ravel(), y.ravel()
  82. # z = x + y*1j
  83. # for t in z:
  84. # reference = complex(mp.zeta(t))
  85. # cases.append((complex(t), reference, 1e-7))
  86. # # Some other points near real axis
  87. # x = np.array([-0.5, 0, 0.2, 0.75])
  88. # y = np.array([1e-15, -1e-15])
  89. # x, y = np.meshgrid(x, y)
  90. # x, y = x.ravel(), y.ravel()
  91. # z = x + y*1j
  92. # for t in z:
  93. # reference = complex(mp.zeta(t))
  94. # cases.append((complex(t), reference, 1e-7))
  95. # # Moderately large real part
  96. # x = np.array([49.33915930750887, 50.55805244181687])
  97. # y = rng.uniform(20, 100, size=3)
  98. # x, y = np.meshgrid(x, y)
  99. # x, y = x.ravel(), y.ravel()
  100. # z = x + y*1j
  101. # for t in z:
  102. # reference = complex(mp.zeta(t))
  103. # cases.append((complex(t), reference, default_rtol))
  104. # # Very large imaginary part
  105. # x = np.array([0.5, 34.812847097948854, 50.55805244181687])
  106. # y = np.array([1e6, -1e6])
  107. # x, y = np.meshgrid(x, y)
  108. # x, y = x.ravel(), y.ravel()
  109. # z = x + y*1j
  110. # for t in z:
  111. # reference = complex(mp.zeta(t))
  112. # rtol = 1e-7 if t.real == 0.5 else default_rtol
  113. # cases.append((complex(t), reference, rtol))
  114. #
  115. # # Naive implementation of reflection formula suffers internal overflow
  116. # x = -rng.uniform(200, 300, 3)
  117. # y = np.array([rng.uniform(10, 30), -rng.uniform(10, 30)])
  118. # x, y = np.meshgrid(x, y)
  119. # x, y = x.ravel(), y.ravel()
  120. # z = x + y*1j
  121. # for t in z:
  122. # reference = complex(mp.zeta(t))
  123. # cases.append((complex(t), reference, default_rtol))
  124. #
  125. # A small point in each quadrant outside of the critical strip
  126. ((3.12838509346655+7.111085974836645j),
  127. (1.0192654793474945+0.08795174413289127j),
  128. 1e-13),
  129. ((7.06791362314716-7.219497492626728j),
  130. (1.0020740683598117-0.006752725913243711j),
  131. 1e-13),
  132. ((-6.806227077655519+2.724411451005281j),
  133. (0.06312488213559667-0.061641496333765956j),
  134. 1e-13),
  135. ((-3.0170751511621026-6.3686522550665945j),
  136. (-0.10330747857150148-1.214541994832571j),
  137. 1e-13),
  138. # Moderately large imaginary part in each quadrant outside of critical strip
  139. ((6.133994402212294+60.03091448000761j),
  140. (0.9885701843417336+0.009636925981078128j),
  141. 1e-13),
  142. ((6.17268142822657-64.74883149743795j),
  143. (1.0080474225840865+0.012032804974965354j),
  144. 1e-13),
  145. ((-3.462191939791879+76.16258975567534j),
  146. (18672.072070850158+2908.5104826247184j),
  147. 1e-13),
  148. ((-6.955735216531752-74.75791554155748j),
  149. (-77672258.72276545+71625206.0401107j),
  150. 1e-13),
  151. # Points in critical strip
  152. ((0.4088038289823922+1.4596830498094384j),
  153. (0.3032837969400845-0.47272237994110344j),
  154. 1e-13),
  155. ((0.9673493951209633+4.918968547259143j),
  156. (0.7488756907431944+0.17281553371482428j),
  157. 1e-13),
  158. ((0.8692482679977754+66.6142398421354j),
  159. (0.5831942469552066-0.26848904799062334j),
  160. 1e-13),
  161. ((0.42771847720003764+21.783747851715468j),
  162. (0.4767032638444329+0.6898148744603123j),
  163. 1e-13),
  164. ((0.20479494678428956+33.17656449538932j),
  165. (-0.6983038977487848+0.18060923618150224j),
  166. 1e-13),
  167. ((0.4088038289823922-1.4596830498094384j),
  168. (0.3032837969400845+0.47272237994110344j),
  169. 1e-13),
  170. ((0.9673493951209633-4.918968547259143j),
  171. (0.7488756907431944-0.17281553371482428j),
  172. 1e-13),
  173. ((0.8692482679977754-66.6142398421354j),
  174. (0.5831942469552066+0.26848904799062334j),
  175. 1e-13),
  176. ((0.42771847720003764-21.783747851715468j),
  177. (0.4767032638444329-0.6898148744603123j),
  178. 1e-13),
  179. ((0.20479494678428956-33.17656449538932j),
  180. (-0.6983038977487848-0.18060923618150224j),
  181. 1e-13),
  182. # Near small trivial zeros
  183. ((-2+1e-15j), (3.288175809370978e-32-3.0448457058393275e-17j), 1e-07),
  184. ((-4+1e-15j), (-2.868707923051182e-33+7.983811450268625e-18j), 1e-07),
  185. ((-6+1e-15j), (-1.7064292323640116e-34-5.8997591435159376e-18j), 1e-07),
  186. ((-8+1e-15j), (2.5060859548261706e-33+8.316161985602247e-18j), 1e-07),
  187. ((-2-1e-15j), (3.288175809371319e-32+3.0448457058393275e-17j), 1e-07),
  188. ((-4-1e-15j), (-2.8687079230520114e-33-7.983811450268625e-18j), 1e-07),
  189. ((-6-1e-15j), (-1.70642923235801e-34+5.8997591435159376e-18j), 1e-07),
  190. ((-8-1e-15j), (2.5060859548253293e-33-8.316161985602247e-18j), 1e-07),
  191. # Some other points near real axis
  192. ((-0.5+1e-15j), (-0.20788622497735457-3.608543395999408e-16j), 1e-07),
  193. (1e-15j, (-0.5-9.189384239689193e-16j), 1e-07),
  194. ((0.2+1e-15j), (-0.7339209248963406-1.4828001150329085e-15j), 1e-07),
  195. ((0.75+1e-15j), (-3.4412853869452227-1.5924832114302393e-14j), 1e-13),
  196. ((-0.5-1e-15j), (-0.20788622497735457+3.608543395999408e-16j), 1e-07),
  197. (-1e-15j, (-0.5+9.189387416062746e-16j), 1e-07),
  198. ((0.2-1e-15j), (-0.7339209248963406+1.4828004007675122e-15j), 1e-07),
  199. ((0.75-1e-15j), (-3.4412853869452227+1.5924831974403957e-14j), 1e-13),
  200. # Moderately large real part
  201. ((49.33915930750887+53.213478698903955j),
  202. (1.0000000000000009+1.0212452494616078e-15j),
  203. 1e-13),
  204. ((50.55805244181687+53.213478698903955j),
  205. (1.0000000000000004+4.387394180390787e-16j),
  206. 1e-13),
  207. ((49.33915930750887+40.6366015728302j),
  208. (0.9999999999999986-1.502268709924849e-16j),
  209. 1e-13),
  210. ((50.55805244181687+40.6366015728302j),
  211. (0.9999999999999994-6.453929613571651e-17j),
  212. 1e-13),
  213. ((49.33915930750887+85.83555435273925j),
  214. (0.9999999999999987-2.7014400611995846e-16j),
  215. 1e-13),
  216. ((50.55805244181687+85.83555435273925j),
  217. (0.9999999999999994-1.160571605555322e-16j),
  218. 1e-13),
  219. # Very large imaginary part
  220. ((0.5+1e6j), (0.0760890697382271+2.805102101019299j), 1e-07),
  221. ((34.812847097948854+1e6j),
  222. (1.0000000000102545+3.150848654056419e-11j),
  223. 1e-13),
  224. ((50.55805244181687+1e6j),
  225. (1.0000000000000002+5.736517078070873e-16j),
  226. 1e-13),
  227. ((0.5-1e6j), (0.0760890697382271-2.805102101019299j), 1e-07),
  228. ((34.812847097948854-1e6j),
  229. (1.0000000000102545-3.150848654056419e-11j),
  230. 1e-13),
  231. ((50.55805244181687-1e6j),
  232. (1.0000000000000002-5.736517078070873e-16j),
  233. 1e-13),
  234. ((-294.86605461349745+13.992648136816397j), (-np.inf+np.inf*1j), 1e-13),
  235. ((-294.86605461349745-16.147667799398363j), (np.inf-np.inf*1j), 1e-13),
  236. ]
  237. )
  238. def test_riemann_zeta_complex(z, desired, rtol):
  239. assert_allclose(sc.zeta(z), desired, rtol=rtol)
  240. # Some of the test cases below fail for intel compilers
  241. cpp_compiler = scipy.__config__.CONFIG["Compilers"]["c++"]["name"]
  242. gcc_linux = cpp_compiler == "gcc" and sys.platform == "linux"
  243. clang_macOS = cpp_compiler == "clang" and sys.platform == "darwin"
  244. @pytest.mark.skipif(
  245. not (gcc_linux or clang_macOS),
  246. reason="Underflow may not be avoided on other platforms",
  247. )
  248. @pytest.mark.parametrize(
  249. "z, desired, rtol",
  250. [
  251. # Test cases generated as part of same script for
  252. # test_riemann_zeta_complex. These cases are split off because
  253. # they fail on some platforms.
  254. #
  255. # Naive implementation of reflection formula suffers internal overflow
  256. ((-217.40285743524163+13.992648136816397j),
  257. (-6.012818500554211e+249-1.926943776932387e+250j),
  258. 5e-13,),
  259. ((-237.71710702931668+13.992648136816397j),
  260. (-8.823803086106129e+281-5.009074181335139e+281j),
  261. 1e-13,),
  262. ((-217.40285743524163-16.147667799398363j),
  263. (-5.111612904844256e+251-4.907132127666742e+250j),
  264. 5e-13,),
  265. ((-237.71710702931668-16.147667799398363j),
  266. (-1.3256112779883167e+283-2.253002003455494e+283j),
  267. 5e-13,),
  268. ],
  269. )
  270. def test_riemann_zeta_complex_avoid_underflow(z, desired, rtol):
  271. assert_allclose(sc.zeta(z), desired, rtol=rtol)