Repository: numba/numba
Pull request: Fix scheduler bug where it rounds to 0 divisions for a chunk.
Resolves #6749

Problem statement:
Parallel accelerator yields incorrect results for high dimensional arrays
Using the parallel accelerator on high-dimensional arrays produces incorrect results. Here is a minimal example, but the same issues occurs for other functions.
```
import numpy as np
from numba import njit

@njit(parallel=True)
def numba_fun(x):
    return x * 1.0

x = np.ones([2, 2, 2, 2, 2, 15])
y = numba_fun(x)
incorrect = np.abs(x - y) > 1e-6

print(incorrect.sum())
print(y.min())
print(y.max())
print(y[incorrect])
```
In my environment (numba 0.51.2, numpy 1.19.2, python 3.8.5) I get 384 incorrect elements of `y`, ranging from `-1.36e+57` to `4.09e+179`.

The problem is fixed if I either (i) disable the parallel accelerator, or (ii) reduce the dimensions of `x` (e.g, setting `x = np.ones([2, 2, 2, 2, 15])`), or (iii) flatten `x` (e.g, setting `x = np.ones([2, 2, 2, 2, 2, 15]).ravel()`).

On the other hand, reducing the number of elements in the last dimensions completely freezes the execution (`x = np.ones([2, 2, 2, 2, 2, 10])`)

Edit: just updated to numba 0.53.0rc1 and can confirm that I continue to see the same error

Hints from issue discussion:
@DrTodd13, looks like the scheduling is wrong:

output snippet from ` NUMBA_DEBUG_ARRAY_OPT=2`:
```
sched[0] = 1 0 1 0 1 0 1 0 1 0 1 0
sched[1] = 1 0 1 0 99021286 131339344 0 426456976 484342656 484343120 440416576 -371173344
sched[2] = 98908042 102008432 484343080 484342656 443588336 484343112 484343120 -371173264 98897604 440416576 0 425920448
sched[3] = 2 -371173072 100070591 -371173216 99245808 -716906423 417163976 440416576 98599109 -1 440416576 -371173136
sched[4] = 98920866 102004328 7 417163904 426582208 -371173056 99034357 127467681 426428096 127467681 102044432 417163904
sched[5] = 463656448 426582208 1882063682 1 103166080 3 103176704 40 485778833 -371172800 1882059711 417163904
sched[6] = 0 1879052288 444071935 -371172896 99156640 426582208 417163904 0 103176704 0 426582208 -371172832
sched[7] = 103166080 417163904 103176704 103161856 463656552 95 -371172512 6 463668224 -1464434688 40 -371172752
```

Commit message: Update numba/np/ufunc/gufunc_scheduler.cpp

Co-authored-by: stuartarchibald <stuartarchibald@users.noreply.github.com>

Patch to review:
diff --git a/numba/np/ufunc/gufunc_scheduler.cpp b/numba/np/ufunc/gufunc_scheduler.cpp
index dfa4ea62886..51333e0a83e 100644
--- a/numba/np/ufunc/gufunc_scheduler.cpp
+++ b/numba/np/ufunc/gufunc_scheduler.cpp
@@ -202,6 +202,9 @@ void divide_work(const RangeActual &full_iteration_space,
         } else {
             // We allocate the remaining threads proportionally to the ratio of the current dimension length to the total.
             divisions_for_this_dim = intp(guround(num_threads * ((float)dims[index].length / total_len)));
+            if (divisions_for_this_dim < 1) {
+                        divisions_for_this_dim = 1;
+            }
         }
 
         // These are used to divide the iteration space.
diff --git a/numba/tests/test_parfors.py b/numba/tests/test_parfors.py
index abdae2cc803..273c4cd876c 100644
--- a/numba/tests/test_parfors.py
+++ b/numba/tests/test_parfors.py
@@ -1839,6 +1839,14 @@ def test_impl(a):
         x = np.ones((3,3))
         self.check(test_impl, x)
 
+    @skip_parfors_unsupported
+    def test_high_dimension1(self):
+        # issue6749
+        def test_impl(x):
+            return x * 1.0
+        x = np.ones((2, 2, 2, 2, 2, 15))
+        self.check(test_impl, x)
+
 
 class TestParforsLeaks(MemoryLeakMixin, TestParforsBase):
     def check(self, pyfunc, *args, **kwargs):


Code review comments:
diff_hunk: @@ -1839,6 +1839,14 @@ def test_impl(a):
         x = np.ones((3,3))
         self.check(test_impl, x)
 
+    @skip_parfors_unsupported
+    def test_high_dimension1(self):
+        # issue6749
+        def test_impl(x):
+            return x * 1.0
path: numba/tests/test_parfors.py
text: @user1:
```suggestion
            return x * 5.0
```
Suggest altering the multiplier given the input is all `1.0` else there's no differentiation between it working and a NOP?