Repository: numpy/numpy

Issue:
triu generates nan when called on matrices with infinite values
In numpy 1.8.1, I've just found some strange behaviour.

Summary: when calling np.triu on a matrix, inf entries in the discarded region get converted to nan.  This broke my code unexpectedly.

```
import numpy as np
a = np.zeros((4, 4))
a[1:, 2] = np.inf
a
>>>array([[  0.,   0.,   0.,   0.],
          [ inf,   0.,   0.,   0.],
          [ inf,   0.,   0.,   0.],
          [ inf,   0.,   0.,   0.]])

np.triu(a)
>>>array([[  0.,   0.,   0.,   0.],
          [ nan,   0.,   0.,   0.],
          [ nan,   0.,   0.,   0.],
          [ nan,   0.,   0.,   0.]])
```

Also posted on stackexchange: https://stackoverflow.com/questions/24678932/numpy-triu-generates-nan-when-called-on-matrices-with-infinite-values



Discussion:
We could use boolean indexing instead of multiplication, and may even make it faster if done properly:

```
In [18]: arr = np.random.rand(1000, 1000)

In [19]: %timeit np.tri(1000) * arr # this is was np.triu currently does
100 loops, best of 3: 8.74 ms per loop

In [28]: %%timeit # this would be the boolean approach
   ....: mask = ~np.tri(1000, dtype=bool)
   ....: out = arr.copy()
   ....: out[mask] = 0
   ....:
100 loops, best of 3: 7 ms per loop
```

I have only done limited testing, but haven't found any faster way of doing this while keeping a pure Python implementation. Ideas are welcome! There may be an extra ms to shave if we change `np.tri` to take an `inverse` kwarg so that it produces the negated mask directly.

Anyway, if there is no major objection to this approach, I'll have a PR ready later today.


Resolving patch:
diff --git a/numpy/lib/twodim_base.py b/numpy/lib/twodim_base.py
--- a/numpy/lib/twodim_base.py
+++ b/numpy/lib/twodim_base.py
@@ -387,7 +387,6 @@ def tri(N, M=None, k=0, dtype=float):
     dtype : dtype, optional
         Data type of the returned array.  The default is float.
 
-
     Returns
     -------
     tri : ndarray of shape (N, M)
@@ -452,7 +451,9 @@ def tril(m, k=0):
 
     """
     m = asanyarray(m)
-    return multiply(tri(*m.shape[-2:], k=k, dtype=bool), m, dtype=m.dtype)
+    mask = tri(*m.shape[-2:], k=k, dtype=bool)
+
+    return where(mask, m, 0)
 
 
 def triu(m, k=0):
@@ -478,7 +479,9 @@ def triu(m, k=0):
 
     """
     m = asanyarray(m)
-    return multiply(~tri(*m.shape[-2:], k=k-1, dtype=bool), m, dtype=m.dtype)
+    mask = tri(*m.shape[-2:], k=k-1, dtype=bool)
+
+    return where(mask, 0, m)
 
 
 # Originally borrowed from John Hunter and matplotlib


Test patch:
diff --git a/numpy/lib/tests/test_twodim_base.py b/numpy/lib/tests/test_twodim_base.py
--- a/numpy/lib/tests/test_twodim_base.py
+++ b/numpy/lib/tests/test_twodim_base.py
@@ -311,6 +311,18 @@ def test_tril_triu_ndim3():
         yield assert_equal, a_triu_observed.dtype, a.dtype
         yield assert_equal, a_tril_observed.dtype, a.dtype
 
+def test_tril_triu_with_inf():
+    # Issue 4859
+    arr = np.array([[1, 1, np.inf],
+                    [1, 1, 1],
+                    [np.inf, 1, 1]])
+    out_tril = np.array([[1, 0, 0],
+                         [1, 1, 0],
+                         [np.inf, 1, 1]])
+    out_triu = out_tril.T
+    assert_array_equal(np.triu(arr), out_triu)
+    assert_array_equal(np.tril(arr), out_tril)
+
 
 def test_mask_indices():
     # simple test without offset