test_mpmath.py 73 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023
  1. """
  2. Test Scipy functions versus mpmath, if available.
  3. """
  4. from __future__ import division, print_function, absolute_import
  5. import numpy as np
  6. from numpy.testing import assert_, assert_allclose
  7. from numpy import pi
  8. import pytest
  9. import itertools
  10. from distutils.version import LooseVersion
  11. import scipy.special as sc
  12. from scipy._lib.six import with_metaclass
  13. from scipy.special._testutils import (
  14. MissingModule, check_version, FuncData,
  15. assert_func_equal)
  16. from scipy.special._mptestutils import (
  17. Arg, FixedArg, ComplexArg, IntArg, assert_mpmath_equal,
  18. nonfunctional_tooslow, trace_args, time_limited, exception_to_nan,
  19. inf_to_nan)
  20. from scipy.special._ufuncs import (
  21. _sinpi, _cospi, _lgam1p, _lanczos_sum_expg_scaled, _log1pmx,
  22. _igam_fac)
  23. try:
  24. import mpmath
  25. except ImportError:
  26. mpmath = MissingModule('mpmath')
  27. _is_32bit_platform = np.intp(0).itemsize < 8
  28. # ------------------------------------------------------------------------------
  29. # expi
  30. # ------------------------------------------------------------------------------
  31. @check_version(mpmath, '0.10')
  32. def test_expi_complex():
  33. dataset = []
  34. for r in np.logspace(-99, 2, 10):
  35. for p in np.linspace(0, 2*np.pi, 30):
  36. z = r*np.exp(1j*p)
  37. dataset.append((z, complex(mpmath.ei(z))))
  38. dataset = np.array(dataset, dtype=np.complex_)
  39. FuncData(sc.expi, dataset, 0, 1).check()
  40. # ------------------------------------------------------------------------------
  41. # expn
  42. # ------------------------------------------------------------------------------
  43. @check_version(mpmath, '0.19')
  44. def test_expn_large_n():
  45. # Test the transition to the asymptotic regime of n.
  46. dataset = []
  47. for n in [50, 51]:
  48. for x in np.logspace(0, 4, 200):
  49. with mpmath.workdps(100):
  50. dataset.append((n, x, float(mpmath.expint(n, x))))
  51. dataset = np.asarray(dataset)
  52. FuncData(sc.expn, dataset, (0, 1), 2, rtol=1e-13).check()
  53. # ------------------------------------------------------------------------------
  54. # hyp0f1
  55. # ------------------------------------------------------------------------------
  56. @check_version(mpmath, '0.19')
  57. def test_hyp0f1_gh5764():
  58. # Do a small and somewhat systematic test that runs quickly
  59. dataset = []
  60. axis = [-99.5, -9.5, -0.5, 0.5, 9.5, 99.5]
  61. for v in axis:
  62. for x in axis:
  63. for y in axis:
  64. z = x + 1j*y
  65. # mpmath computes the answer correctly at dps ~ 17 but
  66. # fails for 20 < dps < 120 (uses a different method);
  67. # set the dps high enough that this isn't an issue
  68. with mpmath.workdps(120):
  69. res = complex(mpmath.hyp0f1(v, z))
  70. dataset.append((v, z, res))
  71. dataset = np.array(dataset)
  72. FuncData(lambda v, z: sc.hyp0f1(v.real, z), dataset, (0, 1), 2,
  73. rtol=1e-13).check()
  74. @check_version(mpmath, '0.19')
  75. def test_hyp0f1_gh_1609():
  76. # this is a regression test for gh-1609
  77. vv = np.linspace(150, 180, 21)
  78. af = sc.hyp0f1(vv, 0.5)
  79. mf = np.array([mpmath.hyp0f1(v, 0.5) for v in vv])
  80. assert_allclose(af, mf.astype(float), rtol=1e-12)
  81. # ------------------------------------------------------------------------------
  82. # hyp2f1
  83. # ------------------------------------------------------------------------------
  84. @check_version(mpmath, '1.0.0')
  85. def test_hyp2f1_strange_points():
  86. pts = [
  87. (2, -1, -1, 0.7), # expected: 2.4
  88. (2, -2, -2, 0.7), # expected: 3.87
  89. ]
  90. pts += list(itertools.product([2, 1, -0.7, -1000], repeat=4))
  91. pts = [
  92. (a, b, c, x) for a, b, c, x in pts
  93. if b == c and round(b) == b and b < 0 and b != -1000
  94. ]
  95. kw = dict(eliminate=True)
  96. dataset = [p + (float(mpmath.hyp2f1(*p, **kw)),) for p in pts]
  97. dataset = np.array(dataset, dtype=np.float_)
  98. FuncData(sc.hyp2f1, dataset, (0,1,2,3), 4, rtol=1e-10).check()
  99. @check_version(mpmath, '0.13')
  100. def test_hyp2f1_real_some_points():
  101. pts = [
  102. (1, 2, 3, 0),
  103. (1./3, 2./3, 5./6, 27./32),
  104. (1./4, 1./2, 3./4, 80./81),
  105. (2,-2, -3, 3),
  106. (2, -3, -2, 3),
  107. (2, -1.5, -1.5, 3),
  108. (1, 2, 3, 0),
  109. (0.7235, -1, -5, 0.3),
  110. (0.25, 1./3, 2, 0.999),
  111. (0.25, 1./3, 2, -1),
  112. (2, 3, 5, 0.99),
  113. (3./2, -0.5, 3, 0.99),
  114. (2, 2.5, -3.25, 0.999),
  115. (-8, 18.016500331508873, 10.805295997850628, 0.90875647507000001),
  116. (-10, 900, -10.5, 0.99),
  117. (-10, 900, 10.5, 0.99),
  118. (-1, 2, 1, 1.0),
  119. (-1, 2, 1, -1.0),
  120. (-3, 13, 5, 1.0),
  121. (-3, 13, 5, -1.0),
  122. (0.5, 1 - 270.5, 1.5, 0.999**2), # from issue 1561
  123. ]
  124. dataset = [p + (float(mpmath.hyp2f1(*p)),) for p in pts]
  125. dataset = np.array(dataset, dtype=np.float_)
  126. olderr = np.seterr(invalid='ignore')
  127. try:
  128. FuncData(sc.hyp2f1, dataset, (0,1,2,3), 4, rtol=1e-10).check()
  129. finally:
  130. np.seterr(**olderr)
  131. @check_version(mpmath, '0.14')
  132. def test_hyp2f1_some_points_2():
  133. # Taken from mpmath unit tests -- this point failed for mpmath 0.13 but
  134. # was fixed in their SVN since then
  135. pts = [
  136. (112, (51,10), (-9,10), -0.99999),
  137. (10,-900,10.5,0.99),
  138. (10,-900,-10.5,0.99),
  139. ]
  140. def fev(x):
  141. if isinstance(x, tuple):
  142. return float(x[0]) / x[1]
  143. else:
  144. return x
  145. dataset = [tuple(map(fev, p)) + (float(mpmath.hyp2f1(*p)),) for p in pts]
  146. dataset = np.array(dataset, dtype=np.float_)
  147. FuncData(sc.hyp2f1, dataset, (0,1,2,3), 4, rtol=1e-10).check()
  148. @check_version(mpmath, '0.13')
  149. def test_hyp2f1_real_some():
  150. dataset = []
  151. for a in [-10, -5, -1.8, 1.8, 5, 10]:
  152. for b in [-2.5, -1, 1, 7.4]:
  153. for c in [-9, -1.8, 5, 20.4]:
  154. for z in [-10, -1.01, -0.99, 0, 0.6, 0.95, 1.5, 10]:
  155. try:
  156. v = float(mpmath.hyp2f1(a, b, c, z))
  157. except Exception:
  158. continue
  159. dataset.append((a, b, c, z, v))
  160. dataset = np.array(dataset, dtype=np.float_)
  161. olderr = np.seterr(invalid='ignore')
  162. try:
  163. FuncData(sc.hyp2f1, dataset, (0,1,2,3), 4, rtol=1e-9,
  164. ignore_inf_sign=True).check()
  165. finally:
  166. np.seterr(**olderr)
  167. @check_version(mpmath, '0.12')
  168. @pytest.mark.slow
  169. def test_hyp2f1_real_random():
  170. npoints = 500
  171. dataset = np.zeros((npoints, 5), np.float_)
  172. np.random.seed(1234)
  173. dataset[:, 0] = np.random.pareto(1.5, npoints)
  174. dataset[:, 1] = np.random.pareto(1.5, npoints)
  175. dataset[:, 2] = np.random.pareto(1.5, npoints)
  176. dataset[:, 3] = 2*np.random.rand(npoints) - 1
  177. dataset[:, 0] *= (-1)**np.random.randint(2, npoints)
  178. dataset[:, 1] *= (-1)**np.random.randint(2, npoints)
  179. dataset[:, 2] *= (-1)**np.random.randint(2, npoints)
  180. for ds in dataset:
  181. if mpmath.__version__ < '0.14':
  182. # mpmath < 0.14 fails for c too much smaller than a, b
  183. if abs(ds[:2]).max() > abs(ds[2]):
  184. ds[2] = abs(ds[:2]).max()
  185. ds[4] = float(mpmath.hyp2f1(*tuple(ds[:4])))
  186. FuncData(sc.hyp2f1, dataset, (0, 1, 2, 3), 4, rtol=1e-9).check()
  187. # ------------------------------------------------------------------------------
  188. # erf (complex)
  189. # ------------------------------------------------------------------------------
  190. @check_version(mpmath, '0.14')
  191. def test_erf_complex():
  192. # need to increase mpmath precision for this test
  193. old_dps, old_prec = mpmath.mp.dps, mpmath.mp.prec
  194. try:
  195. mpmath.mp.dps = 70
  196. x1, y1 = np.meshgrid(np.linspace(-10, 1, 31), np.linspace(-10, 1, 11))
  197. x2, y2 = np.meshgrid(np.logspace(-80, .8, 31), np.logspace(-80, .8, 11))
  198. points = np.r_[x1.ravel(),x2.ravel()] + 1j*np.r_[y1.ravel(), y2.ravel()]
  199. assert_func_equal(sc.erf, lambda x: complex(mpmath.erf(x)), points,
  200. vectorized=False, rtol=1e-13)
  201. assert_func_equal(sc.erfc, lambda x: complex(mpmath.erfc(x)), points,
  202. vectorized=False, rtol=1e-13)
  203. finally:
  204. mpmath.mp.dps, mpmath.mp.prec = old_dps, old_prec
  205. # ------------------------------------------------------------------------------
  206. # lpmv
  207. # ------------------------------------------------------------------------------
  208. @check_version(mpmath, '0.15')
  209. def test_lpmv():
  210. pts = []
  211. for x in [-0.99, -0.557, 1e-6, 0.132, 1]:
  212. pts.extend([
  213. (1, 1, x),
  214. (1, -1, x),
  215. (-1, 1, x),
  216. (-1, -2, x),
  217. (1, 1.7, x),
  218. (1, -1.7, x),
  219. (-1, 1.7, x),
  220. (-1, -2.7, x),
  221. (1, 10, x),
  222. (1, 11, x),
  223. (3, 8, x),
  224. (5, 11, x),
  225. (-3, 8, x),
  226. (-5, 11, x),
  227. (3, -8, x),
  228. (5, -11, x),
  229. (-3, -8, x),
  230. (-5, -11, x),
  231. (3, 8.3, x),
  232. (5, 11.3, x),
  233. (-3, 8.3, x),
  234. (-5, 11.3, x),
  235. (3, -8.3, x),
  236. (5, -11.3, x),
  237. (-3, -8.3, x),
  238. (-5, -11.3, x),
  239. ])
  240. def mplegenp(nu, mu, x):
  241. if mu == int(mu) and x == 1:
  242. # mpmath 0.17 gets this wrong
  243. if mu == 0:
  244. return 1
  245. else:
  246. return 0
  247. return mpmath.legenp(nu, mu, x)
  248. dataset = [p + (mplegenp(p[1], p[0], p[2]),) for p in pts]
  249. dataset = np.array(dataset, dtype=np.float_)
  250. def evf(mu, nu, x):
  251. return sc.lpmv(mu.astype(int), nu, x)
  252. olderr = np.seterr(invalid='ignore')
  253. try:
  254. FuncData(evf, dataset, (0,1,2), 3, rtol=1e-10, atol=1e-14).check()
  255. finally:
  256. np.seterr(**olderr)
  257. # ------------------------------------------------------------------------------
  258. # beta
  259. # ------------------------------------------------------------------------------
  260. @check_version(mpmath, '0.15')
  261. def test_beta():
  262. np.random.seed(1234)
  263. b = np.r_[np.logspace(-200, 200, 4),
  264. np.logspace(-10, 10, 4),
  265. np.logspace(-1, 1, 4),
  266. np.arange(-10, 11, 1),
  267. np.arange(-10, 11, 1) + 0.5,
  268. -1, -2.3, -3, -100.3, -10003.4]
  269. a = b
  270. ab = np.array(np.broadcast_arrays(a[:,None], b[None,:])).reshape(2, -1).T
  271. old_dps, old_prec = mpmath.mp.dps, mpmath.mp.prec
  272. try:
  273. mpmath.mp.dps = 400
  274. assert_func_equal(sc.beta,
  275. lambda a, b: float(mpmath.beta(a, b)),
  276. ab,
  277. vectorized=False,
  278. rtol=1e-10,
  279. ignore_inf_sign=True)
  280. assert_func_equal(
  281. sc.betaln,
  282. lambda a, b: float(mpmath.log(abs(mpmath.beta(a, b)))),
  283. ab,
  284. vectorized=False,
  285. rtol=1e-10)
  286. finally:
  287. mpmath.mp.dps, mpmath.mp.prec = old_dps, old_prec
  288. # ------------------------------------------------------------------------------
  289. # loggamma
  290. # ------------------------------------------------------------------------------
  291. LOGGAMMA_TAYLOR_RADIUS = 0.2
  292. @check_version(mpmath, '0.19')
  293. def test_loggamma_taylor_transition():
  294. # Make sure there isn't a big jump in accuracy when we move from
  295. # using the Taylor series to using the recurrence relation.
  296. r = LOGGAMMA_TAYLOR_RADIUS + np.array([-0.1, -0.01, 0, 0.01, 0.1])
  297. theta = np.linspace(0, 2*np.pi, 20)
  298. r, theta = np.meshgrid(r, theta)
  299. dz = r*np.exp(1j*theta)
  300. z = np.r_[1 + dz, 2 + dz].flatten()
  301. dataset = []
  302. for z0 in z:
  303. dataset.append((z0, complex(mpmath.loggamma(z0))))
  304. dataset = np.array(dataset)
  305. FuncData(sc.loggamma, dataset, 0, 1, rtol=5e-14).check()
  306. @check_version(mpmath, '0.19')
  307. def test_loggamma_taylor():
  308. # Test around the zeros at z = 1, 2.
  309. r = np.logspace(-16, np.log10(LOGGAMMA_TAYLOR_RADIUS), 10)
  310. theta = np.linspace(0, 2*np.pi, 20)
  311. r, theta = np.meshgrid(r, theta)
  312. dz = r*np.exp(1j*theta)
  313. z = np.r_[1 + dz, 2 + dz].flatten()
  314. dataset = []
  315. for z0 in z:
  316. dataset.append((z0, complex(mpmath.loggamma(z0))))
  317. dataset = np.array(dataset)
  318. FuncData(sc.loggamma, dataset, 0, 1, rtol=5e-14).check()
  319. # ------------------------------------------------------------------------------
  320. # rgamma
  321. # ------------------------------------------------------------------------------
  322. @check_version(mpmath, '0.19')
  323. @pytest.mark.slow
  324. def test_rgamma_zeros():
  325. # Test around the zeros at z = 0, -1, -2, ..., -169. (After -169 we
  326. # get values that are out of floating point range even when we're
  327. # within 0.1 of the zero.)
  328. # Can't use too many points here or the test takes forever.
  329. dx = np.r_[-np.logspace(-1, -13, 3), 0, np.logspace(-13, -1, 3)]
  330. dy = dx.copy()
  331. dx, dy = np.meshgrid(dx, dy)
  332. dz = dx + 1j*dy
  333. zeros = np.arange(0, -170, -1).reshape(1, 1, -1)
  334. z = (zeros + np.dstack((dz,)*zeros.size)).flatten()
  335. dataset = []
  336. with mpmath.workdps(100):
  337. for z0 in z:
  338. dataset.append((z0, complex(mpmath.rgamma(z0))))
  339. dataset = np.array(dataset)
  340. FuncData(sc.rgamma, dataset, 0, 1, rtol=1e-12).check()
  341. # ------------------------------------------------------------------------------
  342. # digamma
  343. # ------------------------------------------------------------------------------
  344. @check_version(mpmath, '0.19')
  345. @pytest.mark.slow
  346. def test_digamma_roots():
  347. # Test the special-cased roots for digamma.
  348. root = mpmath.findroot(mpmath.digamma, 1.5)
  349. roots = [float(root)]
  350. root = mpmath.findroot(mpmath.digamma, -0.5)
  351. roots.append(float(root))
  352. roots = np.array(roots)
  353. # If we test beyond a radius of 0.24 mpmath will take forever.
  354. dx = np.r_[-0.24, -np.logspace(-1, -15, 10), 0, np.logspace(-15, -1, 10), 0.24]
  355. dy = dx.copy()
  356. dx, dy = np.meshgrid(dx, dy)
  357. dz = dx + 1j*dy
  358. z = (roots + np.dstack((dz,)*roots.size)).flatten()
  359. dataset = []
  360. with mpmath.workdps(30):
  361. for z0 in z:
  362. dataset.append((z0, complex(mpmath.digamma(z0))))
  363. dataset = np.array(dataset)
  364. FuncData(sc.digamma, dataset, 0, 1, rtol=1e-14).check()
  365. @check_version(mpmath, '0.19')
  366. def test_digamma_negreal():
  367. # Test digamma around the negative real axis. Don't do this in
  368. # TestSystematic because the points need some jiggering so that
  369. # mpmath doesn't take forever.
  370. digamma = exception_to_nan(mpmath.digamma)
  371. x = -np.logspace(300, -30, 100)
  372. y = np.r_[-np.logspace(0, -3, 5), 0, np.logspace(-3, 0, 5)]
  373. x, y = np.meshgrid(x, y)
  374. z = (x + 1j*y).flatten()
  375. dataset = []
  376. with mpmath.workdps(40):
  377. for z0 in z:
  378. res = digamma(z0)
  379. dataset.append((z0, complex(res)))
  380. dataset = np.asarray(dataset)
  381. FuncData(sc.digamma, dataset, 0, 1, rtol=1e-13).check()
  382. @check_version(mpmath, '0.19')
  383. def test_digamma_boundary():
  384. # Check that there isn't a jump in accuracy when we switch from
  385. # using the asymptotic series to the reflection formula.
  386. x = -np.logspace(300, -30, 100)
  387. y = np.array([-6.1, -5.9, 5.9, 6.1])
  388. x, y = np.meshgrid(x, y)
  389. z = (x + 1j*y).flatten()
  390. dataset = []
  391. with mpmath.workdps(30):
  392. for z0 in z:
  393. res = mpmath.digamma(z0)
  394. dataset.append((z0, complex(res)))
  395. dataset = np.asarray(dataset)
  396. FuncData(sc.digamma, dataset, 0, 1, rtol=1e-13).check()
  397. # ------------------------------------------------------------------------------
  398. # gammainc
  399. # ------------------------------------------------------------------------------
  400. @check_version(mpmath, '0.19')
  401. @pytest.mark.slow
  402. def test_gammainc_boundary():
  403. # Test the transition to the asymptotic series.
  404. small = 20
  405. a = np.linspace(0.5*small, 2*small, 50)
  406. x = a.copy()
  407. a, x = np.meshgrid(a, x)
  408. a, x = a.flatten(), x.flatten()
  409. dataset = []
  410. with mpmath.workdps(100):
  411. for a0, x0 in zip(a, x):
  412. dataset.append((a0, x0, float(mpmath.gammainc(a0, b=x0, regularized=True))))
  413. dataset = np.array(dataset)
  414. FuncData(sc.gammainc, dataset, (0, 1), 2, rtol=1e-12).check()
  415. # ------------------------------------------------------------------------------
  416. # spence
  417. # ------------------------------------------------------------------------------
  418. @check_version(mpmath, '0.19')
  419. @pytest.mark.slow
  420. def test_spence_circle():
  421. # The trickiest region for spence is around the circle |z - 1| = 1,
  422. # so test that region carefully.
  423. def spence(z):
  424. return complex(mpmath.polylog(2, 1 - z))
  425. r = np.linspace(0.5, 1.5)
  426. theta = np.linspace(0, 2*pi)
  427. z = (1 + np.outer(r, np.exp(1j*theta))).flatten()
  428. dataset = []
  429. for z0 in z:
  430. dataset.append((z0, spence(z0)))
  431. dataset = np.array(dataset)
  432. FuncData(sc.spence, dataset, 0, 1, rtol=1e-14).check()
  433. # ------------------------------------------------------------------------------
  434. # sinpi and cospi
  435. # ------------------------------------------------------------------------------
  436. @check_version(mpmath, '0.19')
  437. def test_sinpi_zeros():
  438. eps = np.finfo(float).eps
  439. dx = np.r_[-np.logspace(0, -13, 3), 0, np.logspace(-13, 0, 3)]
  440. dy = dx.copy()
  441. dx, dy = np.meshgrid(dx, dy)
  442. dz = dx + 1j*dy
  443. zeros = np.arange(-100, 100, 1).reshape(1, 1, -1)
  444. z = (zeros + np.dstack((dz,)*zeros.size)).flatten()
  445. dataset = []
  446. for z0 in z:
  447. dataset.append((z0, complex(mpmath.sinpi(z0))))
  448. dataset = np.array(dataset)
  449. FuncData(_sinpi, dataset, 0, 1, rtol=2*eps).check()
  450. @check_version(mpmath, '0.19')
  451. def test_cospi_zeros():
  452. eps = np.finfo(float).eps
  453. dx = np.r_[-np.logspace(0, -13, 3), 0, np.logspace(-13, 0, 3)]
  454. dy = dx.copy()
  455. dx, dy = np.meshgrid(dx, dy)
  456. dz = dx + 1j*dy
  457. zeros = (np.arange(-100, 100, 1) + 0.5).reshape(1, 1, -1)
  458. z = (zeros + np.dstack((dz,)*zeros.size)).flatten()
  459. dataset = []
  460. for z0 in z:
  461. dataset.append((z0, complex(mpmath.cospi(z0))))
  462. dataset = np.array(dataset)
  463. FuncData(_cospi, dataset, 0, 1, rtol=2*eps).check()
  464. # ------------------------------------------------------------------------------
  465. # ellipj
  466. # ------------------------------------------------------------------------------
  467. @check_version(mpmath, '0.19')
  468. def test_dn_quarter_period():
  469. def dn(u, m):
  470. return sc.ellipj(u, m)[2]
  471. def mpmath_dn(u, m):
  472. return float(mpmath.ellipfun("dn", u=u, m=m))
  473. m = np.linspace(0, 1, 20)
  474. du = np.r_[-np.logspace(-1, -15, 10), 0, np.logspace(-15, -1, 10)]
  475. dataset = []
  476. for m0 in m:
  477. u0 = float(mpmath.ellipk(m0))
  478. for du0 in du:
  479. p = u0 + du0
  480. dataset.append((p, m0, mpmath_dn(p, m0)))
  481. dataset = np.asarray(dataset)
  482. FuncData(dn, dataset, (0, 1), 2, rtol=1e-10).check()
  483. # ------------------------------------------------------------------------------
  484. # Wright Omega
  485. # ------------------------------------------------------------------------------
  486. def _mpmath_wrightomega(z, dps):
  487. with mpmath.workdps(dps):
  488. z = mpmath.mpc(z)
  489. unwind = mpmath.ceil((z.imag - mpmath.pi)/(2*mpmath.pi))
  490. res = mpmath.lambertw(mpmath.exp(z), unwind)
  491. return res
  492. @pytest.mark.slow
  493. @check_version(mpmath, '0.19')
  494. def test_wrightomega_branch():
  495. x = -np.logspace(10, 0, 25)
  496. picut_above = [np.nextafter(np.pi, np.inf)]
  497. picut_below = [np.nextafter(np.pi, -np.inf)]
  498. npicut_above = [np.nextafter(-np.pi, np.inf)]
  499. npicut_below = [np.nextafter(-np.pi, -np.inf)]
  500. for i in range(50):
  501. picut_above.append(np.nextafter(picut_above[-1], np.inf))
  502. picut_below.append(np.nextafter(picut_below[-1], -np.inf))
  503. npicut_above.append(np.nextafter(npicut_above[-1], np.inf))
  504. npicut_below.append(np.nextafter(npicut_below[-1], -np.inf))
  505. y = np.hstack((picut_above, picut_below, npicut_above, npicut_below))
  506. x, y = np.meshgrid(x, y)
  507. z = (x + 1j*y).flatten()
  508. dataset = []
  509. for z0 in z:
  510. dataset.append((z0, complex(_mpmath_wrightomega(z0, 25))))
  511. dataset = np.asarray(dataset)
  512. FuncData(sc.wrightomega, dataset, 0, 1, rtol=1e-8).check()
  513. @pytest.mark.slow
  514. @check_version(mpmath, '0.19')
  515. def test_wrightomega_region1():
  516. # This region gets less coverage in the TestSystematic test
  517. x = np.linspace(-2, 1)
  518. y = np.linspace(1, 2*np.pi)
  519. x, y = np.meshgrid(x, y)
  520. z = (x + 1j*y).flatten()
  521. dataset = []
  522. for z0 in z:
  523. dataset.append((z0, complex(_mpmath_wrightomega(z0, 25))))
  524. dataset = np.asarray(dataset)
  525. FuncData(sc.wrightomega, dataset, 0, 1, rtol=1e-15).check()
  526. @pytest.mark.slow
  527. @check_version(mpmath, '0.19')
  528. def test_wrightomega_region2():
  529. # This region gets less coverage in the TestSystematic test
  530. x = np.linspace(-2, 1)
  531. y = np.linspace(-2*np.pi, -1)
  532. x, y = np.meshgrid(x, y)
  533. z = (x + 1j*y).flatten()
  534. dataset = []
  535. for z0 in z:
  536. dataset.append((z0, complex(_mpmath_wrightomega(z0, 25))))
  537. dataset = np.asarray(dataset)
  538. FuncData(sc.wrightomega, dataset, 0, 1, rtol=1e-15).check()
  539. # ------------------------------------------------------------------------------
  540. # lambertw
  541. # ------------------------------------------------------------------------------
  542. @pytest.mark.slow
  543. @check_version(mpmath, '0.19')
  544. def test_lambertw_smallz():
  545. x, y = np.linspace(-1, 1, 25), np.linspace(-1, 1, 25)
  546. x, y = np.meshgrid(x, y)
  547. z = (x + 1j*y).flatten()
  548. dataset = []
  549. for z0 in z:
  550. dataset.append((z0, complex(mpmath.lambertw(z0))))
  551. dataset = np.asarray(dataset)
  552. FuncData(sc.lambertw, dataset, 0, 1, rtol=1e-13).check()
  553. # ------------------------------------------------------------------------------
  554. # Systematic tests
  555. # ------------------------------------------------------------------------------
  556. HYPERKW = dict(maxprec=200, maxterms=200)
  557. @pytest.mark.slow
  558. @check_version(mpmath, '0.17')
  559. class TestSystematic(object):
  560. def test_airyai(self):
  561. # oscillating function, limit range
  562. assert_mpmath_equal(lambda z: sc.airy(z)[0],
  563. mpmath.airyai,
  564. [Arg(-1e8, 1e8)],
  565. rtol=1e-5)
  566. assert_mpmath_equal(lambda z: sc.airy(z)[0],
  567. mpmath.airyai,
  568. [Arg(-1e3, 1e3)])
  569. def test_airyai_complex(self):
  570. assert_mpmath_equal(lambda z: sc.airy(z)[0],
  571. mpmath.airyai,
  572. [ComplexArg()])
  573. def test_airyai_prime(self):
  574. # oscillating function, limit range
  575. assert_mpmath_equal(lambda z: sc.airy(z)[1], lambda z:
  576. mpmath.airyai(z, derivative=1),
  577. [Arg(-1e8, 1e8)],
  578. rtol=1e-5)
  579. assert_mpmath_equal(lambda z: sc.airy(z)[1], lambda z:
  580. mpmath.airyai(z, derivative=1),
  581. [Arg(-1e3, 1e3)])
  582. def test_airyai_prime_complex(self):
  583. assert_mpmath_equal(lambda z: sc.airy(z)[1], lambda z:
  584. mpmath.airyai(z, derivative=1),
  585. [ComplexArg()])
  586. def test_airybi(self):
  587. # oscillating function, limit range
  588. assert_mpmath_equal(lambda z: sc.airy(z)[2], lambda z:
  589. mpmath.airybi(z),
  590. [Arg(-1e8, 1e8)],
  591. rtol=1e-5)
  592. assert_mpmath_equal(lambda z: sc.airy(z)[2], lambda z:
  593. mpmath.airybi(z),
  594. [Arg(-1e3, 1e3)])
  595. def test_airybi_complex(self):
  596. assert_mpmath_equal(lambda z: sc.airy(z)[2], lambda z:
  597. mpmath.airybi(z),
  598. [ComplexArg()])
  599. def test_airybi_prime(self):
  600. # oscillating function, limit range
  601. assert_mpmath_equal(lambda z: sc.airy(z)[3], lambda z:
  602. mpmath.airybi(z, derivative=1),
  603. [Arg(-1e8, 1e8)],
  604. rtol=1e-5)
  605. assert_mpmath_equal(lambda z: sc.airy(z)[3], lambda z:
  606. mpmath.airybi(z, derivative=1),
  607. [Arg(-1e3, 1e3)])
  608. def test_airybi_prime_complex(self):
  609. assert_mpmath_equal(lambda z: sc.airy(z)[3], lambda z:
  610. mpmath.airybi(z, derivative=1),
  611. [ComplexArg()])
  612. def test_bei(self):
  613. assert_mpmath_equal(sc.bei,
  614. exception_to_nan(lambda z: mpmath.bei(0, z, **HYPERKW)),
  615. [Arg(-1e3, 1e3)])
  616. def test_ber(self):
  617. assert_mpmath_equal(sc.ber,
  618. exception_to_nan(lambda z: mpmath.ber(0, z, **HYPERKW)),
  619. [Arg(-1e3, 1e3)])
  620. def test_bernoulli(self):
  621. assert_mpmath_equal(lambda n: sc.bernoulli(int(n))[int(n)],
  622. lambda n: float(mpmath.bernoulli(int(n))),
  623. [IntArg(0, 13000)],
  624. rtol=1e-9, n=13000)
  625. def test_besseli(self):
  626. assert_mpmath_equal(sc.iv,
  627. exception_to_nan(lambda v, z: mpmath.besseli(v, z, **HYPERKW)),
  628. [Arg(-1e100, 1e100), Arg()],
  629. atol=1e-270)
  630. def test_besseli_complex(self):
  631. assert_mpmath_equal(lambda v, z: sc.iv(v.real, z),
  632. exception_to_nan(lambda v, z: mpmath.besseli(v, z, **HYPERKW)),
  633. [Arg(-1e100, 1e100), ComplexArg()])
  634. def test_besselj(self):
  635. assert_mpmath_equal(sc.jv,
  636. exception_to_nan(lambda v, z: mpmath.besselj(v, z, **HYPERKW)),
  637. [Arg(-1e100, 1e100), Arg(-1e3, 1e3)],
  638. ignore_inf_sign=True)
  639. # loss of precision at large arguments due to oscillation
  640. assert_mpmath_equal(sc.jv,
  641. exception_to_nan(lambda v, z: mpmath.besselj(v, z, **HYPERKW)),
  642. [Arg(-1e100, 1e100), Arg(-1e8, 1e8)],
  643. ignore_inf_sign=True,
  644. rtol=1e-5)
  645. def test_besselj_complex(self):
  646. assert_mpmath_equal(lambda v, z: sc.jv(v.real, z),
  647. exception_to_nan(lambda v, z: mpmath.besselj(v, z, **HYPERKW)),
  648. [Arg(), ComplexArg()])
  649. def test_besselk(self):
  650. assert_mpmath_equal(sc.kv,
  651. mpmath.besselk,
  652. [Arg(-200, 200), Arg(0, np.inf)],
  653. nan_ok=False, rtol=1e-12)
  654. def test_besselk_int(self):
  655. assert_mpmath_equal(sc.kn,
  656. mpmath.besselk,
  657. [IntArg(-200, 200), Arg(0, np.inf)],
  658. nan_ok=False, rtol=1e-12)
  659. def test_besselk_complex(self):
  660. assert_mpmath_equal(lambda v, z: sc.kv(v.real, z),
  661. exception_to_nan(lambda v, z: mpmath.besselk(v, z, **HYPERKW)),
  662. [Arg(-1e100, 1e100), ComplexArg()])
  663. def test_bessely(self):
  664. def mpbessely(v, x):
  665. r = float(mpmath.bessely(v, x, **HYPERKW))
  666. if abs(r) > 1e305:
  667. # overflowing to inf a bit earlier is OK
  668. r = np.inf * np.sign(r)
  669. if abs(r) == 0 and x == 0:
  670. # invalid result from mpmath, point x=0 is a divergence
  671. return np.nan
  672. return r
  673. assert_mpmath_equal(sc.yv,
  674. exception_to_nan(mpbessely),
  675. [Arg(-1e100, 1e100), Arg(-1e8, 1e8)],
  676. n=5000)
  677. def test_bessely_complex(self):
  678. def mpbessely(v, x):
  679. r = complex(mpmath.bessely(v, x, **HYPERKW))
  680. if abs(r) > 1e305:
  681. # overflowing to inf a bit earlier is OK
  682. olderr = np.seterr(invalid='ignore')
  683. try:
  684. r = np.inf * np.sign(r)
  685. finally:
  686. np.seterr(**olderr)
  687. return r
  688. assert_mpmath_equal(lambda v, z: sc.yv(v.real, z),
  689. exception_to_nan(mpbessely),
  690. [Arg(), ComplexArg()],
  691. n=15000)
  692. def test_bessely_int(self):
  693. def mpbessely(v, x):
  694. r = float(mpmath.bessely(v, x))
  695. if abs(r) == 0 and x == 0:
  696. # invalid result from mpmath, point x=0 is a divergence
  697. return np.nan
  698. return r
  699. assert_mpmath_equal(lambda v, z: sc.yn(int(v), z),
  700. exception_to_nan(mpbessely),
  701. [IntArg(-1000, 1000), Arg(-1e8, 1e8)])
  702. def test_beta(self):
  703. bad_points = []
  704. def beta(a, b, nonzero=False):
  705. if a < -1e12 or b < -1e12:
  706. # Function is defined here only at integers, but due
  707. # to loss of precision this is numerically
  708. # ill-defined. Don't compare values here.
  709. return np.nan
  710. if (a < 0 or b < 0) and (abs(float(a + b)) % 1) == 0:
  711. # close to a zero of the function: mpmath and scipy
  712. # will not round here the same, so the test needs to be
  713. # run with an absolute tolerance
  714. if nonzero:
  715. bad_points.append((float(a), float(b)))
  716. return np.nan
  717. return mpmath.beta(a, b)
  718. assert_mpmath_equal(sc.beta,
  719. lambda a, b: beta(a, b, nonzero=True),
  720. [Arg(), Arg()],
  721. dps=400,
  722. ignore_inf_sign=True)
  723. assert_mpmath_equal(sc.beta,
  724. beta,
  725. np.array(bad_points),
  726. dps=400,
  727. ignore_inf_sign=True,
  728. atol=1e-11)
  729. def test_betainc(self):
  730. assert_mpmath_equal(sc.betainc,
  731. time_limited()(exception_to_nan(lambda a, b, x: mpmath.betainc(a, b, 0, x, regularized=True))),
  732. [Arg(), Arg(), Arg()])
  733. def test_binom(self):
  734. bad_points = []
  735. def binomial(n, k, nonzero=False):
  736. if abs(k) > 1e8*(abs(n) + 1):
  737. # The binomial is rapidly oscillating in this region,
  738. # and the function is numerically ill-defined. Don't
  739. # compare values here.
  740. return np.nan
  741. if n < k and abs(float(n-k) - np.round(float(n-k))) < 1e-15:
  742. # close to a zero of the function: mpmath and scipy
  743. # will not round here the same, so the test needs to be
  744. # run with an absolute tolerance
  745. if nonzero:
  746. bad_points.append((float(n), float(k)))
  747. return np.nan
  748. return mpmath.binomial(n, k)
  749. assert_mpmath_equal(sc.binom,
  750. lambda n, k: binomial(n, k, nonzero=True),
  751. [Arg(), Arg()],
  752. dps=400)
  753. assert_mpmath_equal(sc.binom,
  754. binomial,
  755. np.array(bad_points),
  756. dps=400,
  757. atol=1e-14)
  758. def test_chebyt_int(self):
  759. assert_mpmath_equal(lambda n, x: sc.eval_chebyt(int(n), x),
  760. exception_to_nan(lambda n, x: mpmath.chebyt(n, x, **HYPERKW)),
  761. [IntArg(), Arg()], dps=50)
  762. @pytest.mark.xfail(run=False, reason="some cases in hyp2f1 not fully accurate")
  763. def test_chebyt(self):
  764. assert_mpmath_equal(sc.eval_chebyt,
  765. lambda n, x: time_limited()(exception_to_nan(mpmath.chebyt))(n, x, **HYPERKW),
  766. [Arg(-101, 101), Arg()], n=10000)
  767. def test_chebyu_int(self):
  768. assert_mpmath_equal(lambda n, x: sc.eval_chebyu(int(n), x),
  769. exception_to_nan(lambda n, x: mpmath.chebyu(n, x, **HYPERKW)),
  770. [IntArg(), Arg()], dps=50)
  771. @pytest.mark.xfail(run=False, reason="some cases in hyp2f1 not fully accurate")
  772. def test_chebyu(self):
  773. assert_mpmath_equal(sc.eval_chebyu,
  774. lambda n, x: time_limited()(exception_to_nan(mpmath.chebyu))(n, x, **HYPERKW),
  775. [Arg(-101, 101), Arg()])
  776. def test_chi(self):
  777. def chi(x):
  778. return sc.shichi(x)[1]
  779. assert_mpmath_equal(chi, mpmath.chi, [Arg()])
  780. # check asymptotic series cross-over
  781. assert_mpmath_equal(chi, mpmath.chi, [FixedArg([88 - 1e-9, 88, 88 + 1e-9])])
  782. def test_chi_complex(self):
  783. def chi(z):
  784. return sc.shichi(z)[1]
  785. # chi oscillates as Im[z] -> +- inf, so limit range
  786. assert_mpmath_equal(chi,
  787. mpmath.chi,
  788. [ComplexArg(complex(-np.inf, -1e8), complex(np.inf, 1e8))],
  789. rtol=1e-12)
  790. def test_ci(self):
  791. def ci(x):
  792. return sc.sici(x)[1]
  793. # oscillating function: limit range
  794. assert_mpmath_equal(ci,
  795. mpmath.ci,
  796. [Arg(-1e8, 1e8)])
  797. def test_ci_complex(self):
  798. def ci(z):
  799. return sc.sici(z)[1]
  800. # ci oscillates as Re[z] -> +- inf, so limit range
  801. assert_mpmath_equal(ci,
  802. mpmath.ci,
  803. [ComplexArg(complex(-1e8, -np.inf), complex(1e8, np.inf))],
  804. rtol=1e-8)
  805. def test_cospi(self):
  806. eps = np.finfo(float).eps
  807. assert_mpmath_equal(_cospi,
  808. mpmath.cospi,
  809. [Arg()], nan_ok=False, rtol=eps)
  810. def test_cospi_complex(self):
  811. assert_mpmath_equal(_cospi,
  812. mpmath.cospi,
  813. [ComplexArg()], nan_ok=False, rtol=1e-13)
  814. def test_digamma(self):
  815. assert_mpmath_equal(sc.digamma,
  816. exception_to_nan(mpmath.digamma),
  817. [Arg()], rtol=1e-12, dps=50)
  818. def test_digamma_complex(self):
  819. # Test on a cut plane because mpmath will hang. See
  820. # test_digamma_negreal for tests on the negative real axis.
  821. def param_filter(z):
  822. return np.where((z.real < 0) & (np.abs(z.imag) < 1.12), False, True)
  823. assert_mpmath_equal(sc.digamma,
  824. exception_to_nan(mpmath.digamma),
  825. [ComplexArg()], rtol=1e-13, dps=40,
  826. param_filter=param_filter)
  827. def test_e1(self):
  828. assert_mpmath_equal(sc.exp1,
  829. mpmath.e1,
  830. [Arg()], rtol=1e-14)
  831. def test_e1_complex(self):
  832. # E_1 oscillates as Im[z] -> +- inf, so limit range
  833. assert_mpmath_equal(sc.exp1,
  834. mpmath.e1,
  835. [ComplexArg(complex(-np.inf, -1e8), complex(np.inf, 1e8))],
  836. rtol=1e-11)
  837. # Check cross-over region
  838. assert_mpmath_equal(sc.exp1,
  839. mpmath.e1,
  840. (np.linspace(-50, 50, 171)[:, None] +
  841. np.r_[0, np.logspace(-3, 2, 61),
  842. -np.logspace(-3, 2, 11)]*1j).ravel(),
  843. rtol=1e-11)
  844. assert_mpmath_equal(sc.exp1,
  845. mpmath.e1,
  846. (np.linspace(-50, -35, 10000) + 0j),
  847. rtol=1e-11)
  848. def test_exprel(self):
  849. assert_mpmath_equal(sc.exprel,
  850. lambda x: mpmath.expm1(x)/x if x != 0 else mpmath.mpf('1.0'),
  851. [Arg(a=-np.log(np.finfo(np.double).max), b=np.log(np.finfo(np.double).max))])
  852. assert_mpmath_equal(sc.exprel,
  853. lambda x: mpmath.expm1(x)/x if x != 0 else mpmath.mpf('1.0'),
  854. np.array([1e-12, 1e-24, 0, 1e12, 1e24, np.inf]), rtol=1e-11)
  855. assert_(np.isinf(sc.exprel(np.inf)))
  856. assert_(sc.exprel(-np.inf) == 0)
  857. def test_expm1_complex(self):
  858. # Oscillates as a function of Im[z], so limit range to avoid loss of precision
  859. assert_mpmath_equal(sc.expm1,
  860. mpmath.expm1,
  861. [ComplexArg(complex(-np.inf, -1e7), complex(np.inf, 1e7))])
  862. def test_log1p_complex(self):
  863. assert_mpmath_equal(sc.log1p,
  864. lambda x: mpmath.log(x+1),
  865. [ComplexArg()], dps=60)
  866. def test_log1pmx(self):
  867. assert_mpmath_equal(_log1pmx,
  868. lambda x: mpmath.log(x + 1) - x,
  869. [Arg()], dps=60, rtol=1e-14)
  870. def test_ei(self):
  871. assert_mpmath_equal(sc.expi,
  872. mpmath.ei,
  873. [Arg()],
  874. rtol=1e-11)
  875. def test_ei_complex(self):
  876. # Ei oscillates as Im[z] -> +- inf, so limit range
  877. assert_mpmath_equal(sc.expi,
  878. mpmath.ei,
  879. [ComplexArg(complex(-np.inf, -1e8), complex(np.inf, 1e8))],
  880. rtol=1e-9)
  881. def test_ellipe(self):
  882. assert_mpmath_equal(sc.ellipe,
  883. mpmath.ellipe,
  884. [Arg(b=1.0)])
  885. def test_ellipeinc(self):
  886. assert_mpmath_equal(sc.ellipeinc,
  887. mpmath.ellipe,
  888. [Arg(-1e3, 1e3), Arg(b=1.0)])
  889. def test_ellipeinc_largephi(self):
  890. assert_mpmath_equal(sc.ellipeinc,
  891. mpmath.ellipe,
  892. [Arg(), Arg()])
  893. def test_ellipf(self):
  894. assert_mpmath_equal(sc.ellipkinc,
  895. mpmath.ellipf,
  896. [Arg(-1e3, 1e3), Arg()])
  897. def test_ellipf_largephi(self):
  898. assert_mpmath_equal(sc.ellipkinc,
  899. mpmath.ellipf,
  900. [Arg(), Arg()])
  901. def test_ellipk(self):
  902. assert_mpmath_equal(sc.ellipk,
  903. mpmath.ellipk,
  904. [Arg(b=1.0)])
  905. assert_mpmath_equal(sc.ellipkm1,
  906. lambda m: mpmath.ellipk(1 - m),
  907. [Arg(a=0.0)],
  908. dps=400)
  909. def test_ellipkinc(self):
  910. def ellipkinc(phi, m):
  911. return mpmath.ellippi(0, phi, m)
  912. assert_mpmath_equal(sc.ellipkinc,
  913. ellipkinc,
  914. [Arg(-1e3, 1e3), Arg(b=1.0)],
  915. ignore_inf_sign=True)
  916. def test_ellipkinc_largephi(self):
  917. def ellipkinc(phi, m):
  918. return mpmath.ellippi(0, phi, m)
  919. assert_mpmath_equal(sc.ellipkinc,
  920. ellipkinc,
  921. [Arg(), Arg(b=1.0)],
  922. ignore_inf_sign=True)
  923. def test_ellipfun_sn(self):
  924. def sn(u, m):
  925. # mpmath doesn't get the zero at u = 0--fix that
  926. if u == 0:
  927. return 0
  928. else:
  929. return mpmath.ellipfun("sn", u=u, m=m)
  930. # Oscillating function --- limit range of first argument; the
  931. # loss of precision there is an expected numerical feature
  932. # rather than an actual bug
  933. assert_mpmath_equal(lambda u, m: sc.ellipj(u, m)[0],
  934. sn,
  935. [Arg(-1e6, 1e6), Arg(a=0, b=1)],
  936. rtol=1e-8)
  937. def test_ellipfun_cn(self):
  938. # see comment in ellipfun_sn
  939. assert_mpmath_equal(lambda u, m: sc.ellipj(u, m)[1],
  940. lambda u, m: mpmath.ellipfun("cn", u=u, m=m),
  941. [Arg(-1e6, 1e6), Arg(a=0, b=1)],
  942. rtol=1e-8)
  943. def test_ellipfun_dn(self):
  944. # see comment in ellipfun_sn
  945. assert_mpmath_equal(lambda u, m: sc.ellipj(u, m)[2],
  946. lambda u, m: mpmath.ellipfun("dn", u=u, m=m),
  947. [Arg(-1e6, 1e6), Arg(a=0, b=1)],
  948. rtol=1e-8)
  949. def test_erf(self):
  950. assert_mpmath_equal(sc.erf,
  951. lambda z: mpmath.erf(z),
  952. [Arg()])
  953. def test_erf_complex(self):
  954. assert_mpmath_equal(sc.erf,
  955. lambda z: mpmath.erf(z),
  956. [ComplexArg()], n=200)
  957. def test_erfc(self):
  958. assert_mpmath_equal(sc.erfc,
  959. exception_to_nan(lambda z: mpmath.erfc(z)),
  960. [Arg()], rtol=1e-13)
  961. def test_erfc_complex(self):
  962. assert_mpmath_equal(sc.erfc,
  963. exception_to_nan(lambda z: mpmath.erfc(z)),
  964. [ComplexArg()], n=200)
  965. def test_erfi(self):
  966. assert_mpmath_equal(sc.erfi,
  967. mpmath.erfi,
  968. [Arg()], n=200)
  969. def test_erfi_complex(self):
  970. assert_mpmath_equal(sc.erfi,
  971. mpmath.erfi,
  972. [ComplexArg()], n=200)
  973. def test_ndtr(self):
  974. assert_mpmath_equal(sc.ndtr,
  975. exception_to_nan(lambda z: mpmath.ncdf(z)),
  976. [Arg()], n=200)
  977. def test_ndtr_complex(self):
  978. assert_mpmath_equal(sc.ndtr,
  979. lambda z: mpmath.erfc(-z/np.sqrt(2.))/2.,
  980. [ComplexArg(a=complex(-10000, -10000), b=complex(10000, 10000))], n=400)
  981. def test_log_ndtr(self):
  982. assert_mpmath_equal(sc.log_ndtr,
  983. exception_to_nan(lambda z: mpmath.log(mpmath.ncdf(z))),
  984. [Arg()], n=600, dps=300)
  985. def test_log_ndtr_complex(self):
  986. assert_mpmath_equal(sc.log_ndtr,
  987. exception_to_nan(lambda z: mpmath.log(mpmath.erfc(-z/np.sqrt(2.))/2.)),
  988. [ComplexArg(a=complex(-10000, -100),
  989. b=complex(10000, 100))], n=200, dps=300)
  990. def test_eulernum(self):
  991. assert_mpmath_equal(lambda n: sc.euler(n)[-1],
  992. mpmath.eulernum,
  993. [IntArg(1, 10000)], n=10000)
  994. def test_expint(self):
  995. assert_mpmath_equal(sc.expn,
  996. mpmath.expint,
  997. [IntArg(0, 200), Arg(0, np.inf)],
  998. rtol=1e-13, dps=160)
  999. def test_fresnels(self):
  1000. def fresnels(x):
  1001. return sc.fresnel(x)[0]
  1002. assert_mpmath_equal(fresnels,
  1003. mpmath.fresnels,
  1004. [Arg()])
  1005. def test_fresnelc(self):
  1006. def fresnelc(x):
  1007. return sc.fresnel(x)[1]
  1008. assert_mpmath_equal(fresnelc,
  1009. mpmath.fresnelc,
  1010. [Arg()])
  1011. def test_gamma(self):
  1012. assert_mpmath_equal(sc.gamma,
  1013. exception_to_nan(mpmath.gamma),
  1014. [Arg()])
  1015. def test_gamma_complex(self):
  1016. assert_mpmath_equal(sc.gamma,
  1017. exception_to_nan(mpmath.gamma),
  1018. [ComplexArg()], rtol=5e-13)
  1019. def test_gammainc(self):
  1020. # Larger arguments are tested in test_data.py:test_local
  1021. assert_mpmath_equal(sc.gammainc,
  1022. lambda z, b: mpmath.gammainc(z, b=b, regularized=True),
  1023. [Arg(0, 1e4, inclusive_a=False), Arg(0, 1e4)],
  1024. nan_ok=False, rtol=1e-11)
  1025. def test_gammaincc(self):
  1026. # Larger arguments are tested in test_data.py:test_local
  1027. assert_mpmath_equal(sc.gammaincc,
  1028. lambda z, a: mpmath.gammainc(z, a=a, regularized=True),
  1029. [Arg(0, 1e4, inclusive_a=False), Arg(0, 1e4)],
  1030. nan_ok=False, rtol=1e-11)
  1031. def test_gammaln(self):
  1032. # The real part of loggamma is log(|gamma(z)|).
  1033. def f(z):
  1034. return mpmath.loggamma(z).real
  1035. assert_mpmath_equal(sc.gammaln, exception_to_nan(f), [Arg()])
  1036. @pytest.mark.xfail(run=False)
  1037. def test_gegenbauer(self):
  1038. assert_mpmath_equal(sc.eval_gegenbauer,
  1039. exception_to_nan(mpmath.gegenbauer),
  1040. [Arg(-1e3, 1e3), Arg(), Arg()])
  1041. def test_gegenbauer_int(self):
  1042. # Redefine functions to deal with numerical + mpmath issues
  1043. def gegenbauer(n, a, x):
  1044. # Avoid overflow at large `a` (mpmath would need an even larger
  1045. # dps to handle this correctly, so just skip this region)
  1046. if abs(a) > 1e100:
  1047. return np.nan
  1048. # Deal with n=0, n=1 correctly; mpmath 0.17 doesn't do these
  1049. # always correctly
  1050. if n == 0:
  1051. r = 1.0
  1052. elif n == 1:
  1053. r = 2*a*x
  1054. else:
  1055. r = mpmath.gegenbauer(n, a, x)
  1056. # Mpmath 0.17 gives wrong results (spurious zero) in some cases, so
  1057. # compute the value by perturbing the result
  1058. if float(r) == 0 and a < -1 and float(a) == int(float(a)):
  1059. r = mpmath.gegenbauer(n, a + mpmath.mpf('1e-50'), x)
  1060. if abs(r) < mpmath.mpf('1e-50'):
  1061. r = mpmath.mpf('0.0')
  1062. # Differing overflow thresholds in scipy vs. mpmath
  1063. if abs(r) > 1e270:
  1064. return np.inf
  1065. return r
  1066. def sc_gegenbauer(n, a, x):
  1067. r = sc.eval_gegenbauer(int(n), a, x)
  1068. # Differing overflow thresholds in scipy vs. mpmath
  1069. if abs(r) > 1e270:
  1070. return np.inf
  1071. return r
  1072. assert_mpmath_equal(sc_gegenbauer,
  1073. exception_to_nan(gegenbauer),
  1074. [IntArg(0, 100), Arg(-1e9, 1e9), Arg()],
  1075. n=40000, dps=100,
  1076. ignore_inf_sign=True, rtol=1e-6)
  1077. # Check the small-x expansion
  1078. assert_mpmath_equal(sc_gegenbauer,
  1079. exception_to_nan(gegenbauer),
  1080. [IntArg(0, 100), Arg(), FixedArg(np.logspace(-30, -4, 30))],
  1081. dps=100,
  1082. ignore_inf_sign=True)
  1083. @pytest.mark.xfail(run=False)
  1084. def test_gegenbauer_complex(self):
  1085. assert_mpmath_equal(lambda n, a, x: sc.eval_gegenbauer(int(n), a.real, x),
  1086. exception_to_nan(mpmath.gegenbauer),
  1087. [IntArg(0, 100), Arg(), ComplexArg()])
  1088. @nonfunctional_tooslow
  1089. def test_gegenbauer_complex_general(self):
  1090. assert_mpmath_equal(lambda n, a, x: sc.eval_gegenbauer(n.real, a.real, x),
  1091. exception_to_nan(mpmath.gegenbauer),
  1092. [Arg(-1e3, 1e3), Arg(), ComplexArg()])
  1093. def test_hankel1(self):
  1094. assert_mpmath_equal(sc.hankel1,
  1095. exception_to_nan(lambda v, x: mpmath.hankel1(v, x,
  1096. **HYPERKW)),
  1097. [Arg(-1e20, 1e20), Arg()])
  1098. def test_hankel2(self):
  1099. assert_mpmath_equal(sc.hankel2,
  1100. exception_to_nan(lambda v, x: mpmath.hankel2(v, x, **HYPERKW)),
  1101. [Arg(-1e20, 1e20), Arg()])
  1102. @pytest.mark.xfail(run=False, reason="issues at intermediately large orders")
  1103. def test_hermite(self):
  1104. assert_mpmath_equal(lambda n, x: sc.eval_hermite(int(n), x),
  1105. exception_to_nan(mpmath.hermite),
  1106. [IntArg(0, 10000), Arg()])
  1107. # hurwitz: same as zeta
  1108. def test_hyp0f1(self):
  1109. # mpmath reports no convergence unless maxterms is large enough
  1110. KW = dict(maxprec=400, maxterms=1500)
  1111. # n=500 (non-xslow default) fails for one bad point
  1112. assert_mpmath_equal(sc.hyp0f1,
  1113. lambda a, x: mpmath.hyp0f1(a, x, **KW),
  1114. [Arg(-1e7, 1e7), Arg(0, 1e5)],
  1115. n=5000)
  1116. # NB: The range of the second parameter ("z") is limited from below
  1117. # because of an overflow in the intermediate calculations. The way
  1118. # for fix it is to implement an asymptotic expansion for Bessel J
  1119. # (similar to what is implemented for Bessel I here).
  1120. def test_hyp0f1_complex(self):
  1121. assert_mpmath_equal(lambda a, z: sc.hyp0f1(a.real, z),
  1122. exception_to_nan(lambda a, x: mpmath.hyp0f1(a, x, **HYPERKW)),
  1123. [Arg(-10, 10), ComplexArg(complex(-120, -120), complex(120, 120))])
  1124. # NB: The range of the first parameter ("v") are limited by an overflow
  1125. # in the intermediate calculations. Can be fixed by implementing an
  1126. # asymptotic expansion for Bessel functions for large order.
  1127. @pytest.mark.xfail(run=False)
  1128. def test_hyp1f1(self):
  1129. assert_mpmath_equal(inf_to_nan(sc.hyp1f1),
  1130. exception_to_nan(lambda a, b, x: mpmath.hyp1f1(a, b, x, **HYPERKW)),
  1131. [Arg(-1e5, 1e5), Arg(-1e5, 1e5), Arg()],
  1132. n=2000)
  1133. @pytest.mark.xfail(run=False)
  1134. def test_hyp1f1_complex(self):
  1135. assert_mpmath_equal(inf_to_nan(lambda a, b, x: sc.hyp1f1(a.real, b.real, x)),
  1136. exception_to_nan(lambda a, b, x: mpmath.hyp1f1(a, b, x, **HYPERKW)),
  1137. [Arg(-1e3, 1e3), Arg(-1e3, 1e3), ComplexArg()],
  1138. n=2000)
  1139. @nonfunctional_tooslow
  1140. def test_hyp2f1_complex(self):
  1141. # Scipy's hyp2f1 seems to have performance and accuracy problems
  1142. assert_mpmath_equal(lambda a, b, c, x: sc.hyp2f1(a.real, b.real, c.real, x),
  1143. exception_to_nan(lambda a, b, c, x: mpmath.hyp2f1(a, b, c, x, **HYPERKW)),
  1144. [Arg(-1e2, 1e2), Arg(-1e2, 1e2), Arg(-1e2, 1e2), ComplexArg()],
  1145. n=10)
  1146. @pytest.mark.xfail(run=False)
  1147. def test_hyperu(self):
  1148. assert_mpmath_equal(sc.hyperu,
  1149. exception_to_nan(lambda a, b, x: mpmath.hyperu(a, b, x, **HYPERKW)),
  1150. [Arg(), Arg(), Arg()])
  1151. @pytest.mark.xfail(condition=_is_32bit_platform,
  1152. reason="mpmath issue gh-342: unsupported operand mpz, long for pow")
  1153. def test_igam_fac(self):
  1154. def mp_igam_fac(a, x):
  1155. return mpmath.power(x, a)*mpmath.exp(-x)/mpmath.gamma(a)
  1156. assert_mpmath_equal(_igam_fac,
  1157. mp_igam_fac,
  1158. [Arg(0, 1e14, inclusive_a=False), Arg(0, 1e14)],
  1159. rtol=1e-10)
  1160. def test_j0(self):
  1161. # The Bessel function at large arguments is j0(x) ~ cos(x + phi)/sqrt(x)
  1162. # and at large arguments the phase of the cosine loses precision.
  1163. #
  1164. # This is numerically expected behavior, so we compare only up to
  1165. # 1e8 = 1e15 * 1e-7
  1166. assert_mpmath_equal(sc.j0,
  1167. mpmath.j0,
  1168. [Arg(-1e3, 1e3)])
  1169. assert_mpmath_equal(sc.j0,
  1170. mpmath.j0,
  1171. [Arg(-1e8, 1e8)],
  1172. rtol=1e-5)
  1173. def test_j1(self):
  1174. # See comment in test_j0
  1175. assert_mpmath_equal(sc.j1,
  1176. mpmath.j1,
  1177. [Arg(-1e3, 1e3)])
  1178. assert_mpmath_equal(sc.j1,
  1179. mpmath.j1,
  1180. [Arg(-1e8, 1e8)],
  1181. rtol=1e-5)
  1182. @pytest.mark.xfail(run=False)
  1183. def test_jacobi(self):
  1184. assert_mpmath_equal(sc.eval_jacobi,
  1185. exception_to_nan(lambda a, b, c, x: mpmath.jacobi(a, b, c, x, **HYPERKW)),
  1186. [Arg(), Arg(), Arg(), Arg()])
  1187. assert_mpmath_equal(lambda n, b, c, x: sc.eval_jacobi(int(n), b, c, x),
  1188. exception_to_nan(lambda a, b, c, x: mpmath.jacobi(a, b, c, x, **HYPERKW)),
  1189. [IntArg(), Arg(), Arg(), Arg()])
  1190. def test_jacobi_int(self):
  1191. # Redefine functions to deal with numerical + mpmath issues
  1192. def jacobi(n, a, b, x):
  1193. # Mpmath does not handle n=0 case always correctly
  1194. if n == 0:
  1195. return 1.0
  1196. return mpmath.jacobi(n, a, b, x)
  1197. assert_mpmath_equal(lambda n, a, b, x: sc.eval_jacobi(int(n), a, b, x),
  1198. lambda n, a, b, x: exception_to_nan(jacobi)(n, a, b, x, **HYPERKW),
  1199. [IntArg(), Arg(), Arg(), Arg()],
  1200. n=20000, dps=50)
  1201. def test_kei(self):
  1202. def kei(x):
  1203. if x == 0:
  1204. # work around mpmath issue at x=0
  1205. return -pi/4
  1206. return exception_to_nan(mpmath.kei)(0, x, **HYPERKW)
  1207. assert_mpmath_equal(sc.kei,
  1208. kei,
  1209. [Arg(-1e30, 1e30)], n=1000)
  1210. def test_ker(self):
  1211. assert_mpmath_equal(sc.ker,
  1212. exception_to_nan(lambda x: mpmath.ker(0, x, **HYPERKW)),
  1213. [Arg(-1e30, 1e30)], n=1000)
  1214. @nonfunctional_tooslow
  1215. def test_laguerre(self):
  1216. assert_mpmath_equal(trace_args(sc.eval_laguerre),
  1217. lambda n, x: exception_to_nan(mpmath.laguerre)(n, x, **HYPERKW),
  1218. [Arg(), Arg()])
  1219. def test_laguerre_int(self):
  1220. assert_mpmath_equal(lambda n, x: sc.eval_laguerre(int(n), x),
  1221. lambda n, x: exception_to_nan(mpmath.laguerre)(n, x, **HYPERKW),
  1222. [IntArg(), Arg()], n=20000)
  1223. @pytest.mark.xfail(condition=_is_32bit_platform, reason="see gh-3551 for bad points")
  1224. def test_lambertw_real(self):
  1225. assert_mpmath_equal(lambda x, k: sc.lambertw(x, int(k.real)),
  1226. lambda x, k: mpmath.lambertw(x, int(k.real)),
  1227. [ComplexArg(-np.inf, np.inf), IntArg(0, 10)],
  1228. rtol=1e-13, nan_ok=False)
  1229. def test_lanczos_sum_expg_scaled(self):
  1230. maxgamma = 171.624376956302725
  1231. e = np.exp(1)
  1232. g = 6.024680040776729583740234375
  1233. def gamma(x):
  1234. with np.errstate(over='ignore'):
  1235. fac = ((x + g - 0.5)/e)**(x - 0.5)
  1236. if fac != np.inf:
  1237. res = fac*_lanczos_sum_expg_scaled(x)
  1238. else:
  1239. fac = ((x + g - 0.5)/e)**(0.5*(x - 0.5))
  1240. res = fac*_lanczos_sum_expg_scaled(x)
  1241. res *= fac
  1242. return res
  1243. assert_mpmath_equal(gamma,
  1244. mpmath.gamma,
  1245. [Arg(0, maxgamma, inclusive_a=False)],
  1246. rtol=1e-13)
  1247. @nonfunctional_tooslow
  1248. def test_legendre(self):
  1249. assert_mpmath_equal(sc.eval_legendre,
  1250. mpmath.legendre,
  1251. [Arg(), Arg()])
  1252. def test_legendre_int(self):
  1253. assert_mpmath_equal(lambda n, x: sc.eval_legendre(int(n), x),
  1254. lambda n, x: exception_to_nan(mpmath.legendre)(n, x, **HYPERKW),
  1255. [IntArg(), Arg()],
  1256. n=20000)
  1257. # Check the small-x expansion
  1258. assert_mpmath_equal(lambda n, x: sc.eval_legendre(int(n), x),
  1259. lambda n, x: exception_to_nan(mpmath.legendre)(n, x, **HYPERKW),
  1260. [IntArg(), FixedArg(np.logspace(-30, -4, 20))])
  1261. def test_legenp(self):
  1262. def lpnm(n, m, z):
  1263. try:
  1264. v = sc.lpmn(m, n, z)[0][-1,-1]
  1265. except ValueError:
  1266. return np.nan
  1267. if abs(v) > 1e306:
  1268. # harmonize overflow to inf
  1269. v = np.inf * np.sign(v.real)
  1270. return v
  1271. def lpnm_2(n, m, z):
  1272. v = sc.lpmv(m, n, z)
  1273. if abs(v) > 1e306:
  1274. # harmonize overflow to inf
  1275. v = np.inf * np.sign(v.real)
  1276. return v
  1277. def legenp(n, m, z):
  1278. if (z == 1 or z == -1) and int(n) == n:
  1279. # Special case (mpmath may give inf, we take the limit by
  1280. # continuity)
  1281. if m == 0:
  1282. if n < 0:
  1283. n = -n - 1
  1284. return mpmath.power(mpmath.sign(z), n)
  1285. else:
  1286. return 0
  1287. if abs(z) < 1e-15:
  1288. # mpmath has bad performance here
  1289. return np.nan
  1290. typ = 2 if abs(z) < 1 else 3
  1291. v = exception_to_nan(mpmath.legenp)(n, m, z, type=typ)
  1292. if abs(v) > 1e306:
  1293. # harmonize overflow to inf
  1294. v = mpmath.inf * mpmath.sign(v.real)
  1295. return v
  1296. assert_mpmath_equal(lpnm,
  1297. legenp,
  1298. [IntArg(-100, 100), IntArg(-100, 100), Arg()])
  1299. assert_mpmath_equal(lpnm_2,
  1300. legenp,
  1301. [IntArg(-100, 100), Arg(-100, 100), Arg(-1, 1)],
  1302. atol=1e-10)
  1303. def test_legenp_complex_2(self):
  1304. def clpnm(n, m, z):
  1305. try:
  1306. return sc.clpmn(m.real, n.real, z, type=2)[0][-1,-1]
  1307. except ValueError:
  1308. return np.nan
  1309. def legenp(n, m, z):
  1310. if abs(z) < 1e-15:
  1311. # mpmath has bad performance here
  1312. return np.nan
  1313. return exception_to_nan(mpmath.legenp)(int(n.real), int(m.real), z, type=2)
  1314. # mpmath is quite slow here
  1315. x = np.array([-2, -0.99, -0.5, 0, 1e-5, 0.5, 0.99, 20, 2e3])
  1316. y = np.array([-1e3, -0.5, 0.5, 1.3])
  1317. z = (x[:,None] + 1j*y[None,:]).ravel()
  1318. assert_mpmath_equal(clpnm,
  1319. legenp,
  1320. [FixedArg([-2, -1, 0, 1, 2, 10]), FixedArg([-2, -1, 0, 1, 2, 10]), FixedArg(z)],
  1321. rtol=1e-6,
  1322. n=500)
  1323. def test_legenp_complex_3(self):
  1324. def clpnm(n, m, z):
  1325. try:
  1326. return sc.clpmn(m.real, n.real, z, type=3)[0][-1,-1]
  1327. except ValueError:
  1328. return np.nan
  1329. def legenp(n, m, z):
  1330. if abs(z) < 1e-15:
  1331. # mpmath has bad performance here
  1332. return np.nan
  1333. return exception_to_nan(mpmath.legenp)(int(n.real), int(m.real), z, type=3)
  1334. # mpmath is quite slow here
  1335. x = np.array([-2, -0.99, -0.5, 0, 1e-5, 0.5, 0.99, 20, 2e3])
  1336. y = np.array([-1e3, -0.5, 0.5, 1.3])
  1337. z = (x[:,None] + 1j*y[None,:]).ravel()
  1338. assert_mpmath_equal(clpnm,
  1339. legenp,
  1340. [FixedArg([-2, -1, 0, 1, 2, 10]), FixedArg([-2, -1, 0, 1, 2, 10]), FixedArg(z)],
  1341. rtol=1e-6,
  1342. n=500)
  1343. @pytest.mark.xfail(run=False, reason="apparently picks wrong function at |z| > 1")
  1344. def test_legenq(self):
  1345. def lqnm(n, m, z):
  1346. return sc.lqmn(m, n, z)[0][-1,-1]
  1347. def legenq(n, m, z):
  1348. if abs(z) < 1e-15:
  1349. # mpmath has bad performance here
  1350. return np.nan
  1351. return exception_to_nan(mpmath.legenq)(n, m, z, type=2)
  1352. assert_mpmath_equal(lqnm,
  1353. legenq,
  1354. [IntArg(0, 100), IntArg(0, 100), Arg()])
  1355. @nonfunctional_tooslow
  1356. def test_legenq_complex(self):
  1357. def lqnm(n, m, z):
  1358. return sc.lqmn(int(m.real), int(n.real), z)[0][-1,-1]
  1359. def legenq(n, m, z):
  1360. if abs(z) < 1e-15:
  1361. # mpmath has bad performance here
  1362. return np.nan
  1363. return exception_to_nan(mpmath.legenq)(int(n.real), int(m.real), z, type=2)
  1364. assert_mpmath_equal(lqnm,
  1365. legenq,
  1366. [IntArg(0, 100), IntArg(0, 100), ComplexArg()],
  1367. n=100)
  1368. def test_lgam1p(self):
  1369. def param_filter(x):
  1370. # Filter the poles
  1371. return np.where((np.floor(x) == x) & (x <= 0), False, True)
  1372. def mp_lgam1p(z):
  1373. # The real part of loggamma is log(|gamma(z)|)
  1374. return mpmath.loggamma(1 + z).real
  1375. assert_mpmath_equal(_lgam1p,
  1376. mp_lgam1p,
  1377. [Arg()], rtol=1e-13, dps=100,
  1378. param_filter=param_filter)
  1379. def test_loggamma(self):
  1380. def mpmath_loggamma(z):
  1381. try:
  1382. res = mpmath.loggamma(z)
  1383. except ValueError:
  1384. res = complex(np.nan, np.nan)
  1385. return res
  1386. assert_mpmath_equal(sc.loggamma,
  1387. mpmath_loggamma,
  1388. [ComplexArg()], nan_ok=False,
  1389. distinguish_nan_and_inf=False, rtol=5e-14)
  1390. @pytest.mark.xfail(run=False)
  1391. def test_pcfd(self):
  1392. def pcfd(v, x):
  1393. return sc.pbdv(v, x)[0]
  1394. assert_mpmath_equal(pcfd,
  1395. exception_to_nan(lambda v, x: mpmath.pcfd(v, x, **HYPERKW)),
  1396. [Arg(), Arg()])
  1397. @pytest.mark.xfail(run=False, reason="it's not the same as the mpmath function --- maybe different definition?")
  1398. def test_pcfv(self):
  1399. def pcfv(v, x):
  1400. return sc.pbvv(v, x)[0]
  1401. assert_mpmath_equal(pcfv,
  1402. lambda v, x: time_limited()(exception_to_nan(mpmath.pcfv))(v, x, **HYPERKW),
  1403. [Arg(), Arg()], n=1000)
  1404. def test_pcfw(self):
  1405. def pcfw(a, x):
  1406. return sc.pbwa(a, x)[0]
  1407. def dpcfw(a, x):
  1408. return sc.pbwa(a, x)[1]
  1409. def mpmath_dpcfw(a, x):
  1410. return mpmath.diff(mpmath.pcfw, (a, x), (0, 1))
  1411. # The Zhang and Jin implementation only uses Taylor series and
  1412. # is thus accurate in only a very small range.
  1413. assert_mpmath_equal(pcfw,
  1414. mpmath.pcfw,
  1415. [Arg(-5, 5), Arg(-5, 5)], rtol=2e-8, n=100)
  1416. assert_mpmath_equal(dpcfw,
  1417. mpmath_dpcfw,
  1418. [Arg(-5, 5), Arg(-5, 5)], rtol=2e-9, n=100)
  1419. @pytest.mark.xfail(run=False, reason="issues at large arguments (atol OK, rtol not) and <eps-close to z=0")
  1420. def test_polygamma(self):
  1421. assert_mpmath_equal(sc.polygamma,
  1422. time_limited()(exception_to_nan(mpmath.polygamma)),
  1423. [IntArg(0, 1000), Arg()])
  1424. def test_rgamma(self):
  1425. def rgamma(x):
  1426. if x < -8000:
  1427. return np.inf
  1428. else:
  1429. v = mpmath.rgamma(x)
  1430. return v
  1431. # n=500 (non-xslow default) fails for one bad point
  1432. assert_mpmath_equal(sc.rgamma,
  1433. rgamma,
  1434. [Arg()],
  1435. n=5000,
  1436. ignore_inf_sign=True)
  1437. def test_rgamma_complex(self):
  1438. assert_mpmath_equal(sc.rgamma,
  1439. exception_to_nan(mpmath.rgamma),
  1440. [ComplexArg()], rtol=5e-13)
  1441. @pytest.mark.xfail(reason=("see gh-3551 for bad points on 32 bit "
  1442. "systems and gh-8095 for another bad "
  1443. "point"))
  1444. def test_rf(self):
  1445. if LooseVersion(mpmath.__version__) >= LooseVersion("1.0.0"):
  1446. # no workarounds needed
  1447. mppoch = mpmath.rf
  1448. else:
  1449. def mppoch(a, m):
  1450. # deal with cases where the result in double precision
  1451. # hits exactly a non-positive integer, but the
  1452. # corresponding extended-precision mpf floats don't
  1453. if float(a + m) == int(a + m) and float(a + m) <= 0:
  1454. a = mpmath.mpf(a)
  1455. m = int(a + m) - a
  1456. return mpmath.rf(a, m)
  1457. assert_mpmath_equal(sc.poch,
  1458. mppoch,
  1459. [Arg(), Arg()],
  1460. dps=400)
  1461. def test_sinpi(self):
  1462. eps = np.finfo(float).eps
  1463. assert_mpmath_equal(_sinpi, mpmath.sinpi,
  1464. [Arg()], nan_ok=False, rtol=eps)
  1465. def test_sinpi_complex(self):
  1466. assert_mpmath_equal(_sinpi, mpmath.sinpi,
  1467. [ComplexArg()], nan_ok=False, rtol=2e-14)
  1468. def test_shi(self):
  1469. def shi(x):
  1470. return sc.shichi(x)[0]
  1471. assert_mpmath_equal(shi, mpmath.shi, [Arg()])
  1472. # check asymptotic series cross-over
  1473. assert_mpmath_equal(shi, mpmath.shi, [FixedArg([88 - 1e-9, 88, 88 + 1e-9])])
  1474. def test_shi_complex(self):
  1475. def shi(z):
  1476. return sc.shichi(z)[0]
  1477. # shi oscillates as Im[z] -> +- inf, so limit range
  1478. assert_mpmath_equal(shi,
  1479. mpmath.shi,
  1480. [ComplexArg(complex(-np.inf, -1e8), complex(np.inf, 1e8))],
  1481. rtol=1e-12)
  1482. def test_si(self):
  1483. def si(x):
  1484. return sc.sici(x)[0]
  1485. assert_mpmath_equal(si, mpmath.si, [Arg()])
  1486. def test_si_complex(self):
  1487. def si(z):
  1488. return sc.sici(z)[0]
  1489. # si oscillates as Re[z] -> +- inf, so limit range
  1490. assert_mpmath_equal(si,
  1491. mpmath.si,
  1492. [ComplexArg(complex(-1e8, -np.inf), complex(1e8, np.inf))],
  1493. rtol=1e-12)
  1494. def test_spence(self):
  1495. # mpmath uses a different convention for the dilogarithm
  1496. def dilog(x):
  1497. return mpmath.polylog(2, 1 - x)
  1498. # Spence has a branch cut on the negative real axis
  1499. assert_mpmath_equal(sc.spence,
  1500. exception_to_nan(dilog),
  1501. [Arg(0, np.inf)], rtol=1e-14)
  1502. def test_spence_complex(self):
  1503. def dilog(z):
  1504. return mpmath.polylog(2, 1 - z)
  1505. assert_mpmath_equal(sc.spence,
  1506. exception_to_nan(dilog),
  1507. [ComplexArg()], rtol=1e-14)
  1508. def test_spherharm(self):
  1509. def spherharm(l, m, theta, phi):
  1510. if m > l:
  1511. return np.nan
  1512. return sc.sph_harm(m, l, phi, theta)
  1513. assert_mpmath_equal(spherharm,
  1514. mpmath.spherharm,
  1515. [IntArg(0, 100), IntArg(0, 100),
  1516. Arg(a=0, b=pi), Arg(a=0, b=2*pi)],
  1517. atol=1e-8, n=6000,
  1518. dps=150)
  1519. def test_struveh(self):
  1520. assert_mpmath_equal(sc.struve,
  1521. exception_to_nan(mpmath.struveh),
  1522. [Arg(-1e4, 1e4), Arg(0, 1e4)],
  1523. rtol=5e-10)
  1524. def test_struvel(self):
  1525. def mp_struvel(v, z):
  1526. if v < 0 and z < -v and abs(v) > 1000:
  1527. # larger DPS needed for correct results
  1528. old_dps = mpmath.mp.dps
  1529. try:
  1530. mpmath.mp.dps = 300
  1531. return mpmath.struvel(v, z)
  1532. finally:
  1533. mpmath.mp.dps = old_dps
  1534. return mpmath.struvel(v, z)
  1535. assert_mpmath_equal(sc.modstruve,
  1536. exception_to_nan(mp_struvel),
  1537. [Arg(-1e4, 1e4), Arg(0, 1e4)],
  1538. rtol=5e-10,
  1539. ignore_inf_sign=True)
  1540. def test_wrightomega(self):
  1541. assert_mpmath_equal(sc.wrightomega,
  1542. lambda z: _mpmath_wrightomega(z, 25),
  1543. [ComplexArg()], rtol=1e-14, nan_ok=False)
  1544. def test_zeta(self):
  1545. assert_mpmath_equal(sc.zeta,
  1546. exception_to_nan(mpmath.zeta),
  1547. [Arg(a=1, b=1e10, inclusive_a=False),
  1548. Arg(a=0, inclusive_a=False)])
  1549. def test_zetac(self):
  1550. assert_mpmath_equal(sc.zetac,
  1551. lambda x: mpmath.zeta(x) - 1,
  1552. [Arg(-100, 100)],
  1553. nan_ok=False, dps=45, rtol=1e-13)
  1554. def test_boxcox(self):
  1555. def mp_boxcox(x, lmbda):
  1556. x = mpmath.mp.mpf(x)
  1557. lmbda = mpmath.mp.mpf(lmbda)
  1558. if lmbda == 0:
  1559. return mpmath.mp.log(x)
  1560. else:
  1561. return mpmath.mp.powm1(x, lmbda) / lmbda
  1562. assert_mpmath_equal(sc.boxcox,
  1563. exception_to_nan(mp_boxcox),
  1564. [Arg(a=0, inclusive_a=False), Arg()],
  1565. n=200,
  1566. dps=60,
  1567. rtol=1e-13)
  1568. def test_boxcox1p(self):
  1569. def mp_boxcox1p(x, lmbda):
  1570. x = mpmath.mp.mpf(x)
  1571. lmbda = mpmath.mp.mpf(lmbda)
  1572. one = mpmath.mp.mpf(1)
  1573. if lmbda == 0:
  1574. return mpmath.mp.log(one + x)
  1575. else:
  1576. return mpmath.mp.powm1(one + x, lmbda) / lmbda
  1577. assert_mpmath_equal(sc.boxcox1p,
  1578. exception_to_nan(mp_boxcox1p),
  1579. [Arg(a=-1, inclusive_a=False), Arg()],
  1580. n=200,
  1581. dps=60,
  1582. rtol=1e-13)
  1583. def test_spherical_jn(self):
  1584. def mp_spherical_jn(n, z):
  1585. arg = mpmath.mpmathify(z)
  1586. out = (mpmath.besselj(n + mpmath.mpf(1)/2, arg) /
  1587. mpmath.sqrt(2*arg/mpmath.pi))
  1588. if arg.imag == 0:
  1589. return out.real
  1590. else:
  1591. return out
  1592. assert_mpmath_equal(lambda n, z: sc.spherical_jn(int(n), z),
  1593. exception_to_nan(mp_spherical_jn),
  1594. [IntArg(0, 200), Arg(-1e8, 1e8)],
  1595. dps=300)
  1596. def test_spherical_jn_complex(self):
  1597. def mp_spherical_jn(n, z):
  1598. arg = mpmath.mpmathify(z)
  1599. out = (mpmath.besselj(n + mpmath.mpf(1)/2, arg) /
  1600. mpmath.sqrt(2*arg/mpmath.pi))
  1601. if arg.imag == 0:
  1602. return out.real
  1603. else:
  1604. return out
  1605. assert_mpmath_equal(lambda n, z: sc.spherical_jn(int(n.real), z),
  1606. exception_to_nan(mp_spherical_jn),
  1607. [IntArg(0, 200), ComplexArg()])
  1608. def test_spherical_yn(self):
  1609. def mp_spherical_yn(n, z):
  1610. arg = mpmath.mpmathify(z)
  1611. out = (mpmath.bessely(n + mpmath.mpf(1)/2, arg) /
  1612. mpmath.sqrt(2*arg/mpmath.pi))
  1613. if arg.imag == 0:
  1614. return out.real
  1615. else:
  1616. return out
  1617. assert_mpmath_equal(lambda n, z: sc.spherical_yn(int(n), z),
  1618. exception_to_nan(mp_spherical_yn),
  1619. [IntArg(0, 200), Arg(-1e10, 1e10)],
  1620. dps=100)
  1621. def test_spherical_yn_complex(self):
  1622. def mp_spherical_yn(n, z):
  1623. arg = mpmath.mpmathify(z)
  1624. out = (mpmath.bessely(n + mpmath.mpf(1)/2, arg) /
  1625. mpmath.sqrt(2*arg/mpmath.pi))
  1626. if arg.imag == 0:
  1627. return out.real
  1628. else:
  1629. return out
  1630. assert_mpmath_equal(lambda n, z: sc.spherical_yn(int(n.real), z),
  1631. exception_to_nan(mp_spherical_yn),
  1632. [IntArg(0, 200), ComplexArg()])
  1633. def test_spherical_in(self):
  1634. def mp_spherical_in(n, z):
  1635. arg = mpmath.mpmathify(z)
  1636. out = (mpmath.besseli(n + mpmath.mpf(1)/2, arg) /
  1637. mpmath.sqrt(2*arg/mpmath.pi))
  1638. if arg.imag == 0:
  1639. return out.real
  1640. else:
  1641. return out
  1642. assert_mpmath_equal(lambda n, z: sc.spherical_in(int(n), z),
  1643. exception_to_nan(mp_spherical_in),
  1644. [IntArg(0, 200), Arg()],
  1645. dps=200, atol=10**(-278))
  1646. def test_spherical_in_complex(self):
  1647. def mp_spherical_in(n, z):
  1648. arg = mpmath.mpmathify(z)
  1649. out = (mpmath.besseli(n + mpmath.mpf(1)/2, arg) /
  1650. mpmath.sqrt(2*arg/mpmath.pi))
  1651. if arg.imag == 0:
  1652. return out.real
  1653. else:
  1654. return out
  1655. assert_mpmath_equal(lambda n, z: sc.spherical_in(int(n.real), z),
  1656. exception_to_nan(mp_spherical_in),
  1657. [IntArg(0, 200), ComplexArg()])
  1658. def test_spherical_kn(self):
  1659. def mp_spherical_kn(n, z):
  1660. out = (mpmath.besselk(n + mpmath.mpf(1)/2, z) *
  1661. mpmath.sqrt(mpmath.pi/(2*mpmath.mpmathify(z))))
  1662. if mpmath.mpmathify(z).imag == 0:
  1663. return out.real
  1664. else:
  1665. return out
  1666. assert_mpmath_equal(lambda n, z: sc.spherical_kn(int(n), z),
  1667. exception_to_nan(mp_spherical_kn),
  1668. [IntArg(0, 150), Arg()],
  1669. dps=100)
  1670. @pytest.mark.xfail(run=False, reason="Accuracy issues near z = -1 inherited from kv.")
  1671. def test_spherical_kn_complex(self):
  1672. def mp_spherical_kn(n, z):
  1673. arg = mpmath.mpmathify(z)
  1674. out = (mpmath.besselk(n + mpmath.mpf(1)/2, arg) /
  1675. mpmath.sqrt(2*arg/mpmath.pi))
  1676. if arg.imag == 0:
  1677. return out.real
  1678. else:
  1679. return out
  1680. assert_mpmath_equal(lambda n, z: sc.spherical_kn(int(n.real), z),
  1681. exception_to_nan(mp_spherical_kn),
  1682. [IntArg(0, 200), ComplexArg()],
  1683. dps=200)