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