Repository: numba/numba

Issue:
simpler example of nopython decorated function returning different results than python implementation
Previously, [I created an issue that might be related](https://github.com/numba/numba/issues/1249) (at the very least, the symptoms of the problem are the same), but it had a more complicated example script. Here, I present a much simpler example script: https://gist.github.com/bmer/9d3c445cc3cb3c65930f

The script generates a simple polygon, and then calculates "edge vectors", and prints a numpy generated comparison of both results. We see that the numba calculated results are different from the python calculated results, but only in the "y" column:

```
[[ True False]
 [ True False]
 [ True False]
 [ True False]
 [ True False]
 [ True False]
 [ True False]
 [ True False]
 [ True False]
 [ True False]]
```



Discussion:
Which version of Numba are you using?  Have you tried the latest Numba build that fixed #1244?

It seems tuple unpacking on array views is faulty. If you replace the inner assignment with:

```
        xA = node_coords[i,0]
        yA = node_coords[i,1]
        xB = node_coords[i_plus1,0]
        yB = node_coords[i_plus1,1]
```

then the problem disappears.

@seibert yes, I made sure to run `numba install -c numba numba` again

@pitrou yes, this is what I see too!


Resolving patch:
diff --git a/numba/targets/arrayobj.py b/numba/targets/arrayobj.py
--- a/numba/targets/arrayobj.py
+++ b/numba/targets/arrayobj.py
@@ -233,9 +233,11 @@ def getitem_arraynd_intp(context, builder, sig, args):
     adapted_ary = arystty(context, builder, ary)
     ndim = aryty.ndim
     if ndim == 1:
+        # Return a value
         result = _getitem_array1d(context, builder, aryty, adapted_ary, idx,
                                   wraparound=idxty.signed)
     elif ndim > 1:
+        # Return a subview over the array
         out_ary_ty = make_array(aryty.copy(ndim = ndim - 1))
         out_ary = out_ary_ty(context, builder)
         in_shapes = cgutils.unpack_tuple(builder, adapted_ary.shape, count=ndim)
diff --git a/numba/typing/builtins.py b/numba/typing/builtins.py
--- a/numba/typing/builtins.py
+++ b/numba/typing/builtins.py
@@ -466,7 +466,9 @@ def generic(self, args, kws):
             if ary.ndim == 1:
                 res = ary.dtype
             elif not ary.slice_is_copy and ary.ndim > 1:
-                res = ary.copy(ndim=ary.ndim - 1)
+                # Left-index into a F-contiguous array gives a non-contiguous view
+                layout = 'C' if ary.layout == 'C' else 'A'
+                res = ary.copy(ndim=ary.ndim - 1, layout=layout)
             else:
                 return
 


Test patch:
diff --git a/numba/tests/test_array_iterators.py b/numba/tests/test_array_iterators.py
--- a/numba/tests/test_array_iterators.py
+++ b/numba/tests/test_array_iterators.py
@@ -8,6 +8,18 @@
 from .support import TestCase, CompilationCache
 
 
+def array_iter(arr):
+    total = 0
+    for i, v in enumerate(arr):
+        total += i * v
+    return total
+
+def array_view_iter(arr, idx):
+    total = 0
+    for i, v in enumerate(arr[idx]):
+        total += i * v
+    return total
+
 def array_flat(arr, out):
     for i, v in enumerate(arr.flat):
         out[i] = v
@@ -57,6 +69,20 @@ class TestArrayIterators(TestCase):
     def setUp(self):
         self.ccache = CompilationCache()
 
+    def check_array_iter(self, arr):
+        pyfunc = array_iter
+        cres = compile_isolated(pyfunc, [typeof(arr)])
+        cfunc = cres.entry_point
+        expected = pyfunc(arr)
+        self.assertPreciseEqual(cfunc(arr), expected)
+
+    def check_array_view_iter(self, arr, index):
+        pyfunc = array_view_iter
+        cres = compile_isolated(pyfunc, [typeof(arr), typeof(index)])
+        cfunc = cres.entry_point
+        expected = pyfunc(arr, index)
+        self.assertPreciseEqual(cfunc(arr, index), expected)
+
     def check_array_flat(self, arr, arrty=None):
         out = np.zeros(arr.size, dtype=arr.dtype)
         nb_out = out.copy()
@@ -82,6 +108,23 @@ def check_array_flat_sum(self, arr, arrty):
     def check_array_ndenumerate_sum(self, arr, arrty):
         self.check_array_unary(arr, arrty, array_ndenumerate_sum)
 
+    def test_array_iter(self):
+        # Test iterating over a 1d array
+        arr = np.arange(6)
+        self.check_array_iter(arr)
+        arr = arr[::2]
+        self.assertFalse(arr.flags.c_contiguous)
+        self.assertFalse(arr.flags.f_contiguous)
+        self.check_array_iter(arr)
+
+    def test_array_view_iter(self):
+        # Test iterating over a 1d view over a 2d array
+        arr = np.arange(12).reshape((3, 4))
+        self.check_array_view_iter(arr, 1)
+        self.check_array_view_iter(arr.T, 1)
+        arr = arr[::2]
+        self.check_array_view_iter(arr, 1)
+
     def test_array_flat_3d(self):
         arr = np.arange(24).reshape(4, 2, 3)
 
diff --git a/numba/tests/test_indexing.py b/numba/tests/test_indexing.py
--- a/numba/tests/test_indexing.py
+++ b/numba/tests/test_indexing.py
@@ -87,6 +87,13 @@ def slicing_3d_usecase2(a, index0, stop1, index2):
         total += b[i] * (i + 1)
     return total
 
+def partial_1d_usecase(a, index):
+    b = a[index]
+    total = 0
+    for i in range(b.shape[0]):
+        total += b[i] * (i + 1)
+    return total
+
 def integer_indexing_1d_usecase(a, i):
     return a[i]
 
@@ -482,6 +489,7 @@ def test_1d_integer_indexing_npm(self):
         self.test_1d_integer_indexing(flags=Noflags)
 
     def test_integer_indexing_1d_for_2d(self, flags=enable_pyobj_flags):
+        # Test partial (1d) indexing of a 2d array
         pyfunc = integer_indexing_1d_usecase
         arraytype = types.Array(types.int32, 2, 'C')
         cr = compile_isolated(pyfunc, (arraytype, types.int32), flags=flags)
@@ -492,8 +500,14 @@ def test_integer_indexing_1d_for_2d(self, flags=enable_pyobj_flags):
         self.assertTrue((pyfunc(a, 9) == cfunc(a, 9)).all())
         self.assertTrue((pyfunc(a, -1) == cfunc(a, -1)).all())
 
+        arraytype = types.Array(types.int32, 2, 'A')
+        cr = compile_isolated(pyfunc, (arraytype, types.int32), flags=flags)
+        cfunc = cr.entry_point
+
+        a = np.arange(20, dtype='i4').reshape(5, 4)[::2]
+        self.assertPreciseEqual(pyfunc(a, 0), cfunc(a, 0))
 
-    def test_integer_indexing_1d_for_2d(self):
+    def test_integer_indexing_1d_for_2d_npm(self):
         with self.assertTypingError():
             self.test_integer_indexing_1d_for_2d(flags=Noflags)
 
@@ -556,6 +570,30 @@ def test_2d_float_indexing(self, flags=enable_pyobj_flags):
         self.assertEqual(pyfunc(a, 9, 9), cfunc(a, 9, 9))
         self.assertEqual(pyfunc(a, -1, -1), cfunc(a, -1, -1))
 
+    def test_partial_1d_indexing(self, flags=enable_pyobj_flags):
+        pyfunc = partial_1d_usecase
+
+        def check(arr, arraytype):
+            cr = compile_isolated(pyfunc, (arraytype, types.int32),
+                                  flags=flags)
+            cfunc = cr.entry_point
+            self.assertEqual(pyfunc(arr, 0), cfunc(arr, 0))
+            n = arr.shape[0] - 1
+            self.assertEqual(pyfunc(arr, n), cfunc(arr, n))
+            self.assertEqual(pyfunc(arr, -1), cfunc(arr, -1))
+
+        a = np.arange(12, dtype='i4').reshape((4, 3))
+        arraytype = types.Array(types.int32, 2, 'C')
+        check(a, arraytype)
+
+        a = np.arange(12, dtype='i4').reshape((3, 4)).T
+        arraytype = types.Array(types.int32, 2, 'F')
+        check(a, arraytype)
+
+        a = np.arange(12, dtype='i4').reshape((3, 4))[::2]
+        arraytype = types.Array(types.int32, 2, 'A')
+        check(a, arraytype)
+
     def test_ellipse(self, flags=enable_pyobj_flags):
         pyfunc = ellipse_usecase
         arraytype = types.Array(types.int32, 2, 'C')