test_rbfinterp.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. import pickle
  2. import pytest
  3. import numpy as np
  4. from numpy.linalg import LinAlgError
  5. from scipy._lib._array_api import xp_assert_close, make_xp_test_case
  6. from scipy.stats.qmc import Halton
  7. from scipy.spatial import cKDTree # type: ignore[attr-defined]
  8. from scipy.interpolate._rbfinterp import (
  9. _AVAILABLE, _SCALE_INVARIANT, _NAME_TO_MIN_DEGREE, RBFInterpolator,
  10. _get_backend
  11. )
  12. from scipy.interpolate import _rbfinterp_pythran
  13. from scipy._lib._testutils import _run_concurrent_barrier
  14. skip_xp_backends = pytest.mark.skip_xp_backends
  15. def _vandermonde(x, degree, xp=np):
  16. # Returns a matrix of monomials that span polynomials with the specified
  17. # degree evaluated at x.
  18. backend = _get_backend(xp)
  19. powers = backend._monomial_powers(x.shape[1], degree, xp)
  20. return backend.polynomial_matrix(x, powers, xp)
  21. def _1d_test_function(x, xp):
  22. # Test function used in Wahba's "Spline Models for Observational Data".
  23. # domain ~= (0, 3), range ~= (-1.0, 0.2)
  24. x = x[:, 0]
  25. y = 4.26*(xp.exp(-x) - 4*xp.exp(-2*x) + 3*xp.exp(-3*x))
  26. return y
  27. def _2d_test_function(x, xp):
  28. # Franke's test function.
  29. # domain ~= (0, 1) X (0, 1), range ~= (0.0, 1.2)
  30. x1, x2 = x[:, 0], x[:, 1]
  31. term1 = 0.75 * xp.exp(-(9*x1-2)**2/4 - (9*x2-2)**2/4)
  32. term2 = 0.75 * xp.exp(-(9*x1+1)**2/49 - (9*x2+1)/10)
  33. term3 = 0.5 * xp.exp(-(9*x1-7)**2/4 - (9*x2-3)**2/4)
  34. term4 = -0.2 * xp.exp(-(9*x1-4)**2 - (9*x2-7)**2)
  35. y = term1 + term2 + term3 + term4
  36. return y
  37. def _is_conditionally_positive_definite(kernel, m):
  38. # Tests whether the kernel is conditionally positive definite of order m.
  39. # See chapter 7 of Fasshauer's "Meshfree Approximation Methods with
  40. # MATLAB".
  41. nx = 10
  42. ntests = 100
  43. for ndim in [1, 2, 3, 4, 5]:
  44. # Generate sample points with a Halton sequence to avoid samples that
  45. # are too close to each other, which can make the matrix singular.
  46. seq = Halton(ndim, scramble=False, seed=np.random.RandomState())
  47. for _ in range(ntests):
  48. x = 2*seq.random(nx) - 1
  49. A = _rbfinterp_pythran._kernel_matrix(x, kernel)
  50. P = _vandermonde(x, m - 1)
  51. Q, R = np.linalg.qr(P, mode='complete')
  52. # Q2 forms a basis spanning the space where P.T.dot(x) = 0. Project
  53. # A onto this space, and then see if it is positive definite using
  54. # the Cholesky decomposition. If not, then the kernel is not c.p.d.
  55. # of order m.
  56. Q2 = Q[:, P.shape[1]:]
  57. B = Q2.T.dot(A).dot(Q2)
  58. try:
  59. np.linalg.cholesky(B)
  60. except np.linalg.LinAlgError:
  61. return False
  62. return True
  63. # Sorting the parametrize arguments is necessary to avoid a parallelization
  64. # issue described here: https://github.com/pytest-dev/pytest-xdist/issues/432.
  65. @pytest.mark.parametrize('kernel', sorted(_AVAILABLE))
  66. def test_conditionally_positive_definite(kernel):
  67. # Test if each kernel in _AVAILABLE is conditionally positive definite of
  68. # order m, where m comes from _NAME_TO_MIN_DEGREE. This is a necessary
  69. # condition for the smoothed RBF interpolant to be well-posed in general.
  70. m = _NAME_TO_MIN_DEGREE.get(kernel, -1) + 1
  71. assert _is_conditionally_positive_definite(kernel, m)
  72. class _TestRBFInterpolator:
  73. @pytest.mark.parametrize('kernel', sorted(_SCALE_INVARIANT))
  74. def test_scale_invariance_1d(self, kernel, xp):
  75. # Verify that the functions in _SCALE_INVARIANT are insensitive to the
  76. # shape parameter (when smoothing == 0) in 1d.
  77. seq = Halton(1, scramble=False, seed=np.random.RandomState())
  78. x = 3*seq.random(50)
  79. x = xp.asarray(x)
  80. y = _1d_test_function(x, xp)
  81. xitp = 3*seq.random(50)
  82. xitp = xp.asarray(xitp)
  83. yitp1 = self.build(x, y, epsilon=1.0, kernel=kernel)(xitp)
  84. yitp2 = self.build(x, y, epsilon=2.0, kernel=kernel)(xitp)
  85. xp_assert_close(yitp1, yitp2, atol=1e-8)
  86. @pytest.mark.parametrize('kernel', sorted(_SCALE_INVARIANT))
  87. def test_scale_invariance_2d(self, kernel, xp):
  88. # Verify that the functions in _SCALE_INVARIANT are insensitive to the
  89. # shape parameter (when smoothing == 0) in 2d.
  90. seq = Halton(2, scramble=False, seed=np.random.RandomState())
  91. x = seq.random(100)
  92. x = xp.asarray(x)
  93. y = _2d_test_function(x, xp)
  94. xitp = seq.random(100)
  95. xitp = xp.asarray(xitp)
  96. yitp1 = self.build(x, y, epsilon=1.0, kernel=kernel)(xitp)
  97. yitp2 = self.build(x, y, epsilon=2.0, kernel=kernel)(xitp)
  98. xp_assert_close(yitp1, yitp2, atol=1e-8)
  99. @pytest.mark.parametrize('kernel', sorted(_AVAILABLE))
  100. def test_extreme_domains(self, kernel, xp):
  101. # Make sure the interpolant remains numerically stable for very
  102. # large/small domains.
  103. seq = Halton(2, scramble=False, seed=np.random.RandomState())
  104. scale = 1e50
  105. shift = 1e55
  106. x = seq.random(100)
  107. x = xp.asarray(x)
  108. y = _2d_test_function(x, xp)
  109. xitp = seq.random(100)
  110. xitp = xp.asarray(xitp)
  111. if kernel in _SCALE_INVARIANT:
  112. yitp1 = self.build(x, y, kernel=kernel)(xitp)
  113. yitp2 = self.build(
  114. x*scale + shift, y,
  115. kernel=kernel
  116. )(xitp*scale + shift)
  117. else:
  118. yitp1 = self.build(x, y, epsilon=5.0, kernel=kernel)(xitp)
  119. yitp2 = self.build(
  120. x*scale + shift, y,
  121. epsilon=5.0/scale,
  122. kernel=kernel
  123. )(xitp*scale + shift)
  124. xp_assert_close(yitp1, yitp2, atol=1e-8)
  125. def test_polynomial_reproduction(self, xp):
  126. # If the observed data comes from a polynomial, then the interpolant
  127. # should be able to reproduce the polynomial exactly, provided that
  128. # `degree` is sufficiently high.
  129. rng = np.random.RandomState(0)
  130. seq = Halton(2, scramble=False, seed=rng)
  131. degree = 3
  132. x = seq.random(50)
  133. xitp = seq.random(50)
  134. x = xp.asarray(x)
  135. xitp = xp.asarray(xitp)
  136. P = _vandermonde(x, degree, xp)
  137. Pitp = _vandermonde(xitp, degree, xp)
  138. poly_coeffs = rng.normal(0.0, 1.0, P.shape[1])
  139. poly_coeffs = xp.asarray(poly_coeffs)
  140. y = P @ poly_coeffs #y = P.dot(poly_coeffs)
  141. yitp1 = Pitp @ poly_coeffs #yitp1 = Pitp.dot(poly_coeffs)
  142. yitp2 = self.build(x, y, degree=degree)(xitp)
  143. xp_assert_close(yitp1, yitp2, atol=1e-8)
  144. @pytest.mark.slow
  145. def test_chunking(self, monkeypatch, xp):
  146. # If the observed data comes from a polynomial, then the interpolant
  147. # should be able to reproduce the polynomial exactly, provided that
  148. # `degree` is sufficiently high.
  149. rng = np.random.RandomState(0)
  150. seq = Halton(2, scramble=False, seed=rng)
  151. degree = 3
  152. largeN = 1000 + 33
  153. # this is large to check that chunking of the RBFInterpolator is tested
  154. x = seq.random(50)
  155. xitp = seq.random(largeN)
  156. x = xp.asarray(x)
  157. xitp = xp.asarray(xitp)
  158. P = _vandermonde(x, degree, xp)
  159. Pitp = _vandermonde(xitp, degree, xp)
  160. poly_coeffs = rng.normal(0.0, 1.0, P.shape[1])
  161. poly_coeffs = xp.asarray(poly_coeffs)
  162. y = P @ poly_coeffs # y = P.dot(poly_coeffs)
  163. yitp1 = Pitp @ poly_coeffs # yitp1 = Pitp.dot(poly_coeffs)
  164. interp = self.build(x, y, degree=degree)
  165. ce_real = interp._chunk_evaluator
  166. def _chunk_evaluator(*args, **kwargs):
  167. kwargs.update(memory_budget=100)
  168. return ce_real(*args, **kwargs)
  169. monkeypatch.setattr(interp, '_chunk_evaluator', _chunk_evaluator)
  170. yitp2 = interp(xitp)
  171. xp_assert_close(yitp1, yitp2, atol=1e-8)
  172. def test_vector_data(self, xp):
  173. # Make sure interpolating a vector field is the same as interpolating
  174. # each component separately.
  175. seq = Halton(2, scramble=False, seed=np.random.RandomState())
  176. x = seq.random(100)
  177. xitp = seq.random(100)
  178. x = xp.asarray(x)
  179. xitp = xp.asarray(xitp)
  180. y = xp.stack([_2d_test_function(x, xp),
  181. _2d_test_function(xp.flip(x, axis=1), xp)]).T
  182. yitp1 = self.build(x, y)(xitp)
  183. yitp2 = self.build(x, y[:, 0])(xitp)
  184. yitp3 = self.build(x, y[:, 1])(xitp)
  185. xp_assert_close(yitp1[:, 0], yitp2)
  186. xp_assert_close(yitp1[:, 1], yitp3)
  187. def test_complex_data(self, xp):
  188. # Interpolating complex input should be the same as interpolating the
  189. # real and complex components.
  190. seq = Halton(2, scramble=False, seed=np.random.RandomState())
  191. x = seq.random(100)
  192. xitp = seq.random(100)
  193. y = _2d_test_function(x, np) + 1j*_2d_test_function(x[:, ::-1], np)
  194. x, xitp, y = map(xp.asarray, (x, xitp, y))
  195. yitp1 = self.build(x, y)(xitp)
  196. yitp2 = self.build(x, y.real)(xitp)
  197. yitp3 = self.build(x, y.imag)(xitp)
  198. xp_assert_close(yitp1.real, yitp2)
  199. xp_assert_close(yitp1.imag, yitp3)
  200. @pytest.mark.parametrize('kernel', sorted(_AVAILABLE))
  201. def test_interpolation_misfit_1d(self, kernel, xp):
  202. # Make sure that each kernel, with its default `degree` and an
  203. # appropriate `epsilon`, does a good job at interpolation in 1d.
  204. seq = Halton(1, scramble=False, seed=np.random.RandomState())
  205. x = 3*seq.random(50)
  206. xitp = 3*seq.random(50)
  207. x = xp.asarray(x)
  208. xitp = xp.asarray(xitp)
  209. y = _1d_test_function(x, xp)
  210. ytrue = _1d_test_function(xitp, xp)
  211. yitp = self.build(x, y, epsilon=5.0, kernel=kernel)(xitp)
  212. mse = xp.mean((yitp - ytrue)**2)
  213. assert mse < 1.0e-4
  214. @pytest.mark.parametrize('kernel', sorted(_AVAILABLE))
  215. def test_interpolation_misfit_2d(self, kernel, xp):
  216. # Make sure that each kernel, with its default `degree` and an
  217. # appropriate `epsilon`, does a good job at interpolation in 2d.
  218. seq = Halton(2, scramble=False, seed=np.random.RandomState())
  219. x = seq.random(100)
  220. xitp = seq.random(100)
  221. x = xp.asarray(x)
  222. xitp = xp.asarray(xitp)
  223. y = _2d_test_function(x, xp)
  224. ytrue = _2d_test_function(xitp, xp)
  225. yitp = self.build(x, y, epsilon=5.0, kernel=kernel)(xitp)
  226. mse = xp.mean((yitp - ytrue)**2)
  227. assert mse < 2.0e-4
  228. @pytest.mark.parametrize('kernel', sorted(_AVAILABLE))
  229. def test_smoothing_misfit(self, kernel, xp):
  230. # Make sure we can find a smoothing parameter for each kernel that
  231. # removes a sufficient amount of noise.
  232. rng = np.random.RandomState(0)
  233. seq = Halton(1, scramble=False, seed=rng)
  234. noise = 0.2
  235. rmse_tol = 0.1
  236. smoothing_range = 10**xp.linspace(-4, 1, 20)
  237. x = 3*seq.random(100)
  238. y = _1d_test_function(x, np) + rng.normal(0.0, noise, (100,))
  239. x = xp.asarray(x)
  240. y = xp.asarray(y)
  241. ytrue = _1d_test_function(x, xp)
  242. rmse_within_tol = False
  243. for smoothing in smoothing_range:
  244. ysmooth = self.build(
  245. x, y,
  246. epsilon=1.0,
  247. smoothing=smoothing,
  248. kernel=kernel)(x)
  249. rmse = xp.sqrt(xp.mean((ysmooth - ytrue)**2))
  250. if rmse < rmse_tol:
  251. rmse_within_tol = True
  252. break
  253. assert rmse_within_tol
  254. def test_array_smoothing(self, xp):
  255. # Test using an array for `smoothing` to give less weight to a known
  256. # outlier.
  257. rng = np.random.RandomState(0)
  258. seq = Halton(1, scramble=False, seed=rng)
  259. degree = 2
  260. x = seq.random(50)
  261. P = _vandermonde(x, degree)
  262. poly_coeffs = rng.normal(0.0, 1.0, P.shape[1])
  263. y = P @ poly_coeffs # y = P.dot(poly_coeffs)
  264. y_with_outlier = y.copy()
  265. y_with_outlier[10] += 1.0
  266. smoothing = np.zeros((50,))
  267. smoothing[10] = 1000.0
  268. x, P, poly_coeffs, y = map(xp.asarray, (x, P, poly_coeffs, y))
  269. y_with_outlier, smoothing = map(xp.asarray, (y_with_outlier, smoothing))
  270. yitp = self.build(x, y_with_outlier, smoothing=smoothing)(x)
  271. # Should be able to reproduce the uncorrupted data almost exactly.
  272. xp_assert_close(yitp, y, atol=1e-4)
  273. def test_inconsistent_x_dimensions_error(self):
  274. # ValueError should be raised if the observation points and evaluation
  275. # points have a different number of dimensions.
  276. y = Halton(2, scramble=False, seed=np.random.RandomState()).random(10)
  277. d = _2d_test_function(y, np)
  278. x = Halton(1, scramble=False, seed=np.random.RandomState()).random(10)
  279. match = 'Expected the second axis of `x`'
  280. with pytest.raises(ValueError, match=match):
  281. self.build(y, d)(x)
  282. def test_inconsistent_d_length_error(self):
  283. y = np.linspace(0, 1, 5)[:, None]
  284. d = np.zeros(1)
  285. match = 'Expected the first axis of `d`'
  286. with pytest.raises(ValueError, match=match):
  287. self.build(y, d)
  288. def test_y_not_2d_error(self):
  289. y = np.linspace(0, 1, 5)
  290. d = np.zeros(5)
  291. match = '`y` must be a 2-dimensional array.'
  292. with pytest.raises(ValueError, match=match):
  293. self.build(y, d)
  294. def test_inconsistent_smoothing_length_error(self):
  295. y = np.linspace(0, 1, 5)[:, None]
  296. d = np.zeros(5)
  297. smoothing = np.ones(1)
  298. match = 'Expected `smoothing` to be'
  299. with pytest.raises(ValueError, match=match):
  300. self.build(y, d, smoothing=smoothing)
  301. def test_invalid_kernel_name_error(self):
  302. y = np.linspace(0, 1, 5)[:, None]
  303. d = np.zeros(5)
  304. match = '`kernel` must be one of'
  305. with pytest.raises(ValueError, match=match):
  306. self.build(y, d, kernel='test')
  307. def test_epsilon_not_specified_error(self):
  308. y = np.linspace(0, 1, 5)[:, None]
  309. d = np.zeros(5)
  310. for kernel in _AVAILABLE:
  311. if kernel in _SCALE_INVARIANT:
  312. continue
  313. match = '`epsilon` must be specified'
  314. with pytest.raises(ValueError, match=match):
  315. self.build(y, d, kernel=kernel)
  316. def test_x_not_2d_error(self):
  317. y = np.linspace(0, 1, 5)[:, None]
  318. x = np.linspace(0, 1, 5)
  319. d = np.zeros(5)
  320. match = '`x` must be a 2-dimensional array.'
  321. with pytest.raises(ValueError, match=match):
  322. self.build(y, d)(x)
  323. def test_not_enough_observations_error(self):
  324. y = np.linspace(0, 1, 1)[:, None]
  325. d = np.zeros(1)
  326. match = 'At least 2 data points are required'
  327. with pytest.raises(ValueError, match=match):
  328. self.build(y, d, kernel='thin_plate_spline')
  329. def test_degree_warning(self):
  330. y = np.linspace(0, 1, 5)[:, None]
  331. d = np.zeros(5)
  332. for kernel, deg in _NAME_TO_MIN_DEGREE.items():
  333. # Only test for kernels that its minimum degree is not 0.
  334. if deg >= 1:
  335. match = f'`degree` should not be below {deg}'
  336. with pytest.warns(Warning, match=match):
  337. self.build(y, d, epsilon=1.0, kernel=kernel, degree=deg-1)
  338. def test_minus_one_degree(self):
  339. # Make sure a degree of -1 is accepted without any warning.
  340. y = np.linspace(0, 1, 5)[:, None]
  341. d = np.zeros(5)
  342. for kernel, _ in _NAME_TO_MIN_DEGREE.items():
  343. self.build(y, d, epsilon=1.0, kernel=kernel, degree=-1)
  344. @skip_xp_backends("jax.numpy", reason="solve raises no error for a singular matrix")
  345. @skip_xp_backends("cupy", reason="solve raises no error for a singular matrix")
  346. def test_rank_error(self, xp):
  347. # An error should be raised when `kernel` is "thin_plate_spline" and
  348. # observations are 2-D and collinear.
  349. y = xp.asarray([[2.0, 0.0], [1.0, 0.0], [0.0, 0.0]])
  350. d = xp.asarray([0.0, 0.0, 0.0])
  351. match = 'does not have full column rank'
  352. with pytest.raises(LinAlgError, match=match):
  353. self.build(y, d, kernel='thin_plate_spline')(y)
  354. def test_single_point(self, xp):
  355. # Make sure interpolation still works with only one point (in 1, 2, and
  356. # 3 dimensions).
  357. for dim in [1, 2, 3]:
  358. y = xp.zeros((1, dim))
  359. d = xp.ones((1,), dtype=xp.float64)
  360. f = self.build(y, d, kernel='linear')(y)
  361. xp_assert_close(f, d)
  362. def test_pickleable(self, xp):
  363. # Make sure we can pickle and unpickle the interpolant without any
  364. # changes in the behavior.
  365. seq = Halton(1, scramble=False, seed=np.random.RandomState(2305982309))
  366. x = 3*seq.random(50)
  367. xitp = 3*seq.random(50)
  368. x, xitp = xp.asarray(x), xp.asarray(xitp)
  369. y = _1d_test_function(x, xp)
  370. interp = self.build(x, y)
  371. yitp1 = interp(xitp)
  372. yitp2 = pickle.loads(pickle.dumps(interp))(xitp)
  373. xp_assert_close(yitp1, yitp2, atol=1e-16)
  374. @make_xp_test_case(RBFInterpolator)
  375. class TestRBFInterpolatorNeighborsNone(_TestRBFInterpolator):
  376. def build(self, *args, **kwargs):
  377. return RBFInterpolator(*args, **kwargs)
  378. def test_smoothing_limit_1d(self):
  379. # For large smoothing parameters, the interpolant should approach a
  380. # least squares fit of a polynomial with the specified degree.
  381. seq = Halton(1, scramble=False, seed=np.random.RandomState())
  382. degree = 3
  383. smoothing = 1e8
  384. x = 3*seq.random(50)
  385. xitp = 3*seq.random(50)
  386. y = _1d_test_function(x, np)
  387. yitp1 = self.build(
  388. x, y,
  389. degree=degree,
  390. smoothing=smoothing
  391. )(xitp)
  392. P = _vandermonde(x, degree)
  393. Pitp = _vandermonde(xitp, degree)
  394. yitp2 = Pitp.dot(np.linalg.lstsq(P, y, rcond=None)[0])
  395. xp_assert_close(yitp1, yitp2, atol=1e-8)
  396. def test_smoothing_limit_2d(self):
  397. # For large smoothing parameters, the interpolant should approach a
  398. # least squares fit of a polynomial with the specified degree.
  399. seq = Halton(2, scramble=False, seed=np.random.RandomState())
  400. degree = 3
  401. smoothing = 1e8
  402. x = seq.random(100)
  403. xitp = seq.random(100)
  404. y = _2d_test_function(x, np)
  405. yitp1 = self.build(
  406. x, y,
  407. degree=degree,
  408. smoothing=smoothing
  409. )(xitp)
  410. P = _vandermonde(x, degree)
  411. Pitp = _vandermonde(xitp, degree)
  412. yitp2 = Pitp.dot(np.linalg.lstsq(P, y, rcond=None)[0])
  413. xp_assert_close(yitp1, yitp2, atol=1e-8)
  414. @skip_xp_backends(np_only=True, reason="neighbors not None uses KDTree")
  415. class TestRBFInterpolatorNeighbors20(_TestRBFInterpolator):
  416. # RBFInterpolator using 20 nearest neighbors.
  417. def build(self, *args, **kwargs):
  418. return RBFInterpolator(*args, **kwargs, neighbors=20)
  419. def test_equivalent_to_rbf_interpolator(self):
  420. seq = Halton(2, scramble=False, seed=np.random.RandomState())
  421. x = seq.random(100)
  422. xitp = seq.random(100)
  423. y = _2d_test_function(x, np)
  424. yitp1 = self.build(x, y)(xitp)
  425. yitp2 = []
  426. tree = cKDTree(x)
  427. for xi in xitp:
  428. _, nbr = tree.query(xi, 20)
  429. yitp2.append(RBFInterpolator(x[nbr], y[nbr])(xi[None])[0])
  430. xp_assert_close(yitp1, yitp2, atol=1e-8)
  431. def test_concurrency(self):
  432. # Check that no segfaults appear with concurrent access to
  433. # RbfInterpolator
  434. seq = Halton(2, scramble=False, seed=np.random.RandomState(0))
  435. x = seq.random(100)
  436. xitp = seq.random(100)
  437. y = _2d_test_function(x, np)
  438. interp = self.build(x, y)
  439. def worker_fn(_, interp, xp):
  440. interp(xp)
  441. _run_concurrent_barrier(10, worker_fn, interp, xitp)
  442. @skip_xp_backends(np_only=True, reason="neighbors not None uses KDTree")
  443. class TestRBFInterpolatorNeighborsInf(TestRBFInterpolatorNeighborsNone):
  444. # RBFInterpolator using neighbors=np.inf. This should give exactly the same
  445. # results as neighbors=None, but it will be slower.
  446. def build(self, *args, **kwargs):
  447. return RBFInterpolator(*args, **kwargs, neighbors=np.inf)
  448. def test_equivalent_to_rbf_interpolator(self):
  449. seq = Halton(1, scramble=False, seed=np.random.RandomState())
  450. x = 3*seq.random(50)
  451. xitp = 3*seq.random(50)
  452. y = _1d_test_function(x, np)
  453. yitp1 = self.build(x, y)(xitp)
  454. yitp2 = RBFInterpolator(x, y)(xitp)
  455. xp_assert_close(yitp1, yitp2, atol=1e-8)