codekingpro/portable-devtools
114k
1import pytest
2
3import numpy as np
4from numpy.lib._index_tricks_impl import (
5 c_,
6 diag_indices,
7 diag_indices_from,
8 fill_diagonal,
9 index_exp,
10 ix_,
11 mgrid,
12 ndenumerate,
13 ndindex,
14 ogrid,
15 r_,
16 s_,
17)
18from numpy.testing import (
19 assert_,
20 assert_almost_equal,
21 assert_array_almost_equal,
22 assert_array_equal,
23 assert_equal,
24 assert_raises,
25 assert_raises_regex,
26)
27
28
29class TestRavelUnravelIndex:
30 def test_basic(self):
31 assert_equal(np.unravel_index(2, (2, 2)), (1, 0))
32
33 # test that new shape argument works properly
34 assert_equal(np.unravel_index(indices=2,
35 shape=(2, 2)),
36 (1, 0))
37
38 # test that an invalid second keyword argument
39 # is properly handled, including the old name `dims`.
40 with assert_raises(TypeError):
41 np.unravel_index(indices=2, hape=(2, 2))
42
43 with assert_raises(TypeError):
44 np.unravel_index(2, hape=(2, 2))
45
46 with assert_raises(TypeError):
47 np.unravel_index(254, ims=(17, 94))
48
49 with assert_raises(TypeError):
50 np.unravel_index(254, dims=(17, 94))
51
52 assert_equal(np.ravel_multi_index((1, 0), (2, 2)), 2)
53 assert_equal(np.unravel_index(254, (17, 94)), (2, 66))
54 assert_equal(np.ravel_multi_index((2, 66), (17, 94)), 254)
55 assert_raises(ValueError, np.unravel_index, -1, (2, 2))
56 assert_raises(TypeError, np.unravel_index, 0.5, (2, 2))
57 assert_raises(ValueError, np.unravel_index, 4, (2, 2))
58 assert_raises(ValueError, np.ravel_multi_index, (-3, 1), (2, 2))
59 assert_raises(ValueError, np.ravel_multi_index, (2, 1), (2, 2))
60 assert_raises(ValueError, np.ravel_multi_index, (0, -3), (2, 2))
61 assert_raises(ValueError, np.ravel_multi_index, (0, 2), (2, 2))
62 assert_raises(TypeError, np.ravel_multi_index, (0.1, 0.), (2, 2))
63
64 assert_equal(np.unravel_index((2 * 3 + 1) * 6 + 4, (4, 3, 6)), [2, 1, 4])
65 assert_equal(
66 np.ravel_multi_index([2, 1, 4], (4, 3, 6)), (2 * 3 + 1) * 6 + 4)
67
68 arr = np.array([[3, 6, 6], [4, 5, 1]])
69 assert_equal(np.ravel_multi_index(arr, (7, 6)), [22, 41, 37])
70 assert_equal(
71 np.ravel_multi_index(arr, (7, 6), order='F'), [31, 41, 13])
72 assert_equal(
73 np.ravel_multi_index(arr, (4, 6), mode='clip'), [22, 23, 19])
74 assert_equal(np.ravel_multi_index(arr, (4, 4), mode=('clip', 'wrap')),
75 [12, 13, 13])
76 assert_equal(np.ravel_multi_index((3, 1, 4, 1), (6, 7, 8, 9)), 1621)
77
78 assert_equal(np.unravel_index(np.array([22, 41, 37]), (7, 6)),
79 [[3, 6, 6], [4, 5, 1]])
80 assert_equal(
81 np.unravel_index(np.array([31, 41, 13]), (7, 6), order='F'),
82 [[3, 6, 6], [4, 5, 1]])
83 assert_equal(np.unravel_index(1621, (6, 7, 8, 9)), [3, 1, 4, 1])
84
85 def test_empty_indices(self):
86 msg1 = 'indices must be integral: the provided empty sequence was'
87 msg2 = 'only int indices permitted'
88 assert_raises_regex(TypeError, msg1, np.unravel_index, [], (10, 3, 5))
89 assert_raises_regex(TypeError, msg1, np.unravel_index, (), (10, 3, 5))
90 assert_raises_regex(TypeError, msg2, np.unravel_index, np.array([]),
91 (10, 3, 5))
92 assert_equal(np.unravel_index(np.array([], dtype=int), (10, 3, 5)),
93 [[], [], []])
94 assert_raises_regex(TypeError, msg1, np.ravel_multi_index, ([], []),
95 (10, 3))
96 assert_raises_regex(TypeError, msg1, np.ravel_multi_index, ([], ['abc']),
97 (10, 3))
98 assert_raises_regex(TypeError, msg2, np.ravel_multi_index,
99 (np.array([]), np.array([])), (5, 3))
100 assert_equal(np.ravel_multi_index(
101 (np.array([], dtype=int), np.array([], dtype=int)), (5, 3)), [])
102 assert_equal(np.ravel_multi_index(np.array([[], []], dtype=int),
103 (5, 3)), [])
104
105 def test_big_indices(self):
106 # ravel_multi_index for big indices (issue #7546)
107 if np.intp == np.int64:
108 arr = ([1, 29], [3, 5], [3, 117], [19, 2],
109 [2379, 1284], [2, 2], [0, 1])
110 assert_equal(
111 np.ravel_multi_index(arr, (41, 7, 120, 36, 2706, 8, 6)),
112 [5627771580, 117259570957])
113
114 # test unravel_index for big indices (issue #9538)
115 assert_raises(ValueError, np.unravel_index, 1, (2**32 - 1, 2**31 + 1))
116
117 # test overflow checking for too big array (issue #7546)
118 dummy_arr = ([0], [0])
119 half_max = np.iinfo(np.intp).max // 2
120 assert_equal(
121 np.ravel_multi_index(dummy_arr, (half_max, 2)), [0])
122 assert_raises(ValueError,
123 np.ravel_multi_index, dummy_arr, (half_max + 1, 2))
124 assert_equal(
125 np.ravel_multi_index(dummy_arr, (half_max, 2), order='F'), [0])
126 assert_raises(ValueError,
127 np.ravel_multi_index, dummy_arr, (half_max + 1, 2), order='F')
128
129 def test_dtypes(self):
130 # Test with different data types
131 for dtype in [np.int16, np.uint16, np.int32,
132 np.uint32, np.int64, np.uint64]:
133 coords = np.array(
134 [[1, 0, 1, 2, 3, 4], [1, 6, 1, 3, 2, 0]], dtype=dtype)
135 shape = (5, 8)
136 uncoords = 8 * coords[0] + coords[1]
137 assert_equal(np.ravel_multi_index(coords, shape), uncoords)
138 assert_equal(coords, np.unravel_index(uncoords, shape))
139 uncoords = coords[0] + 5 * coords[1]
140 assert_equal(
141 np.ravel_multi_index(coords, shape, order='F'), uncoords)
142 assert_equal(coords, np.unravel_index(uncoords, shape, order='F'))
143
144 coords = np.array(
145 [[1, 0, 1, 2, 3, 4], [1, 6, 1, 3, 2, 0], [1, 3, 1, 0, 9, 5]],
146 dtype=dtype)
147 shape = (5, 8, 10)
148 uncoords = 10 * (8 * coords[0] + coords[1]) + coords[2]
149 assert_equal(np.ravel_multi_index(coords, shape), uncoords)
150 assert_equal(coords, np.unravel_index(uncoords, shape))
151 uncoords = coords[0] + 5 * (coords[1] + 8 * coords[2])
152 assert_equal(
153 np.ravel_multi_index(coords, shape, order='F'), uncoords)
154 assert_equal(coords, np.unravel_index(uncoords, shape, order='F'))
155
156 def test_clipmodes(self):
157 # Test clipmodes
158 assert_equal(
159 np.ravel_multi_index([5, 1, -1, 2], (4, 3, 7, 12), mode='wrap'),
160 np.ravel_multi_index([1, 1, 6, 2], (4, 3, 7, 12)))
161 assert_equal(np.ravel_multi_index([5, 1, -1, 2], (4, 3, 7, 12),
162 mode=(
163 'wrap', 'raise', 'clip', 'raise')),
164 np.ravel_multi_index([1, 1, 0, 2], (4, 3, 7, 12)))
165 assert_raises(
166 ValueError, np.ravel_multi_index, [5, 1, -1, 2], (4, 3, 7, 12))
167
168 def test_writeability(self):
169 # gh-7269
170 x, y = np.unravel_index([1, 2, 3], (4, 5))
171 assert_(x.flags.writeable)
172 assert_(y.flags.writeable)
173
174 def test_0d(self):
175 # gh-580
176 x = np.unravel_index(0, ())
177 assert_equal(x, ())
178
179 assert_raises_regex(ValueError, "0d array", np.unravel_index, [0], ())
180 assert_raises_regex(
181 ValueError, "out of bounds", np.unravel_index, [1], ())
182
183 @pytest.mark.parametrize("mode", ["clip", "wrap", "raise"])
184 def test_empty_array_ravel(self, mode):
185 res = np.ravel_multi_index(
186 np.zeros((3, 0), dtype=np.intp), (2, 1, 0), mode=mode)
187 assert res.shape == (0,)
188
189 with assert_raises(ValueError):
190 np.ravel_multi_index(
191 np.zeros((3, 1), dtype=np.intp), (2, 1, 0), mode=mode)
192
193 def test_empty_array_unravel(self):
194 res = np.unravel_index(np.zeros(0, dtype=np.intp), (2, 1, 0))
195 # res is a tuple of three empty arrays
196 assert len(res) == 3
197 assert all(a.shape == (0,) for a in res)
198
199 with assert_raises(ValueError):
200 np.unravel_index([1], (2, 1, 0))
201
202 def test_regression_size_1_index(self):
203 # actually tests the nditer size one index tracking
204 # regression test for gh-29690
205 np.unravel_index(np.array([[1, 0, 1, 0]], dtype=np.uint32), (4,))
206
207class TestGrid:
208 def test_basic(self):
209 a = mgrid[-1:1:10j]
210 b = mgrid[-1:1:0.1]
211 assert_(a.shape == (10,))
212 assert_(b.shape == (20,))
213 assert_(a[0] == -1)
214 assert_almost_equal(a[-1], 1)
215 assert_(b[0] == -1)
216 assert_almost_equal(b[1] - b[0], 0.1, 11)
217 assert_almost_equal(b[-1], b[0] + 19 * 0.1, 11)
218 assert_almost_equal(a[1] - a[0], 2.0 / 9.0, 11)
219
220 def test_linspace_equivalence(self):
221 y, st = np.linspace(2, 10, retstep=True)
222 assert_almost_equal(st, 8 / 49.0)
223 assert_array_almost_equal(y, mgrid[2:10:50j], 13)
224
225 def test_nd(self):
226 c = mgrid[-1:1:10j, -2:2:10j]
227 d = mgrid[-1:1:0.1, -2:2:0.2]
228 assert_(c.shape == (2, 10, 10))
229 assert_(d.shape == (2, 20, 20))
230 assert_array_equal(c[0][0, :], -np.ones(10, 'd'))
231 assert_array_equal(c[1][:, 0], -2 * np.ones(10, 'd'))
232 assert_array_almost_equal(c[0][-1, :], np.ones(10, 'd'), 11)
233 assert_array_almost_equal(c[1][:, -1], 2 * np.ones(10, 'd'), 11)
234 assert_array_almost_equal(d[0, 1, :] - d[0, 0, :],
235 0.1 * np.ones(20, 'd'), 11)
236 assert_array_almost_equal(d[1, :, 1] - d[1, :, 0],
237 0.2 * np.ones(20, 'd'), 11)
238
239 def test_sparse(self):
240 grid_full = mgrid[-1:1:10j, -2:2:10j]
241 grid_sparse = ogrid[-1:1:10j, -2:2:10j]
242
243 # sparse grids can be made dense by broadcasting
244 grid_broadcast = np.broadcast_arrays(*grid_sparse)
245 for f, b in zip(grid_full, grid_broadcast):
246 assert_equal(f, b)
247
248 @pytest.mark.parametrize("start, stop, step, expected", [
249 (None, 10, 10j, (200, 10)),
250 (-10, 20, None, (1800, 30)),
251 ])
252 def test_mgrid_size_none_handling(self, start, stop, step, expected):
253 # regression test None value handling for
254 # start and step values used by mgrid;
255 # internally, this aims to cover previously
256 # unexplored code paths in nd_grid()
257 grid = mgrid[start:stop:step, start:stop:step]
258 # need a smaller grid to explore one of the
259 # untested code paths
260 grid_small = mgrid[start:stop:step]
261 assert_equal(grid.size, expected[0])
262 assert_equal(grid_small.size, expected[1])
263
264 def test_accepts_npfloating(self):
265 # regression test for #16466
266 grid64 = mgrid[0.1:0.33:0.1, ]
267 grid32 = mgrid[np.float32(0.1):np.float32(0.33):np.float32(0.1), ]
268 assert_array_almost_equal(grid64, grid32)
269 # At some point this was float64, but NEP 50 changed it:
270 assert grid32.dtype == np.float32
271 assert grid64.dtype == np.float64
272
273 # different code path for single slice
274 grid64 = mgrid[0.1:0.33:0.1]
275 grid32 = mgrid[np.float32(0.1):np.float32(0.33):np.float32(0.1)]
276 assert_(grid32.dtype == np.float64)
277 assert_array_almost_equal(grid64, grid32)
278
279 def test_accepts_longdouble(self):
280 # regression tests for #16945
281 grid64 = mgrid[0.1:0.33:0.1, ]
282 grid128 = mgrid[
283 np.longdouble(0.1):np.longdouble(0.33):np.longdouble(0.1),
284 ]
285 assert_(grid128.dtype == np.longdouble)
286 assert_array_almost_equal(grid64, grid128)
287
288 grid128c_a = mgrid[0:np.longdouble(1):3.4j]
289 grid128c_b = mgrid[0:np.longdouble(1):3.4j, ]
290 assert_(grid128c_a.dtype == grid128c_b.dtype == np.longdouble)
291 assert_array_equal(grid128c_a, grid128c_b[0])
292
293 # different code path for single slice
294 grid64 = mgrid[0.1:0.33:0.1]
295 grid128 = mgrid[
296 np.longdouble(0.1):np.longdouble(0.33):np.longdouble(0.1)
297 ]
298 assert_(grid128.dtype == np.longdouble)
299 assert_array_almost_equal(grid64, grid128)
300
301 def test_accepts_npcomplexfloating(self):
302 # Related to #16466
303 assert_array_almost_equal(
304 mgrid[0.1:0.3:3j, ], mgrid[0.1:0.3:np.complex64(3j), ]
305 )
306
307 # different code path for single slice
308 assert_array_almost_equal(
309 mgrid[0.1:0.3:3j], mgrid[0.1:0.3:np.complex64(3j)]
310 )
311
312 # Related to #16945
313 grid64_a = mgrid[0.1:0.3:3.3j]
314 grid64_b = mgrid[0.1:0.3:3.3j, ][0]
315 assert_(grid64_a.dtype == grid64_b.dtype == np.float64)
316 assert_array_equal(grid64_a, grid64_b)
317
318 grid128_a = mgrid[0.1:0.3:np.clongdouble(3.3j)]
319 grid128_b = mgrid[0.1:0.3:np.clongdouble(3.3j), ][0]
320 assert_(grid128_a.dtype == grid128_b.dtype == np.longdouble)
321 assert_array_equal(grid64_a, grid64_b)
322
323
324class TestConcatenator:
325 def test_1d(self):
326 assert_array_equal(r_[1, 2, 3, 4, 5, 6], np.array([1, 2, 3, 4, 5, 6]))
327 b = np.ones(5)
328 c = r_[b, 0, 0, b]
329 assert_array_equal(c, [1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1])
330
331 def test_mixed_type(self):
332 g = r_[10.1, 1:10]
333 assert_(g.dtype == 'f8')
334
335 def test_more_mixed_type(self):
336 g = r_[-10.1, np.array([1]), np.array([2, 3, 4]), 10.0]
337 assert_(g.dtype == 'f8')
338
339 def test_complex_step(self):
340 # Regression test for #12262
341 g = r_[0:36:100j]
342 assert_(g.shape == (100,))
343
344 # Related to #16466
345 g = r_[0:36:np.complex64(100j)]
346 assert_(g.shape == (100,))
347
348 def test_2d(self):
349 b = np.random.rand(5, 5)
350 c = np.random.rand(5, 5)
351 d = r_['1', b, c] # append columns
352 assert_(d.shape == (5, 10))
353 assert_array_equal(d[:, :5], b)
354 assert_array_equal(d[:, 5:], c)
355 d = r_[b, c]
356 assert_(d.shape == (10, 5))
357 assert_array_equal(d[:5, :], b)
358 assert_array_equal(d[5:, :], c)
359
360 def test_0d(self):
361 assert_equal(r_[0, np.array(1), 2], [0, 1, 2])
362 assert_equal(r_[[0, 1, 2], np.array(3)], [0, 1, 2, 3])
363 assert_equal(r_[np.array(0), [1, 2, 3]], [0, 1, 2, 3])
364
365
366class TestNdenumerate:
367 def test_basic(self):
368 a = np.array([[1, 2], [3, 4]])
369 assert_equal(list(ndenumerate(a)),
370 [((0, 0), 1), ((0, 1), 2), ((1, 0), 3), ((1, 1), 4)])
371
372
373class TestIndexExpression:
374 def test_regression_1(self):
375 # ticket #1196
376 a = np.arange(2)
377 assert_equal(a[:-1], a[s_[:-1]])
378 assert_equal(a[:-1], a[index_exp[:-1]])
379
380 def test_simple_1(self):
381 a = np.random.rand(4, 5, 6)
382
383 assert_equal(a[:, :3, [1, 2]], a[index_exp[:, :3, [1, 2]]])
384 assert_equal(a[:, :3, [1, 2]], a[s_[:, :3, [1, 2]]])
385
386
387class TestIx_:
388 def test_regression_1(self):
389 # Test empty untyped inputs create outputs of indexing type, gh-5804
390 a, = np.ix_(range(0))
391 assert_equal(a.dtype, np.intp)
392
393 a, = np.ix_([])
394 assert_equal(a.dtype, np.intp)
395
396 # but if the type is specified, don't change it
397 a, = np.ix_(np.array([], dtype=np.float32))
398 assert_equal(a.dtype, np.float32)
399
400 def test_shape_and_dtype(self):
401 sizes = (4, 5, 3, 2)
402 # Test both lists and arrays
403 for func in (range, np.arange):
404 arrays = np.ix_(*[func(sz) for sz in sizes])
405 for k, (a, sz) in enumerate(zip(arrays, sizes)):
406 assert_equal(a.shape[k], sz)
407 assert_(all(sh == 1 for j, sh in enumerate(a.shape) if j != k))
408 assert_(np.issubdtype(a.dtype, np.integer))
409
410 def test_bool(self):
411 bool_a = [True, False, True, True]
412 int_a, = np.nonzero(bool_a)
413 assert_equal(np.ix_(bool_a)[0], int_a)
414
415 def test_1d_only(self):
416 idx2d = [[1, 2, 3], [4, 5, 6]]
417 assert_raises(ValueError, np.ix_, idx2d)
418
419 def test_repeated_input(self):
420 length_of_vector = 5
421 x = np.arange(length_of_vector)
422 out = ix_(x, x)
423 assert_equal(out[0].shape, (length_of_vector, 1))
424 assert_equal(out[1].shape, (1, length_of_vector))
425 # check that input shape is not modified
426 assert_equal(x.shape, (length_of_vector,))
427
428
429def test_c_():
430 a = c_[np.array([[1, 2, 3]]), 0, 0, np.array([[4, 5, 6]])]
431 assert_equal(a, [[1, 2, 3, 0, 0, 4, 5, 6]])
432
433
434class TestFillDiagonal:
435 def test_basic(self):
436 a = np.zeros((3, 3), int)
437 fill_diagonal(a, 5)
438 assert_array_equal(
439 a, np.array([[5, 0, 0],
440 [0, 5, 0],
441 [0, 0, 5]])
442 )
443
444 def test_tall_matrix(self):
445 a = np.zeros((10, 3), int)
446 fill_diagonal(a, 5)
447 assert_array_equal(
448 a, np.array([[5, 0, 0],
449 [0, 5, 0],
450 [0, 0, 5],
451 [0, 0, 0],
452 [0, 0, 0],
453 [0, 0, 0],
454 [0, 0, 0],
455 [0, 0, 0],
456 [0, 0, 0],
457 [0, 0, 0]])
458 )
459
460 def test_tall_matrix_wrap(self):
461 a = np.zeros((10, 3), int)
462 fill_diagonal(a, 5, True)
463 assert_array_equal(
464 a, np.array([[5, 0, 0],
465 [0, 5, 0],
466 [0, 0, 5],
467 [0, 0, 0],
468 [5, 0, 0],
469 [0, 5, 0],
470 [0, 0, 5],
471 [0, 0, 0],
472 [5, 0, 0],
473 [0, 5, 0]])
474 )
475
476 def test_wide_matrix(self):
477 a = np.zeros((3, 10), int)
478 fill_diagonal(a, 5)
479 assert_array_equal(
480 a, np.array([[5, 0, 0, 0, 0, 0, 0, 0, 0, 0],
481 [0, 5, 0, 0, 0, 0, 0, 0, 0, 0],
482 [0, 0, 5, 0, 0, 0, 0, 0, 0, 0]])
483 )
484
485 def test_operate_4d_array(self):
486 a = np.zeros((3, 3, 3, 3), int)
487 fill_diagonal(a, 4)
488 i = np.array([0, 1, 2])
489 assert_equal(np.where(a != 0), (i, i, i, i))
490
491 def test_low_dim_handling(self):
492 # raise error with low dimensionality
493 a = np.zeros(3, int)
494 with assert_raises_regex(ValueError, "at least 2-d"):
495 fill_diagonal(a, 5)
496
497 def test_hetero_shape_handling(self):
498 # raise error with high dimensionality and
499 # shape mismatch
500 a = np.zeros((3, 3, 7, 3), int)
501 with assert_raises_regex(ValueError, "equal length"):
502 fill_diagonal(a, 2)
503
504
505def test_diag_indices():
506 di = diag_indices(4)
507 a = np.array([[1, 2, 3, 4],
508 [5, 6, 7, 8],
509 [9, 10, 11, 12],
510 [13, 14, 15, 16]])
511 a[di] = 100
512 assert_array_equal(
513 a, np.array([[100, 2, 3, 4],
514 [5, 100, 7, 8],
515 [9, 10, 100, 12],
516 [13, 14, 15, 100]])
517 )
518
519 # Now, we create indices to manipulate a 3-d array:
520 d3 = diag_indices(2, 3)
521
522 # And use it to set the diagonal of a zeros array to 1:
523 a = np.zeros((2, 2, 2), int)
524 a[d3] = 1
525 assert_array_equal(
526 a, np.array([[[1, 0],
527 [0, 0]],
528 [[0, 0],
529 [0, 1]]])
530 )
531
532
533class TestDiagIndicesFrom:
534
535 def test_diag_indices_from(self):
536 x = np.random.random((4, 4))
537 r, c = diag_indices_from(x)
538 assert_array_equal(r, np.arange(4))
539 assert_array_equal(c, np.arange(4))
540
541 def test_error_small_input(self):
542 x = np.ones(7)
543 with assert_raises_regex(ValueError, "at least 2-d"):
544 diag_indices_from(x)
545
546 def test_error_shape_mismatch(self):
547 x = np.zeros((3, 3, 2, 3), int)
548 with assert_raises_regex(ValueError, "equal length"):
549 diag_indices_from(x)
550
551
552def test_ndindex():
553 x = list(ndindex(1, 2, 3))
554 expected = [ix for ix, e in ndenumerate(np.zeros((1, 2, 3)))]
555 assert_array_equal(x, expected)
556
557 x = list(ndindex((1, 2, 3)))
558 assert_array_equal(x, expected)
559
560 # Test use of scalars and tuples
561 x = list(ndindex((3,)))
562 assert_array_equal(x, list(ndindex(3)))
563
564 # Make sure size argument is optional
565 x = list(ndindex())
566 assert_equal(x, [()])
567
568 x = list(ndindex(()))
569 assert_equal(x, [()])
570
571 # Make sure 0-sized ndindex works correctly
572 x = list(ndindex(*[0]))
573 assert_equal(x, [])
574
575
576def test_ndindex_zero_dimensions_explicit():
577 """Test ndindex produces empty iterators for explicit
578 zero-length dimensions."""
579 assert list(np.ndindex(0, 3)) == []
580 assert list(np.ndindex(3, 0, 2)) == []
581 assert list(np.ndindex(0)) == []
582
583
584@pytest.mark.parametrize("bad_shape", [2.5, "2", [2, 3], (2.0, 3)])
585def test_ndindex_non_integer_dimensions(bad_shape):
586 """Test that non-integer dimensions raise TypeError."""
587 with pytest.raises(TypeError):
588 # Passing invalid_shape_arg directly to ndindex. It will try to use it
589 # as a dimension and should trigger a TypeError.
590 list(np.ndindex(bad_shape))
591
592
593def test_ndindex_stop_iteration_behavior():
594 """Test that StopIteration is raised properly after exhaustion."""
595 it = np.ndindex(2, 2)
596 # Exhaust the iterator
597 list(it)
598 # Should raise StopIteration on subsequent calls
599 with pytest.raises(StopIteration):
600 next(it)
601
602
603def test_ndindex_iterator_independence():
604 """Test that each ndindex instance creates independent iterators."""
605 shape = (2, 3)
606 iter1 = np.ndindex(*shape)
607 iter2 = np.ndindex(*shape)
608
609 next(iter1)
610 next(iter1)
611
612 assert_equal(next(iter2), (0, 0))
613 assert_equal(next(iter1), (0, 2))
614
615
616def test_ndindex_tuple_vs_args_consistency():
617 """Test that ndindex(shape) and ndindex(*shape) produce same results."""
618 # Single dimension
619 assert_equal(list(np.ndindex(5)), list(np.ndindex((5,))))
620
621 # Multiple dimensions
622 assert_equal(list(np.ndindex(2, 3)), list(np.ndindex((2, 3))))
623
624 # Complex shape
625 shape = (2, 1, 4)
626 assert_equal(list(np.ndindex(*shape)), list(np.ndindex(shape)))
627
628
629def test_ndindex_against_ndenumerate_compatibility():
630 """Test ndindex produces same indices as ndenumerate."""
631 for shape in [(1, 2, 3), (3,), (2, 2), ()]:
632 ndindex_result = list(np.ndindex(shape))
633 ndenumerate_indices = [ix for ix, _ in np.ndenumerate(np.zeros(shape))]
634 assert_array_equal(ndindex_result, ndenumerate_indices)
635
636
637def test_ndindex_multidimensional_correctness():
638 """Test ndindex produces correct indices for multidimensional arrays."""
639 shape = (2, 1, 3)
640 result = list(np.ndindex(*shape))
641 expected = [
642 (0, 0, 0),
643 (0, 0, 1),
644 (0, 0, 2),
645 (1, 0, 0),
646 (1, 0, 1),
647 (1, 0, 2),
648 ]
649 assert_equal(result, expected)
650
651
652def test_ndindex_large_dimensions_behavior():
653 """Test ndindex behaves correctly when initialized with large dimensions."""
654 large_shape = (1000, 1000)
655 iter_obj = np.ndindex(*large_shape)
656 first_element = next(iter_obj)
657 assert_equal(first_element, (0, 0))
658
659
660def test_ndindex_empty_iterator_behavior():
661 """Test detailed behavior of empty iterators."""
662 empty_iter = np.ndindex(0, 5)
663 assert_equal(list(empty_iter), [])
664
665 empty_iter2 = np.ndindex(3, 0, 2)
666 with pytest.raises(StopIteration):
667 next(empty_iter2)
668
669
670@pytest.mark.parametrize(
671 "negative_shape_arg",
672 [
673 (-1,), # Single negative dimension
674 (2, -3, 4), # Negative dimension in the middle
675 (5, 0, -2), # Mix of valid (0) and invalid (negative) dimensions
676 ],
677)
678def test_ndindex_negative_dimensions(negative_shape_arg):
679 """Test that negative dimensions raise ValueError."""
680 with pytest.raises(ValueError):
681 ndindex(negative_shape_arg)
682
683
684def test_ndindex_empty_shape():
685 import numpy as np
686 # ndindex() and ndindex(()) should return a single empty tuple
687 assert list(np.ndindex()) == [()]
688 assert list(np.ndindex(())) == [()]
689
690def test_ndindex_negative_dim_raises():
691 # ndindex(-1) should raise a ValueError
692 with pytest.raises(ValueError):
693 list(np.ndindex(-1))
694 