Repository: numba/numba

Issue:
the execution time of JITed function varies very much (>13x) among different running without any code changes
the execution time of JITed function varies among different running without any code changes. Normally it costs about 0.9s for the execution but sometimes it costs 13s for the same code and same input. The script code and input data and dump of output are at 
https://groups.google.com/a/continuum.io/forum/#!topic/anaconda/JAqXcLzFDbk 

---

Current conda install:

```
         platform : linux-64
    conda version : 3.7.0
```

  conda-build version : 1.8.2
       python version : 3.4.1.final.0
     requests version : 2.4.1
     root environment : /opt/anaconda3  (read only)
  default environment : /opt/anaconda3
     envs directories : /home/justicui/envs
                        /opt/anaconda3/envs
        package cache : /home/justicui/envs/.pkgs
                        /opt/anaconda3/pkgs
         channel URLs : http://repo.continuum.io/pkgs/free/linux-64/
                        http://repo.continuum.io/pkgs/pro/linux-64/
          config file : None
    is foreign system : False

---

![eye-diagram](https://cloud.githubusercontent.com/assets/7495860/4564541/7f9b1262-4f18-11e4-8c8e-3f0a82351c27.png)



Discussion:
OK, I can reproduce the problem with this simplified version of your code:

``` python
import numpy as np
from numba import jit

@jit(nopython=True)
def bres_segment_count_slow(x0, y0, x1, y1, grid):
    """Bresenham's algorithm.

    The value of grid[x,y] is incremented for each x,y
    in the line from (x0,y0) up to but not including (x1, y1).
    """

    nrows, ncols = grid.shape

    dx = abs(x1 - x0)
    dy = abs(y1 - y0)

    sx = 0
    if x0 < x1:
        sx = 1
    else:
        sx = -1
    sy = 0
    if y0 < y1:
        sy = 1
    else:
        sy = -1

    err = dx - dy

    while True:
        # Note: this test is moved before setting
        # the value, so we don't set the last point.
        if x0 == x1 and y0 == y1:
            break

        if 0 <= x0 < nrows and 0 <= y0 < ncols:
            grid[x0, y0] += 1

        e2 = 2 * err
        if e2 > -dy:
            err -= dy
            x0 += sx
        if e2 < dx:
            err += dx
            y0 += sy


g = np.zeros(shape=(10,10), dtype=np.int32)
bres_segment_count_slow(np.int32(0), np.int32(0), np.int32(1), np.int32(1), g)
```

Sometimes this will fail type inference and sometimes it will work.  Interestingly, this code always works if the four integer arguments are Python integers instead of NumPy int32 scalars.


Resolving patch:
diff --git a/numba/typing/context.py b/numba/typing/context.py
--- a/numba/typing/context.py
+++ b/numba/typing/context.py
@@ -364,14 +364,24 @@ def unify_pairs(self, first, second):
         elif d == 'promote':
             return second
         elif d in ('safe', 'unsafe'):
-            assert first in types.number_domain
-            assert second in types.number_domain
-            a = numpy.dtype(str(first))
-            b = numpy.dtype(str(second))
-            # Just use NumPy coercion rules
-            sel = numpy.promote_types(a, b)
-            # Convert NumPy dtype back to Numba types
-            return getattr(types, str(sel))
+            if first in types.number_domain and second in types.number_domain:
+                a = numpy.dtype(str(first))
+                b = numpy.dtype(str(second))
+                # Just use NumPy coercion rules
+                sel = numpy.promote_types(a, b)
+                # Convert NumPy dtype back to Numba types
+                return getattr(types, str(sel))
+            elif (isinstance(first, types.UniTuple) and
+                      isinstance(second, types.UniTuple)):
+                a = numpy.dtype(str(first.dtype))
+                b = numpy.dtype(str(second.dtype))
+                if a > b:
+                    return first
+                else:
+                    return second
+            else:
+                msg = "unrecognized '{0}' unify for {1} and {2}"
+                raise TypeError(msg.format(d, first, second))
         elif d in 'int-tuple-coerce':
             return types.UniTuple(dtype=types.intp, count=len(first))
         else:


Test patch:
diff --git a/numba/tests/test_typeinfer.py b/numba/tests/test_typeinfer.py
--- a/numba/tests/test_typeinfer.py
+++ b/numba/tests/test_typeinfer.py
@@ -4,8 +4,7 @@
 import numpy as np
 from numba import unittest_support as unittest
 from numba.compiler import compile_isolated
-from numba import types, typeinfer
-from numba import typing
+from numba import types, typeinfer, typing, jit
 
 
 class TestArgRetCasting(unittest.TestCase):
@@ -123,5 +122,52 @@ def test_bitwidth_number_types(self):
         for ty in types.number_domain:
             self.assertTrue(hasattr(ty, "bitwidth"))
 
+
+def issue_797(x0, y0, x1, y1, grid):
+    nrows, ncols = grid.shape
+
+    dx = abs(x1 - x0)
+    dy = abs(y1 - y0)
+
+    sx = 0
+    if x0 < x1:
+        sx = 1
+    else:
+        sx = -1
+    sy = 0
+    if y0 < y1:
+        sy = 1
+    else:
+        sy = -1
+
+    err = dx - dy
+
+    while True:
+        if x0 == x1 and y0 == y1:
+            break
+
+        if 0 <= x0 < nrows and 0 <= y0 < ncols:
+            grid[x0, y0] += 1
+
+        e2 = 2 * err
+        if e2 > -dy:
+            err -= dy
+            x0 += sx
+        if e2 < dx:
+            err += dx
+            y0 += sy
+
+
+class TestIssue(object):
+    def test_issue_797(self):
+        """https://github.com/numba/numba/issues/797#issuecomment-58592401
+
+        Undeterministic triggering of tuple coercion error
+        """
+        foo = jit(nopython=True)(issue_797)
+        g = np.zeros(shape=(10, 10), dtype=np.int32)
+        foo(np.int32(0), np.int32(0), np.int32(1), np.int32(1), g)
+
+
 if __name__ == '__main__':
     unittest.main()