Repository: numba/numba

Issue:
Numba division zero error with numpy linalg functions
```
@jit(nopython=True, error_model='numpy')
def is_inv(matrix):
    cond_threshold = 1 / np.finfo(np.float64).eps
    cond = np.linalg.cond(matrix)
    return cond < cond_threshold
```
I want to check if a matrix is singular or not because I can't try and catch the singular error in numba. However, np.linalg.cond is now giving my division by zero error. I used error_model='numpy', but it still gives me a python semantic error.

The fixed I have is to go into targets/linalg.py, and change line s[0] / s[-1] to np.divide in cond_imple. 


Discussion:
Thanks for the report. Full reproducer:
```python
import numpy as np
from numba import njit

def is_inv(matrix):
    cond_threshold = 1 / np.finfo(np.float64).eps
    cond = np.linalg.cond(matrix)
    return cond < cond_threshold

x = np.arange(12.).reshape((4, 3))
x [-2:,:] = 0.

is_inv(x)

try:
    njit(is_inv)(x)
except ZeroDivisionError as e:
    print(e)

try:
    njit(error_model='numpy')(is_inv)(x)
except ZeroDivisionError as e:
    print(e)
```
suggested fix seems appropriate, would you be interested in contributing a patch for this and corresponding tests? Thanks.

Resolving patch:
diff --git a/numba/targets/linalg.py b/numba/targets/linalg.py
--- a/numba/targets/linalg.py
+++ b/numba/targets/linalg.py
@@ -2377,46 +2377,43 @@ def cond_impl(a, p=None):
 
     _check_linalg_matrix(a, "cond")
 
-    def _get_cond_impl(a, p):
-        # handle the p==None case separately for type inference to work ok
-        if p in (None, types.none):
-            def cond_none_impl(a, p=None):
-                s = _compute_singular_values(a)
-                return s[0] / s[-1]
-            return cond_none_impl
+    def impl(a, p=None):
+        # This is extracted for performance, numpy does approximately:
+        # `condition = norm(a) * norm(inv(a))`
+        # in the cases of `p == 2` or `p ==-2` singular values are used
+        # for computing norms. This costs numpy an svd of `a` then an
+        # inversion of `a` and another svd of `a`.
+        # Below is a different approach, which also gives a more
+        # accurate answer as there is no inversion involved.
+        # Recall that the singular values of an inverted matrix are the
+        # reciprocal of singular values of the original matrix.
+        # Therefore calling `svd(a)` once yields all the information
+        # needed about both `a` and `inv(a)` without the cost or
+        # potential loss of accuracy incurred through inversion.
+        # For the case of `p == 2`, the result is just the ratio of
+        # `largest singular value/smallest singular value`, and for the
+        # case of `p==-2` the result is simply the
+        # `smallest singular value/largest singular value`.
+        # As a result of this, numba accepts non-square matrices as
+        # input when p==+/-2 as well as when p==None.
+        if p == 2 or p == -2 or p is None:
+            s = _compute_singular_values(a)
+            if p == 2 or p is None:
+                r = np.divide(s[0], s[-1])
+            else:
+                r = np.divide(s[-1], s[0])
+        else:  # cases np.inf, -np.inf, 1, -1
+            norm_a = np.linalg.norm(a, p)
+            norm_inv_a = np.linalg.norm(np.linalg.inv(a), p)
+            r = norm_a * norm_inv_a
+        # NumPy uses a NaN mask, if the input has a NaN, it will return NaN,
+        # Numba calls ban NaN through the use of _check_finite_matrix but this
+        # catches cases where NaN occurs through floating point use
+        if np.isnan(r):
+            return np.inf
         else:
-            def cond_not_none_impl(a, p=None):
-                # This is extracted for performance, numpy does approximately:
-                # `condition = norm(a) * norm(inv(a))`
-                # in the cases of `p == 2` or `p ==-2` singular values are used
-                # for computing norms. This costs numpy an svd of `a` then an
-                # inversion of `a` and another svd of `a`.
-                # Below is a different approach, which also gives a more
-                # accurate answer as there is no inversion involved.
-                # Recall that the singular values of an inverted matrix are the
-                # reciprocal of singular values of the original matrix.
-                # Therefore calling `svd(a)` once yields all the information
-                # needed about both `a` and `inv(a)` without the cost or
-                # potential loss of accuracy incurred through inversion.
-                # For the case of `p == 2`, the result is just the ratio of
-                # `largest singular value/smallest singular value`, and for the
-                # case of `p==-2` the result is simply the
-                # `smallest singular value/largest singular value`.
-                # As a result of this, numba accepts non-square matrices as
-                # input when p==+/-2 as well as when p==None.
-                if p == 2 or p == -2:
-                    s = _compute_singular_values(a)
-                    if p == 2:
-                        return s[0] / s[-1]
-                    else:
-                        return s[-1] / s[0]
-                else:  # cases np.inf, -np.inf, 1, -1
-                    norm_a = np.linalg.norm(a, p)
-                    norm_inv_a = np.linalg.norm(np.linalg.inv(a), p)
-                    return norm_a * norm_inv_a
-            return cond_not_none_impl
-
-    return _get_cond_impl(a, p)
+            return r
+    return impl
 
 
 @register_jitable


Test patch:
diff --git a/numba/tests/test_linalg.py b/numba/tests/test_linalg.py
--- a/numba/tests/test_linalg.py
+++ b/numba/tests/test_linalg.py
@@ -2052,6 +2052,16 @@ def check(a, **kwargs):
         for sz in [(0, 1), (1, 0), (0, 0)]:
             self.assert_raise_on_empty(cfunc, (np.empty(sz),))
 
+        # singular systems to trip divide-by-zero
+        # only for np > 1.14, before this norm was computed via inversion which
+        # will fail with numpy.linalg.linalg.LinAlgError: Singular matrix
+        if numpy_version > (1, 14):
+            x = np.array([[1, 0], [0, 0]], dtype=np.float64)
+            check(x)
+            check(x, p=2)
+            x = np.array([[0, 0], [0, 0]], dtype=np.float64)
+            check(x, p=-2)
+
         # try an ill-conditioned system with 2-norm, make sure np raises an
         # overflow warning as the result is `+inf` and that the result from
         # numba matches.