test_quadpack.py 27 KB

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