1import contextlib 2import gc 3from itertools import product, cycle 4import sys 5import warnings 6from numbers import Number, Integral 7import platform 8 9import numpy as np 10 11from numba import jit, njit, typeof 12from numba.core import errors 13from numba.tests.support import (TestCase, tag, needs_lapack, needs_blas, 14 _is_armv7l, skip_ppc64le_issue4026) 15from .matmul_usecase import matmul_usecase 16import unittest 17 18 19def dot2(a, b): 20 return np.dot(a, b) 21 22 23def dot3(a, b, out): 24 return np.dot(a, b, out=out) 25 26 27def vdot(a, b): 28 return np.vdot(a, b) 29 30 31class TestProduct(TestCase): 32 """ 33 Tests for dot products. 34 """ 35 36 dtypes = (np.float64, np.float32, np.complex128, np.complex64) 37 38 def setUp(self): 39 # Collect leftovers from previous test cases before checking for leaks 40 gc.collect() 41 42 def sample_vector(self, n, dtype): 43 # Be careful to generate only exactly representable float values, 44 # to avoid rounding discrepancies between Numpy and Numba 45 base = np.arange(n) 46 if issubclass(dtype, np.complexfloating): 47 return (base * (1 - 0.5j) + 2j).astype(dtype) 48 else: 49 return (base * 0.5 - 1).astype(dtype) 50 51 def sample_matrix(self, m, n, dtype): 52 return self.sample_vector(m * n, dtype).reshape((m, n)) 53 54 @contextlib.contextmanager 55 def check_contiguity_warning(self, pyfunc): 56 """ 57 Check performance warning(s) for non-contiguity. 58 """ 59 with warnings.catch_warnings(record=True) as w: 60 warnings.simplefilter('always', errors.NumbaPerformanceWarning) 61 yield 62 self.assertGreaterEqual(len(w), 1) 63 self.assertIs(w[0].category, errors.NumbaPerformanceWarning) 64 self.assertIn("faster on contiguous arrays", str(w[0].message)) 65 self.assertEqual(w[0].filename, pyfunc.__code__.co_filename) 66 # This works because our functions are one-liners 67 self.assertEqual(w[0].lineno, pyfunc.__code__.co_firstlineno + 1) 68 69 def check_func(self, pyfunc, cfunc, args): 70 with self.assertNoNRTLeak(): 71 expected = pyfunc(*args) 72 got = cfunc(*args) 73 self.assertPreciseEqual(got, expected, ignore_sign_on_zero=True) 74 del got, expected 75 76 77 def _aligned_copy(self, arr): 78 # This exists for armv7l because NumPy wants aligned arrays for the 79 # `out` arg of functions, but np.empty/np.copy doesn't seem to always 80 # produce them, in particular for complex dtypes 81 size = (arr.size + 1) * arr.itemsize + 1 82 datasize = arr.size * arr.itemsize 83 tmp = np.empty(size, dtype=np.uint8) 84 for i in range(arr.itemsize + 1): 85 new = tmp[i : i + datasize].view(dtype=arr.dtype) 86 if new.flags.aligned: 87 break 88 else: 89 raise Exception("Could not obtain aligned array") 90 if arr.flags.c_contiguous: 91 new = np.reshape(new, arr.shape, order='C') 92 else: 93 new = np.reshape(new, arr.shape, order='F') 94 new[:] = arr[:] 95 assert new.flags.aligned 96 return new 97 98 def check_func_out(self, pyfunc, cfunc, args, out): 99 copier = self._aligned_copy if _is_armv7l else np.copy 100 with self.assertNoNRTLeak(): 101 expected = copier(out) 102 got = copier(out) 103 self.assertIs(pyfunc(*args, out=expected), expected) 104 self.assertIs(cfunc(*args, out=got), got) 105 self.assertPreciseEqual(got, expected, ignore_sign_on_zero=True) 106 del got, expected 107 108 def assert_mismatching_sizes(self, cfunc, args, is_out=False): 109 with self.assertRaises(ValueError) as raises: 110 cfunc(*args) 111 msg = ("incompatible output array size" if is_out else 112 "incompatible array sizes") 113 self.assertIn(msg, str(raises.exception)) 114 115 def assert_mismatching_dtypes(self, cfunc, args, func_name="np.dot()"): 116 with self.assertRaises(errors.TypingError) as raises: 117 cfunc(*args) 118 self.assertIn("%s arguments must all have the same dtype" 119 % (func_name,), 120 str(raises.exception)) 121 122 def check_dot_vv(self, pyfunc, func_name): 123 n = 3 124 cfunc = jit(nopython=True)(pyfunc) 125 for dtype in self.dtypes: 126 a = self.sample_vector(n, dtype) 127 b = self.sample_vector(n, dtype) 128 self.check_func(pyfunc, cfunc, (a, b)) 129 # Non-contiguous 130 self.check_func(pyfunc, cfunc, (a[::-1], b[::-1])) 131 132 # Mismatching sizes 133 a = self.sample_vector(n - 1, np.float64) 134 b = self.sample_vector(n, np.float64) 135 self.assert_mismatching_sizes(cfunc, (a, b)) 136 # Mismatching dtypes 137 a = self.sample_vector(n, np.float32) 138 b = self.sample_vector(n, np.float64) 139 self.assert_mismatching_dtypes(cfunc, (a, b), func_name=func_name) 140 141 @needs_blas 142 def test_dot_vv(self): 143 """ 144 Test vector * vector np.dot() 145 """ 146 self.check_dot_vv(dot2, "np.dot()") 147 148 @needs_blas 149 def test_vdot(self): 150 """ 151 Test np.vdot() 152 """ 153 self.check_dot_vv(vdot, "np.vdot()") 154 155 def check_dot_vm(self, pyfunc2, pyfunc3, func_name): 156 157 def samples(m, n): 158 for order in 'CF': 159 a = self.sample_matrix(m, n, np.float64).copy(order=order) 160 b = self.sample_vector(n, np.float64) 161 yield a, b 162 for dtype in self.dtypes: 163 a = self.sample_matrix(m, n, dtype) 164 b = self.sample_vector(n, dtype) 165 yield a, b 166 # Non-contiguous 167 yield a[::-1], b[::-1] 168 169 cfunc2 = jit(nopython=True)(pyfunc2) 170 if pyfunc3 is not None: 171 cfunc3 = jit(nopython=True)(pyfunc3) 172 173 for m, n in [(2, 3), 174 (3, 0), 175 (0, 3) 176 ]: 177 for a, b in samples(m, n): 178 self.check_func(pyfunc2, cfunc2, (a, b)) 179 self.check_func(pyfunc2, cfunc2, (b, a.T)) 180 if pyfunc3 is not None: 181 for a, b in samples(m, n): 182 out = np.empty(m, dtype=a.dtype) 183 self.check_func_out(pyfunc3, cfunc3, (a, b), out) 184 self.check_func_out(pyfunc3, cfunc3, (b, a.T), out) 185 186 # Mismatching sizes 187 m, n = 2, 3 188 a = self.sample_matrix(m, n - 1, np.float64) 189 b = self.sample_vector(n, np.float64) 190 self.assert_mismatching_sizes(cfunc2, (a, b)) 191 self.assert_mismatching_sizes(cfunc2, (b, a.T)) 192 if pyfunc3 is not None: 193 out = np.empty(m, np.float64) 194 self.assert_mismatching_sizes(cfunc3, (a, b, out)) 195 self.assert_mismatching_sizes(cfunc3, (b, a.T, out)) 196 a = self.sample_matrix(m, m, np.float64) 197 b = self.sample_vector(m, np.float64) 198 out = np.empty(m - 1, np.float64) 199 self.assert_mismatching_sizes(cfunc3, (a, b, out), is_out=True) 200 self.assert_mismatching_sizes(cfunc3, (b, a.T, out), is_out=True) 201 # Mismatching dtypes 202 a = self.sample_matrix(m, n, np.float32) 203 b = self.sample_vector(n, np.float64) 204 self.assert_mismatching_dtypes(cfunc2, (a, b), func_name) 205 if pyfunc3 is not None: 206 a = self.sample_matrix(m, n, np.float64) 207 b = self.sample_vector(n, np.float64) 208 out = np.empty(m, np.float32) 209 self.assert_mismatching_dtypes(cfunc3, (a, b, out), func_name) 210 211 @needs_blas 212 def test_dot_vm(self): 213 """ 214 Test vector * matrix and matrix * vector np.dot() 215 """ 216 self.check_dot_vm(dot2, dot3, "np.dot()") 217 218 def check_dot_mm(self, pyfunc2, pyfunc3, func_name): 219 220 def samples(m, n, k): 221 for order_a, order_b in product('CF', 'CF'): 222 a = self.sample_matrix(m, k, np.float64).copy(order=order_a) 223 b = self.sample_matrix(k, n, np.float64).copy(order=order_b) 224 yield a, b 225 for dtype in self.dtypes: 226 a = self.sample_matrix(m, k, dtype) 227 b = self.sample_matrix(k, n, dtype) 228 yield a, b 229 # Non-contiguous 230 yield a[::-1], b[::-1] 231 232 cfunc2 = jit(nopython=True)(pyfunc2) 233 if pyfunc3 is not None: 234 cfunc3 = jit(nopython=True)(pyfunc3) 235 236 # Test generic matrix * matrix as well as "degenerate" cases 237 # where one of the outer dimensions is 1 (i.e. really represents 238 # a vector, which may select a different implementation), 239 # one of the matrices is empty, or both matrices are empty. 240 for m, n, k in [(2, 3, 4), # Generic matrix * matrix 241 (1, 3, 4), # 2d vector * matrix 242 (1, 1, 4), # 2d vector * 2d vector 243 (0, 3, 2), # Empty matrix * matrix, empty output 244 (3, 0, 2), # Matrix * empty matrix, empty output 245 (0, 0, 3), # Both arguments empty, empty output 246 (3, 2, 0), # Both arguments empty, nonempty output 247 ]: 248 for a, b in samples(m, n, k): 249 self.check_func(pyfunc2, cfunc2, (a, b)) 250 self.check_func(pyfunc2, cfunc2, (b.T, a.T)) 251 if pyfunc3 is not None: 252 for a, b in samples(m, n, k): 253 out = np.empty((m, n), dtype=a.dtype) 254 self.check_func_out(pyfunc3, cfunc3, (a, b), out) 255 out = np.empty((n, m), dtype=a.dtype) 256 self.check_func_out(pyfunc3, cfunc3, (b.T, a.T), out) 257 258 # Mismatching sizes 259 m, n, k = 2, 3, 4 260 a = self.sample_matrix(m, k - 1, np.float64) 261 b = self.sample_matrix(k, n, np.float64) 262 self.assert_mismatching_sizes(cfunc2, (a, b)) 263 if pyfunc3 is not None: 264 out = np.empty((m, n), np.float64) 265 self.assert_mismatching_sizes(cfunc3, (a, b, out)) 266 a = self.sample_matrix(m, k, np.float64) 267 b = self.sample_matrix(k, n, np.float64) 268 out = np.empty((m, n - 1), np.float64) 269 self.assert_mismatching_sizes(cfunc3, (a, b, out), is_out=True) 270 # Mismatching dtypes 271 a = self.sample_matrix(m, k, np.float32) 272 b = self.sample_matrix(k, n, np.float64) 273 self.assert_mismatching_dtypes(cfunc2, (a, b), func_name) 274 if pyfunc3 is not None: 275 a = self.sample_matrix(m, k, np.float64) 276 b = self.sample_matrix(k, n, np.float64) 277 out = np.empty((m, n), np.float32) 278 self.assert_mismatching_dtypes(cfunc3, (a, b, out), func_name) 279 280 @needs_blas 281 def test_dot_mm(self): 282 """ 283 Test matrix * matrix np.dot() 284 """ 285 self.check_dot_mm(dot2, dot3, "np.dot()") 286 287 @needs_blas 288 def test_matmul_vv(self): 289 """ 290 Test vector @ vector 291 """ 292 self.check_dot_vv(matmul_usecase, "'@'") 293 294 @needs_blas 295 def test_matmul_vm(self): 296 """ 297 Test vector @ matrix and matrix @ vector 298 """ 299 self.check_dot_vm(matmul_usecase, None, "'@'") 300 301 @needs_blas 302 def test_matmul_mm(self): 303 """ 304 Test matrix @ matrix 305 """ 306 self.check_dot_mm(matmul_usecase, None, "'@'") 307 308 @needs_blas 309 def test_contiguity_warnings(self): 310 m, k, n = 2, 3, 4 311 dtype = np.float64 312 a = self.sample_matrix(m, k, dtype)[::-1] 313 b = self.sample_matrix(k, n, dtype)[::-1] 314 out = np.empty((m, n), dtype) 315 316 cfunc = jit(nopython=True)(dot2) 317 with self.check_contiguity_warning(cfunc.py_func): 318 cfunc(a, b) 319 cfunc = jit(nopython=True)(dot3) 320 with self.check_contiguity_warning(cfunc.py_func): 321 cfunc(a, b, out) 322 323 a = self.sample_vector(n, dtype)[::-1] 324 b = self.sample_vector(n, dtype)[::-1] 325 326 cfunc = jit(nopython=True)(vdot) 327 with self.check_contiguity_warning(cfunc.py_func): 328 cfunc(a, b) 329 330 331# Implementation definitions for the purpose of jitting. 332 333def invert_matrix(a): 334 return np.linalg.inv(a) 335 336 337def cholesky_matrix(a): 338 return np.linalg.cholesky(a) 339 340 341def eig_matrix(a): 342 return np.linalg.eig(a) 343 344 345def eigvals_matrix(a): 346 return np.linalg.eigvals(a) 347 348 349def eigh_matrix(a): 350 return np.linalg.eigh(a) 351 352 353def eigvalsh_matrix(a): 354 return np.linalg.eigvalsh(a) 355 356 357def svd_matrix(a, full_matrices=1): 358 return np.linalg.svd(a, full_matrices) 359 360 361def qr_matrix(a): 362 return np.linalg.qr(a) 363 364 365def lstsq_system(A, B, rcond=-1): 366 return np.linalg.lstsq(A, B, rcond) 367 368 369def solve_system(A, B): 370 return np.linalg.solve(A, B) 371 372 373def pinv_matrix(A, rcond=1e-15): # 1e-15 from numpy impl 374 return np.linalg.pinv(A) 375 376 377def slogdet_matrix(a): 378 return np.linalg.slogdet(a) 379 380 381def det_matrix(a): 382 return np.linalg.det(a) 383 384 385def norm_matrix(a, ord=None): 386 return np.linalg.norm(a, ord) 387 388 389def cond_matrix(a, p=None): 390 return np.linalg.cond(a, p) 391 392 393def matrix_rank_matrix(a, tol=None): 394 return np.linalg.matrix_rank(a, tol) 395 396 397def matrix_power_matrix(a, n): 398 return np.linalg.matrix_power(a, n) 399 400 401def trace_matrix(a, offset=0): 402 return np.trace(a, offset) 403 404 405def trace_matrix_no_offset(a): 406 return np.trace(a) 407 408 409def outer_matrix(a, b, out=None): 410 return np.outer(a, b, out=out) 411 412 413def kron_matrix(a, b): 414 return np.kron(a, b) 415 416 417class TestLinalgBase(TestCase): 418 """ 419 Provides setUp and common data/error modes for testing np.linalg functions. 420 """ 421 422 # supported dtypes 423 dtypes = (np.float64, np.float32, np.complex128, np.complex64) 424 425 def setUp(self): 426 # Collect leftovers from previous test cases before checking for leaks 427 gc.collect() 428 429 def sample_vector(self, n, dtype): 430 # Be careful to generate only exactly representable float values, 431 # to avoid rounding discrepancies between Numpy and Numba 432 base = np.arange(n) 433 if issubclass(dtype, np.complexfloating): 434 return (base * (1 - 0.5j) + 2j).astype(dtype) 435 else: 436 return (base * 0.5 + 1).astype(dtype) 437 438 def specific_sample_matrix( 439 self, size, dtype, order, rank=None, condition=None): 440 """ 441 Provides a sample matrix with an optionally specified rank or condition 442 number. 443 444 size: (rows, columns), the dimensions of the returned matrix. 445 dtype: the dtype for the returned matrix. 446 order: the memory layout for the returned matrix, 'F' or 'C'. 447 rank: the rank of the matrix, an integer value, defaults to full rank. 448 condition: the condition number of the matrix (defaults to 1.) 449 450 NOTE: Only one of rank or condition may be set. 451 """ 452 453 # default condition 454 d_cond = 1. 455 456 if len(size) != 2: 457 raise ValueError("size must be a length 2 tuple.") 458 459 if order not in ['F', 'C']: 460 raise ValueError("order must be one of 'F' or 'C'.") 461 462 if dtype not in [np.float32, np.float64, np.complex64, np.complex128]: 463 raise ValueError("dtype must be a numpy floating point type.") 464 465 if rank is not None and condition is not None: 466 raise ValueError("Only one of rank or condition can be specified.") 467 468 if condition is None: 469 condition = d_cond 470 471 if condition < 1: 472 raise ValueError("Condition number must be >=1.") 473 474 np.random.seed(0) # repeatable seed 475 m, n = size 476 477 if m < 0 or n < 0: 478 raise ValueError("Negative dimensions given for matrix shape.") 479 480 minmn = min(m, n) 481 if rank is None: 482 rv = minmn 483 else: 484 if rank <= 0: 485 raise ValueError("Rank must be greater than zero.") 486 if not isinstance(rank, Integral): 487 raise ValueError("Rank must an integer.") 488 rv = rank 489 if rank > minmn: 490 raise ValueError("Rank given greater than full rank.") 491 492 if m == 1 or n == 1: 493 # vector, must be rank 1 (enforced above) 494 # condition of vector is also 1 495 if condition != d_cond: 496 raise ValueError( 497 "Condition number was specified for a vector (always 1.).") 498 maxmn = max(m, n) 499 Q = self.sample_vector(maxmn, dtype).reshape(m, n) 500 else: 501 # Build a sample matrix via combining SVD like inputs. 502 503 # Create matrices of left and right singular vectors. 504 # This could use Modified Gram-Schmidt and perhaps be quicker, 505 # at present it uses QR decompositions to obtain orthonormal 506 # matrices. 507 tmp = self.sample_vector(m * m, dtype).reshape(m, m) 508 U, _ = np.linalg.qr(tmp) 509 # flip the second array, else for m==n the identity matrix appears 510 tmp = self.sample_vector(n * n, dtype)[::-1].reshape(n, n) 511 V, _ = np.linalg.qr(tmp) 512 # create singular values. 513 sv = np.linspace(d_cond, condition, rv) 514 S = np.zeros((m, n)) 515 idx = np.nonzero(np.eye(m, n)) 516 S[idx[0][:rv], idx[1][:rv]] = sv 517 Q = np.dot(np.dot(U, S), V.T) # construct 518 Q = np.array(Q, dtype=dtype, order=order) # sort out order/type 519 520 return Q 521 522 def assert_error(self, cfunc, args, msg, err=ValueError): 523 with self.assertRaises(err) as raises: 524 cfunc(*args) 525 self.assertIn(msg, str(raises.exception)) 526 527 def assert_non_square(self, cfunc, args): 528 msg = "Last 2 dimensions of the array must be square." 529 self.assert_error(cfunc, args, msg, np.linalg.LinAlgError) 530 531 def assert_wrong_dtype(self, name, cfunc, args): 532 msg = "np.linalg.%s() only supported on float and complex arrays" % name 533 self.assert_error(cfunc, args, msg, errors.TypingError) 534 535 def assert_wrong_dimensions(self, name, cfunc, args, la_prefix=True): 536 prefix = "np.linalg" if la_prefix else "np" 537 msg = "%s.%s() only supported on 2-D arrays" % (prefix, name) 538 self.assert_error(cfunc, args, msg, errors.TypingError) 539 540 def assert_no_nan_or_inf(self, cfunc, args): 541 msg = "Array must not contain infs or NaNs." 542 self.assert_error(cfunc, args, msg, np.linalg.LinAlgError) 543 544 def assert_contig_sanity(self, got, expected_contig): 545 """ 546 This checks that in a computed result from numba (array, possibly tuple 547 of arrays) all the arrays are contiguous in memory and that they are 548 all at least one of "C_CONTIGUOUS" or "F_CONTIGUOUS". The computed 549 result of the contiguousness is then compared against a hardcoded 550 expected result. 551 552 got: is the computed results from numba 553 expected_contig: is "C" or "F" and is the expected type of 554 contiguousness across all input values 555 (and therefore tests). 556 """ 557 558 if isinstance(got, tuple): 559 # tuple present, check all results 560 for a in got: 561 self.assert_contig_sanity(a, expected_contig) 562 else: 563 if not isinstance(got, Number): 564 # else a single array is present 565 c_contig = got.flags.c_contiguous 566 f_contig = got.flags.f_contiguous 567 568 # check that the result (possible set of) is at least one of 569 # C or F contiguous. 570 msg = "Results are not at least one of all C or F contiguous." 571 self.assertTrue(c_contig | f_contig, msg) 572 573 msg = "Computed contiguousness does not match expected." 574 if expected_contig == "C": 575 self.assertTrue(c_contig, msg) 576 elif expected_contig == "F": 577 self.assertTrue(f_contig, msg) 578 else: 579 raise ValueError("Unknown contig") 580 581 def assert_raise_on_singular(self, cfunc, args): 582 msg = "Matrix is singular to machine precision." 583 self.assert_error(cfunc, args, msg, err=np.linalg.LinAlgError) 584 585 def assert_is_identity_matrix(self, got, rtol=None, atol=None): 586 """ 587 Checks if a matrix is equal to the identity matrix. 588 """ 589 # check it is square 590 self.assertEqual(got.shape[-1], got.shape[-2]) 591 # create identity matrix 592 eye = np.eye(got.shape[-1], dtype=got.dtype) 593 resolution = 5 * np.finfo(got.dtype).resolution 594 if rtol is None: 595 rtol = 10 * resolution 596 if atol is None: 597 atol = 100 * resolution # zeros tend to be fuzzy 598 # check it matches 599 np.testing.assert_allclose(got, eye, rtol, atol) 600 601 def assert_invalid_norm_kind(self, cfunc, args): 602 """ 603 For use in norm() and cond() tests. 604 """ 605 msg = "Invalid norm order for matrices." 606 self.assert_error(cfunc, args, msg, ValueError) 607 608 def assert_raise_on_empty(self, cfunc, args): 609 msg = 'Arrays cannot be empty' 610 self.assert_error(cfunc, args, msg, np.linalg.LinAlgError) 611 612 613class TestTestLinalgBase(TestCase): 614 """ 615 The sample matrix code TestLinalgBase.specific_sample_matrix() 616 is a bit involved, this class tests it works as intended. 617 """ 618 619 def test_specific_sample_matrix(self): 620 621 # add a default test to the ctor, it never runs so doesn't matter 622 inst = TestLinalgBase('specific_sample_matrix') 623 624 sizes = [(7, 1), (11, 5), (5, 11), (3, 3), (1, 7)] 625 626 # test loop 627 for size, dtype, order in product(sizes, inst.dtypes, 'FC'): 628 629 m, n = size 630 minmn = min(m, n) 631 632 # test default full rank 633 A = inst.specific_sample_matrix(size, dtype, order) 634 self.assertEqual(A.shape, size) 635 self.assertEqual(np.linalg.matrix_rank(A), minmn) 636 637 # test reduced rank if a reduction is possible 638 if minmn > 1: 639 rank = minmn - 1 640 A = inst.specific_sample_matrix(size, dtype, order, rank=rank) 641 self.assertEqual(A.shape, size) 642 self.assertEqual(np.linalg.matrix_rank(A), rank) 643 644 resolution = 5 * np.finfo(dtype).resolution 645 646 # test default condition 647 A = inst.specific_sample_matrix(size, dtype, order) 648 self.assertEqual(A.shape, size) 649 np.testing.assert_allclose(np.linalg.cond(A), 650 1., 651 rtol=resolution, 652 atol=resolution) 653 654 # test specified condition if matrix is > 1D 655 if minmn > 1: 656 condition = 10. 657 A = inst.specific_sample_matrix( 658 size, dtype, order, condition=condition) 659 self.assertEqual(A.shape, size) 660 np.testing.assert_allclose(np.linalg.cond(A), 661 10., 662 rtol=resolution, 663 atol=resolution) 664 665 # check errors are raised appropriately 666 def check_error(args, msg, err=ValueError): 667 with self.assertRaises(err) as raises: 668 inst.specific_sample_matrix(*args) 669 self.assertIn(msg, str(raises.exception)) 670 671 # check the checker runs ok 672 with self.assertRaises(AssertionError) as raises: 673 msg = "blank" 674 check_error(((2, 3), np.float64, 'F'), msg, err=ValueError) 675 676 # check invalid inputs... 677 678 # bad size 679 msg = "size must be a length 2 tuple." 680 check_error(((1,), np.float64, 'F'), msg, err=ValueError) 681 682 # bad order 683 msg = "order must be one of 'F' or 'C'." 684 check_error(((2, 3), np.float64, 'z'), msg, err=ValueError) 685 686 # bad type 687 msg = "dtype must be a numpy floating point type." 688 check_error(((2, 3), np.int32, 'F'), msg, err=ValueError) 689 690 # specifying both rank and condition 691 msg = "Only one of rank or condition can be specified." 692 check_error(((2, 3), np.float64, 'F', 1, 1), msg, err=ValueError) 693 694 # specifying negative condition 695 msg = "Condition number must be >=1." 696 check_error(((2, 3), np.float64, 'F', None, -1), msg, err=ValueError) 697 698 # specifying negative matrix dimension 699 msg = "Negative dimensions given for matrix shape." 700 check_error(((2, -3), np.float64, 'F'), msg, err=ValueError) 701 702 # specifying negative rank 703 msg = "Rank must be greater than zero." 704 check_error(((2, 3), np.float64, 'F', -1), msg, err=ValueError) 705 706 # specifying a rank greater than maximum rank 707 msg = "Rank given greater than full rank." 708 check_error(((2, 3), np.float64, 'F', 4), msg, err=ValueError) 709 710 # specifying a condition number for a vector 711 msg = "Condition number was specified for a vector (always 1.)." 712 check_error(((1, 3), np.float64, 'F', None, 10), msg, err=ValueError) 713 714 # specifying a non integer rank 715 msg = "Rank must an integer." 716 check_error(((2, 3), np.float64, 'F', 1.5), msg, err=ValueError) 717 718 719class TestLinalgInv(TestLinalgBase): 720 """ 721 Tests for np.linalg.inv. 722 """ 723 724 @needs_lapack 725 def test_linalg_inv(self): 726 """ 727 Test np.linalg.inv 728 """ 729 n = 10 730 cfunc = jit(nopython=True)(invert_matrix) 731 732 def check(a, **kwargs): 733 expected = invert_matrix(a) 734 got = cfunc(a) 735 self.assert_contig_sanity(got, "F") 736 737 use_reconstruction = False 738 739 # try strict 740 try: 741 np.testing.assert_array_almost_equal_nulp(got, expected, 742 nulp=10) 743 except AssertionError: 744 # fall back to reconstruction 745 use_reconstruction = True 746 747 if use_reconstruction: 748 rec = np.dot(got, a) 749 self.assert_is_identity_matrix(rec) 750 751 # Ensure proper resource management 752 with self.assertNoNRTLeak(): 753 cfunc(a) 754 755 for dtype, order in product(self.dtypes, 'CF'): 756 a = self.specific_sample_matrix((n, n), dtype, order) 757 check(a) 758 759 # 0 dimensioned matrix 760 check(np.empty((0, 0))) 761 762 # Non square matrix 763 self.assert_non_square(cfunc, (np.ones((2, 3)),)) 764 765 # Wrong dtype 766 self.assert_wrong_dtype("inv", cfunc, 767 (np.ones((2, 2), dtype=np.int32),)) 768 769 # Dimension issue 770 self.assert_wrong_dimensions("inv", cfunc, (np.ones(10),)) 771 772 # Singular matrix 773 self.assert_raise_on_singular(cfunc, (np.zeros((2, 2)),)) 774 775 @needs_lapack 776 def test_no_input_mutation(self): 777 X = np.array([[1., 3, 2, 7,], 778 [-5, 4, 2, 3,], 779 [9, -3, 1, 1,], 780 [2, -2, 2, 8,]], order='F') 781 782 X_orig = np.copy(X) 783 784 @jit(nopython=True) 785 def ainv(X, test): 786 if test: 787 # not executed, but necessary to trigger A ordering in X 788 X = X[1:2, :] 789 return np.linalg.inv(X) 790 791 expected = ainv.py_func(X, False) 792 np.testing.assert_allclose(X, X_orig) 793 794 got = ainv(X, False) 795 np.testing.assert_allclose(X, X_orig) 796 797 np.testing.assert_allclose(expected, got) 798 799 800@skip_ppc64le_issue4026 801class TestLinalgCholesky(TestLinalgBase): 802 """ 803 Tests for np.linalg.cholesky. 804 """ 805 806 def sample_matrix(self, m, dtype, order): 807 # pd. (positive definite) matrix has eigenvalues in Z+ 808 np.random.seed(0) # repeatable seed 809 A = np.random.rand(m, m) 810 # orthonormal q needed to form up q^{-1}*D*q 811 # no "orth()" in numpy 812 q, _ = np.linalg.qr(A) 813 L = np.arange(1, m + 1) # some positive eigenvalues 814 Q = np.dot(np.dot(q.T, np.diag(L)), q) # construct 815 Q = np.array(Q, dtype=dtype, order=order) # sort out order/type 816 return Q 817 818 def assert_not_pd(self, cfunc, args): 819 msg = "Matrix is not positive definite." 820 self.assert_error(cfunc, args, msg, np.linalg.LinAlgError) 821 822 @needs_lapack 823 def test_linalg_cholesky(self): 824 """ 825 Test np.linalg.cholesky 826 """ 827 n = 10 828 cfunc = jit(nopython=True)(cholesky_matrix) 829 830 def check(a): 831 expected = cholesky_matrix(a) 832 got = cfunc(a) 833 use_reconstruction = False 834 # check that the computed results are contig and in the same way 835 self.assert_contig_sanity(got, "C") 836 837 # try strict 838 try: 839 np.testing.assert_array_almost_equal_nulp(got, expected, 840 nulp=10) 841 except AssertionError: 842 # fall back to reconstruction 843 use_reconstruction = True 844 845 # try via reconstruction 846 if use_reconstruction: 847 rec = np.dot(got, np.conj(got.T)) 848 resolution = 5 * np.finfo(a.dtype).resolution 849 np.testing.assert_allclose( 850 a, 851 rec, 852 rtol=resolution, 853 atol=resolution 854 ) 855 856 # Ensure proper resource management 857 with self.assertNoNRTLeak(): 858 cfunc(a) 859 860 for dtype, order in product(self.dtypes, 'FC'): 861 a = self.sample_matrix(n, dtype, order) 862 check(a) 863 864 # 0 dimensioned matrix 865 check(np.empty((0, 0))) 866 867 rn = "cholesky" 868 # Non square matrices 869 self.assert_non_square(cfunc, (np.ones((2, 3), dtype=np.float64),)) 870 871 # Wrong dtype 872 self.assert_wrong_dtype(rn, cfunc, 873 (np.ones((2, 2), dtype=np.int32),)) 874 875 # Dimension issue 876 self.assert_wrong_dimensions(rn, cfunc, 877 (np.ones(10, dtype=np.float64),)) 878 879 # not pd 880 self.assert_not_pd(cfunc, 881 (np.ones(4, dtype=np.float64).reshape(2, 2),)) 882 883 884class TestLinalgEigenSystems(TestLinalgBase): 885 """ 886 Tests for np.linalg.eig/eigvals. 887 """ 888 889 def sample_matrix(self, m, dtype, order): 890 # This is a tridiag with the same but skewed values on the diagonals 891 v = self.sample_vector(m, dtype) 892 Q = np.diag(v) 893 idx = np.nonzero(np.eye(Q.shape[0], Q.shape[1], 1)) 894 Q[idx] = v[1:] 895 idx = np.nonzero(np.eye(Q.shape[0], Q.shape[1], -1)) 896 Q[idx] = v[:-1] 897 Q = np.array(Q, dtype=dtype, order=order) 898 return Q 899 900 def assert_no_domain_change(self, name, cfunc, args): 901 msg = name + "() argument must not cause a domain change." 902 self.assert_error(cfunc, args, msg) 903 904 def _check_worker(self, cfunc, name, expected_res_len, 905 check_for_domain_change): 906 def check(*args): 907 expected = cfunc.py_func(*args) 908 got = cfunc(*args) 909 a = args[0] 910 # check that the returned tuple is same length 911 self.assertEqual(len(expected), len(got)) 912 # and that dimension is correct 913 res_is_tuple = False 914 if isinstance(got, tuple): 915 res_is_tuple = True 916 self.assertEqual(len(got), expected_res_len) 917 else: # its an array 918 self.assertEqual(got.ndim, expected_res_len) 919 920 # and that the computed results are contig and in the same way 921 self.assert_contig_sanity(got, "F") 922 923 use_reconstruction = False 924 # try plain match of each array to np first 925 for k in range(len(expected)): 926 try: 927 np.testing.assert_array_almost_equal_nulp( 928 got[k], expected[k], nulp=10) 929 except AssertionError: 930 # plain match failed, test by reconstruction 931 use_reconstruction = True 932 933 # If plain match fails then reconstruction is used. 934 # this checks that A*V ~== V*diag(W) 935 # i.e. eigensystem ties out 936 # this is required as numpy uses only double precision lapack 937 # routines and computation of eigenvectors is numerically 938 # sensitive, numba uses the type specific routines therefore 939 # sometimes comes out with a different (but entirely 940 # valid) answer (eigenvectors are not unique etc.). 941 # This is only applicable if eigenvectors are computed 942 # along with eigenvalues i.e. result is a tuple. 943 resolution = 5 * np.finfo(a.dtype).resolution 944 if use_reconstruction: 945 if res_is_tuple: 946 w, v = got 947 # modify 'a' if hermitian eigensystem functionality is 948 # being tested. 'L' for use lower part is default and 949 # the only thing used at present so we conjugate transpose 950 # the lower part into the upper for use in the 951 # reconstruction. By construction the sample matrix is 952 # tridiag so this is just a question of copying the lower 953 # diagonal into the upper and conjugating on the way. 954 if name[-1] == 'h': 955 idxl = np.nonzero(np.eye(a.shape[0], a.shape[1], -1)) 956 idxu = np.nonzero(np.eye(a.shape[0], a.shape[1], 1)) 957 cfunc(*args) 958 # upper idx must match lower for default uplo="L" 959 # if complex, conjugate 960 a[idxu] = np.conj(a[idxl]) 961 # also, only the real part of the diagonals is 962 # considered in the calculation so the imag is zeroed 963 # out for the purposes of use in reconstruction. 964 a[np.diag_indices(a.shape[0])] = np.real(np.diag(a)) 965 966 lhs = np.dot(a, v) 967 rhs = np.dot(v, np.diag(w)) 968 969 np.testing.assert_allclose( 970 lhs.real, 971 rhs.real, 972 rtol=resolution, 973 atol=resolution 974 ) 975 if np.iscomplexobj(v): 976 np.testing.assert_allclose( 977 lhs.imag, 978 rhs.imag, 979 rtol=resolution, 980 atol=resolution 981 ) 982 else: 983 # This isn't technically reconstruction but is here to 984 # deal with that the order of the returned eigenvalues 985 # may differ in the case of routines just returning 986 # eigenvalues and there's no true reconstruction 987 # available with which to perform a check. 988 np.testing.assert_allclose( 989 np.sort(expected), 990 np.sort(got), 991 rtol=resolution, 992 atol=resolution 993 ) 994 995 # Ensure proper resource management 996 with self.assertNoNRTLeak(): 997 cfunc(*args) 998 return check 999 1000 def checker_for_linalg_eig( 1001 self, name, func, expected_res_len, check_for_domain_change=None): 1002 """ 1003 Test np.linalg.eig 1004 """ 1005 n = 10 1006 cfunc = jit(nopython=True)(func) 1007 check = self._check_worker(cfunc, name, expected_res_len, 1008 check_for_domain_change) 1009 1010 1011 # The main test loop 1012 for dtype, order in product(self.dtypes, 'FC'): 1013 a = self.sample_matrix(n, dtype, order) 1014 check(a) 1015 1016 # Test both a real and complex type as the impls are different 1017 for ty in [np.float32, np.complex64]: 1018 1019 # 0 dimensioned matrix 1020 check(np.empty((0, 0), dtype=ty)) 1021 1022 # Non square matrices 1023 self.assert_non_square(cfunc, (np.ones((2, 3), dtype=ty),)) 1024 1025 # Wrong dtype 1026 self.assert_wrong_dtype(name, cfunc, 1027 (np.ones((2, 2), dtype=np.int32),)) 1028 1029 # Dimension issue 1030 self.assert_wrong_dimensions(name, cfunc, (np.ones(10, dtype=ty),)) 1031 1032 # no nans or infs 1033 self.assert_no_nan_or_inf(cfunc, 1034 (np.array([[1., 2., ], [np.inf, np.nan]], 1035 dtype=ty),)) 1036 1037 if check_for_domain_change: 1038 # By design numba does not support dynamic return types, numpy does 1039 # and uses this in the case of returning eigenvalues/vectors of 1040 # a real matrix. The return type of np.linalg.eig(), when 1041 # operating on a matrix in real space depends on the values present 1042 # in the matrix itself (recalling that eigenvalues are the roots of the 1043 # characteristic polynomial of the system matrix, which will by 1044 # construction depend on the values present in the system matrix). 1045 # This test asserts that if a domain change is required on the return 1046 # type, i.e. complex eigenvalues from a real input, an error is raised. 1047 # For complex types, regardless of the value of the imaginary part of 1048 # the returned eigenvalues, a complex type will be returned, this 1049 # follows numpy and fits in with numba. 1050 1051 # First check that the computation is valid (i.e. in complex space) 1052 A = np.array([[1, -2], [2, 1]]) 1053 check(A.astype(np.complex128)) 1054 # and that the imaginary part is nonzero 1055 l, _ = func(A) 1056 self.assertTrue(np.any(l.imag)) 1057 1058 # Now check that the computation fails in real space 1059 for ty in [np.float32, np.float64]: 1060 self.assert_no_domain_change(name, cfunc, (A.astype(ty),)) 1061 1062 @needs_lapack 1063 def test_linalg_eig(self): 1064 self.checker_for_linalg_eig("eig", eig_matrix, 2, True) 1065 1066 @needs_lapack 1067 def test_linalg_eigvals(self): 1068 self.checker_for_linalg_eig("eigvals", eigvals_matrix, 1, True) 1069 1070 @needs_lapack 1071 def test_linalg_eigh(self): 1072 self.checker_for_linalg_eig("eigh", eigh_matrix, 2, False) 1073 1074 @needs_lapack 1075 def test_linalg_eigvalsh(self): 1076 self.checker_for_linalg_eig("eigvalsh", eigvalsh_matrix, 1, False) 1077 1078 @needs_lapack 1079 def test_no_input_mutation(self): 1080 # checks inputs are not mutated 1081 1082 for c in (('eig', 2, True), 1083 ('eigvals', 1, True), 1084 ('eigh', 2, False), 1085 ('eigvalsh', 1, False)): 1086 1087 m, nout, domain_change = c 1088 1089 meth = getattr(np.linalg, m) 1090 1091 @jit(nopython=True) 1092 def func(X, test): 1093 if test: 1094 # not executed, but necessary to trigger A ordering in X 1095 X = X[1:2, :] 1096 return meth(X) 1097 1098 check = self._check_worker(func, m, nout, domain_change) 1099 1100 for dtype in (np.float64, np.complex128): 1101 with self.subTest(meth=meth, dtype=dtype): 1102 # trivial system, doesn't matter, just checking if it gets 1103 # mutated 1104 X = np.array([[10., 1, 0, 1], 1105 [1, 9, 0, 0], 1106 [0, 0, 8, 0], 1107 [1, 0, 0, 7], 1108 ], order='F', dtype=dtype) 1109 1110 X_orig = np.copy(X) 1111 1112 expected = func.py_func(X, False) 1113 np.testing.assert_allclose(X, X_orig) 1114 1115 got = func(X, False) 1116 np.testing.assert_allclose(X, X_orig) 1117 1118 check(X, False) 1119 1120 1121class TestLinalgSvd(TestLinalgBase): 1122 """ 1123 Tests for np.linalg.svd. 1124 """ 1125 1126 @needs_lapack 1127 def test_linalg_svd(self): 1128 """ 1129 Test np.linalg.svd 1130 """ 1131 cfunc = jit(nopython=True)(svd_matrix) 1132 1133 def check(a, **kwargs): 1134 expected = svd_matrix(a, **kwargs) 1135 got = cfunc(a, **kwargs) 1136 # check that the returned tuple is same length 1137 self.assertEqual(len(expected), len(got)) 1138 # and that length is 3 1139 self.assertEqual(len(got), 3) 1140 # and that the computed results are contig and in the same way 1141 self.assert_contig_sanity(got, "F") 1142 1143 use_reconstruction = False 1144 # try plain match of each array to np first 1145 for k in range(len(expected)): 1146 1147 try: 1148 np.testing.assert_array_almost_equal_nulp( 1149 got[k], expected[k], nulp=10) 1150 except AssertionError: 1151 # plain match failed, test by reconstruction 1152 use_reconstruction = True 1153 1154 # if plain match fails then reconstruction is used. 1155 # this checks that A ~= U*S*V**H 1156 # i.e. SV decomposition ties out 1157 # this is required as numpy uses only double precision lapack 1158 # routines and computation of svd is numerically 1159 # sensitive, numba using the type specific routines therefore 1160 # sometimes comes out with a different answer (orthonormal bases 1161 # are not unique etc.). 1162 if use_reconstruction: 1163 u, sv, vt = got 1164 1165 # check they are dimensionally correct 1166 for k in range(len(expected)): 1167 self.assertEqual(got[k].shape, expected[k].shape) 1168 1169 # regardless of full_matrices cols in u and rows in vt 1170 # dictates the working size of s 1171 s = np.zeros((u.shape[1], vt.shape[0])) 1172 np.fill_diagonal(s, sv) 1173 1174 rec = np.dot(np.dot(u, s), vt) 1175 resolution = np.finfo(a.dtype).resolution 1176 np.testing.assert_allclose( 1177 a, 1178 rec, 1179 rtol=10 * resolution, 1180 atol=100 * resolution # zeros tend to be fuzzy 1181 ) 1182 1183 # Ensure proper resource management 1184 with self.assertNoNRTLeak(): 1185 cfunc(a, **kwargs) 1186 1187 # test: column vector, tall, wide, square, row vector 1188 # prime sizes 1189 sizes = [(7, 1), (7, 5), (5, 7), (3, 3), (1, 7)] 1190 1191 # flip on reduced or full matrices 1192 full_matrices = (True, False) 1193 1194 # test loop 1195 for size, dtype, fmat, order in \ 1196 product(sizes, self.dtypes, full_matrices, 'FC'): 1197 1198 a = self.specific_sample_matrix(size, dtype, order) 1199 check(a, full_matrices=fmat) 1200 1201 rn = "svd" 1202 1203 # Wrong dtype 1204 self.assert_wrong_dtype(rn, cfunc, 1205 (np.ones((2, 2), dtype=np.int32),)) 1206 1207 # Dimension issue 1208 self.assert_wrong_dimensions(rn, cfunc, 1209 (np.ones(10, dtype=np.float64),)) 1210 1211 # no nans or infs 1212 self.assert_no_nan_or_inf(cfunc, 1213 (np.array([[1., 2., ], [np.inf, np.nan]], 1214 dtype=np.float64),)) 1215 # empty 1216 for sz in [(0, 1), (1, 0), (0, 0)]: 1217 args = (np.empty(sz), True) 1218 self.assert_raise_on_empty(cfunc, args) 1219 1220 @needs_lapack 1221 def test_no_input_mutation(self): 1222 X = np.array([[1., 3, 2, 7,], 1223 [-5, 4, 2, 3,], 1224 [9, -3, 1, 1,], 1225 [2, -2, 2, 8,]], order='F') 1226 1227 X_orig = np.copy(X) 1228 1229 @jit(nopython=True) 1230 def func(X, test): 1231 if test: 1232 # not executed, but necessary to trigger A ordering in X 1233 X = X[1:2, :] 1234 return np.linalg.svd(X) 1235 1236 expected = func.py_func(X, False) 1237 np.testing.assert_allclose(X, X_orig) 1238 1239 got = func(X, False) 1240 np.testing.assert_allclose(X, X_orig) 1241 1242 for e_a, g_a in zip(expected, got): 1243 np.testing.assert_allclose(e_a, g_a) 1244 1245 1246class TestLinalgQr(TestLinalgBase): 1247 """ 1248 Tests for np.linalg.qr. 1249 """ 1250 1251 @needs_lapack 1252 def test_linalg_qr(self): 1253 """ 1254 Test np.linalg.qr 1255 """ 1256 cfunc = jit(nopython=True)(qr_matrix) 1257 1258 def check(a, **kwargs): 1259 expected = qr_matrix(a, **kwargs) 1260 got = cfunc(a, **kwargs) 1261 1262 # check that the returned tuple is same length 1263 self.assertEqual(len(expected), len(got)) 1264 # and that length is 2 1265 self.assertEqual(len(got), 2) 1266 # and that the computed results are contig and in the same way 1267 self.assert_contig_sanity(got, "F") 1268 1269 use_reconstruction = False 1270 # try plain match of each array to np first 1271 for k in range(len(expected)): 1272 try: 1273 np.testing.assert_array_almost_equal_nulp( 1274 got[k], expected[k], nulp=10) 1275 except AssertionError: 1276 # plain match failed, test by reconstruction 1277 use_reconstruction = True 1278 1279 # if plain match fails then reconstruction is used. 1280 # this checks that A ~= Q*R and that (Q^H)*Q = I 1281 # i.e. QR decomposition ties out 1282 # this is required as numpy uses only double precision lapack 1283 # routines and computation of qr is numerically 1284 # sensitive, numba using the type specific routines therefore 1285 # sometimes comes out with a different answer (orthonormal bases 1286 # are not unique etc.). 1287 if use_reconstruction: 1288 q, r = got 1289 1290 # check they are dimensionally correct 1291 for k in range(len(expected)): 1292 self.assertEqual(got[k].shape, expected[k].shape) 1293 1294 # check A=q*r 1295 rec = np.dot(q, r) 1296 resolution = np.finfo(a.dtype).resolution 1297 np.testing.assert_allclose( 1298 a, 1299 rec, 1300 rtol=10 * resolution, 1301 atol=100 * resolution # zeros tend to be fuzzy 1302 ) 1303 1304 # check q is orthonormal 1305 self.assert_is_identity_matrix(np.dot(np.conjugate(q.T), q)) 1306 1307 # Ensure proper resource management 1308 with self.assertNoNRTLeak(): 1309 cfunc(a, **kwargs) 1310 1311 # test: column vector, tall, wide, square, row vector 1312 # prime sizes 1313 sizes = [(7, 1), (11, 5), (5, 11), (3, 3), (1, 7)] 1314 1315 # test loop 1316 for size, dtype, order in \ 1317 product(sizes, self.dtypes, 'FC'): 1318 a = self.specific_sample_matrix(size, dtype, order) 1319 check(a) 1320 1321 rn = "qr" 1322 1323 # Wrong dtype 1324 self.assert_wrong_dtype(rn, cfunc, 1325 (np.ones((2, 2), dtype=np.int32),)) 1326 1327 # Dimension issue 1328 self.assert_wrong_dimensions(rn, cfunc, 1329 (np.ones(10, dtype=np.float64),)) 1330 1331 # no nans or infs 1332 self.assert_no_nan_or_inf(cfunc, 1333 (np.array([[1., 2., ], [np.inf, np.nan]], 1334 dtype=np.float64),)) 1335 1336 # empty 1337 for sz in [(0, 1), (1, 0), (0, 0)]: 1338 self.assert_raise_on_empty(cfunc, (np.empty(sz),)) 1339 1340 @needs_lapack 1341 def test_no_input_mutation(self): 1342 X = np.array([[1., 3, 2, 7,], 1343 [-5, 4, 2, 3,], 1344 [9, -3, 1, 1,], 1345 [2, -2, 2, 8,]], order='F') 1346 1347 X_orig = np.copy(X) 1348 1349 @jit(nopython=True) 1350 def func(X, test): 1351 if test: 1352 # not executed, but necessary to trigger A ordering in X 1353 X = X[1:2, :] 1354 return np.linalg.qr(X) 1355 1356 expected = func.py_func(X, False) 1357 np.testing.assert_allclose(X, X_orig) 1358 1359 got = func(X, False) 1360 np.testing.assert_allclose(X, X_orig) 1361 1362 for e_a, g_a in zip(expected, got): 1363 np.testing.assert_allclose(e_a, g_a) 1364 1365 1366class TestLinalgSystems(TestLinalgBase): 1367 """ 1368 Base class for testing "system" solvers from np.linalg. 1369 Namely np.linalg.solve() and np.linalg.lstsq(). 1370 """ 1371 1372 # check for RHS with dimension > 2 raises 1373 def assert_wrong_dimensions_1D(self, name, cfunc, args, la_prefix=True): 1374 prefix = "np.linalg" if la_prefix else "np" 1375 msg = "%s.%s() only supported on 1 and 2-D arrays" % (prefix, name) 1376 self.assert_error(cfunc, args, msg, errors.TypingError) 1377 1378 # check that a dimensionally invalid system raises 1379 def assert_dimensionally_invalid(self, cfunc, args): 1380 msg = "Incompatible array sizes, system is not dimensionally valid." 1381 self.assert_error(cfunc, args, msg, np.linalg.LinAlgError) 1382 1383 # check that args with differing dtypes raise 1384 def assert_homogeneous_dtypes(self, name, cfunc, args): 1385 msg = "np.linalg.%s() only supports inputs that have homogeneous dtypes." % name 1386 self.assert_error(cfunc, args, msg, errors.TypingError) 1387 1388 1389class TestLinalgLstsq(TestLinalgSystems): 1390 """ 1391 Tests for np.linalg.lstsq. 1392 """ 1393 1394 # NOTE: The testing of this routine is hard as it has to handle numpy 1395 # using double precision routines on single precision input, this has 1396 # a knock on effect especially in rank deficient cases and cases where 1397 # conditioning is generally poor. As a result computed ranks can differ 1398 # and consequently the calculated residual can differ. 1399 # The tests try and deal with this as best as they can through the use 1400 # of reconstruction and measures like residual norms. 1401 # Suggestions for improvements are welcomed! 1402 1403 @needs_lapack 1404 def test_linalg_lstsq(self): 1405 """ 1406 Test np.linalg.lstsq 1407 """ 1408 cfunc = jit(nopython=True)(lstsq_system) 1409 1410 def check(A, B, **kwargs): 1411 expected = lstsq_system(A, B, **kwargs) 1412 got = cfunc(A, B, **kwargs) 1413 1414 # check that the returned tuple is same length 1415 self.assertEqual(len(expected), len(got)) 1416 # and that length is 4 1417 self.assertEqual(len(got), 4) 1418 # and that the computed results are contig and in the same way 1419 self.assert_contig_sanity(got, "C") 1420 1421 use_reconstruction = False 1422 1423 # check the ranks are the same and continue to a standard 1424 # match if that is the case (if ranks differ, then output 1425 # in e.g. residual array is of different size!). 1426 try: 1427 self.assertEqual(got[2], expected[2]) 1428 # try plain match of each array to np first 1429 for k in range(len(expected)): 1430 try: 1431 np.testing.assert_array_almost_equal_nulp( 1432 got[k], expected[k], nulp=10) 1433 except AssertionError: 1434 # plain match failed, test by reconstruction 1435 use_reconstruction = True 1436 except AssertionError: 1437 use_reconstruction = True 1438 1439 if use_reconstruction: 1440 x, res, rank, s = got 1441 1442 # indicies in the output which are ndarrays 1443 out_array_idx = [0, 1, 3] 1444 1445 try: 1446 # check the ranks are the same 1447 self.assertEqual(rank, expected[2]) 1448 # check they are dimensionally correct, skip [2] = rank. 1449 for k in out_array_idx: 1450 if isinstance(expected[k], np.ndarray): 1451 self.assertEqual(got[k].shape, expected[k].shape) 1452 except AssertionError: 1453 # check the rank differs by 1. (numerical fuzz) 1454 self.assertTrue(abs(rank - expected[2]) < 2) 1455 1456 # check if A*X = B 1457 resolution = np.finfo(A.dtype).resolution 1458 try: 1459 # this will work so long as the conditioning is 1460 # ok and the rank is full 1461 rec = np.dot(A, x) 1462 np.testing.assert_allclose( 1463 B, 1464 rec, 1465 rtol=10 * resolution, 1466 atol=10 * resolution 1467 ) 1468 except AssertionError: 1469 # system is probably under/over determined and/or 1470 # poorly conditioned. Check slackened equality 1471 # and that the residual norm is the same. 1472 for k in out_array_idx: 1473 try: 1474 np.testing.assert_allclose( 1475 expected[k], 1476 got[k], 1477 rtol=100 * resolution, 1478 atol=100 * resolution 1479 ) 1480 except AssertionError: 1481 # check the fail is likely due to bad conditioning 1482 c = np.linalg.cond(A) 1483 self.assertGreater(10 * c, (1. / resolution)) 1484 1485 # make sure the residual 2-norm is ok 1486 # if this fails its probably due to numpy using double 1487 # precision LAPACK routines for singles. 1488 res_expected = np.linalg.norm( 1489 B - np.dot(A, expected[0])) 1490 res_got = np.linalg.norm(B - np.dot(A, x)) 1491 # rtol = 10. as all the systems are products of orthonormals 1492 # and on the small side (rows, cols) < 100. 1493 np.testing.assert_allclose( 1494 res_expected, res_got, rtol=10.) 1495 1496 # Ensure proper resource management 1497 with self.assertNoNRTLeak(): 1498 cfunc(A, B, **kwargs) 1499 1500 # test: column vector, tall, wide, square, row vector 1501 # prime sizes, the A's 1502 sizes = [(7, 1), (11, 5), (5, 11), (3, 3), (1, 7)] 1503 # compatible B's for Ax=B must have same number of rows and 1 or more 1504 # columns 1505 1506 # This test takes ages! So combinations are trimmed via cycling 1507 1508 # gets a dtype 1509 cycle_dt = cycle(self.dtypes) 1510 1511 orders = ['F', 'C'] 1512 # gets a memory order flag 1513 cycle_order = cycle(orders) 1514 1515 # a specific condition number to use in the following tests 1516 # there is nothing special about it other than it is not magic 1517 specific_cond = 10. 1518 1519 # inner test loop, extracted as there's additional logic etc required 1520 # that'd end up with this being repeated a lot 1521 def inner_test_loop_fn(A, dt, **kwargs): 1522 # test solve Ax=B for (column, matrix) B, same dtype as A 1523 b_sizes = (1, 13) 1524 1525 for b_size in b_sizes: 1526 1527 # check 2D B 1528 b_order = next(cycle_order) 1529 B = self.specific_sample_matrix( 1530 (A.shape[0], b_size), dt, b_order) 1531 check(A, B, **kwargs) 1532 1533 # check 1D B 1534 b_order = next(cycle_order) 1535 tmp = B[:, 0].copy(order=b_order) 1536 check(A, tmp, **kwargs) 1537 1538 # test loop 1539 for a_size in sizes: 1540 1541 dt = next(cycle_dt) 1542 a_order = next(cycle_order) 1543 1544 # A full rank, well conditioned system 1545 A = self.specific_sample_matrix(a_size, dt, a_order) 1546 1547 # run the test loop 1548 inner_test_loop_fn(A, dt) 1549 1550 m, n = a_size 1551 minmn = min(m, n) 1552 1553 # operations that only make sense with a 2D matrix system 1554 if m != 1 and n != 1: 1555 1556 # Test a rank deficient system 1557 r = minmn - 1 1558 A = self.specific_sample_matrix( 1559 a_size, dt, a_order, rank=r) 1560 # run the test loop 1561 inner_test_loop_fn(A, dt) 1562 1563 # Test a system with a given condition number for use in 1564 # testing the rcond parameter. 1565 # This works because the singular values in the 1566 # specific_sample_matrix code are linspace (1, cond, [0... if 1567 # rank deficient]) 1568 A = self.specific_sample_matrix( 1569 a_size, dt, a_order, condition=specific_cond) 1570 # run the test loop 1571 rcond = 1. / specific_cond 1572 approx_half_rank_rcond = minmn * rcond 1573 inner_test_loop_fn(A, dt, 1574 rcond=approx_half_rank_rcond) 1575 1576 # check empty arrays 1577 empties = [ 1578 [(0, 1), (1,)], # empty A, valid b 1579 [(1, 0), (1,)], # empty A, valid b 1580 [(1, 1), (0,)], # valid A, empty 1D b 1581 [(1, 1), (1, 0)], # valid A, empty 2D b 1582 ] 1583 1584 for A, b in empties: 1585 args = (np.empty(A), np.empty(b)) 1586 self.assert_raise_on_empty(cfunc, args) 1587 1588 # Test input validation 1589 ok = np.array([[1., 2.], [3., 4.]], dtype=np.float64) 1590 1591 # check ok input is ok 1592 cfunc, (ok, ok) 1593 1594 # check bad inputs 1595 rn = "lstsq" 1596 1597 # Wrong dtype 1598 bad = np.array([[1, 2], [3, 4]], dtype=np.int32) 1599 self.assert_wrong_dtype(rn, cfunc, (ok, bad)) 1600 self.assert_wrong_dtype(rn, cfunc, (bad, ok)) 1601 1602 # different dtypes 1603 bad = np.array([[1, 2], [3, 4]], dtype=np.float32) 1604 self.assert_homogeneous_dtypes(rn, cfunc, (ok, bad)) 1605 self.assert_homogeneous_dtypes(rn, cfunc, (bad, ok)) 1606 1607 # Dimension issue 1608 bad = np.array([1, 2], dtype=np.float64) 1609 self.assert_wrong_dimensions(rn, cfunc, (bad, ok)) 1610 1611 # no nans or infs 1612 bad = np.array([[1., 2., ], [np.inf, np.nan]], dtype=np.float64) 1613 self.assert_no_nan_or_inf(cfunc, (ok, bad)) 1614 self.assert_no_nan_or_inf(cfunc, (bad, ok)) 1615 1616 # check 1D is accepted for B (2D is done previously) 1617 # and then that anything of higher dimension raises 1618 oneD = np.array([1., 2.], dtype=np.float64) 1619 cfunc, (ok, oneD) 1620 bad = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]], dtype=np.float64) 1621 self.assert_wrong_dimensions_1D(rn, cfunc, (ok, bad)) 1622 1623 # check a dimensionally invalid system raises (1D and 2D cases 1624 # checked) 1625 bad1D = np.array([1.], dtype=np.float64) 1626 bad2D = np.array([[1.], [2.], [3.]], dtype=np.float64) 1627 self.assert_dimensionally_invalid(cfunc, (ok, bad1D)) 1628 self.assert_dimensionally_invalid(cfunc, (ok, bad2D)) 1629 1630 @needs_lapack 1631 def test_issue3368(self): 1632 X = np.array([[1., 7.54, 6.52], 1633 [1., 2.70, 4.00], 1634 [1., 2.50, 3.80], 1635 [1., 1.15, 5.64], 1636 [1., 4.22, 3.27], 1637 [1., 1.41, 5.70],], order='F') 1638 1639 X_orig = np.copy(X) 1640 y = np.array([1., 2., 3., 4., 5., 6.]) 1641 1642 @jit(nopython=True) 1643 def f2(X, y, test): 1644 if test: 1645 # never executed, but necessary to trigger the bug 1646 X = X[1:2, :] 1647 return np.linalg.lstsq(X, y) 1648 1649 f2(X, y, False) 1650 np.testing.assert_allclose(X, X_orig) 1651 1652 1653class TestLinalgSolve(TestLinalgSystems): 1654 """ 1655 Tests for np.linalg.solve. 1656 """ 1657 1658 @needs_lapack 1659 def test_linalg_solve(self): 1660 """ 1661 Test np.linalg.solve 1662 """ 1663 cfunc = jit(nopython=True)(solve_system) 1664 1665 def check(a, b, **kwargs): 1666 expected = solve_system(a, b, **kwargs) 1667 got = cfunc(a, b, **kwargs) 1668 1669 # check that the computed results are contig and in the same way 1670 self.assert_contig_sanity(got, "F") 1671 1672 use_reconstruction = False 1673 # try plain match of the result first 1674 try: 1675 np.testing.assert_array_almost_equal_nulp( 1676 got, expected, nulp=10) 1677 except AssertionError: 1678 # plain match failed, test by reconstruction 1679 use_reconstruction = True 1680 1681 # If plain match fails then reconstruction is used, 1682 # this checks that AX ~= B. 1683 # Plain match can fail due to numerical fuzziness associated 1684 # with system size and conditioning, or more simply from 1685 # numpy using double precision routines for computation that 1686 # could be done in single precision (which is what numba does). 1687 # Therefore minor differences in results can appear due to 1688 # e.g. numerical roundoff being different between two precisions. 1689 if use_reconstruction: 1690 # check they are dimensionally correct 1691 self.assertEqual(got.shape, expected.shape) 1692 1693 # check AX=B 1694 rec = np.dot(a, got) 1695 resolution = np.finfo(a.dtype).resolution 1696 np.testing.assert_allclose( 1697 b, 1698 rec, 1699 rtol=10 * resolution, 1700 atol=100 * resolution # zeros tend to be fuzzy 1701 ) 1702 1703 # Ensure proper resource management 1704 with self.assertNoNRTLeak(): 1705 cfunc(a, b, **kwargs) 1706 1707 # test: prime size squares 1708 sizes = [(1, 1), (3, 3), (7, 7)] 1709 1710 # test loop 1711 for size, dtype, order in \ 1712 product(sizes, self.dtypes, 'FC'): 1713 A = self.specific_sample_matrix(size, dtype, order) 1714 1715 b_sizes = (1, 13) 1716 1717 for b_size, b_order in product(b_sizes, 'FC'): 1718 # check 2D B 1719 B = self.specific_sample_matrix( 1720 (A.shape[0], b_size), dtype, b_order) 1721 check(A, B) 1722 1723 # check 1D B 1724 tmp = B[:, 0].copy(order=b_order) 1725 check(A, tmp) 1726 1727 # check empty 1728 cfunc(np.empty((0, 0)), np.empty((0,))) 1729 1730 # Test input validation 1731 ok = np.array([[1., 0.], [0., 1.]], dtype=np.float64) 1732 1733 # check ok input is ok 1734 cfunc(ok, ok) 1735 1736 # check bad inputs 1737 rn = "solve" 1738 1739 # Wrong dtype 1740 bad = np.array([[1, 0], [0, 1]], dtype=np.int32) 1741 self.assert_wrong_dtype(rn, cfunc, (ok, bad)) 1742 self.assert_wrong_dtype(rn, cfunc, (bad, ok)) 1743 1744 # different dtypes 1745 bad = np.array([[1, 2], [3, 4]], dtype=np.float32) 1746 self.assert_homogeneous_dtypes(rn, cfunc, (ok, bad)) 1747 self.assert_homogeneous_dtypes(rn, cfunc, (bad, ok)) 1748 1749 # Dimension issue 1750 bad = np.array([1, 0], dtype=np.float64) 1751 self.assert_wrong_dimensions(rn, cfunc, (bad, ok)) 1752 1753 # no nans or infs 1754 bad = np.array([[1., 0., ], [np.inf, np.nan]], dtype=np.float64) 1755 self.assert_no_nan_or_inf(cfunc, (ok, bad)) 1756 self.assert_no_nan_or_inf(cfunc, (bad, ok)) 1757 1758 # check 1D is accepted for B (2D is done previously) 1759 # and then that anything of higher dimension raises 1760 ok_oneD = np.array([1., 2.], dtype=np.float64) 1761 cfunc(ok, ok_oneD) 1762 bad = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]], dtype=np.float64) 1763 self.assert_wrong_dimensions_1D(rn, cfunc, (ok, bad)) 1764 1765 # check an invalid system raises (1D and 2D cases checked) 1766 bad1D = np.array([1.], dtype=np.float64) 1767 bad2D = np.array([[1.], [2.], [3.]], dtype=np.float64) 1768 self.assert_dimensionally_invalid(cfunc, (ok, bad1D)) 1769 self.assert_dimensionally_invalid(cfunc, (ok, bad2D)) 1770 1771 # check that a singular system raises 1772 bad2D = self.specific_sample_matrix((2, 2), np.float64, 'C', rank=1) 1773 self.assert_raise_on_singular(cfunc, (bad2D, ok)) 1774 1775 @needs_lapack 1776 def test_no_input_mutation(self): 1777 X = np.array([[1., 1, 1, 1], 1778 [0., 1, 1, 1], 1779 [0., 0, 1, 1], 1780 [1., 0, 0, 1],], order='F') 1781 1782 X_orig = np.copy(X) 1783 y = np.array([1., 2., 3., 4]) 1784 y_orig = np.copy(y) 1785 1786 @jit(nopython=True) 1787 def func(X, y, test): 1788 if test: 1789 # not executed, triggers A order in X 1790 X = X[1:2, :] 1791 return np.linalg.solve(X, y) 1792 1793 expected = func.py_func(X, y, False) 1794 np.testing.assert_allclose(X, X_orig) 1795 np.testing.assert_allclose(y, y_orig) 1796 1797 got = func(X, y, False) 1798 np.testing.assert_allclose(X, X_orig) 1799 np.testing.assert_allclose(y, y_orig) 1800 1801 np.testing.assert_allclose(expected, got) 1802 1803 1804class TestLinalgPinv(TestLinalgBase): 1805 """ 1806 Tests for np.linalg.pinv. 1807 """ 1808 1809 @needs_lapack 1810 def test_linalg_pinv(self): 1811 """ 1812 Test np.linalg.pinv 1813 """ 1814 cfunc = jit(nopython=True)(pinv_matrix) 1815 1816 def check(a, **kwargs): 1817 expected = pinv_matrix(a, **kwargs) 1818 got = cfunc(a, **kwargs) 1819 1820 # check that the computed results are contig and in the same way 1821 self.assert_contig_sanity(got, "F") 1822 1823 use_reconstruction = False 1824 # try plain match of each array to np first 1825 1826 try: 1827 np.testing.assert_array_almost_equal_nulp( 1828 got, expected, nulp=10) 1829 except AssertionError: 1830 # plain match failed, test by reconstruction 1831 use_reconstruction = True 1832 1833 # If plain match fails then reconstruction is used. 1834 # This can occur due to numpy using double precision 1835 # LAPACK when single can be used, this creates round off 1836 # problems. Also, if the matrix has machine precision level 1837 # zeros in its singular values then the singular vectors are 1838 # likely to vary depending on round off. 1839 if use_reconstruction: 1840 1841 # check they are dimensionally correct 1842 self.assertEqual(got.shape, expected.shape) 1843 1844 # check pinv(A)*A~=eye 1845 # if the problem is numerical fuzz then this will probably 1846 # work, if the problem is rank deficiency then it won't! 1847 rec = np.dot(got, a) 1848 try: 1849 self.assert_is_identity_matrix(rec) 1850 except AssertionError: 1851 # check A=pinv(pinv(A)) 1852 resolution = 5 * np.finfo(a.dtype).resolution 1853 rec = cfunc(got) 1854 np.testing.assert_allclose( 1855 rec, 1856 a, 1857 rtol=10 * resolution, 1858 atol=100 * resolution # zeros tend to be fuzzy 1859 ) 1860 if a.shape[0] >= a.shape[1]: 1861 # if it is overdetermined or fully determined 1862 # use numba lstsq function (which is type specific) to 1863 # compute the inverse and check against that. 1864 lstsq = jit(nopython=True)(lstsq_system) 1865 lstsq_pinv = lstsq( 1866 a, np.eye( 1867 a.shape[0]).astype( 1868 a.dtype), **kwargs)[0] 1869 np.testing.assert_allclose( 1870 got, 1871 lstsq_pinv, 1872 rtol=10 * resolution, 1873 atol=100 * resolution # zeros tend to be fuzzy 1874 ) 1875 # check the 2 norm of the difference is small 1876 self.assertLess(np.linalg.norm(got - expected), resolution) 1877 1878 # Ensure proper resource management 1879 with self.assertNoNRTLeak(): 1880 cfunc(a, **kwargs) 1881 1882 # test: column vector, tall, wide, square, row vector 1883 # prime sizes 1884 sizes = [(7, 1), (11, 5), (5, 11), (3, 3), (1, 7)] 1885 1886 # When required, a specified condition number 1887 specific_cond = 10. 1888 1889 # test loop 1890 for size, dtype, order in \ 1891 product(sizes, self.dtypes, 'FC'): 1892 # check a full rank matrix 1893 a = self.specific_sample_matrix(size, dtype, order) 1894 check(a) 1895 1896 m, n = size 1897 if m != 1 and n != 1: 1898 # check a rank deficient matrix 1899 minmn = min(m, n) 1900 a = self.specific_sample_matrix(size, dtype, order, 1901 condition=specific_cond) 1902 rcond = 1. / specific_cond 1903 approx_half_rank_rcond = minmn * rcond 1904 check(a, rcond=approx_half_rank_rcond) 1905 1906 # check empty 1907 for sz in [(0, 1), (1, 0)]: 1908 check(np.empty(sz)) 1909 1910 rn = "pinv" 1911 1912 # Wrong dtype 1913 self.assert_wrong_dtype(rn, cfunc, 1914 (np.ones((2, 2), dtype=np.int32),)) 1915 1916 # Dimension issue 1917 self.assert_wrong_dimensions(rn, cfunc, 1918 (np.ones(10, dtype=np.float64),)) 1919 1920 # no nans or infs 1921 self.assert_no_nan_or_inf(cfunc, 1922 (np.array([[1., 2., ], [np.inf, np.nan]], 1923 dtype=np.float64),)) 1924 1925 @needs_lapack 1926 def test_issue5870(self): 1927 # testing for mutation of input matrix 1928 @jit(nopython=True) 1929 def some_fn(v): 1930 return np.linalg.pinv(v[0]) 1931 1932 v_data = np.array([[1., 3, 2, 7,], 1933 [-5, 4, 2, 3,], 1934 [9, -3, 1, 1,], 1935 [2, -2, 2, 8,]], order='F') 1936 1937 v_orig = np.copy(v_data) 1938 reshaped_v = v_data.reshape((1, 4, 4)) 1939 1940 expected = some_fn.py_func(reshaped_v) 1941 np.testing.assert_allclose(v_data, v_orig) 1942 1943 got = some_fn(reshaped_v) 1944 np.testing.assert_allclose(v_data, v_orig) 1945 1946 np.testing.assert_allclose(expected, got) 1947 1948 1949class TestLinalgDetAndSlogdet(TestLinalgBase): 1950 """ 1951 Tests for np.linalg.det. and np.linalg.slogdet. 1952 Exactly the same inputs are used for both tests as 1953 det() is a trivial function of slogdet(), the tests 1954 are therefore combined. 1955 """ 1956 1957 def check_det(self, cfunc, a, **kwargs): 1958 expected = det_matrix(a, **kwargs) 1959 got = cfunc(a, **kwargs) 1960 1961 resolution = 5 * np.finfo(a.dtype).resolution 1962 1963 # check the determinants are the same 1964 np.testing.assert_allclose(got, expected, rtol=resolution) 1965 1966 # Ensure proper resource management 1967 with self.assertNoNRTLeak(): 1968 cfunc(a, **kwargs) 1969 1970 def check_slogdet(self, cfunc, a, **kwargs): 1971 expected = slogdet_matrix(a, **kwargs) 1972 got = cfunc(a, **kwargs) 1973 1974 # As numba returns python floats types and numpy returns 1975 # numpy float types, some more adjustment and different 1976 # types of comparison than those used with array based 1977 # results is required. 1978 1979 # check that the returned tuple is same length 1980 self.assertEqual(len(expected), len(got)) 1981 # and that length is 2 1982 self.assertEqual(len(got), 2) 1983 1984 # check that the domain of the results match 1985 for k in range(2): 1986 self.assertEqual( 1987 np.iscomplexobj(got[k]), 1988 np.iscomplexobj(expected[k])) 1989 1990 # turn got[0] into the same dtype as `a` 1991 # this is so checking with nulp will work 1992 got_conv = a.dtype.type(got[0]) 1993 np.testing.assert_array_almost_equal_nulp( 1994 got_conv, expected[0], nulp=10) 1995 # compare log determinant magnitude with a more fuzzy value 1996 # as numpy values come from higher precision lapack routines 1997 resolution = 5 * np.finfo(a.dtype).resolution 1998 np.testing.assert_allclose( 1999 got[1], expected[1], rtol=resolution, atol=resolution) 2000 2001 # Ensure proper resource management 2002 with self.assertNoNRTLeak(): 2003 cfunc(a, **kwargs) 2004 2005 def do_test(self, rn, check, cfunc): 2006 2007 # test: 1x1 as it is unusual, 4x4 as it is even and 7x7 as is it odd! 2008 sizes = [(1, 1), (4, 4), (7, 7)] 2009 2010 # test loop 2011 for size, dtype, order in \ 2012 product(sizes, self.dtypes, 'FC'): 2013 # check a full rank matrix 2014 a = self.specific_sample_matrix(size, dtype, order) 2015 check(cfunc, a) 2016 2017 # use a matrix of zeros to trip xgetrf U(i,i)=0 singular test 2018 for dtype, order in product(self.dtypes, 'FC'): 2019 a = np.zeros((3, 3), dtype=dtype) 2020 check(cfunc, a) 2021 2022 # check empty 2023 check(cfunc, np.empty((0, 0))) 2024 2025 # Wrong dtype 2026 self.assert_wrong_dtype(rn, cfunc, 2027 (np.ones((2, 2), dtype=np.int32),)) 2028 2029 # Dimension issue 2030 self.assert_wrong_dimensions(rn, cfunc, 2031 (np.ones(10, dtype=np.float64),)) 2032 2033 # no nans or infs 2034 self.assert_no_nan_or_inf(cfunc, 2035 (np.array([[1., 2., ], [np.inf, np.nan]], 2036 dtype=np.float64),)) 2037 2038 @needs_lapack 2039 def test_linalg_det(self): 2040 cfunc = jit(nopython=True)(det_matrix) 2041 self.do_test("det", self.check_det, cfunc) 2042 2043 @needs_lapack 2044 def test_linalg_slogdet(self): 2045 cfunc = jit(nopython=True)(slogdet_matrix) 2046 self.do_test("slogdet", self.check_slogdet, cfunc) 2047 2048 @needs_lapack 2049 def test_no_input_mutation(self): 2050 X = np.array([[1., 3, 2, 7,], 2051 [-5, 4, 2, 3,], 2052 [9, -3, 1, 1,], 2053 [2, -2, 2, 8,]], order='F') 2054 2055 X_orig = np.copy(X) 2056 2057 @jit(nopython=True) 2058 def func(X, test): 2059 if test: 2060 # not executed, but necessary to trigger A ordering in X 2061 X = X[1:2, :] 2062 return np.linalg.slogdet(X) 2063 2064 expected = func.py_func(X, False) 2065 np.testing.assert_allclose(X, X_orig) 2066 2067 got = func(X, False) 2068 np.testing.assert_allclose(X, X_orig) 2069 2070 np.testing.assert_allclose(expected, got) 2071 2072# Use TestLinalgSystems as a base to get access to additional 2073# testing for 1 and 2D inputs. 2074 2075 2076class TestLinalgNorm(TestLinalgSystems): 2077 """ 2078 Tests for np.linalg.norm. 2079 """ 2080 2081 @needs_lapack 2082 def test_linalg_norm(self): 2083 """ 2084 Test np.linalg.norm 2085 """ 2086 cfunc = jit(nopython=True)(norm_matrix) 2087 2088 def check(a, **kwargs): 2089 expected = norm_matrix(a, **kwargs) 2090 got = cfunc(a, **kwargs) 2091 2092 # All results should be in the real domain 2093 self.assertTrue(not np.iscomplexobj(got)) 2094 2095 resolution = 5 * np.finfo(a.dtype).resolution 2096 2097 # check the norms are the same to the arg `a` precision 2098 np.testing.assert_allclose(got, expected, rtol=resolution) 2099 2100 # Ensure proper resource management 2101 with self.assertNoNRTLeak(): 2102 cfunc(a, **kwargs) 2103 2104 # Check 1D inputs 2105 sizes = [1, 4, 7] 2106 nrm_types = [None, np.inf, -np.inf, 0, 1, -1, 2, -2, 5, 6.7, -4.3] 2107 2108 # standard 1D input 2109 for size, dtype, nrm_type in \ 2110 product(sizes, self.dtypes, nrm_types): 2111 a = self.sample_vector(size, dtype) 2112 check(a, ord=nrm_type) 2113 2114 # sliced 1D input 2115 for dtype, nrm_type in \ 2116 product(self.dtypes, nrm_types): 2117 a = self.sample_vector(10, dtype)[::3] 2118 check(a, ord=nrm_type) 2119 2120 # Check 2D inputs: 2121 # test: column vector, tall, wide, square, row vector 2122 # prime sizes 2123 sizes = [(7, 1), (11, 5), (5, 11), (3, 3), (1, 7)] 2124 nrm_types = [None, np.inf, -np.inf, 1, -1, 2, -2] 2125 2126 # standard 2D input 2127 for size, dtype, order, nrm_type in \ 2128 product(sizes, self.dtypes, 'FC', nrm_types): 2129 # check a full rank matrix 2130 a = self.specific_sample_matrix(size, dtype, order) 2131 check(a, ord=nrm_type) 2132 2133 # check 2D slices work for the case where xnrm2 is called from 2134 # BLAS (ord=None) to make sure it is working ok. 2135 nrm_types = [None] 2136 for dtype, nrm_type, order in \ 2137 product(self.dtypes, nrm_types, 'FC'): 2138 a = self.specific_sample_matrix((17, 13), dtype, order) 2139 # contig for C order 2140 check(a[:3], ord=nrm_type) 2141 2142 # contig for Fortran order 2143 check(a[:, 3:], ord=nrm_type) 2144 2145 # contig for neither order 2146 check(a[1, 4::3], ord=nrm_type) 2147 2148 # check that numba returns zero for empty arrays. Numpy returns zero 2149 # for most norm types and raises ValueError for +/-np.inf. 2150 # there is not a great deal of consistency in Numpy's response so 2151 # it is not being emulated in Numba 2152 for dtype, nrm_type, order in \ 2153 product(self.dtypes, nrm_types, 'FC'): 2154 a = np.empty((0,), dtype=dtype, order=order) 2155 self.assertEqual(cfunc(a, nrm_type), 0.0) 2156 a = np.empty((0, 0), dtype=dtype, order=order) 2157 self.assertEqual(cfunc(a, nrm_type), 0.0) 2158 2159 rn = "norm" 2160 2161 # Wrong dtype 2162 self.assert_wrong_dtype(rn, cfunc, 2163 (np.ones((2, 2), dtype=np.int32),)) 2164 2165 # Dimension issue, reuse the test from the TestLinalgSystems class 2166 self.assert_wrong_dimensions_1D( 2167 rn, cfunc, (np.ones( 2168 12, dtype=np.float64).reshape( 2169 2, 2, 3),)) 2170 2171 # no nans or infs for 2d case when SVD is used (e.g 2-norm) 2172 self.assert_no_nan_or_inf(cfunc, 2173 (np.array([[1., 2.], [np.inf, np.nan]], 2174 dtype=np.float64), 2)) 2175 2176 # assert 2D input raises for an invalid norm kind kwarg 2177 self.assert_invalid_norm_kind(cfunc, (np.array([[1., 2.], [3., 4.]], 2178 dtype=np.float64), 6)) 2179 2180 2181class TestLinalgCond(TestLinalgBase): 2182 """ 2183 Tests for np.linalg.cond. 2184 """ 2185 2186 @needs_lapack 2187 def test_linalg_cond(self): 2188 """ 2189 Test np.linalg.cond 2190 """ 2191 2192 cfunc = jit(nopython=True)(cond_matrix) 2193 2194 def check(a, **kwargs): 2195 expected = cond_matrix(a, **kwargs) 2196 got = cfunc(a, **kwargs) 2197 2198 # All results should be in the real domain 2199 self.assertTrue(not np.iscomplexobj(got)) 2200 2201 resolution = 5 * np.finfo(a.dtype).resolution 2202 2203 # check the cond is the same to the arg `a` precision 2204 np.testing.assert_allclose(got, expected, rtol=resolution) 2205 2206 # Ensure proper resource management 2207 with self.assertNoNRTLeak(): 2208 cfunc(a, **kwargs) 2209 2210 # valid p values (used to indicate norm type) 2211 ps = [None, np.inf, -np.inf, 1, -1, 2, -2] 2212 sizes = [(3, 3), (7, 7)] 2213 2214 for size, dtype, order, p in \ 2215 product(sizes, self.dtypes, 'FC', ps): 2216 a = self.specific_sample_matrix(size, dtype, order) 2217 check(a, p=p) 2218 2219 # When p=None non-square matrices are accepted. 2220 sizes = [(7, 1), (11, 5), (5, 11), (1, 7)] 2221 for size, dtype, order in \ 2222 product(sizes, self.dtypes, 'FC'): 2223 a = self.specific_sample_matrix(size, dtype, order) 2224 check(a) 2225 2226 # empty 2227 for sz in [(0, 1), (1, 0), (0, 0)]: 2228 self.assert_raise_on_empty(cfunc, (np.empty(sz),)) 2229 2230 # singular systems to trip divide-by-zero 2231 x = np.array([[1, 0], [0, 0]], dtype=np.float64) 2232 check(x) 2233 check(x, p=2) 2234 x = np.array([[0, 0], [0, 0]], dtype=np.float64) 2235 check(x, p=-2) 2236 2237 # try an ill-conditioned system with 2-norm, make sure np raises an 2238 # overflow warning as the result is `+inf` and that the result from 2239 # numba matches. 2240 with warnings.catch_warnings(): 2241 a = np.array([[1.e308, 0], [0, 0.1]], dtype=np.float64) 2242 warnings.simplefilter("ignore", RuntimeWarning) 2243 check(a) 2244 2245 rn = "cond" 2246 2247 # Wrong dtype 2248 self.assert_wrong_dtype(rn, cfunc, 2249 (np.ones((2, 2), dtype=np.int32),)) 2250 2251 # Dimension issue 2252 self.assert_wrong_dimensions(rn, cfunc, 2253 (np.ones(10, dtype=np.float64),)) 2254 2255 # no nans or infs when p="None" (default for kwarg). 2256 self.assert_no_nan_or_inf(cfunc, 2257 (np.array([[1., 2., ], [np.inf, np.nan]], 2258 dtype=np.float64),)) 2259 2260 # assert raises for an invalid norm kind kwarg 2261 self.assert_invalid_norm_kind(cfunc, (np.array([[1., 2.], [3., 4.]], 2262 dtype=np.float64), 6)) 2263 2264 2265class TestLinalgMatrixRank(TestLinalgSystems): 2266 """ 2267 Tests for np.linalg.matrix_rank. 2268 """ 2269 2270 @needs_lapack 2271 def test_linalg_matrix_rank(self): 2272 """ 2273 Test np.linalg.matrix_rank 2274 """ 2275 2276 cfunc = jit(nopython=True)(matrix_rank_matrix) 2277 2278 def check(a, **kwargs): 2279 expected = matrix_rank_matrix(a, **kwargs) 2280 got = cfunc(a, **kwargs) 2281 2282 # Ranks are integral so comparison should be trivial. 2283 # check the rank is the same 2284 np.testing.assert_allclose(got, expected) 2285 2286 # Ensure proper resource management 2287 with self.assertNoNRTLeak(): 2288 cfunc(a, **kwargs) 2289 2290 sizes = [(7, 1), (11, 5), (5, 11), (3, 3), (1, 7)] 2291 2292 for size, dtype, order in \ 2293 product(sizes, self.dtypes, 'FC'): 2294 # check full rank system 2295 a = self.specific_sample_matrix(size, dtype, order) 2296 check(a) 2297 2298 # If the system is a matrix, check rank deficiency is reported 2299 # correctly. Check all ranks from 0 to (full rank - 1). 2300 tol = 1e-13 2301 # first check 1 to (full rank - 1) 2302 for k in range(1, min(size) - 1): 2303 # check rank k 2304 a = self.specific_sample_matrix(size, dtype, order, rank=k) 2305 self.assertEqual(cfunc(a), k) 2306 check(a) 2307 # check provision of a tolerance works as expected 2308 # create a (m x n) diagonal matrix with a singular value 2309 # guaranteed below the tolerance 1e-13 2310 m, n = a.shape 2311 a[:, :] = 0. # reuse `a`'s memory 2312 idx = np.nonzero(np.eye(m, n)) 2313 if np.iscomplexobj(a): 2314 b = 1. + np.random.rand(k) + 1.j +\ 2315 1.j * np.random.rand(k) 2316 # min singular value is sqrt(2)*1e-14 2317 b[0] = 1e-14 + 1e-14j 2318 else: 2319 b = 1. + np.random.rand(k) 2320 b[0] = 1e-14 # min singular value is 1e-14 2321 a[idx[0][:k], idx[1][:k]] = b.astype(dtype) 2322 # rank should be k-1 (as tol is present) 2323 self.assertEqual(cfunc(a, tol), k - 1) 2324 check(a, tol=tol) 2325 # then check zero rank 2326 a[:, :] = 0. 2327 self.assertEqual(cfunc(a), 0) 2328 check(a) 2329 # add in a singular value that is small 2330 if np.iscomplexobj(a): 2331 a[-1, -1] = 1e-14 + 1e-14j 2332 else: 2333 a[-1, -1] = 1e-14 2334 # check the system has zero rank to a given tolerance 2335 self.assertEqual(cfunc(a, tol), 0) 2336 check(a, tol=tol) 2337 2338 # check the zero vector returns rank 0 and a nonzero vector 2339 # returns rank 1. 2340 for dt in self.dtypes: 2341 a = np.zeros((5), dtype=dt) 2342 self.assertEqual(cfunc(a), 0) 2343 check(a) 2344 # make it a nonzero vector 2345 a[0] = 1. 2346 self.assertEqual(cfunc(a), 1) 2347 check(a) 2348 2349 # empty 2350 for sz in [(0, 1), (1, 0), (0, 0)]: 2351 for tol in [None, 1e-13]: 2352 self.assert_raise_on_empty(cfunc, (np.empty(sz), tol)) 2353 2354 rn = "matrix_rank" 2355 2356 # Wrong dtype 2357 self.assert_wrong_dtype(rn, cfunc, 2358 (np.ones((2, 2), dtype=np.int32),)) 2359 2360 # Dimension issue 2361 self.assert_wrong_dimensions_1D( 2362 rn, cfunc, (np.ones( 2363 12, dtype=np.float64).reshape( 2364 2, 2, 3),)) 2365 2366 # no nans or infs for 2D case 2367 self.assert_no_nan_or_inf(cfunc, 2368 (np.array([[1., 2., ], [np.inf, np.nan]], 2369 dtype=np.float64),)) 2370 2371 @needs_lapack 2372 def test_no_input_mutation(self): 2373 # this is here to test no input mutation by 2374 # numba.np.linalg._compute_singular_values 2375 # which is the workhorse for norm with 2d input, rank and cond. 2376 2377 X = np.array([[1., 3, 2, 7,], 2378 [-5, 4, 2, 3,], 2379 [9, -3, 1, 1,], 2380 [2, -2, 2, 8,]], order='F') 2381 2382 X_orig = np.copy(X) 2383 2384 @jit(nopython=True) 2385 def func(X, test): 2386 if test: 2387 # not executed, but necessary to trigger A ordering in X 2388 X = X[1:2, :] 2389 return np.linalg.matrix_rank(X) 2390 2391 expected = func.py_func(X, False) 2392 np.testing.assert_allclose(X, X_orig) 2393 2394 got = func(X, False) 2395 np.testing.assert_allclose(X, X_orig) 2396 2397 np.testing.assert_allclose(expected, got) 2398 2399 2400class TestLinalgMatrixPower(TestLinalgBase): 2401 """ 2402 Tests for np.linalg.matrix_power. 2403 """ 2404 2405 def assert_int_exponenent(self, cfunc, args): 2406 # validate first arg is ok 2407 cfunc(args[0], 1) 2408 # pass in both args and assert fail 2409 with self.assertRaises(errors.TypingError): 2410 cfunc(*args) 2411 2412 @needs_lapack 2413 def test_linalg_matrix_power(self): 2414 cfunc = jit(nopython=True)(matrix_power_matrix) 2415 2416 def check(a, pwr): 2417 expected = matrix_power_matrix(a, pwr) 2418 got = cfunc(a, pwr) 2419 2420 # check that the computed results are contig and in the same way 2421 self.assert_contig_sanity(got, "C") 2422 2423 res = 5 * np.finfo(a.dtype).resolution 2424 np.testing.assert_allclose(got, expected, rtol=res, atol=res) 2425 2426 # Ensure proper resource management 2427 with self.assertNoNRTLeak(): 2428 cfunc(a, pwr) 2429 2430 sizes = [(1, 1), (5, 5), (7, 7)] 2431 powers = [-33, -17] + list(range(-10, 10)) + [17, 33] 2432 2433 for size, pwr, dtype, order in \ 2434 product(sizes, powers, self.dtypes, 'FC'): 2435 a = self.specific_sample_matrix(size, dtype, order) 2436 check(a, pwr) 2437 a = np.empty((0, 0), dtype=dtype, order=order) 2438 check(a, pwr) 2439 2440 rn = "matrix_power" 2441 2442 # Wrong dtype 2443 self.assert_wrong_dtype(rn, cfunc, 2444 (np.ones((2, 2), dtype=np.int32), 1)) 2445 2446 # not an int power 2447 self.assert_wrong_dtype(rn, cfunc, 2448 (np.ones((2, 2), dtype=np.int32), 1)) 2449 2450 # non square system 2451 args = (np.ones((3, 5)), 1) 2452 msg = 'input must be a square array' 2453 self.assert_error(cfunc, args, msg) 2454 2455 # Dimension issue 2456 self.assert_wrong_dimensions(rn, cfunc, 2457 (np.ones(10, dtype=np.float64), 1)) 2458 2459 # non-integer supplied as exponent 2460 self.assert_int_exponenent(cfunc, (np.ones((2, 2)), 1.2)) 2461 2462 # singular matrix is not invertible 2463 self.assert_raise_on_singular(cfunc, (np.array([[0., 0], [1, 1]]), -1)) 2464 2465 2466class TestTrace(TestLinalgBase): 2467 """ 2468 Tests for np.trace. 2469 """ 2470 2471 def setUp(self): 2472 super(TestTrace, self).setUp() 2473 # compile two versions, one with and one without the offset kwarg 2474 self.cfunc_w_offset = jit(nopython=True)(trace_matrix) 2475 self.cfunc_no_offset = jit(nopython=True)(trace_matrix_no_offset) 2476 2477 def assert_int_offset(self, cfunc, a, **kwargs): 2478 # validate first arg is ok 2479 cfunc(a) 2480 # pass in kwarg and assert fail 2481 with self.assertRaises(errors.TypingError): 2482 cfunc(a, **kwargs) 2483 2484 def test_trace(self): 2485 2486 def check(a, **kwargs): 2487 if 'offset' in kwargs: 2488 expected = trace_matrix(a, **kwargs) 2489 cfunc = self.cfunc_w_offset 2490 else: 2491 expected = trace_matrix_no_offset(a, **kwargs) 2492 cfunc = self.cfunc_no_offset 2493 2494 got = cfunc(a, **kwargs) 2495 2496 res = 5 * np.finfo(a.dtype).resolution 2497 np.testing.assert_allclose(got, expected, rtol=res, atol=res) 2498 2499 # Ensure proper resource management 2500 with self.assertNoNRTLeak(): 2501 cfunc(a, **kwargs) 2502 2503 # test: column vector, tall, wide, square, row vector 2504 # prime sizes 2505 sizes = [(7, 1), (11, 5), (5, 11), (3, 3), (1, 7)] 2506 2507 # offsets to cover the range of the matrix sizes above 2508 offsets = [-13, -12, -11] + list(range(-10, 10)) + [11, 12, 13] 2509 2510 for size, offset, dtype, order in \ 2511 product(sizes, offsets, self.dtypes, 'FC'): 2512 a = self.specific_sample_matrix(size, dtype, order) 2513 check(a, offset=offset) 2514 if offset == 0: 2515 check(a) 2516 a = np.empty((0, 0), dtype=dtype, order=order) 2517 check(a, offset=offset) 2518 if offset == 0: 2519 check(a) 2520 2521 rn = "trace" 2522 2523 # Dimension issue 2524 self.assert_wrong_dimensions(rn, self.cfunc_w_offset, 2525 (np.ones(10, dtype=np.float64), 1), False) 2526 self.assert_wrong_dimensions(rn, self.cfunc_no_offset, 2527 (np.ones(10, dtype=np.float64),), False) 2528 2529 # non-integer supplied as exponent 2530 self.assert_int_offset( 2531 self.cfunc_w_offset, np.ones( 2532 (2, 2)), offset=1.2) 2533 2534 def test_trace_w_optional_input(self): 2535 "Issue 2314" 2536 @jit("(optional(float64[:,:]),)", nopython=True) 2537 def tested(a): 2538 return np.trace(a) 2539 2540 a = np.ones((5, 5), dtype=np.float64) 2541 tested(a) 2542 2543 with self.assertRaises(TypeError) as raises: 2544 tested(None) 2545 2546 errmsg = str(raises.exception) 2547 self.assertEqual('expected array(float64, 2d, A), got None', errmsg) 2548 2549 2550class TestBasics(TestLinalgSystems): # TestLinalgSystems for 1d test 2551 2552 order1 = cycle(['F', 'C', 'C', 'F']) 2553 order2 = cycle(['C', 'F', 'C', 'F']) 2554 2555 # test: column vector, matrix, row vector, 1d sizes 2556 # (7, 1, 3) and two scalars 2557 sizes = [(7, 1), (3, 3), (1, 7), (7,), (1,), (3,), 3., 5.] 2558 2559 def _assert_wrong_dim(self, rn, cfunc): 2560 # Dimension issue 2561 self.assert_wrong_dimensions_1D( 2562 rn, cfunc, (np.array([[[1]]], dtype=np.float64), np.ones(1)), False) 2563 self.assert_wrong_dimensions_1D( 2564 rn, cfunc, (np.ones(1), np.array([[[1]]], dtype=np.float64)), False) 2565 2566 def _gen_input(self, size, dtype, order): 2567 if not isinstance(size, tuple): 2568 return size 2569 else: 2570 if len(size) == 1: 2571 return self.sample_vector(size[0], dtype) 2572 else: 2573 return self.sample_vector( 2574 size[0] * size[1], 2575 dtype).reshape( 2576 size, order=order) 2577 2578 def _get_input(self, size1, size2, dtype): 2579 a = self._gen_input(size1, dtype, next(self.order1)) 2580 b = self._gen_input(size2, dtype, next(self.order2)) 2581 # force domain consistency as underlying ufuncs require it 2582 if np.iscomplexobj(a): 2583 b = b + 1j 2584 if np.iscomplexobj(b): 2585 a = a + 1j 2586 return (a, b) 2587 2588 def test_outer(self): 2589 cfunc = jit(nopython=True)(outer_matrix) 2590 2591 def check(a, b, **kwargs): 2592 2593 # check without kwargs 2594 expected = outer_matrix(a, b) 2595 got = cfunc(a, b) 2596 2597 res = 5 * np.finfo(np.asarray(a).dtype).resolution 2598 np.testing.assert_allclose(got, expected, rtol=res, atol=res) 2599 2600 # if kwargs present check with them too 2601 if 'out' in kwargs: 2602 got = cfunc(a, b, **kwargs) 2603 np.testing.assert_allclose(got, expected, rtol=res, 2604 atol=res) 2605 np.testing.assert_allclose(kwargs['out'], expected, 2606 rtol=res, atol=res) 2607 2608 # Ensure proper resource management 2609 with self.assertNoNRTLeak(): 2610 cfunc(a, b, **kwargs) 2611 2612 for size1, size2, dtype in \ 2613 product(self.sizes, self.sizes, self.dtypes): 2614 (a, b) = self._get_input(size1, size2, dtype) 2615 check(a, b) 2616 c = np.empty((np.asarray(a).size, np.asarray(b).size), 2617 dtype=np.asarray(a).dtype) 2618 check(a, b, out=c) 2619 2620 self._assert_wrong_dim("outer", cfunc) 2621 2622 def test_kron(self): 2623 cfunc = jit(nopython=True)(kron_matrix) 2624 2625 def check(a, b, **kwargs): 2626 2627 expected = kron_matrix(a, b) 2628 got = cfunc(a, b) 2629 2630 res = 5 * np.finfo(np.asarray(a).dtype).resolution 2631 np.testing.assert_allclose(got, expected, rtol=res, atol=res) 2632 2633 # Ensure proper resource management 2634 with self.assertNoNRTLeak(): 2635 cfunc(a, b) 2636 2637 for size1, size2, dtype in \ 2638 product(self.sizes, self.sizes, self.dtypes): 2639 (a, b) = self._get_input(size1, size2, dtype) 2640 check(a, b) 2641 2642 self._assert_wrong_dim("kron", cfunc) 2643 2644 args = (np.empty(10)[::2], np.empty(10)[::2]) 2645 msg = "only supports 'C' or 'F' layout" 2646 self.assert_error(cfunc, args, msg, err=errors.TypingError) 2647 2648 2649class TestHelpers(TestCase): 2650 def test_copy_to_fortran_order(self): 2651 from numba.np.linalg import _copy_to_fortran_order 2652 2653 def check(udt, expectfn, shapes, dtypes, orders): 2654 for shape, dtype, order in product(shapes, dtypes, orders): 2655 a = np.arange(np.prod(shape)).reshape(shape, order=order) 2656 2657 r = udt(a) 2658 # check correct operation 2659 self.assertPreciseEqual(expectfn(a), r) 2660 # check new copy has made 2661 self.assertNotEqual(a.ctypes.data, r.ctypes.data) 2662 2663 @njit 2664 def direct_call(a): 2665 return _copy_to_fortran_order(a) 2666 2667 shapes = [(3, 4), (3, 2, 5)] 2668 dtypes = [np.intp] 2669 orders = ['C', 'F'] 2670 check(direct_call, np.asfortranarray, shapes, dtypes, orders) 2671 2672 2673 @njit 2674 def slice_to_any(a): 2675 # make a 'any' layout slice 2676 sliced = a[::2][0] 2677 return _copy_to_fortran_order(sliced) 2678 2679 shapes = [(3, 3, 4), (3, 3, 2, 5)] 2680 dtypes = [np.intp] 2681 orders = ['C', 'F'] 2682 2683 def expected_slice_to_any(a): 2684 # make a 'any' layout slice 2685 sliced = a[::2][0] 2686 return np.asfortranarray(sliced) 2687 2688 check(slice_to_any, expected_slice_to_any, shapes, dtypes, orders) 2689 2690if __name__ == '__main__': 2691 unittest.main() 2692