test_quadpack.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  1. import sys
  2. import math
  3. import numpy as np
  4. from numpy import sqrt, cos, sin, arctan, exp, log, pi
  5. from numpy.testing import (assert_,
  6. assert_allclose, assert_array_less, assert_almost_equal, assert_equal)
  7. import pytest
  8. from scipy.integrate import quad, dblquad, tplquad, nquad
  9. from scipy.special import erf, erfc
  10. from scipy._lib._ccallback import LowLevelCallable
  11. from scipy._lib._array_api import make_xp_test_case
  12. import ctypes
  13. import ctypes.util
  14. from scipy._lib._ccallback_c import sine_ctypes
  15. import scipy.integrate._test_multivariate as clib_test
  16. def assert_quad(value_and_err, tabled_value, error_tolerance=1.5e-8):
  17. value, err = value_and_err
  18. assert_allclose(value, tabled_value, atol=err, rtol=0)
  19. if error_tolerance is not None:
  20. assert_array_less(err, error_tolerance)
  21. def get_clib_test_routine(name, restype, *argtypes):
  22. ptr = getattr(clib_test, name)
  23. return ctypes.cast(ptr, ctypes.CFUNCTYPE(restype, *argtypes))
  24. @make_xp_test_case(quad)
  25. class TestCtypesQuad:
  26. def setup_method(self):
  27. if sys.platform == 'win32':
  28. files = ['api-ms-win-crt-math-l1-1-0.dll']
  29. elif sys.platform == 'darwin':
  30. files = ['libm.dylib']
  31. else:
  32. files = ['libm.so', 'libm.so.6']
  33. for file in files:
  34. try:
  35. self.lib = ctypes.CDLL(file)
  36. break
  37. except OSError:
  38. pass
  39. else:
  40. # This test doesn't work on some Linux platforms (Fedora for
  41. # example) that put an ld script in libm.so - see gh-5370
  42. pytest.skip("Ctypes can't import libm.so")
  43. restype = ctypes.c_double
  44. argtypes = (ctypes.c_double,)
  45. for name in ['sin', 'cos', 'tan']:
  46. func = getattr(self.lib, name)
  47. func.restype = restype
  48. func.argtypes = argtypes
  49. def test_typical(self):
  50. assert_quad(quad(self.lib.sin, 0, 5), quad(math.sin, 0, 5)[0])
  51. assert_quad(quad(self.lib.cos, 0, 5), quad(math.cos, 0, 5)[0])
  52. assert_quad(quad(self.lib.tan, 0, 1), quad(math.tan, 0, 1)[0])
  53. def test_ctypes_sine(self):
  54. quad(LowLevelCallable(sine_ctypes), 0, 1)
  55. def test_ctypes_variants(self):
  56. sin_0 = get_clib_test_routine('_sin_0', ctypes.c_double,
  57. ctypes.c_double, ctypes.c_void_p)
  58. sin_1 = get_clib_test_routine('_sin_1', ctypes.c_double,
  59. ctypes.c_int, ctypes.POINTER(ctypes.c_double),
  60. ctypes.c_void_p)
  61. sin_2 = get_clib_test_routine('_sin_2', ctypes.c_double,
  62. ctypes.c_double)
  63. sin_3 = get_clib_test_routine('_sin_3', ctypes.c_double,
  64. ctypes.c_int, ctypes.POINTER(ctypes.c_double))
  65. sin_4 = get_clib_test_routine('_sin_3', ctypes.c_double,
  66. ctypes.c_int, ctypes.c_double)
  67. all_sigs = [sin_0, sin_1, sin_2, sin_3, sin_4]
  68. legacy_sigs = [sin_2, sin_4]
  69. legacy_only_sigs = [sin_4]
  70. # LowLevelCallables work for new signatures
  71. for j, func in enumerate(all_sigs):
  72. callback = LowLevelCallable(func)
  73. if func in legacy_only_sigs:
  74. pytest.raises(ValueError, quad, callback, 0, pi)
  75. else:
  76. assert_allclose(quad(callback, 0, pi)[0], 2.0)
  77. # Plain ctypes items work only for legacy signatures
  78. for j, func in enumerate(legacy_sigs):
  79. if func in legacy_sigs:
  80. assert_allclose(quad(func, 0, pi)[0], 2.0)
  81. else:
  82. pytest.raises(ValueError, quad, func, 0, pi)
  83. @make_xp_test_case(quad)
  84. class TestMultivariateCtypesQuad:
  85. def setup_method(self):
  86. restype = ctypes.c_double
  87. argtypes = (ctypes.c_int, ctypes.c_double)
  88. for name in ['_multivariate_typical', '_multivariate_indefinite',
  89. '_multivariate_sin']:
  90. func = get_clib_test_routine(name, restype, *argtypes)
  91. setattr(self, name, func)
  92. def test_typical(self):
  93. # 1) Typical function with two extra arguments:
  94. assert_quad(quad(self._multivariate_typical, 0, pi, (2, 1.8)),
  95. 0.30614353532540296487)
  96. def test_indefinite(self):
  97. # 2) Infinite integration limits --- Euler's constant
  98. assert_quad(quad(self._multivariate_indefinite, 0, np.inf),
  99. 0.577215664901532860606512)
  100. def test_threadsafety(self):
  101. # Ensure multivariate ctypes are threadsafe
  102. def threadsafety(y):
  103. return y + quad(self._multivariate_sin, 0, 1)[0]
  104. assert_quad(quad(threadsafety, 0, 1), 0.9596976941318602)
  105. @make_xp_test_case(quad)
  106. class TestQuad:
  107. def test_typical(self):
  108. # 1) Typical function with two extra arguments:
  109. def myfunc(x, n, z): # Bessel function integrand
  110. return cos(n*x-z*sin(x))/pi
  111. assert_quad(quad(myfunc, 0, pi, (2, 1.8)), 0.30614353532540296487)
  112. def test_indefinite(self):
  113. # 2) Infinite integration limits --- Euler's constant
  114. def myfunc(x): # Euler's constant integrand
  115. return -exp(-x)*log(x)
  116. assert_quad(quad(myfunc, 0, np.inf), 0.577215664901532860606512)
  117. def test_singular(self):
  118. # 3) Singular points in region of integration.
  119. def myfunc(x):
  120. if 0 < x < 2.5:
  121. return sin(x)
  122. elif 2.5 <= x <= 5.0:
  123. return exp(-x)
  124. else:
  125. return 0.0
  126. assert_quad(quad(myfunc, 0, 10, points=[2.5, 5.0]),
  127. 1 - cos(2.5) + exp(-2.5) - exp(-5.0))
  128. def test_sine_weighted_finite(self):
  129. # 4) Sine weighted integral (finite limits)
  130. def myfunc(x, a):
  131. return exp(a*(x-1))
  132. ome = 2.0**3.4
  133. assert_quad(quad(myfunc, 0, 1, args=20, weight='sin', wvar=ome),
  134. (20*sin(ome)-ome*cos(ome)+ome*exp(-20))/(20**2 + ome**2))
  135. def test_sine_weighted_infinite(self):
  136. # 5) Sine weighted integral (infinite limits)
  137. def myfunc(x, a):
  138. return exp(-x*a)
  139. a = 4.0
  140. ome = 3.0
  141. assert_quad(quad(myfunc, 0, np.inf, args=a, weight='sin', wvar=ome),
  142. ome/(a**2 + ome**2))
  143. def test_cosine_weighted_infinite(self):
  144. # 6) Cosine weighted integral (negative infinite limits)
  145. def myfunc(x, a):
  146. return exp(x*a)
  147. a = 2.5
  148. ome = 2.3
  149. assert_quad(quad(myfunc, -np.inf, 0, args=a, weight='cos', wvar=ome),
  150. a/(a**2 + ome**2))
  151. def test_algebraic_log_weight(self):
  152. # 6) Algebraic-logarithmic weight.
  153. def myfunc(x, a):
  154. return 1/(1+x+2**(-a))
  155. a = 1.5
  156. assert_quad(quad(myfunc, -1, 1, args=a, weight='alg',
  157. wvar=(-0.5, -0.5)),
  158. pi/sqrt((1+2**(-a))**2 - 1))
  159. def test_cauchypv_weight(self):
  160. # 7) Cauchy prinicpal value weighting w(x) = 1/(x-c)
  161. def myfunc(x, a):
  162. return 2.0**(-a)/((x-1)**2+4.0**(-a))
  163. a = 0.4
  164. tabledValue = ((2.0**(-0.4)*log(1.5) -
  165. 2.0**(-1.4)*log((4.0**(-a)+16) / (4.0**(-a)+1)) -
  166. arctan(2.0**(a+2)) -
  167. arctan(2.0**a)) /
  168. (4.0**(-a) + 1))
  169. assert_quad(quad(myfunc, 0, 5, args=0.4, weight='cauchy', wvar=2.0),
  170. tabledValue, error_tolerance=1.9e-8)
  171. def test_b_less_than_a(self):
  172. def f(x, p, q):
  173. return p * np.exp(-q*x)
  174. val_1, err_1 = quad(f, 0, np.inf, args=(2, 3))
  175. val_2, err_2 = quad(f, np.inf, 0, args=(2, 3))
  176. assert_allclose(val_1, -val_2, atol=max(err_1, err_2))
  177. def test_b_less_than_a_2(self):
  178. def f(x, s):
  179. return np.exp(-x**2 / 2 / s) / np.sqrt(2.*s)
  180. val_1, err_1 = quad(f, -np.inf, np.inf, args=(2,))
  181. val_2, err_2 = quad(f, np.inf, -np.inf, args=(2,))
  182. assert_allclose(val_1, -val_2, atol=max(err_1, err_2))
  183. def test_b_less_than_a_3(self):
  184. def f(x):
  185. return 1.0
  186. val_1, err_1 = quad(f, 0, 1, weight='alg', wvar=(0, 0))
  187. val_2, err_2 = quad(f, 1, 0, weight='alg', wvar=(0, 0))
  188. assert_allclose(val_1, -val_2, atol=max(err_1, err_2))
  189. def test_b_less_than_a_full_output(self):
  190. def f(x):
  191. return 1.0
  192. res_1 = quad(f, 0, 1, weight='alg', wvar=(0, 0), full_output=True)
  193. res_2 = quad(f, 1, 0, weight='alg', wvar=(0, 0), full_output=True)
  194. err = max(res_1[1], res_2[1])
  195. assert_allclose(res_1[0], -res_2[0], atol=err)
  196. @pytest.mark.parametrize("complex_func", [True, False])
  197. def test_b_equals_a(self, complex_func):
  198. def f(x):
  199. return 1/x
  200. upper = lower = 0.
  201. limit = 50
  202. expected_infodict = {"neval": 0, "last": 0,
  203. "alist": np.full(limit, np.nan, dtype=np.float64),
  204. "blist": np.full(limit, np.nan, dtype=np.float64),
  205. "rlist": np.zeros(limit, dtype=np.float64),
  206. "elist": np.zeros(limit, dtype=np.float64),
  207. "iord" : np.zeros(limit, dtype=np.int32)}
  208. zero, err, infodict = quad(f, lower, upper, full_output=1,
  209. complex_func=complex_func)
  210. assert (zero, err) == (0., 0.)
  211. if complex_func:
  212. assert_equal(infodict, {"real": expected_infodict,
  213. "imag": expected_infodict})
  214. else:
  215. assert_equal(infodict, expected_infodict)
  216. def test_complex(self):
  217. def tfunc(x):
  218. return np.exp(1j*x)
  219. assert np.allclose(
  220. quad(tfunc, 0, np.pi/2, complex_func=True)[0],
  221. 1+1j)
  222. # We consider a divergent case in order to force quadpack
  223. # to return an error message. The output is compared
  224. # against what is returned by explicit integration
  225. # of the parts.
  226. kwargs = {'a': 0, 'b': np.inf, 'full_output': True,
  227. 'weight': 'cos', 'wvar': 1}
  228. res_c = quad(tfunc, complex_func=True, **kwargs)
  229. res_r = quad(lambda x: np.real(np.exp(1j*x)),
  230. complex_func=False,
  231. **kwargs)
  232. res_i = quad(lambda x: np.imag(np.exp(1j*x)),
  233. complex_func=False,
  234. **kwargs)
  235. np.testing.assert_equal(res_c[0], res_r[0] + 1j*res_i[0])
  236. np.testing.assert_equal(res_c[1], res_r[1] + 1j*res_i[1])
  237. assert len(res_c[2]['real']) == len(res_r[2:]) == 3
  238. assert res_c[2]['real'][2] == res_r[4]
  239. assert res_c[2]['real'][1] == res_r[3]
  240. assert res_c[2]['real'][0]['lst'] == res_r[2]['lst']
  241. assert len(res_c[2]['imag']) == len(res_i[2:]) == 1
  242. assert res_c[2]['imag'][0]['lst'] == res_i[2]['lst']
  243. @make_xp_test_case(dblquad)
  244. class TestDblquad:
  245. def test_double_integral(self):
  246. # 8) Double Integral test
  247. def simpfunc(y, x): # Note order of arguments.
  248. return x+y
  249. a, b = 1.0, 2.0
  250. assert_quad(dblquad(simpfunc, a, b, lambda x: x, lambda x: 2*x),
  251. 5/6.0 * (b**3.0-a**3.0))
  252. def test_double_integral2(self):
  253. def func(x0, x1, t0, t1):
  254. return x0 + x1 + t0 + t1
  255. def g(x):
  256. return x
  257. def h(x):
  258. return 2 * x
  259. args = 1, 2
  260. assert_quad(dblquad(func, 1, 2, g, h, args=args),35./6 + 9*.5)
  261. def test_double_integral3(self):
  262. def func(x0, x1):
  263. return x0 + x1 + 1 + 2
  264. assert_quad(dblquad(func, 1, 2, 1, 2),6.)
  265. @pytest.mark.parametrize(
  266. "x_lower, x_upper, y_lower, y_upper, expected",
  267. [
  268. # Multiple integration of a function in n = 2 variables: f(x, y, z)
  269. # over domain D = [-inf, 0] for all n.
  270. (-np.inf, 0, -np.inf, 0, np.pi / 4),
  271. # Multiple integration of a function in n = 2 variables: f(x, y, z)
  272. # over domain D = [-inf, -1] for each n (one at a time).
  273. (-np.inf, -1, -np.inf, 0, np.pi / 4 * erfc(1)),
  274. (-np.inf, 0, -np.inf, -1, np.pi / 4 * erfc(1)),
  275. # Multiple integration of a function in n = 2 variables: f(x, y, z)
  276. # over domain D = [-inf, -1] for all n.
  277. (-np.inf, -1, -np.inf, -1, np.pi / 4 * (erfc(1) ** 2)),
  278. # Multiple integration of a function in n = 2 variables: f(x, y, z)
  279. # over domain D = [-inf, 1] for each n (one at a time).
  280. (-np.inf, 1, -np.inf, 0, np.pi / 4 * (erf(1) + 1)),
  281. (-np.inf, 0, -np.inf, 1, np.pi / 4 * (erf(1) + 1)),
  282. # Multiple integration of a function in n = 2 variables: f(x, y, z)
  283. # over domain D = [-inf, 1] for all n.
  284. (-np.inf, 1, -np.inf, 1, np.pi / 4 * ((erf(1) + 1) ** 2)),
  285. # Multiple integration of a function in n = 2 variables: f(x, y, z)
  286. # over domain Dx = [-inf, -1] and Dy = [-inf, 1].
  287. (-np.inf, -1, -np.inf, 1, np.pi / 4 * ((erf(1) + 1) * erfc(1))),
  288. # Multiple integration of a function in n = 2 variables: f(x, y, z)
  289. # over domain Dx = [-inf, 1] and Dy = [-inf, -1].
  290. (-np.inf, 1, -np.inf, -1, np.pi / 4 * ((erf(1) + 1) * erfc(1))),
  291. # Multiple integration of a function in n = 2 variables: f(x, y, z)
  292. # over domain D = [0, inf] for all n.
  293. (0, np.inf, 0, np.inf, np.pi / 4),
  294. # Multiple integration of a function in n = 2 variables: f(x, y, z)
  295. # over domain D = [1, inf] for each n (one at a time).
  296. (1, np.inf, 0, np.inf, np.pi / 4 * erfc(1)),
  297. (0, np.inf, 1, np.inf, np.pi / 4 * erfc(1)),
  298. # Multiple integration of a function in n = 2 variables: f(x, y, z)
  299. # over domain D = [1, inf] for all n.
  300. (1, np.inf, 1, np.inf, np.pi / 4 * (erfc(1) ** 2)),
  301. # Multiple integration of a function in n = 2 variables: f(x, y, z)
  302. # over domain D = [-1, inf] for each n (one at a time).
  303. (-1, np.inf, 0, np.inf, np.pi / 4 * (erf(1) + 1)),
  304. (0, np.inf, -1, np.inf, np.pi / 4 * (erf(1) + 1)),
  305. # Multiple integration of a function in n = 2 variables: f(x, y, z)
  306. # over domain D = [-1, inf] for all n.
  307. (-1, np.inf, -1, np.inf, np.pi / 4 * ((erf(1) + 1) ** 2)),
  308. # Multiple integration of a function in n = 2 variables: f(x, y, z)
  309. # over domain Dx = [-1, inf] and Dy = [1, inf].
  310. (-1, np.inf, 1, np.inf, np.pi / 4 * ((erf(1) + 1) * erfc(1))),
  311. # Multiple integration of a function in n = 2 variables: f(x, y, z)
  312. # over domain Dx = [1, inf] and Dy = [-1, inf].
  313. (1, np.inf, -1, np.inf, np.pi / 4 * ((erf(1) + 1) * erfc(1))),
  314. # Multiple integration of a function in n = 2 variables: f(x, y, z)
  315. # over domain D = [-inf, inf] for all n.
  316. (-np.inf, np.inf, -np.inf, np.inf, np.pi),
  317. # Multiple integration of a function in n = 2 variables: f(x, y, z)
  318. # over domain D = [0, 0] for each n (one at a time).
  319. (0, 0, 0, np.inf, 0.),
  320. (0, np.inf, 0, 0, 0.),
  321. ]
  322. )
  323. def test_double_integral_improper(
  324. self, x_lower, x_upper, y_lower, y_upper, expected
  325. ):
  326. # The Gaussian Integral.
  327. def f(x, y):
  328. return np.exp(-x ** 2 - y ** 2)
  329. assert_quad(
  330. dblquad(f, x_lower, x_upper, y_lower, y_upper),
  331. expected,
  332. error_tolerance=3e-8
  333. )
  334. @make_xp_test_case(tplquad)
  335. class TestTplquad:
  336. def test_triple_integral(self):
  337. # 9) Triple Integral test
  338. def simpfunc(z, y, x, t): # Note order of arguments.
  339. return (x+y+z)*t
  340. a, b = 1.0, 2.0
  341. assert_quad(tplquad(simpfunc, a, b,
  342. lambda x: x, lambda x: 2*x,
  343. lambda x, y: x - y, lambda x, y: x + y,
  344. (2.,)),
  345. 2*8/3.0 * (b**4.0 - a**4.0))
  346. @pytest.mark.xslow
  347. @pytest.mark.parametrize(
  348. "x_lower, x_upper, y_lower, y_upper, z_lower, z_upper, expected",
  349. [
  350. # Multiple integration of a function in n = 3 variables: f(x, y, z)
  351. # over domain D = [-inf, 0] for all n.
  352. (-np.inf, 0, -np.inf, 0, -np.inf, 0, (np.pi ** (3 / 2)) / 8),
  353. # Multiple integration of a function in n = 3 variables: f(x, y, z)
  354. # over domain D = [-inf, -1] for each n (one at a time).
  355. (-np.inf, -1, -np.inf, 0, -np.inf, 0,
  356. (np.pi ** (3 / 2)) / 8 * erfc(1)),
  357. (-np.inf, 0, -np.inf, -1, -np.inf, 0,
  358. (np.pi ** (3 / 2)) / 8 * erfc(1)),
  359. (-np.inf, 0, -np.inf, 0, -np.inf, -1,
  360. (np.pi ** (3 / 2)) / 8 * erfc(1)),
  361. # Multiple integration of a function in n = 3 variables: f(x, y, z)
  362. # over domain D = [-inf, -1] for each n (two at a time).
  363. (-np.inf, -1, -np.inf, -1, -np.inf, 0,
  364. (np.pi ** (3 / 2)) / 8 * (erfc(1) ** 2)),
  365. (-np.inf, -1, -np.inf, 0, -np.inf, -1,
  366. (np.pi ** (3 / 2)) / 8 * (erfc(1) ** 2)),
  367. (-np.inf, 0, -np.inf, -1, -np.inf, -1,
  368. (np.pi ** (3 / 2)) / 8 * (erfc(1) ** 2)),
  369. # Multiple integration of a function in n = 3 variables: f(x, y, z)
  370. # over domain D = [-inf, -1] for all n.
  371. (-np.inf, -1, -np.inf, -1, -np.inf, -1,
  372. (np.pi ** (3 / 2)) / 8 * (erfc(1) ** 3)),
  373. # Multiple integration of a function in n = 3 variables: f(x, y, z)
  374. # over domain Dx = [-inf, -1] and Dy = Dz = [-inf, 1].
  375. (-np.inf, -1, -np.inf, 1, -np.inf, 1,
  376. (np.pi ** (3 / 2)) / 8 * (((erf(1) + 1) ** 2) * erfc(1))),
  377. # Multiple integration of a function in n = 3 variables: f(x, y, z)
  378. # over domain Dx = Dy = [-inf, -1] and Dz = [-inf, 1].
  379. (-np.inf, -1, -np.inf, -1, -np.inf, 1,
  380. (np.pi ** (3 / 2)) / 8 * ((erf(1) + 1) * (erfc(1) ** 2))),
  381. # Multiple integration of a function in n = 3 variables: f(x, y, z)
  382. # over domain Dx = Dz = [-inf, -1] and Dy = [-inf, 1].
  383. (-np.inf, -1, -np.inf, 1, -np.inf, -1,
  384. (np.pi ** (3 / 2)) / 8 * ((erf(1) + 1) * (erfc(1) ** 2))),
  385. # Multiple integration of a function in n = 3 variables: f(x, y, z)
  386. # over domain Dx = [-inf, 1] and Dy = Dz = [-inf, -1].
  387. (-np.inf, 1, -np.inf, -1, -np.inf, -1,
  388. (np.pi ** (3 / 2)) / 8 * ((erf(1) + 1) * (erfc(1) ** 2))),
  389. # Multiple integration of a function in n = 3 variables: f(x, y, z)
  390. # over domain Dx = Dy = [-inf, 1] and Dz = [-inf, -1].
  391. (-np.inf, 1, -np.inf, 1, -np.inf, -1,
  392. (np.pi ** (3 / 2)) / 8 * (((erf(1) + 1) ** 2) * erfc(1))),
  393. # Multiple integration of a function in n = 3 variables: f(x, y, z)
  394. # over domain Dx = Dz = [-inf, 1] and Dy = [-inf, -1].
  395. (-np.inf, 1, -np.inf, -1, -np.inf, 1,
  396. (np.pi ** (3 / 2)) / 8 * (((erf(1) + 1) ** 2) * erfc(1))),
  397. # Multiple integration of a function in n = 3 variables: f(x, y, z)
  398. # over domain D = [-inf, 1] for each n (one at a time).
  399. (-np.inf, 1, -np.inf, 0, -np.inf, 0,
  400. (np.pi ** (3 / 2)) / 8 * (erf(1) + 1)),
  401. (-np.inf, 0, -np.inf, 1, -np.inf, 0,
  402. (np.pi ** (3 / 2)) / 8 * (erf(1) + 1)),
  403. (-np.inf, 0, -np.inf, 0, -np.inf, 1,
  404. (np.pi ** (3 / 2)) / 8 * (erf(1) + 1)),
  405. # Multiple integration of a function in n = 3 variables: f(x, y, z)
  406. # over domain D = [-inf, 1] for each n (two at a time).
  407. (-np.inf, 1, -np.inf, 1, -np.inf, 0,
  408. (np.pi ** (3 / 2)) / 8 * ((erf(1) + 1) ** 2)),
  409. (-np.inf, 1, -np.inf, 0, -np.inf, 1,
  410. (np.pi ** (3 / 2)) / 8 * ((erf(1) + 1) ** 2)),
  411. (-np.inf, 0, -np.inf, 1, -np.inf, 1,
  412. (np.pi ** (3 / 2)) / 8 * ((erf(1) + 1) ** 2)),
  413. # Multiple integration of a function in n = 3 variables: f(x, y, z)
  414. # over domain D = [-inf, 1] for all n.
  415. (-np.inf, 1, -np.inf, 1, -np.inf, 1,
  416. (np.pi ** (3 / 2)) / 8 * ((erf(1) + 1) ** 3)),
  417. # Multiple integration of a function in n = 3 variables: f(x, y, z)
  418. # over domain D = [0, inf] for all n.
  419. (0, np.inf, 0, np.inf, 0, np.inf, (np.pi ** (3 / 2)) / 8),
  420. # Multiple integration of a function in n = 3 variables: f(x, y, z)
  421. # over domain D = [1, inf] for each n (one at a time).
  422. (1, np.inf, 0, np.inf, 0, np.inf,
  423. (np.pi ** (3 / 2)) / 8 * erfc(1)),
  424. (0, np.inf, 1, np.inf, 0, np.inf,
  425. (np.pi ** (3 / 2)) / 8 * erfc(1)),
  426. (0, np.inf, 0, np.inf, 1, np.inf,
  427. (np.pi ** (3 / 2)) / 8 * erfc(1)),
  428. # Multiple integration of a function in n = 3 variables: f(x, y, z)
  429. # over domain D = [1, inf] for each n (two at a time).
  430. (1, np.inf, 1, np.inf, 0, np.inf,
  431. (np.pi ** (3 / 2)) / 8 * (erfc(1) ** 2)),
  432. (1, np.inf, 0, np.inf, 1, np.inf,
  433. (np.pi ** (3 / 2)) / 8 * (erfc(1) ** 2)),
  434. (0, np.inf, 1, np.inf, 1, np.inf,
  435. (np.pi ** (3 / 2)) / 8 * (erfc(1) ** 2)),
  436. # Multiple integration of a function in n = 3 variables: f(x, y, z)
  437. # over domain D = [1, inf] for all n.
  438. (1, np.inf, 1, np.inf, 1, np.inf,
  439. (np.pi ** (3 / 2)) / 8 * (erfc(1) ** 3)),
  440. # Multiple integration of a function in n = 3 variables: f(x, y, z)
  441. # over domain D = [-1, inf] for each n (one at a time).
  442. (-1, np.inf, 0, np.inf, 0, np.inf,
  443. (np.pi ** (3 / 2)) / 8 * (erf(1) + 1)),
  444. (0, np.inf, -1, np.inf, 0, np.inf,
  445. (np.pi ** (3 / 2)) / 8 * (erf(1) + 1)),
  446. (0, np.inf, 0, np.inf, -1, np.inf,
  447. (np.pi ** (3 / 2)) / 8 * (erf(1) + 1)),
  448. # Multiple integration of a function in n = 3 variables: f(x, y, z)
  449. # over domain D = [-1, inf] for each n (two at a time).
  450. (-1, np.inf, -1, np.inf, 0, np.inf,
  451. (np.pi ** (3 / 2)) / 8 * ((erf(1) + 1) ** 2)),
  452. (-1, np.inf, 0, np.inf, -1, np.inf,
  453. (np.pi ** (3 / 2)) / 8 * ((erf(1) + 1) ** 2)),
  454. (0, np.inf, -1, np.inf, -1, np.inf,
  455. (np.pi ** (3 / 2)) / 8 * ((erf(1) + 1) ** 2)),
  456. # Multiple integration of a function in n = 3 variables: f(x, y, z)
  457. # over domain D = [-1, inf] for all n.
  458. (-1, np.inf, -1, np.inf, -1, np.inf,
  459. (np.pi ** (3 / 2)) / 8 * ((erf(1) + 1) ** 3)),
  460. # Multiple integration of a function in n = 3 variables: f(x, y, z)
  461. # over domain Dx = [1, inf] and Dy = Dz = [-1, inf].
  462. (1, np.inf, -1, np.inf, -1, np.inf,
  463. (np.pi ** (3 / 2)) / 8 * (((erf(1) + 1) ** 2) * erfc(1))),
  464. # Multiple integration of a function in n = 3 variables: f(x, y, z)
  465. # over domain Dx = Dy = [1, inf] and Dz = [-1, inf].
  466. (1, np.inf, 1, np.inf, -1, np.inf,
  467. (np.pi ** (3 / 2)) / 8 * ((erf(1) + 1) * (erfc(1) ** 2))),
  468. # Multiple integration of a function in n = 3 variables: f(x, y, z)
  469. # over domain Dx = Dz = [1, inf] and Dy = [-1, inf].
  470. (1, np.inf, -1, np.inf, 1, np.inf,
  471. (np.pi ** (3 / 2)) / 8 * ((erf(1) + 1) * (erfc(1) ** 2))),
  472. # Multiple integration of a function in n = 3 variables: f(x, y, z)
  473. # over domain Dx = [-1, inf] and Dy = Dz = [1, inf].
  474. (-1, np.inf, 1, np.inf, 1, np.inf,
  475. (np.pi ** (3 / 2)) / 8 * ((erf(1) + 1) * (erfc(1) ** 2))),
  476. # Multiple integration of a function in n = 3 variables: f(x, y, z)
  477. # over domain Dx = Dy = [-1, inf] and Dz = [1, inf].
  478. (-1, np.inf, -1, np.inf, 1, np.inf,
  479. (np.pi ** (3 / 2)) / 8 * (((erf(1) + 1) ** 2) * erfc(1))),
  480. # Multiple integration of a function in n = 3 variables: f(x, y, z)
  481. # over domain Dx = Dz = [-1, inf] and Dy = [1, inf].
  482. (-1, np.inf, 1, np.inf, -1, np.inf,
  483. (np.pi ** (3 / 2)) / 8 * (((erf(1) + 1) ** 2) * erfc(1))),
  484. # Multiple integration of a function in n = 3 variables: f(x, y, z)
  485. # over domain D = [-inf, inf] for all n.
  486. (-np.inf, np.inf, -np.inf, np.inf, -np.inf, np.inf,
  487. np.pi ** (3 / 2)),
  488. # Multiple integration of a function in n = 3 variables: f(x, y, z)
  489. # over domain D = [0, 0] for each n (one at a time).
  490. (0, 0, 0, np.inf, 0, np.inf, 0),
  491. (0, np.inf, 0, 0, 0, np.inf, 0),
  492. (0, np.inf, 0, np.inf, 0, 0, 0),
  493. ],
  494. )
  495. def test_triple_integral_improper(
  496. self,
  497. x_lower,
  498. x_upper,
  499. y_lower,
  500. y_upper,
  501. z_lower,
  502. z_upper,
  503. expected
  504. ):
  505. # The Gaussian Integral.
  506. def f(x, y, z):
  507. return np.exp(-x ** 2 - y ** 2 - z ** 2)
  508. assert_quad(
  509. tplquad(f, x_lower, x_upper, y_lower, y_upper, z_lower, z_upper),
  510. expected,
  511. error_tolerance=6e-8
  512. )
  513. @make_xp_test_case(nquad)
  514. class TestNQuad:
  515. @pytest.mark.fail_slow(5)
  516. def test_fixed_limits(self):
  517. def func1(x0, x1, x2, x3):
  518. val = (x0**2 + x1*x2 - x3**3 + np.sin(x0) +
  519. (1 if (x0 - 0.2*x3 - 0.5 - 0.25*x1 > 0) else 0))
  520. return val
  521. def opts_basic(*args):
  522. return {'points': [0.2*args[2] + 0.5 + 0.25*args[0]]}
  523. res = nquad(func1, [[0, 1], [-1, 1], [.13, .8], [-.15, 1]],
  524. opts=[opts_basic, {}, {}, {}], full_output=True)
  525. assert_quad(res[:-1], 1.5267454070738635)
  526. assert_(res[-1]['neval'] > 0 and res[-1]['neval'] < 4e5)
  527. @pytest.mark.fail_slow(5)
  528. def test_variable_limits(self):
  529. scale = .1
  530. def func2(x0, x1, x2, x3, t0, t1):
  531. val = (x0*x1*x3**2 + np.sin(x2) + 1 +
  532. (1 if x0 + t1*x1 - t0 > 0 else 0))
  533. return val
  534. def lim0(x1, x2, x3, t0, t1):
  535. return [scale * (x1**2 + x2 + np.cos(x3)*t0*t1 + 1) - 1,
  536. scale * (x1**2 + x2 + np.cos(x3)*t0*t1 + 1) + 1]
  537. def lim1(x2, x3, t0, t1):
  538. return [scale * (t0*x2 + t1*x3) - 1,
  539. scale * (t0*x2 + t1*x3) + 1]
  540. def lim2(x3, t0, t1):
  541. return [scale * (x3 + t0**2*t1**3) - 1,
  542. scale * (x3 + t0**2*t1**3) + 1]
  543. def lim3(t0, t1):
  544. return [scale * (t0 + t1) - 1, scale * (t0 + t1) + 1]
  545. def opts0(x1, x2, x3, t0, t1):
  546. return {'points': [t0 - t1*x1]}
  547. def opts1(x2, x3, t0, t1):
  548. return {}
  549. def opts2(x3, t0, t1):
  550. return {}
  551. def opts3(t0, t1):
  552. return {}
  553. res = nquad(func2, [lim0, lim1, lim2, lim3], args=(0, 0),
  554. opts=[opts0, opts1, opts2, opts3])
  555. assert_quad(res, 25.066666666666663)
  556. def test_square_separate_ranges_and_opts(self):
  557. def f(y, x):
  558. return 1.0
  559. assert_quad(nquad(f, [[-1, 1], [-1, 1]], opts=[{}, {}]), 4.0)
  560. def test_square_aliased_ranges_and_opts(self):
  561. def f(y, x):
  562. return 1.0
  563. r = [-1, 1]
  564. opt = {}
  565. assert_quad(nquad(f, [r, r], opts=[opt, opt]), 4.0)
  566. def test_square_separate_fn_ranges_and_opts(self):
  567. def f(y, x):
  568. return 1.0
  569. def fn_range0(*args):
  570. return (-1, 1)
  571. def fn_range1(*args):
  572. return (-1, 1)
  573. def fn_opt0(*args):
  574. return {}
  575. def fn_opt1(*args):
  576. return {}
  577. ranges = [fn_range0, fn_range1]
  578. opts = [fn_opt0, fn_opt1]
  579. assert_quad(nquad(f, ranges, opts=opts), 4.0)
  580. def test_square_aliased_fn_ranges_and_opts(self):
  581. def f(y, x):
  582. return 1.0
  583. def fn_range(*args):
  584. return (-1, 1)
  585. def fn_opt(*args):
  586. return {}
  587. ranges = [fn_range, fn_range]
  588. opts = [fn_opt, fn_opt]
  589. assert_quad(nquad(f, ranges, opts=opts), 4.0)
  590. def test_matching_quad(self):
  591. def func(x):
  592. return x**2 + 1
  593. res, reserr = quad(func, 0, 4)
  594. res2, reserr2 = nquad(func, ranges=[[0, 4]])
  595. assert_almost_equal(res, res2)
  596. assert_almost_equal(reserr, reserr2)
  597. def test_matching_dblquad(self):
  598. def func2d(x0, x1):
  599. return x0**2 + x1**3 - x0 * x1 + 1
  600. res, reserr = dblquad(func2d, -2, 2, lambda x: -3, lambda x: 3)
  601. res2, reserr2 = nquad(func2d, [[-3, 3], (-2, 2)])
  602. assert_almost_equal(res, res2)
  603. assert_almost_equal(reserr, reserr2)
  604. def test_matching_tplquad(self):
  605. def func3d(x0, x1, x2, c0, c1):
  606. return x0**2 + c0 * x1**3 - x0 * x1 + 1 + c1 * np.sin(x2)
  607. res = tplquad(func3d, -1, 2, lambda x: -2, lambda x: 2,
  608. lambda x, y: -np.pi, lambda x, y: np.pi,
  609. args=(2, 3))
  610. res2 = nquad(func3d, [[-np.pi, np.pi], [-2, 2], (-1, 2)], args=(2, 3))
  611. assert_almost_equal(res, res2)
  612. def test_dict_as_opts(self):
  613. try:
  614. nquad(lambda x, y: x * y, [[0, 1], [0, 1]], opts={'epsrel': 0.0001})
  615. except TypeError:
  616. assert False