test_rgi.py 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233
  1. import itertools
  2. import pytest
  3. import numpy as np
  4. from numpy.exceptions import ComplexWarning
  5. from scipy._lib._array_api import (
  6. xp_assert_equal, xp_assert_close, assert_array_almost_equal,
  7. make_xp_test_case
  8. )
  9. from scipy.conftest import skip_xp_invalid_arg
  10. from pytest import raises as assert_raises
  11. from scipy.interpolate import (RegularGridInterpolator, interpn,
  12. RectBivariateSpline,
  13. NearestNDInterpolator, LinearNDInterpolator)
  14. from scipy.sparse._sputils import matrix
  15. from scipy._lib._testutils import _run_concurrent_barrier
  16. parametrize_rgi_interp_methods = pytest.mark.parametrize(
  17. "method", RegularGridInterpolator._ALL_METHODS
  18. )
  19. @make_xp_test_case(RegularGridInterpolator)
  20. class TestRegularGridInterpolator:
  21. def _get_sample_4d(self, xp):
  22. # create a 4-D grid of 3 points in each dimension
  23. points = [(0., .5, 1.)] * 4
  24. values = xp.asarray([0., .5, 1.])
  25. values0 = values[:, np.newaxis, np.newaxis, np.newaxis]
  26. values1 = values[np.newaxis, :, np.newaxis, np.newaxis]
  27. values2 = values[np.newaxis, np.newaxis, :, np.newaxis]
  28. values3 = values[np.newaxis, np.newaxis, np.newaxis, :]
  29. values = (values0 + values1 * 10 + values2 * 100 + values3 * 1000)
  30. return points, values
  31. def _get_sample_4d_2(self, xp):
  32. # create another 4-D grid of 3 points in each dimension
  33. points = [(0., .5, 1.)] * 2 + [(0., 5., 10.)] * 2
  34. values = xp.asarray([0., .5, 1.])
  35. values0 = values[:, np.newaxis, np.newaxis, np.newaxis]
  36. values1 = values[np.newaxis, :, np.newaxis, np.newaxis]
  37. values2 = values[np.newaxis, np.newaxis, :, np.newaxis]
  38. values3 = values[np.newaxis, np.newaxis, np.newaxis, :]
  39. values = (values0 + values1 * 10 + values2 * 100 + values3 * 1000)
  40. return points, values
  41. def _get_sample_4d_3(self, xp):
  42. # create another 4-D grid of 7 points in each dimension
  43. points = [(0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0)] * 4
  44. values = xp.asarray([0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0])
  45. values0 = values[:, np.newaxis, np.newaxis, np.newaxis]
  46. values1 = values[np.newaxis, :, np.newaxis, np.newaxis]
  47. values2 = values[np.newaxis, np.newaxis, :, np.newaxis]
  48. values3 = values[np.newaxis, np.newaxis, np.newaxis, :]
  49. values = (values0 + values1 * 10 + values2 * 100 + values3 * 1000)
  50. return points, values
  51. def _get_sample_4d_4(self, xp):
  52. # create another 4-D grid of 2 points in each dimension
  53. points = [(0.0, 1.0)] * 4
  54. values = xp.asarray([0.0, 1.0])
  55. values0 = values[:, np.newaxis, np.newaxis, np.newaxis]
  56. values1 = values[np.newaxis, :, np.newaxis, np.newaxis]
  57. values2 = values[np.newaxis, np.newaxis, :, np.newaxis]
  58. values3 = values[np.newaxis, np.newaxis, np.newaxis, :]
  59. values = (values0 + values1 * 10 + values2 * 100 + values3 * 1000)
  60. return points, values
  61. @parametrize_rgi_interp_methods
  62. def test_list_input(self, method):
  63. points, values = self._get_sample_4d_3(xp=np)
  64. sample = np.asarray([[0.1, 0.1, 1., .9], [0.2, 0.1, .45, .8],
  65. [0.5, 0.5, .5, .5]])
  66. interp = RegularGridInterpolator(points,
  67. values.tolist(),
  68. method=method)
  69. v1 = interp(sample.tolist())
  70. interp = RegularGridInterpolator(points,
  71. values,
  72. method=method)
  73. v2 = interp(sample)
  74. xp_assert_close(v1, v2)
  75. @pytest.mark.parametrize('method', ['cubic', 'quintic', 'pchip'])
  76. def test_spline_dim_error(self, method, xp):
  77. points, values = self._get_sample_4d_4(xp)
  78. points = list(xp.asarray(p) for p in points)
  79. match = "points in dimension"
  80. # Check error raise when creating interpolator
  81. with pytest.raises(ValueError, match=match):
  82. RegularGridInterpolator(points, values, method=method)
  83. # Check error raise when creating interpolator
  84. interp = RegularGridInterpolator(points, values)
  85. sample = xp.asarray([[0.1, 0.1, 1., .9], [0.2, 0.1, .45, .8],
  86. [0.5, 0.5, .5, .5]])
  87. with pytest.raises(ValueError, match=match):
  88. interp(sample, method=method)
  89. @pytest.mark.parametrize(
  90. "points_values, sample",
  91. [
  92. (
  93. _get_sample_4d,
  94. np.asarray(
  95. [[0.1, 0.1, 1.0, 0.9],
  96. [0.2, 0.1, 0.45, 0.8],
  97. [0.5, 0.5, 0.5, 0.5]]
  98. ),
  99. ),
  100. (_get_sample_4d_2, np.asarray([0.1, 0.1, 10.0, 9.0])),
  101. ],
  102. )
  103. def test_linear_and_slinear_close(self, points_values, sample, xp):
  104. points, values = points_values(self, xp)
  105. points, sample = list(xp.asarray(p) for p in points), xp.asarray(sample)
  106. interp = RegularGridInterpolator(points, values, method="linear")
  107. v1 = interp(sample)
  108. interp = RegularGridInterpolator(points, values, method="slinear")
  109. v2 = interp(sample)
  110. xp_assert_close(v1, v2)
  111. def test_derivatives(self, xp):
  112. points, values = self._get_sample_4d(xp)
  113. points = list(xp.asarray(p) for p in points)
  114. sample = xp.asarray([[0.1 , 0.1 , 1. , 0.9 ],
  115. [0.2 , 0.1 , 0.45, 0.8 ],
  116. [0.5 , 0.5 , 0.5 , 0.5 ]])
  117. interp = RegularGridInterpolator(points, values, method="slinear")
  118. with assert_raises(ValueError):
  119. # wrong number of derivatives (need 4)
  120. interp(sample, nu=1)
  121. xp_assert_close(interp(sample, nu=(1, 0, 0, 0)),
  122. xp.asarray([1.0, 1, 1], dtype=xp.float64), atol=1e-15)
  123. xp_assert_close(interp(sample, nu=(0, 1, 0, 0)),
  124. xp.asarray([10.0, 10, 10], dtype=xp.float64), atol=1e-15)
  125. # 2nd derivatives of a linear function are zero
  126. xp_assert_close(interp(sample, nu=(0, 1, 1, 0)),
  127. xp.asarray([0.0, 0, 0], dtype=xp.float64), atol=2e-12)
  128. @parametrize_rgi_interp_methods
  129. def test_complex(self, method, xp):
  130. if method == "pchip":
  131. pytest.skip("pchip does not make sense for complex data")
  132. points, values = self._get_sample_4d_3(xp)
  133. points = list(xp.asarray(p) for p in points)
  134. values = values - 2j*values
  135. sample = xp.asarray([[0.1, 0.1, 1., .9], [0.2, 0.1, .45, .8],
  136. [0.5, 0.5, .5, .5]])
  137. interp = RegularGridInterpolator(points, values, method=method)
  138. rinterp = RegularGridInterpolator(points, xp.real(values), method=method)
  139. iinterp = RegularGridInterpolator(points, xp.imag(values), method=method)
  140. v1 = interp(sample)
  141. v2 = rinterp(sample) + 1j*iinterp(sample)
  142. xp_assert_close(v1, v2)
  143. def test_cubic_vs_pchip(self, xp):
  144. x, y = xp.asarray([1, 2, 3, 4]), xp.asarray([1, 2, 3, 4])
  145. xg, yg = xp.meshgrid(x, y, indexing='ij')
  146. values = (lambda x, y: x**4 * y**4)(xg, yg)
  147. cubic = RegularGridInterpolator((x, y), values, method='cubic')
  148. pchip = RegularGridInterpolator((x, y), values, method='pchip')
  149. vals_cubic = cubic([1.5, 2])
  150. vals_pchip = pchip([1.5, 2])
  151. #assert not np.allclose(vals_cubic, vals_pchip, atol=1e-14, rtol=0)
  152. assert not xp.all(xp.abs(vals_cubic - vals_pchip) < 1e-14)
  153. def test_linear_xi1d(self, xp):
  154. points, values = self._get_sample_4d_2(xp)
  155. points = list(xp.asarray(p) for p in points)
  156. interp = RegularGridInterpolator(points, values)
  157. sample = xp.asarray([0.1, 0.1, 10., 9.])
  158. wanted = xp.asarray([1001.1], dtype=xp.float64)
  159. assert_array_almost_equal(interp(sample), wanted)
  160. def test_linear_xi3d(self, xp):
  161. points, values = self._get_sample_4d(xp)
  162. points = list(xp.asarray(p) for p in points)
  163. interp = RegularGridInterpolator(points, values)
  164. sample = xp.asarray([[0.1, 0.1, 1., .9], [0.2, 0.1, .45, .8],
  165. [0.5, 0.5, .5, .5]])
  166. wanted = xp.asarray([1001.1, 846.2, 555.5])
  167. assert_array_almost_equal(interp(sample), wanted)
  168. @pytest.mark.parametrize(
  169. "sample, wanted",
  170. [
  171. ([0.1, 0.1, 0.9, 0.9], 1100.0),
  172. ([0.1, 0.1, 0.1, 0.1], 0.0),
  173. ([0.0, 0.0, 0.0, 0.0], 0.0),
  174. ([1.0, 1.0, 1.0, 1.0], 1111.0),
  175. ([0.1, 0.4, 0.6, 0.9], 1055.0),
  176. ],
  177. )
  178. def test_nearest(self, sample, wanted, xp):
  179. points, values = self._get_sample_4d(xp)
  180. points, sample = tuple(xp.asarray(p) for p in points), xp.asarray(sample)
  181. interp = RegularGridInterpolator(points, values, method="nearest")
  182. wanted = xp.asarray([wanted], dtype=xp.float64)
  183. assert_array_almost_equal(interp(sample), wanted)
  184. def test_linear_edges(self, xp):
  185. points, values = self._get_sample_4d(xp)
  186. points = list(xp.asarray(p) for p in points)
  187. interp = RegularGridInterpolator(points, values)
  188. sample = xp.asarray([[0., 0., 0., 0.], [1., 1., 1., 1.]])
  189. wanted = xp.asarray([0., 1111.])
  190. assert_array_almost_equal(interp(sample), wanted)
  191. def test_valid_create(self):
  192. # create a 2-D grid of 3 points in each dimension
  193. points = [(0., .5, 1.), (0., 1., .5)]
  194. values = np.asarray([0., .5, 1.])
  195. values0 = values[:, np.newaxis]
  196. values1 = values[np.newaxis, :]
  197. values = (values0 + values1 * 10)
  198. assert_raises(ValueError, RegularGridInterpolator, points, values)
  199. points = [((0., .5, 1.), ), (0., .5, 1.)]
  200. assert_raises(ValueError, RegularGridInterpolator, points, values)
  201. points = [(0., .5, .75, 1.), (0., .5, 1.)]
  202. assert_raises(ValueError, RegularGridInterpolator, points, values)
  203. points = [(0., .5, 1.), (0., .5, 1.), (0., .5, 1.)]
  204. assert_raises(ValueError, RegularGridInterpolator, points, values)
  205. points = [(0., .5, 1.), (0., .5, 1.)]
  206. assert_raises(ValueError, RegularGridInterpolator, points, values,
  207. method="undefmethod")
  208. def test_valid_call(self, xp):
  209. points, values = self._get_sample_4d(xp)
  210. points = list(xp.asarray(p) for p in points)
  211. interp = RegularGridInterpolator(points, values)
  212. sample = xp.asarray([[0., 0., 0., 0.], [1., 1., 1., 1.]])
  213. with assert_raises(ValueError):
  214. interp(sample, "undefmethod")
  215. sample = xp.asarray([[0., 0., 0.], [1., 1., 1.]])
  216. with assert_raises(ValueError):
  217. interp(sample)
  218. sample = xp.asarray([[0., 0., 0., 0.], [1., 1., 1., 1.1]])
  219. with assert_raises(ValueError):
  220. interp(sample)
  221. def test_out_of_bounds_extrap(self, xp):
  222. points, values = self._get_sample_4d(xp)
  223. points = list(xp.asarray(p) for p in points)
  224. interp = RegularGridInterpolator(points, values, bounds_error=False,
  225. fill_value=None)
  226. sample = xp.asarray([[-.1, -.1, -.1, -.1], [1.1, 1.1, 1.1, 1.1],
  227. [21, 2.1, -1.1, -11], [2.1, 2.1, -1.1, -1.1]],
  228. dtype=xp.float64)
  229. wanted = xp.asarray([0., 1111., 11., 11.], dtype=xp.float64)
  230. assert_array_almost_equal(interp(sample, method="nearest"), wanted)
  231. wanted = xp.asarray([-111.1, 1222.1, -11068., -1186.9], dtype=xp.float64)
  232. assert_array_almost_equal(interp(sample, method="linear"), wanted)
  233. def test_out_of_bounds_extrap2(self, xp):
  234. points, values = self._get_sample_4d_2(xp)
  235. points = list(xp.asarray(p) for p in points)
  236. interp = RegularGridInterpolator(points, values, bounds_error=False,
  237. fill_value=None)
  238. sample = xp.asarray([[-.1, -.1, -.1, -.1], [1.1, 1.1, 1.1, 1.1],
  239. [21, 2.1, -1.1, -11], [2.1, 2.1, -1.1, -1.1]],
  240. dtype=xp.float64)
  241. wanted = xp.asarray([0., 11., 11., 11.], dtype=xp.float64)
  242. assert_array_almost_equal(interp(sample, method="nearest"), wanted)
  243. wanted = xp.asarray([-12.1, 133.1, -1069., -97.9], dtype=xp.float64)
  244. assert_array_almost_equal(interp(sample, method="linear"), wanted)
  245. def test_out_of_bounds_fill(self, xp):
  246. points, values = self._get_sample_4d(xp)
  247. points = list(xp.asarray(p) for p in points)
  248. interp = RegularGridInterpolator(points, values, bounds_error=False,
  249. fill_value=xp.nan)
  250. sample = xp.asarray([[-.1, -.1, -.1, -.1], [1.1, 1.1, 1.1, 1.1],
  251. [2.1, 2.1, -1.1, -1.1]])
  252. wanted = xp.asarray([xp.nan, xp.nan, xp.nan])
  253. assert_array_almost_equal(interp(sample, method="nearest"), wanted)
  254. assert_array_almost_equal(interp(sample, method="linear"), wanted)
  255. sample = xp.asarray([[0.1, 0.1, 1., .9], [0.2, 0.1, .45, .8],
  256. [0.5, 0.5, .5, .5]])
  257. wanted = xp.asarray([1001.1, 846.2, 555.5])
  258. assert_array_almost_equal(interp(sample), wanted)
  259. def test_nearest_compare_qhull(self):
  260. points, values = self._get_sample_4d(np)
  261. interp = RegularGridInterpolator(points, values, method="nearest")
  262. points_qhull = itertools.product(*points)
  263. points_qhull = [p for p in points_qhull]
  264. points_qhull = np.asarray(points_qhull)
  265. values_qhull = values.reshape(-1)
  266. interp_qhull = NearestNDInterpolator(points_qhull, values_qhull)
  267. sample = np.asarray([[0.1, 0.1, 1., .9], [0.2, 0.1, .45, .8],
  268. [0.5, 0.5, .5, .5]])
  269. assert_array_almost_equal(interp(sample), interp_qhull(sample))
  270. def test_linear_compare_qhull(self):
  271. points, values = self._get_sample_4d(np)
  272. interp = RegularGridInterpolator(points, values)
  273. points_qhull = itertools.product(*points)
  274. points_qhull = [p for p in points_qhull]
  275. points_qhull = np.asarray(points_qhull)
  276. values_qhull = values.reshape(-1)
  277. interp_qhull = LinearNDInterpolator(points_qhull, values_qhull)
  278. sample = np.asarray([[0.1, 0.1, 1., .9], [0.2, 0.1, .45, .8],
  279. [0.5, 0.5, .5, .5]])
  280. assert_array_almost_equal(interp(sample), interp_qhull(sample))
  281. @pytest.mark.parametrize("method", ["nearest", "linear"])
  282. def test_duck_typed_values(self, method):
  283. x = np.linspace(0, 2, 5)
  284. y = np.linspace(0, 1, 7)
  285. values = MyValue((5, 7))
  286. interp = RegularGridInterpolator((x, y), values, method=method)
  287. v1 = interp([0.4, 0.7])
  288. interp = RegularGridInterpolator((x, y), values._v, method=method)
  289. v2 = interp([0.4, 0.7])
  290. xp_assert_close(v1, v2, check_dtype=False)
  291. def test_invalid_fill_value(self):
  292. np.random.seed(1234)
  293. x = np.linspace(0, 2, 5)
  294. y = np.linspace(0, 1, 7)
  295. values = np.random.rand(5, 7)
  296. # integers can be cast to floats
  297. RegularGridInterpolator((x, y), values, fill_value=1)
  298. # complex values cannot
  299. assert_raises(ValueError, RegularGridInterpolator,
  300. (x, y), values, fill_value=1+2j)
  301. def test_fillvalue_type(self):
  302. # from #3703; test that interpolator object construction succeeds
  303. values = np.ones((10, 20, 30), dtype='>f4')
  304. points = [np.arange(n) for n in values.shape]
  305. # xi = [(1, 1, 1)]
  306. RegularGridInterpolator(points, values)
  307. RegularGridInterpolator(points, values, fill_value=0.)
  308. @pytest.mark.parametrize("dtype", [np.float32, np.float64])
  309. @pytest.mark.parametrize("ndim", [1, 2, 3])
  310. @pytest.mark.parametrize("method", ["linear", "nearest"])
  311. def test_length_one_axis_all(self, dtype, ndim, method):
  312. # gh-23171: length-1 axes in all dimensions are legal
  313. # for all methods parametrized above.
  314. # Construct test point 'x0' with coordinates[0, 1, ..., ndim-1].
  315. # NOTE: choice of coordinates is arbitrary, could be random numbers,
  316. # but using np.arange for convenience.
  317. x0 = np.arange(ndim, dtype=dtype)
  318. # Unpack 'x0'; loosly speaking this is the inverse of np.mgrid.
  319. # By construction 'points' defines a grid of length one along all axes.
  320. points = tuple(np.asarray([xi]) for xi in x0)
  321. # Construct 'values' array of dimensions (1, 1, ...) from 0D 'val'.
  322. val = np.asarray(1/7, dtype=dtype)
  323. values = np.full(shape=(1, )*ndim, fill_value=val)
  324. # Fill value, as a 0D array of correct dtype.
  325. fill = np.asarray(1/42, dtype=dtype)
  326. # method "linear" promotes results to np.float64
  327. promoted_dtype = np.float64 if method == "linear" else dtype
  328. # Create interpolator instances, with and without 'bounds_error' check.
  329. interp_fill = RegularGridInterpolator(
  330. points, values, method=method, bounds_error=False, fill_value=fill
  331. )
  332. interp_err = RegularGridInterpolator(
  333. points, values, method=method, bounds_error=True,
  334. )
  335. # Check interpolator returns correct value for valid sample.
  336. sample = np.asarray([x0])
  337. wanted = np.asarray([val], dtype=promoted_dtype)
  338. for result in [interp_fill(sample), interp_err(sample)]:
  339. xp_assert_equal(result, wanted)
  340. # Check out of bound point along first direction.
  341. x0[0] += 1
  342. sample = np.asarray([x0])
  343. wanted = np.asarray([fill], dtype=promoted_dtype)
  344. result = interp_fill(sample)
  345. xp_assert_equal(result, wanted)
  346. with pytest.raises(
  347. ValueError,
  348. match="^One of the requested xi is out of bounds in dimension 0$",
  349. ):
  350. interp_err(sample)
  351. # check point with NaN in first direction
  352. x0[0] = np.nan
  353. sample = np.asarray([x0])
  354. wanted = np.asarray([np.nan], dtype=promoted_dtype)
  355. result = interp_fill(sample)
  356. xp_assert_equal(result, wanted)
  357. with pytest.raises(
  358. ValueError,
  359. match="^One of the requested xi is out of bounds in dimension 0$",
  360. ):
  361. interp_err(sample)
  362. def test_length_one_axis(self):
  363. # gh-5890, gh-9524 : length-1 axis is legal for method='linear'.
  364. # Along the axis it's linear interpolation; away from the length-1
  365. # axis, it's an extrapolation, so fill_value should be used.
  366. def f(x, y):
  367. return x + y
  368. x = np.linspace(1, 1, 1)
  369. y = np.linspace(1, 10, 10)
  370. data = f(*np.meshgrid(x, y, indexing="ij", sparse=True))
  371. interp = RegularGridInterpolator((x, y), data, method="linear",
  372. bounds_error=False, fill_value=101)
  373. # check values at the grid
  374. xp_assert_close(interp(np.array([[1, 1], [1, 5], [1, 10]])),
  375. np.asarray([2.0, 6, 11]),
  376. atol=1e-14)
  377. # check off-grid interpolation is indeed linear
  378. xp_assert_close(interp(np.array([[1, 1.4], [1, 5.3], [1, 10]])),
  379. [2.4, 6.3, 11],
  380. atol=1e-14)
  381. # check exrapolation w/ fill_value
  382. xp_assert_close(interp(np.array([1.1, 2.4])),
  383. interp.fill_value,
  384. check_dtype=False, check_shape=False, check_0d=False,
  385. atol=1e-14)
  386. # check extrapolation: linear along the `y` axis, const along `x`
  387. interp.fill_value = None
  388. xp_assert_close(interp([[1, 0.3], [1, 11.5]]),
  389. [1.3, 12.5], atol=1e-15)
  390. xp_assert_close(interp([[1.5, 0.3], [1.9, 11.5]]),
  391. [1.3, 12.5], atol=1e-15)
  392. # extrapolation with method='nearest'
  393. interp = RegularGridInterpolator((x, y), data, method="nearest",
  394. bounds_error=False, fill_value=None)
  395. xp_assert_close(interp([[1.5, 1.8], [-4, 5.1]]),
  396. np.asarray([3.0, 6]),
  397. atol=1e-15)
  398. @pytest.mark.parametrize("fill_value", [None, np.nan, np.pi])
  399. @pytest.mark.parametrize("method", ['linear', 'nearest'])
  400. def test_length_one_axis2(self, fill_value, method):
  401. options = {"fill_value": fill_value, "bounds_error": False,
  402. "method": method}
  403. x = np.linspace(0, 2*np.pi, 20)
  404. z = np.sin(x)
  405. fa = RegularGridInterpolator((x,), z[:], **options)
  406. fb = RegularGridInterpolator((x, [0]), z[:, None], **options)
  407. x1a = np.linspace(-1, 2*np.pi+1, 100)
  408. za = fa(x1a)
  409. # evaluated at provided y-value, fb should behave exactly as fa
  410. y1b = np.zeros(100)
  411. zb = fb(np.vstack([x1a, y1b]).T)
  412. xp_assert_close(zb, za)
  413. # evaluated at a different y-value, fb should return fill value
  414. y1b = np.ones(100)
  415. zb = fb(np.vstack([x1a, y1b]).T)
  416. if fill_value is None:
  417. xp_assert_close(zb, za)
  418. else:
  419. xp_assert_close(zb, np.full_like(zb, fill_value))
  420. @pytest.mark.parametrize("method", ['nearest', 'linear'])
  421. def test_nan_x_1d(self, method):
  422. # gh-6624 : if x is nan, result should be nan
  423. f = RegularGridInterpolator(([1, 2, 3],), [10, 20, 30], fill_value=1,
  424. bounds_error=False, method=method)
  425. assert np.isnan(f([np.nan]))
  426. # test arbitrary nan pattern
  427. rng = np.random.default_rng(8143215468)
  428. x = rng.random(size=100)*4
  429. i = rng.random(size=100) > 0.5
  430. x[i] = np.nan
  431. with np.errstate(invalid='ignore'):
  432. # out-of-bounds comparisons, `out_of_bounds += x < grid[0]`,
  433. # generate numpy warnings if `x` contains nans.
  434. # These warnings should propagate to user (since `x` is user
  435. # input) and we simply filter them out.
  436. res = f(x)
  437. assert np.isnan(res[i]).all()
  438. xp_assert_equal(res[~i], f(x[~i]))
  439. # also test the length-one axis f(nan)
  440. x = [1, 2, 3]
  441. y = [1, ]
  442. data = np.ones((3, 1))
  443. f = RegularGridInterpolator((x, y), data, fill_value=1,
  444. bounds_error=False, method=method)
  445. assert np.all(np.isnan(f([np.nan, 1])))
  446. assert np.all(np.isnan(f([1, np.nan])))
  447. @pytest.mark.parametrize("method", ['nearest', 'linear'])
  448. def test_nan_x_2d(self, method):
  449. x, y = np.array([0, 1, 2]), np.array([1, 3, 7])
  450. def f(x, y):
  451. return x**2 + y**2
  452. xg, yg = np.meshgrid(x, y, indexing='ij', sparse=True)
  453. data = f(xg, yg)
  454. interp = RegularGridInterpolator((x, y), data,
  455. method=method, bounds_error=False)
  456. with np.errstate(invalid='ignore'):
  457. res = interp([[1.5, np.nan], [1, 1]])
  458. xp_assert_close(res[1], 2.0, atol=1e-14)
  459. assert np.isnan(res[0])
  460. # test arbitrary nan pattern
  461. rng = np.random.default_rng(8143215468)
  462. x = rng.random(size=100)*4-1
  463. y = rng.random(size=100)*8
  464. i1 = rng.random(size=100) > 0.5
  465. i2 = rng.random(size=100) > 0.5
  466. i = i1 | i2
  467. x[i1] = np.nan
  468. y[i2] = np.nan
  469. z = np.array([x, y]).T
  470. with np.errstate(invalid='ignore'):
  471. # out-of-bounds comparisons, `out_of_bounds += x < grid[0]`,
  472. # generate numpy warnings if `x` contains nans.
  473. # These warnings should propagate to user (since `x` is user
  474. # input) and we simply filter them out.
  475. res = interp(z)
  476. assert np.isnan(res[i]).all()
  477. xp_assert_equal(res[~i], interp(z[~i]), check_dtype=False)
  478. @pytest.mark.fail_slow(10)
  479. @parametrize_rgi_interp_methods
  480. @pytest.mark.parametrize(("ndims", "func"), [
  481. (2, lambda x, y: 2 * x ** 3 + 3 * y ** 2),
  482. (3, lambda x, y, z: 2 * x ** 3 + 3 * y ** 2 - z),
  483. (4, lambda x, y, z, a: 2 * x ** 3 + 3 * y ** 2 - z + a),
  484. (5, lambda x, y, z, a, b: 2 * x ** 3 + 3 * y ** 2 - z + a * b),
  485. ])
  486. def test_descending_points_nd(self, method, ndims, func):
  487. if ndims >= 4 and method in {"cubic", "quintic"}:
  488. pytest.skip("too slow; OOM (quintic); or nearly so (cubic)")
  489. rng = np.random.default_rng(42)
  490. sample_low = 1
  491. sample_high = 5
  492. test_points = rng.uniform(sample_low, sample_high, size=(2, ndims))
  493. ascending_points = [np.linspace(sample_low, sample_high, 12)
  494. for _ in range(ndims)]
  495. ascending_values = func(*np.meshgrid(*ascending_points,
  496. indexing="ij",
  497. sparse=True))
  498. ascending_interp = RegularGridInterpolator(ascending_points,
  499. ascending_values,
  500. method=method)
  501. ascending_result = ascending_interp(test_points)
  502. descending_points = [xi[::-1] for xi in ascending_points]
  503. descending_values = func(*np.meshgrid(*descending_points,
  504. indexing="ij",
  505. sparse=True))
  506. descending_interp = RegularGridInterpolator(descending_points,
  507. descending_values,
  508. method=method)
  509. descending_result = descending_interp(test_points)
  510. xp_assert_equal(ascending_result, descending_result)
  511. def test_invalid_points_order(self):
  512. def val_func_2d(x, y):
  513. return 2 * x ** 3 + 3 * y ** 2
  514. x = np.array([.5, 2., 0., 4., 5.5]) # not ascending or descending
  515. y = np.array([.5, 2., 3., 4., 5.5])
  516. points = (x, y)
  517. values = val_func_2d(*np.meshgrid(*points, indexing='ij',
  518. sparse=True))
  519. match = "must be strictly ascending or descending"
  520. with pytest.raises(ValueError, match=match):
  521. RegularGridInterpolator(points, values)
  522. @parametrize_rgi_interp_methods
  523. def test_fill_value(self, method):
  524. interp = RegularGridInterpolator([np.arange(6)], np.ones(6),
  525. method=method, bounds_error=False)
  526. assert np.isnan(interp([10]))
  527. @pytest.mark.fail_slow(5)
  528. @parametrize_rgi_interp_methods
  529. def test_nonscalar_values(self, method):
  530. if method == "quintic":
  531. pytest.skip("Way too slow.")
  532. # Verify that non-scalar valued values also works
  533. points = [(0.0, 0.5, 1.0, 1.5, 2.0, 2.5)] * 2 + [
  534. (0.0, 5.0, 10.0, 15.0, 20, 25.0)
  535. ] * 2
  536. rng = np.random.default_rng(1234)
  537. values = rng.random((6, 6, 6, 6, 8))
  538. sample = rng.random((7, 3, 4))
  539. interp = RegularGridInterpolator(points, values, method=method,
  540. bounds_error=False)
  541. v = interp(sample)
  542. assert v.shape == (7, 3, 8), method
  543. vs = []
  544. for j in range(8):
  545. interp = RegularGridInterpolator(points, values[..., j],
  546. method=method,
  547. bounds_error=False)
  548. vs.append(interp(sample))
  549. v2 = np.array(vs).transpose(1, 2, 0)
  550. xp_assert_close(v, v2, atol=1e-14, err_msg=method)
  551. @parametrize_rgi_interp_methods
  552. @pytest.mark.parametrize("flip_points", [False, True])
  553. def test_nonscalar_values_2(self, method, flip_points):
  554. if method in {"cubic", "quintic"}:
  555. pytest.skip("Way too slow.")
  556. # Verify that non-scalar valued values also work : use different
  557. # lengths of axes to simplify tracing the internals
  558. points = [(0.0, 0.5, 1.0, 1.5, 2.0, 2.5),
  559. (0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0),
  560. (0.0, 5.0, 10.0, 15.0, 20, 25.0, 35.0, 36.0),
  561. (0.0, 5.0, 10.0, 15.0, 20, 25.0, 35.0, 36.0, 47)]
  562. # verify, that strictly decreasing dimensions work
  563. if flip_points:
  564. points = [tuple(reversed(p)) for p in points]
  565. rng = np.random.default_rng(1234)
  566. trailing_points = (3, 2)
  567. # NB: values has a `num_trailing_dims` trailing dimension
  568. values = rng.random((6, 7, 8, 9, *trailing_points))
  569. sample = rng.random(4) # a single sample point !
  570. interp = RegularGridInterpolator(points, values, method=method,
  571. bounds_error=False)
  572. v = interp(sample)
  573. # v has a single sample point *per entry in the trailing dimensions*
  574. assert v.shape == (1, *trailing_points)
  575. # check the values, too : manually loop over the trailing dimensions
  576. vs = np.empty(values.shape[-2:])
  577. for i in range(values.shape[-2]):
  578. for j in range(values.shape[-1]):
  579. interp = RegularGridInterpolator(points, values[..., i, j],
  580. method=method,
  581. bounds_error=False)
  582. vs[i, j] = interp(sample).item()
  583. v2 = np.expand_dims(vs, axis=0)
  584. xp_assert_close(v, v2, atol=1e-14, err_msg=method)
  585. def test_nonscalar_values_linear_2D(self):
  586. # Verify that non-scalar values work in the 2D fast path
  587. method = 'linear'
  588. points = [(0.0, 0.5, 1.0, 1.5, 2.0, 2.5),
  589. (0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0), ]
  590. rng = np.random.default_rng(1234)
  591. trailing_points = (3, 4)
  592. # NB: values has a `num_trailing_dims` trailing dimension
  593. values = rng.random((6, 7, *trailing_points))
  594. sample = rng.random(2) # a single sample point !
  595. interp = RegularGridInterpolator(points, values, method=method,
  596. bounds_error=False)
  597. v = interp(sample)
  598. # v has a single sample point *per entry in the trailing dimensions*
  599. assert v.shape == (1, *trailing_points)
  600. # check the values, too : manually loop over the trailing dimensions
  601. vs = np.empty(values.shape[-2:])
  602. for i in range(values.shape[-2]):
  603. for j in range(values.shape[-1]):
  604. interp = RegularGridInterpolator(points, values[..., i, j],
  605. method=method,
  606. bounds_error=False)
  607. vs[i, j] = interp(sample).item()
  608. v2 = np.expand_dims(vs, axis=0)
  609. xp_assert_close(v, v2, atol=1e-14, err_msg=method)
  610. @pytest.mark.parametrize(
  611. "dtype",
  612. [np.float32, np.float64, np.complex64, np.complex128]
  613. )
  614. @pytest.mark.parametrize("xi_dtype", [np.float32, np.float64])
  615. def test_float32_values(self, dtype, xi_dtype):
  616. # regression test for gh-17718: values.dtype=float32 fails
  617. def f(x, y):
  618. return 2 * x**3 + 3 * y**2
  619. x = np.linspace(1, 4, 11)
  620. y = np.linspace(4, 7, 22)
  621. xg, yg = np.meshgrid(x, y, indexing='ij', sparse=True)
  622. data = f(xg, yg)
  623. data = data.astype(dtype)
  624. interp = RegularGridInterpolator((x, y), data)
  625. pts = np.array([[2.1, 6.2],
  626. [3.3, 5.2]], dtype=xi_dtype)
  627. # the values here are just what the call returns; the test checks that
  628. # that the call succeeds at all, instead of failing with cython not
  629. # having a float32 kernel
  630. xp_assert_close(interp(pts), [134.10469388, 153.40069388],
  631. atol=1e-7, rtol=1e-7, check_dtype=False)
  632. def test_bad_solver(self):
  633. x = np.linspace(0, 3, 7)
  634. y = np.linspace(0, 3, 7)
  635. xg, yg = np.meshgrid(x, y, indexing='ij', sparse=True)
  636. data = xg + yg
  637. # default method 'linear' does not accept 'solver'
  638. with assert_raises(ValueError):
  639. RegularGridInterpolator((x, y), data, solver=lambda x: x)
  640. with assert_raises(TypeError):
  641. # wrong solver interface
  642. RegularGridInterpolator(
  643. (x, y), data, method='slinear', solver=lambda x: x
  644. )
  645. with assert_raises(TypeError):
  646. # unknown argument
  647. RegularGridInterpolator(
  648. (x, y), data, method='slinear', solver=lambda x: x, woof='woof'
  649. )
  650. with assert_raises(TypeError):
  651. # unknown argument
  652. RegularGridInterpolator(
  653. (x, y), data, method='slinear', solver_args={'woof': 42}
  654. )
  655. def test_concurrency(self):
  656. points, values = self._get_sample_4d(np)
  657. sample = np.array([[0.1 , 0.1 , 1. , 0.9 ],
  658. [0.2 , 0.1 , 0.45, 0.8 ],
  659. [0.5 , 0.5 , 0.5 , 0.5 ],
  660. [0.3 , 0.1 , 0.2 , 0.4 ]])
  661. interp = RegularGridInterpolator(points, values, method="slinear")
  662. # A call to RGI with a method different from the one specified on the
  663. # constructor, should not mutate it.
  664. methods = ['slinear', 'nearest']
  665. def worker_fn(tid, interp):
  666. spline = interp._spline
  667. method = methods[tid % 2]
  668. interp(sample, method=method)
  669. assert interp._spline is spline
  670. _run_concurrent_barrier(10, worker_fn, interp)
  671. class MyValue:
  672. """
  673. Minimal indexable object
  674. """
  675. def __init__(self, shape):
  676. self.ndim = 2
  677. self.shape = shape
  678. self._v = np.arange(np.prod(shape)).reshape(shape)
  679. def __getitem__(self, idx):
  680. return self._v[idx]
  681. def __array_interface__(self):
  682. return None
  683. def __array__(self, dtype=None, copy=None):
  684. raise RuntimeError("No array representation")
  685. class TestInterpN:
  686. def _sample_2d_data(self):
  687. x = np.array([.5, 2., 3., 4., 5.5, 6.])
  688. y = np.array([.5, 2., 3., 4., 5.5, 6.])
  689. z = np.array(
  690. [
  691. [1, 2, 1, 2, 1, 1],
  692. [1, 2, 1, 2, 1, 1],
  693. [1, 2, 3, 2, 1, 1],
  694. [1, 2, 2, 2, 1, 1],
  695. [1, 2, 1, 2, 1, 1],
  696. [1, 2, 2, 2, 1, 1],
  697. ]
  698. )
  699. return x, y, z
  700. def test_spline_2d(self):
  701. x, y, z = self._sample_2d_data()
  702. lut = RectBivariateSpline(x, y, z)
  703. xi = np.array([[1, 2.3, 5.3, 0.5, 3.3, 1.2, 3],
  704. [1, 3.3, 1.2, 4.0, 5.0, 1.0, 3]]).T
  705. assert_array_almost_equal(interpn((x, y), z, xi, method="splinef2d"),
  706. lut.ev(xi[:, 0], xi[:, 1]))
  707. @parametrize_rgi_interp_methods
  708. def test_list_input(self, method):
  709. x, y, z = self._sample_2d_data()
  710. xi = np.array([[1, 2.3, 5.3, 0.5, 3.3, 1.2, 3],
  711. [1, 3.3, 1.2, 4.0, 5.0, 1.0, 3]]).T
  712. v1 = interpn((x, y), z, xi, method=method)
  713. v2 = interpn(
  714. (x.tolist(), y.tolist()), z.tolist(), xi.tolist(), method=method
  715. )
  716. xp_assert_close(v1, v2, err_msg=method)
  717. def test_spline_2d_outofbounds(self):
  718. x = np.array([.5, 2., 3., 4., 5.5])
  719. y = np.array([.5, 2., 3., 4., 5.5])
  720. z = np.array([[1, 2, 1, 2, 1], [1, 2, 1, 2, 1], [1, 2, 3, 2, 1],
  721. [1, 2, 2, 2, 1], [1, 2, 1, 2, 1]])
  722. lut = RectBivariateSpline(x, y, z)
  723. xi = np.array([[1, 2.3, 6.3, 0.5, 3.3, 1.2, 3],
  724. [1, 3.3, 1.2, -4.0, 5.0, 1.0, 3]]).T
  725. actual = interpn((x, y), z, xi, method="splinef2d",
  726. bounds_error=False, fill_value=999.99)
  727. expected = lut.ev(xi[:, 0], xi[:, 1])
  728. expected[2:4] = 999.99
  729. assert_array_almost_equal(actual, expected)
  730. # no extrapolation for splinef2d
  731. assert_raises(ValueError, interpn, (x, y), z, xi, method="splinef2d",
  732. bounds_error=False, fill_value=None)
  733. def _sample_4d_data(self):
  734. points = [(0., .5, 1.)] * 2 + [(0., 5., 10.)] * 2
  735. values = np.asarray([0., .5, 1.])
  736. values0 = values[:, np.newaxis, np.newaxis, np.newaxis]
  737. values1 = values[np.newaxis, :, np.newaxis, np.newaxis]
  738. values2 = values[np.newaxis, np.newaxis, :, np.newaxis]
  739. values3 = values[np.newaxis, np.newaxis, np.newaxis, :]
  740. values = (values0 + values1 * 10 + values2 * 100 + values3 * 1000)
  741. return points, values
  742. def test_linear_4d(self):
  743. # create a 4-D grid of 3 points in each dimension
  744. points, values = self._sample_4d_data()
  745. interp_rg = RegularGridInterpolator(points, values)
  746. sample = np.asarray([[0.1, 0.1, 10., 9.]])
  747. wanted = interpn(points, values, sample, method="linear")
  748. assert_array_almost_equal(interp_rg(sample), wanted)
  749. def test_4d_linear_outofbounds(self):
  750. # create a 4-D grid of 3 points in each dimension
  751. points, values = self._sample_4d_data()
  752. sample = np.asarray([[0.1, -0.1, 10.1, 9.]])
  753. wanted = np.asarray([999.99])
  754. actual = interpn(points, values, sample, method="linear",
  755. bounds_error=False, fill_value=999.99)
  756. assert_array_almost_equal(actual, wanted)
  757. def test_nearest_4d(self):
  758. # create a 4-D grid of 3 points in each dimension
  759. points, values = self._sample_4d_data()
  760. interp_rg = RegularGridInterpolator(points, values, method="nearest")
  761. sample = np.asarray([[0.1, 0.1, 10., 9.]])
  762. wanted = interpn(points, values, sample, method="nearest")
  763. assert_array_almost_equal(interp_rg(sample), wanted)
  764. def test_4d_nearest_outofbounds(self):
  765. # create a 4-D grid of 3 points in each dimension
  766. points, values = self._sample_4d_data()
  767. sample = np.asarray([[0.1, -0.1, 10.1, 9.]])
  768. wanted = np.asarray([999.99])
  769. actual = interpn(points, values, sample, method="nearest",
  770. bounds_error=False, fill_value=999.99)
  771. assert_array_almost_equal(actual, wanted)
  772. def test_xi_1d(self):
  773. # verify that 1-D xi works as expected
  774. points, values = self._sample_4d_data()
  775. sample = np.asarray([0.1, 0.1, 10., 9.])
  776. v1 = interpn(points, values, sample, bounds_error=False)
  777. v2 = interpn(points, values, sample[None,:], bounds_error=False)
  778. xp_assert_close(v1, v2)
  779. def test_xi_nd(self):
  780. # verify that higher-d xi works as expected
  781. points, values = self._sample_4d_data()
  782. np.random.seed(1234)
  783. sample = np.random.rand(2, 3, 4)
  784. v1 = interpn(points, values, sample, method='nearest',
  785. bounds_error=False)
  786. assert v1.shape == (2, 3)
  787. v2 = interpn(points, values, sample.reshape(-1, 4),
  788. method='nearest', bounds_error=False)
  789. xp_assert_close(v1, v2.reshape(v1.shape))
  790. @parametrize_rgi_interp_methods
  791. def test_xi_broadcast(self, method):
  792. # verify that the interpolators broadcast xi
  793. x, y, values = self._sample_2d_data()
  794. points = (x, y)
  795. xi = np.linspace(0, 1, 2)
  796. yi = np.linspace(0, 3, 3)
  797. sample = (xi[:, None], yi[None, :])
  798. v1 = interpn(points, values, sample, method=method, bounds_error=False)
  799. assert v1.shape == (2, 3)
  800. xx, yy = np.meshgrid(xi, yi)
  801. sample = np.c_[xx.T.ravel(), yy.T.ravel()]
  802. v2 = interpn(points, values, sample,
  803. method=method, bounds_error=False)
  804. xp_assert_close(v1, v2.reshape(v1.shape))
  805. @pytest.mark.fail_slow(5)
  806. @parametrize_rgi_interp_methods
  807. def test_nonscalar_values(self, method):
  808. if method == "quintic":
  809. pytest.skip("Way too slow.")
  810. # Verify that non-scalar valued values also works
  811. points = [(0.0, 0.5, 1.0, 1.5, 2.0, 2.5)] * 2 + [
  812. (0.0, 5.0, 10.0, 15.0, 20, 25.0)
  813. ] * 2
  814. rng = np.random.default_rng(1234)
  815. values = rng.random((6, 6, 6, 6, 8))
  816. sample = rng.random((7, 3, 4))
  817. v = interpn(points, values, sample, method=method,
  818. bounds_error=False)
  819. assert v.shape == (7, 3, 8), method
  820. vs = [interpn(points, values[..., j], sample, method=method,
  821. bounds_error=False) for j in range(8)]
  822. v2 = np.array(vs).transpose(1, 2, 0)
  823. xp_assert_close(v, v2, atol=1e-14, err_msg=method)
  824. @parametrize_rgi_interp_methods
  825. def test_nonscalar_values_2(self, method):
  826. if method in {"cubic", "quintic"}:
  827. pytest.skip("Way too slow.")
  828. # Verify that non-scalar valued values also work : use different
  829. # lengths of axes to simplify tracing the internals
  830. points = [(0.0, 0.5, 1.0, 1.5, 2.0, 2.5),
  831. (0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0),
  832. (0.0, 5.0, 10.0, 15.0, 20, 25.0, 35.0, 36.0),
  833. (0.0, 5.0, 10.0, 15.0, 20, 25.0, 35.0, 36.0, 47)]
  834. rng = np.random.default_rng(1234)
  835. trailing_points = (3, 2)
  836. # NB: values has a `num_trailing_dims` trailing dimension
  837. values = rng.random((6, 7, 8, 9, *trailing_points))
  838. sample = rng.random(4) # a single sample point !
  839. v = interpn(points, values, sample, method=method, bounds_error=False)
  840. # v has a single sample point *per entry in the trailing dimensions*
  841. assert v.shape == (1, *trailing_points)
  842. # check the values, too : manually loop over the trailing dimensions
  843. vs = [[
  844. interpn(points, values[..., i, j], sample, method=method,
  845. bounds_error=False) for i in range(values.shape[-2])
  846. ] for j in range(values.shape[-1])]
  847. xp_assert_close(v, np.asarray(vs).T, atol=1e-14, err_msg=method)
  848. def test_non_scalar_values_splinef2d(self):
  849. # Vector-valued splines supported with fitpack
  850. points, values = self._sample_4d_data()
  851. np.random.seed(1234)
  852. values = np.random.rand(3, 3, 3, 3, 6)
  853. sample = np.random.rand(7, 11, 4)
  854. assert_raises(ValueError, interpn, points, values, sample,
  855. method='splinef2d')
  856. @parametrize_rgi_interp_methods
  857. def test_complex(self, method):
  858. if method == "pchip":
  859. pytest.skip("pchip does not make sense for complex data")
  860. x, y, values = self._sample_2d_data()
  861. points = (x, y)
  862. values = values - 2j*values
  863. sample = np.array([[1, 2.3, 5.3, 0.5, 3.3, 1.2, 3],
  864. [1, 3.3, 1.2, 4.0, 5.0, 1.0, 3]]).T
  865. v1 = interpn(points, values, sample, method=method)
  866. v2r = interpn(points, values.real, sample, method=method)
  867. v2i = interpn(points, values.imag, sample, method=method)
  868. v2 = v2r + 1j*v2i
  869. xp_assert_close(v1, v2)
  870. def test_complex_pchip(self):
  871. # Complex-valued data deprecated for pchip
  872. x, y, values = self._sample_2d_data()
  873. points = (x, y)
  874. values = values - 2j*values
  875. sample = np.array([[1, 2.3, 5.3, 0.5, 3.3, 1.2, 3],
  876. [1, 3.3, 1.2, 4.0, 5.0, 1.0, 3]]).T
  877. with pytest.raises(ValueError, match='real'):
  878. interpn(points, values, sample, method='pchip')
  879. def test_complex_spline2fd(self):
  880. # Complex-valued data not supported by spline2fd
  881. x, y, values = self._sample_2d_data()
  882. points = (x, y)
  883. values = values - 2j*values
  884. sample = np.array([[1, 2.3, 5.3, 0.5, 3.3, 1.2, 3],
  885. [1, 3.3, 1.2, 4.0, 5.0, 1.0, 3]]).T
  886. with pytest.warns(ComplexWarning):
  887. interpn(points, values, sample, method='splinef2d')
  888. @pytest.mark.parametrize(
  889. "method",
  890. ["linear", "nearest"]
  891. )
  892. def test_duck_typed_values(self, method):
  893. x = np.linspace(0, 2, 5)
  894. y = np.linspace(0, 1, 7)
  895. values = MyValue((5, 7))
  896. v1 = interpn((x, y), values, [0.4, 0.7], method=method)
  897. v2 = interpn((x, y), values._v, [0.4, 0.7], method=method)
  898. xp_assert_close(v1, v2, check_dtype=False)
  899. @skip_xp_invalid_arg
  900. @parametrize_rgi_interp_methods
  901. def test_matrix_input(self, method):
  902. """np.matrix inputs are allowed for backwards compatibility"""
  903. x = np.linspace(0, 2, 6)
  904. y = np.linspace(0, 1, 7)
  905. values = matrix(np.random.rand(6, 7))
  906. sample = np.random.rand(3, 7, 2)
  907. v1 = interpn((x, y), values, sample, method=method)
  908. v2 = interpn((x, y), np.asarray(values), sample, method=method)
  909. if method == "quintic":
  910. # https://github.com/scipy/scipy/issues/20472
  911. xp_assert_close(v1, v2, atol=5e-5, rtol=2e-6)
  912. else:
  913. xp_assert_close(v1, v2)
  914. def test_length_one_axis(self):
  915. # gh-5890, gh-9524 : length-1 axis is legal for method='linear'.
  916. # Along the axis it's linear interpolation; away from the length-1
  917. # axis, it's an extrapolation, so fill_value should be used.
  918. values = np.array([[0.1, 1, 10]])
  919. xi = np.array([[1, 2.2], [1, 3.2], [1, 3.8]])
  920. res = interpn(([1], [2, 3, 4]), values, xi)
  921. wanted = [0.9*0.2 + 0.1, # on [2, 3) it's 0.9*(x-2) + 0.1
  922. 9*0.2 + 1, # on [3, 4] it's 9*(x-3) + 1
  923. 9*0.8 + 1]
  924. xp_assert_close(res, wanted, atol=1e-15)
  925. # check extrapolation
  926. xi = np.array([[1.1, 2.2], [1.5, 3.2], [-2.3, 3.8]])
  927. res = interpn(([1], [2, 3, 4]), values, xi,
  928. bounds_error=False, fill_value=None)
  929. xp_assert_close(res, wanted, atol=1e-15)
  930. def test_descending_points(self):
  931. def value_func_4d(x, y, z, a):
  932. return 2 * x ** 3 + 3 * y ** 2 - z - a
  933. x1 = np.array([0, 1, 2, 3])
  934. x2 = np.array([0, 10, 20, 30])
  935. x3 = np.array([0, 10, 20, 30])
  936. x4 = np.array([0, .1, .2, .30])
  937. points = (x1, x2, x3, x4)
  938. values = value_func_4d(
  939. *np.meshgrid(*points, indexing='ij', sparse=True))
  940. pts = (0.1, 0.3, np.transpose(np.linspace(0, 30, 4)),
  941. np.linspace(0, 0.3, 4))
  942. correct_result = interpn(points, values, pts)
  943. x1_descend = x1[::-1]
  944. x2_descend = x2[::-1]
  945. x3_descend = x3[::-1]
  946. x4_descend = x4[::-1]
  947. points_shuffled = (x1_descend, x2_descend, x3_descend, x4_descend)
  948. values_shuffled = value_func_4d(
  949. *np.meshgrid(*points_shuffled, indexing='ij', sparse=True))
  950. test_result = interpn(points_shuffled, values_shuffled, pts)
  951. xp_assert_equal(correct_result, test_result)
  952. def test_invalid_points_order(self):
  953. x = np.array([.5, 2., 0., 4., 5.5]) # not ascending or descending
  954. y = np.array([.5, 2., 3., 4., 5.5])
  955. z = np.array([[1, 2, 1, 2, 1], [1, 2, 1, 2, 1], [1, 2, 3, 2, 1],
  956. [1, 2, 2, 2, 1], [1, 2, 1, 2, 1]])
  957. xi = np.array([[1, 2.3, 6.3, 0.5, 3.3, 1.2, 3],
  958. [1, 3.3, 1.2, -4.0, 5.0, 1.0, 3]]).T
  959. match = "must be strictly ascending or descending"
  960. with pytest.raises(ValueError, match=match):
  961. interpn((x, y), z, xi)
  962. def test_invalid_xi_dimensions(self):
  963. # https://github.com/scipy/scipy/issues/16519
  964. points = [(0, 1)]
  965. values = [0, 1]
  966. xi = np.ones((1, 1, 3))
  967. msg = ("The requested sample points xi have dimension 3, but this "
  968. "RegularGridInterpolator has dimension 1")
  969. with assert_raises(ValueError, match=msg):
  970. interpn(points, values, xi)
  971. def test_readonly_grid(self):
  972. # https://github.com/scipy/scipy/issues/17716
  973. x = np.linspace(0, 4, 5)
  974. y = np.linspace(0, 5, 6)
  975. z = np.linspace(0, 6, 7)
  976. points = (x, y, z)
  977. values = np.ones((5, 6, 7))
  978. point = np.array([2.21, 3.12, 1.15])
  979. for d in points:
  980. d.flags.writeable = False
  981. values.flags.writeable = False
  982. point.flags.writeable = False
  983. interpn(points, values, point)
  984. RegularGridInterpolator(points, values)(point)
  985. def test_2d_readonly_grid(self):
  986. # https://github.com/scipy/scipy/issues/17716
  987. # test special 2d case
  988. x = np.linspace(0, 4, 5)
  989. y = np.linspace(0, 5, 6)
  990. points = (x, y)
  991. values = np.ones((5, 6))
  992. point = np.array([2.21, 3.12])
  993. for d in points:
  994. d.flags.writeable = False
  995. values.flags.writeable = False
  996. point.flags.writeable = False
  997. interpn(points, values, point)
  998. RegularGridInterpolator(points, values)(point)
  999. def test_non_c_contiguous_grid(self):
  1000. # https://github.com/scipy/scipy/issues/17716
  1001. x = np.linspace(0, 4, 5)
  1002. x = np.vstack((x, np.empty_like(x))).T.copy()[:, 0]
  1003. assert not x.flags.c_contiguous
  1004. y = np.linspace(0, 5, 6)
  1005. z = np.linspace(0, 6, 7)
  1006. points = (x, y, z)
  1007. values = np.ones((5, 6, 7))
  1008. point = np.array([2.21, 3.12, 1.15])
  1009. interpn(points, values, point)
  1010. RegularGridInterpolator(points, values)(point)
  1011. @pytest.mark.parametrize("dtype", ['>f8', '<f8'])
  1012. def test_endianness(self, dtype):
  1013. # https://github.com/scipy/scipy/issues/17716
  1014. # test special 2d case
  1015. x = np.linspace(0, 4, 5, dtype=dtype)
  1016. y = np.linspace(0, 5, 6, dtype=dtype)
  1017. points = (x, y)
  1018. values = np.ones((5, 6), dtype=dtype)
  1019. point = np.array([2.21, 3.12], dtype=dtype)
  1020. interpn(points, values, point)
  1021. RegularGridInterpolator(points, values)(point)