_quad_vec.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676
  1. import sys
  2. import copy
  3. import heapq
  4. import collections
  5. import functools
  6. import numpy as np
  7. from scipy._lib._util import MapWrapper, _FunctionWrapper
  8. from scipy._lib._array_api import xp_capabilities
  9. class LRUDict(collections.OrderedDict):
  10. def __init__(self, max_size):
  11. self.__max_size = max_size
  12. def __setitem__(self, key, value):
  13. existing_key = (key in self)
  14. super().__setitem__(key, value)
  15. if existing_key:
  16. self.move_to_end(key)
  17. elif len(self) > self.__max_size:
  18. self.popitem(last=False)
  19. def update(self, other):
  20. # Not needed below
  21. raise NotImplementedError()
  22. class SemiInfiniteFunc:
  23. """
  24. Argument transform from (start, +-oo) to (0, 1)
  25. """
  26. def __init__(self, func, start, infty):
  27. self._func = func
  28. self._start = start
  29. self._sgn = -1 if infty < 0 else 1
  30. # Overflow threshold for the 1/t**2 factor
  31. self._tmin = sys.float_info.min**0.5
  32. def get_t(self, x):
  33. z = self._sgn * (x - self._start) + 1
  34. if z == 0:
  35. # Can happen only if point not in range
  36. return np.inf
  37. return 1 / z
  38. def __call__(self, t):
  39. if t < self._tmin:
  40. return 0.0
  41. else:
  42. x = self._start + self._sgn * (1 - t) / t
  43. f = self._func(x)
  44. return self._sgn * (f / t) / t
  45. class DoubleInfiniteFunc:
  46. """
  47. Argument transform from (-oo, oo) to (-1, 1)
  48. """
  49. def __init__(self, func):
  50. self._func = func
  51. # Overflow threshold for the 1/t**2 factor
  52. self._tmin = sys.float_info.min**0.5
  53. def get_t(self, x):
  54. s = -1 if x < 0 else 1
  55. return s / (abs(x) + 1)
  56. def __call__(self, t):
  57. if abs(t) < self._tmin:
  58. return 0.0
  59. else:
  60. x = (1 - abs(t)) / t
  61. f = self._func(x)
  62. return (f / t) / t
  63. def _max_norm(x):
  64. return np.amax(abs(x))
  65. def _get_sizeof(obj):
  66. try:
  67. return sys.getsizeof(obj)
  68. except TypeError:
  69. # occurs on pypy
  70. if hasattr(obj, '__sizeof__'):
  71. return int(obj.__sizeof__())
  72. return 64
  73. class _Bunch:
  74. def __init__(self, **kwargs):
  75. self.__keys = kwargs.keys()
  76. self.__dict__.update(**kwargs)
  77. def __repr__(self):
  78. key_value_pairs = ', '.join(
  79. f'{k}={repr(self.__dict__[k])}' for k in self.__keys
  80. )
  81. return f"_Bunch({key_value_pairs})"
  82. @xp_capabilities(np_only=True)
  83. def quad_vec(f, a, b, epsabs=1e-200, epsrel=1e-8, norm='2', cache_size=100e6,
  84. limit=10000, workers=1, points=None, quadrature=None, full_output=False,
  85. *, args=()):
  86. r"""Adaptive integration of a vector-valued function.
  87. Parameters
  88. ----------
  89. f : callable
  90. Vector-valued function f(x) to integrate.
  91. a : float
  92. Initial point.
  93. b : float
  94. Final point.
  95. epsabs : float, optional
  96. Absolute tolerance.
  97. epsrel : float, optional
  98. Relative tolerance.
  99. norm : {'max', '2'}, optional
  100. Vector norm to use for error estimation.
  101. cache_size : int, optional
  102. Number of bytes to use for memoization.
  103. limit : float or int, optional
  104. An upper bound on the number of subintervals used in the adaptive
  105. algorithm.
  106. workers : int or map-like callable, optional
  107. If `workers` is an integer, part of the computation is done in
  108. parallel subdivided to this many tasks (using
  109. :class:`python:multiprocessing.pool.Pool`).
  110. Supply `-1` to use all cores available to the Process.
  111. Alternatively, supply a map-like callable, such as
  112. :meth:`python:multiprocessing.pool.Pool.map` for evaluating the
  113. population in parallel.
  114. This evaluation is carried out as ``workers(func, iterable)``.
  115. points : list, optional
  116. List of additional breakpoints.
  117. quadrature : {'gk21', 'gk15', 'trapezoid'}, optional
  118. Quadrature rule to use on subintervals.
  119. Options: 'gk21' (Gauss-Kronrod 21-point rule),
  120. 'gk15' (Gauss-Kronrod 15-point rule),
  121. 'trapezoid' (composite trapezoid rule).
  122. Default: 'gk21' for finite intervals and 'gk15' for (semi-)infinite.
  123. full_output : bool, optional
  124. Return an additional ``info`` object.
  125. args : tuple, optional
  126. Extra arguments to pass to function, if any.
  127. .. versionadded:: 1.8.0
  128. Returns
  129. -------
  130. res : {float, array-like}
  131. Estimate for the result
  132. err : float
  133. Error estimate for the result in the given norm
  134. info : object
  135. Returned only when ``full_output=True``.
  136. Result object with the attributes:
  137. success : bool
  138. Whether integration reached target precision.
  139. status : int
  140. Indicator for convergence, success (0),
  141. failure (1), and failure due to rounding error (2).
  142. neval : int
  143. Number of function evaluations.
  144. intervals : ndarray, shape (num_intervals, 2)
  145. Start and end points of subdivision intervals.
  146. integrals : ndarray, shape (num_intervals, ...)
  147. Integral for each interval.
  148. Note that at most ``cache_size`` values are recorded,
  149. and the array may contains *nan* for missing items.
  150. errors : ndarray, shape (num_intervals,)
  151. Estimated integration error for each interval.
  152. Notes
  153. -----
  154. The algorithm mainly follows the implementation of QUADPACK's
  155. DQAG* algorithms, implementing global error control and adaptive
  156. subdivision.
  157. The algorithm here has some differences to the QUADPACK approach:
  158. Instead of subdividing one interval at a time, the algorithm
  159. subdivides N intervals with largest errors at once. This enables
  160. (partial) parallelization of the integration.
  161. The logic of subdividing "next largest" intervals first is then
  162. not implemented, and we rely on the above extension to avoid
  163. concentrating on "small" intervals only.
  164. The Wynn epsilon table extrapolation is not used (QUADPACK uses it
  165. for infinite intervals). This is because the algorithm here is
  166. supposed to work on vector-valued functions, in an user-specified
  167. norm, and the extension of the epsilon algorithm to this case does
  168. not appear to be widely agreed. For max-norm, using elementwise
  169. Wynn epsilon could be possible, but we do not do this here with
  170. the hope that the epsilon extrapolation is mainly useful in
  171. special cases.
  172. References
  173. ----------
  174. [1] R. Piessens, E. de Doncker, QUADPACK (1983).
  175. Examples
  176. --------
  177. We can compute integrations of a vector-valued function:
  178. >>> from scipy.integrate import quad_vec
  179. >>> import numpy as np
  180. >>> import matplotlib.pyplot as plt
  181. >>> alpha = np.linspace(0.0, 2.0, num=30)
  182. >>> f = lambda x: x**alpha
  183. >>> x0, x1 = 0, 2
  184. >>> y, err = quad_vec(f, x0, x1)
  185. >>> plt.plot(alpha, y)
  186. >>> plt.xlabel(r"$\alpha$")
  187. >>> plt.ylabel(r"$\int_{0}^{2} x^\alpha dx$")
  188. >>> plt.show()
  189. When using the argument `workers`, one should ensure
  190. that the main module is import-safe, for instance
  191. by rewriting the example above as:
  192. .. code-block:: python
  193. from scipy.integrate import quad_vec
  194. import numpy as np
  195. import matplotlib.pyplot as plt
  196. alpha = np.linspace(0.0, 2.0, num=30)
  197. x0, x1 = 0, 2
  198. def f(x):
  199. return x**alpha
  200. if __name__ == "__main__":
  201. y, err = quad_vec(f, x0, x1, workers=2)
  202. """
  203. a = float(a)
  204. b = float(b)
  205. if args:
  206. if not isinstance(args, tuple):
  207. args = (args,)
  208. # create a wrapped function to allow the use of map and Pool.map
  209. f = _FunctionWrapper(f, args)
  210. # Use simple transformations to deal with integrals over infinite
  211. # intervals.
  212. kwargs = dict(epsabs=epsabs,
  213. epsrel=epsrel,
  214. norm=norm,
  215. cache_size=cache_size,
  216. limit=limit,
  217. workers=workers,
  218. points=points,
  219. quadrature='gk15' if quadrature is None else quadrature,
  220. full_output=full_output)
  221. if np.isfinite(a) and np.isinf(b):
  222. f2 = SemiInfiniteFunc(f, start=a, infty=b)
  223. if points is not None:
  224. kwargs['points'] = tuple(f2.get_t(xp) for xp in points)
  225. return quad_vec(f2, 0, 1, **kwargs)
  226. elif np.isfinite(b) and np.isinf(a):
  227. f2 = SemiInfiniteFunc(f, start=b, infty=a)
  228. if points is not None:
  229. kwargs['points'] = tuple(f2.get_t(xp) for xp in points)
  230. res = quad_vec(f2, 0, 1, **kwargs)
  231. return (-res[0],) + res[1:]
  232. elif np.isinf(a) and np.isinf(b):
  233. sgn = -1 if b < a else 1
  234. # NB. explicitly split integral at t=0, which separates
  235. # the positive and negative sides
  236. f2 = DoubleInfiniteFunc(f)
  237. if points is not None:
  238. kwargs['points'] = (0,) + tuple(f2.get_t(xp) for xp in points)
  239. else:
  240. kwargs['points'] = (0,)
  241. if a != b:
  242. res = quad_vec(f2, -1, 1, **kwargs)
  243. else:
  244. res = quad_vec(f2, 1, 1, **kwargs)
  245. return (res[0]*sgn,) + res[1:]
  246. elif not (np.isfinite(a) and np.isfinite(b)):
  247. raise ValueError(f"invalid integration bounds a={a}, b={b}")
  248. norm_funcs = {
  249. None: _max_norm,
  250. 'max': _max_norm,
  251. '2': np.linalg.norm
  252. }
  253. if callable(norm):
  254. norm_func = norm
  255. else:
  256. norm_func = norm_funcs[norm]
  257. parallel_count = 128
  258. min_intervals = 2
  259. try:
  260. _quadrature = {None: _quadrature_gk21,
  261. 'gk21': _quadrature_gk21,
  262. 'gk15': _quadrature_gk15,
  263. 'trapezoid': _quadrature_trapezoid}[quadrature]
  264. except KeyError as e:
  265. raise ValueError(f"unknown quadrature {quadrature!r}") from e
  266. # Initial interval set
  267. if points is None:
  268. initial_intervals = [(a, b)]
  269. else:
  270. prev = a
  271. initial_intervals = []
  272. for p in sorted(points):
  273. p = float(p)
  274. if not (a < p < b) or p == prev:
  275. continue
  276. initial_intervals.append((prev, p))
  277. prev = p
  278. initial_intervals.append((prev, b))
  279. global_integral = None
  280. global_error = None
  281. rounding_error = None
  282. interval_cache = None
  283. intervals = []
  284. neval = 0
  285. for x1, x2 in initial_intervals:
  286. ig, err, rnd = _quadrature(x1, x2, f, norm_func)
  287. neval += _quadrature.num_eval
  288. if global_integral is None:
  289. if isinstance(ig, float | complex):
  290. # Specialize for scalars
  291. if norm_func in (_max_norm, np.linalg.norm):
  292. norm_func = abs
  293. global_integral = ig
  294. global_error = float(err)
  295. rounding_error = float(rnd)
  296. cache_count = cache_size // _get_sizeof(ig)
  297. interval_cache = LRUDict(cache_count)
  298. else:
  299. global_integral += ig
  300. global_error += err
  301. rounding_error += rnd
  302. interval_cache[(x1, x2)] = copy.copy(ig)
  303. intervals.append((-err, x1, x2))
  304. heapq.heapify(intervals)
  305. CONVERGED = 0
  306. NOT_CONVERGED = 1
  307. ROUNDING_ERROR = 2
  308. NOT_A_NUMBER = 3
  309. status_msg = {
  310. CONVERGED: "Target precision reached.",
  311. NOT_CONVERGED: "Target precision not reached.",
  312. ROUNDING_ERROR: "Target precision could not be reached due to rounding error.",
  313. NOT_A_NUMBER: "Non-finite values encountered."
  314. }
  315. # Process intervals
  316. with MapWrapper(workers) as mapwrapper:
  317. ier = NOT_CONVERGED
  318. while intervals and len(intervals) < limit:
  319. # Select intervals with largest errors for subdivision
  320. tol = max(epsabs, epsrel*norm_func(global_integral))
  321. to_process = []
  322. err_sum = 0
  323. for j in range(parallel_count):
  324. if not intervals:
  325. break
  326. if j > 0 and err_sum > global_error - tol/8:
  327. # avoid unnecessary parallel splitting
  328. break
  329. interval = heapq.heappop(intervals)
  330. neg_old_err, a, b = interval
  331. old_int = interval_cache.pop((a, b), None)
  332. to_process.append(
  333. ((-neg_old_err, a, b, old_int), f, norm_func, _quadrature)
  334. )
  335. err_sum += -neg_old_err
  336. # Subdivide intervals
  337. for parts in mapwrapper(_subdivide_interval, to_process):
  338. dint, derr, dround_err, subint, dneval = parts
  339. neval += dneval
  340. global_integral += dint
  341. global_error += derr
  342. rounding_error += dround_err
  343. for x in subint:
  344. x1, x2, ig, err = x
  345. interval_cache[(x1, x2)] = ig
  346. heapq.heappush(intervals, (-err, x1, x2))
  347. # Termination check
  348. if len(intervals) >= min_intervals:
  349. tol = max(epsabs, epsrel*norm_func(global_integral))
  350. if global_error < tol/8:
  351. ier = CONVERGED
  352. break
  353. if global_error < rounding_error:
  354. ier = ROUNDING_ERROR
  355. break
  356. if not (np.isfinite(global_error) and np.isfinite(rounding_error)):
  357. ier = NOT_A_NUMBER
  358. break
  359. res = global_integral
  360. err = global_error + rounding_error
  361. if full_output:
  362. res_arr = np.asarray(res)
  363. dummy = np.full(res_arr.shape, np.nan, dtype=res_arr.dtype)
  364. integrals = np.array([interval_cache.get((z[1], z[2]), dummy)
  365. for z in intervals], dtype=res_arr.dtype)
  366. errors = np.array([-z[0] for z in intervals])
  367. intervals = np.array([[z[1], z[2]] for z in intervals])
  368. info = _Bunch(neval=neval,
  369. success=(ier == CONVERGED),
  370. status=ier,
  371. message=status_msg[ier],
  372. intervals=intervals,
  373. integrals=integrals,
  374. errors=errors)
  375. return (res, err, info)
  376. else:
  377. return (res, err)
  378. def _subdivide_interval(args):
  379. interval, f, norm_func, _quadrature = args
  380. old_err, a, b, old_int = interval
  381. c = 0.5 * (a + b)
  382. # Left-hand side
  383. if getattr(_quadrature, 'cache_size', 0) > 0:
  384. f = functools.lru_cache(_quadrature.cache_size)(f)
  385. s1, err1, round1 = _quadrature(a, c, f, norm_func)
  386. dneval = _quadrature.num_eval
  387. s2, err2, round2 = _quadrature(c, b, f, norm_func)
  388. dneval += _quadrature.num_eval
  389. if old_int is None:
  390. old_int, _, _ = _quadrature(a, b, f, norm_func)
  391. dneval += _quadrature.num_eval
  392. if getattr(_quadrature, 'cache_size', 0) > 0:
  393. dneval = f.cache_info().misses
  394. dint = s1 + s2 - old_int
  395. derr = err1 + err2 - old_err
  396. dround_err = round1 + round2
  397. subintervals = ((a, c, s1, err1), (c, b, s2, err2))
  398. return dint, derr, dround_err, subintervals, dneval
  399. def _quadrature_trapezoid(x1, x2, f, norm_func):
  400. """
  401. Composite trapezoid quadrature
  402. """
  403. x3 = 0.5*(x1 + x2)
  404. f1 = f(x1)
  405. f2 = f(x2)
  406. f3 = f(x3)
  407. s2 = 0.25 * (x2 - x1) * (f1 + 2*f3 + f2)
  408. round_err = 0.25 * abs(x2 - x1) * (float(norm_func(f1))
  409. + 2*float(norm_func(f3))
  410. + float(norm_func(f2))) * 2e-16
  411. s1 = 0.5 * (x2 - x1) * (f1 + f2)
  412. err = 1/3 * float(norm_func(s1 - s2))
  413. return s2, err, round_err
  414. _quadrature_trapezoid.cache_size = 3 * 3
  415. _quadrature_trapezoid.num_eval = 3
  416. def _quadrature_gk(a, b, f, norm_func, x, w, v):
  417. """
  418. Generic Gauss-Kronrod quadrature
  419. """
  420. fv = [0.0]*len(x)
  421. c = 0.5 * (a + b)
  422. h = 0.5 * (b - a)
  423. # Gauss-Kronrod
  424. s_k = 0.0
  425. s_k_abs = 0.0
  426. for i in range(len(x)):
  427. ff = f(c + h*x[i])
  428. fv[i] = ff
  429. vv = v[i]
  430. # \int f(x)
  431. s_k += vv * ff
  432. # \int |f(x)|
  433. s_k_abs += vv * abs(ff)
  434. # Gauss
  435. s_g = 0.0
  436. for i in range(len(w)):
  437. s_g += w[i] * fv[2*i + 1]
  438. # Quadrature of abs-deviation from average
  439. s_k_dabs = 0.0
  440. y0 = s_k / 2.0
  441. for i in range(len(x)):
  442. # \int |f(x) - y0|
  443. s_k_dabs += v[i] * abs(fv[i] - y0)
  444. # Use similar error estimation as quadpack
  445. err = float(norm_func((s_k - s_g) * h))
  446. dabs = float(norm_func(s_k_dabs * h))
  447. if dabs != 0 and err != 0:
  448. err = dabs * min(1.0, (200 * err / dabs)**1.5)
  449. eps = sys.float_info.epsilon
  450. round_err = float(norm_func(50 * eps * h * s_k_abs))
  451. if round_err > sys.float_info.min:
  452. err = max(err, round_err)
  453. return h * s_k, err, round_err
  454. def _quadrature_gk21(a, b, f, norm_func):
  455. """
  456. Gauss-Kronrod 21 quadrature with error estimate
  457. """
  458. # Gauss-Kronrod points
  459. x = (0.995657163025808080735527280689003,
  460. 0.973906528517171720077964012084452,
  461. 0.930157491355708226001207180059508,
  462. 0.865063366688984510732096688423493,
  463. 0.780817726586416897063717578345042,
  464. 0.679409568299024406234327365114874,
  465. 0.562757134668604683339000099272694,
  466. 0.433395394129247190799265943165784,
  467. 0.294392862701460198131126603103866,
  468. 0.148874338981631210884826001129720,
  469. 0,
  470. -0.148874338981631210884826001129720,
  471. -0.294392862701460198131126603103866,
  472. -0.433395394129247190799265943165784,
  473. -0.562757134668604683339000099272694,
  474. -0.679409568299024406234327365114874,
  475. -0.780817726586416897063717578345042,
  476. -0.865063366688984510732096688423493,
  477. -0.930157491355708226001207180059508,
  478. -0.973906528517171720077964012084452,
  479. -0.995657163025808080735527280689003)
  480. # 10-point weights
  481. w = (0.066671344308688137593568809893332,
  482. 0.149451349150580593145776339657697,
  483. 0.219086362515982043995534934228163,
  484. 0.269266719309996355091226921569469,
  485. 0.295524224714752870173892994651338,
  486. 0.295524224714752870173892994651338,
  487. 0.269266719309996355091226921569469,
  488. 0.219086362515982043995534934228163,
  489. 0.149451349150580593145776339657697,
  490. 0.066671344308688137593568809893332)
  491. # 21-point weights
  492. v = (0.011694638867371874278064396062192,
  493. 0.032558162307964727478818972459390,
  494. 0.054755896574351996031381300244580,
  495. 0.075039674810919952767043140916190,
  496. 0.093125454583697605535065465083366,
  497. 0.109387158802297641899210590325805,
  498. 0.123491976262065851077958109831074,
  499. 0.134709217311473325928054001771707,
  500. 0.142775938577060080797094273138717,
  501. 0.147739104901338491374841515972068,
  502. 0.149445554002916905664936468389821,
  503. 0.147739104901338491374841515972068,
  504. 0.142775938577060080797094273138717,
  505. 0.134709217311473325928054001771707,
  506. 0.123491976262065851077958109831074,
  507. 0.109387158802297641899210590325805,
  508. 0.093125454583697605535065465083366,
  509. 0.075039674810919952767043140916190,
  510. 0.054755896574351996031381300244580,
  511. 0.032558162307964727478818972459390,
  512. 0.011694638867371874278064396062192)
  513. return _quadrature_gk(a, b, f, norm_func, x, w, v)
  514. _quadrature_gk21.num_eval = 21
  515. def _quadrature_gk15(a, b, f, norm_func):
  516. """
  517. Gauss-Kronrod 15 quadrature with error estimate
  518. """
  519. # Gauss-Kronrod points
  520. x = (0.991455371120812639206854697526329,
  521. 0.949107912342758524526189684047851,
  522. 0.864864423359769072789712788640926,
  523. 0.741531185599394439863864773280788,
  524. 0.586087235467691130294144838258730,
  525. 0.405845151377397166906606412076961,
  526. 0.207784955007898467600689403773245,
  527. 0.000000000000000000000000000000000,
  528. -0.207784955007898467600689403773245,
  529. -0.405845151377397166906606412076961,
  530. -0.586087235467691130294144838258730,
  531. -0.741531185599394439863864773280788,
  532. -0.864864423359769072789712788640926,
  533. -0.949107912342758524526189684047851,
  534. -0.991455371120812639206854697526329)
  535. # 7-point weights
  536. w = (0.129484966168869693270611432679082,
  537. 0.279705391489276667901467771423780,
  538. 0.381830050505118944950369775488975,
  539. 0.417959183673469387755102040816327,
  540. 0.381830050505118944950369775488975,
  541. 0.279705391489276667901467771423780,
  542. 0.129484966168869693270611432679082)
  543. # 15-point weights
  544. v = (0.022935322010529224963732008058970,
  545. 0.063092092629978553290700663189204,
  546. 0.104790010322250183839876322541518,
  547. 0.140653259715525918745189590510238,
  548. 0.169004726639267902826583426598550,
  549. 0.190350578064785409913256402421014,
  550. 0.204432940075298892414161999234649,
  551. 0.209482141084727828012999174891714,
  552. 0.204432940075298892414161999234649,
  553. 0.190350578064785409913256402421014,
  554. 0.169004726639267902826583426598550,
  555. 0.140653259715525918745189590510238,
  556. 0.104790010322250183839876322541518,
  557. 0.063092092629978553290700663189204,
  558. 0.022935322010529224963732008058970)
  559. return _quadrature_gk(a, b, f, norm_func, x, w, v)
  560. _quadrature_gk15.num_eval = 15