Repository: numba/numba

Issue:
Support ndarray.fill under parfors
`ndarray.fill` has parallel semantics and should be supported in ParallelAccelerator


Discussion:
Is this issue still open?  If so what would it take to get started?
@EPronovost I believe so. I think the first step would be adding an array analysis routine `_analyze_op_call_numpy_fill` to `numba.array_analysis.ArrayAnalysis`. As a warning, this code path is in general very involved.
@EPronovost @stuartarchibald This is probably not a good first issue.  I also don't believe it is an array analysis issue since it doesn't create or change the size of an array.  I am looking at it and it requires changes to parfor/_convert_numpy.
Indeed, this is not a first issue :)

@DrTodd13, I see, I guess as there's no shape change that part can be skipped.


Also, @EPronovost if you are looking for something more involved to do with Numba then please ping me on https://gitter.im/numba/numba-dev and we can discuss if you like.

Parfor from ParallelAccelerator (the technology behind `parallel=True`) is really challenging on a number of fronts and historically it takes quite some time to get to a point where it can be debugged and developed. I wouldn't want to discourage looking at it, but it's probably better to try a few other things first.

Resolving patch:
diff --git a/numba/parfor.py b/numba/parfor.py
--- a/numba/parfor.py
+++ b/numba/parfor.py
@@ -427,6 +427,29 @@ def linspace_3(start, stop, num):
     ('linspace', 'numpy'): linspace_parallel_impl,
 }
 
+def fill_parallel_impl(return_type, arr, val):
+    """Parallel implemention of ndarray.fill.  The array on
+       which to operate is retrieved from get_call_name and
+       is passed along with the value to fill.
+    """
+    if arr.ndim == 1:
+        def fill_1(in_arr, val):
+            numba.parfor.init_prange()
+            for i in numba.parfor.internal_prange(len(in_arr)):
+                in_arr[i] = val
+            return None
+    else:
+        def fill_1(in_arr, val):
+            numba.parfor.init_prange()
+            for i in numba.pndindex(in_arr.shape):
+                in_arr[i] = val
+            return None
+    return fill_1
+
+replace_functions_ndarray = {
+    'fill': fill_parallel_impl,
+}
+
 @register_jitable
 def max_checker(arr_size):
     if arr_size == 0:
@@ -1336,6 +1359,16 @@ def replace_func():
                             func_def = get_definition(self.func_ir, expr.func)
                             callname = find_callname(self.func_ir, expr)
                             repl_func = replace_functions_map.get(callname, None)
+                            if (repl_func is None and
+                                len(callname) == 2 and
+                                isinstance(callname[1], ir.Var) and
+                                isinstance(self.typemap[callname[1].name],
+                                           types.npytypes.Array)):
+                                repl_func = replace_functions_ndarray.get(callname[0], None)
+                                if repl_func is not None:
+                                    # Add the array that the method is on to the arg list.
+                                    expr.args.insert(0, callname[1])
+
                             require(repl_func != None)
                             typs = tuple(self.typemap[x.name] for x in expr.args)
                             try:


Test patch:
diff --git a/numba/tests/test_parfors.py b/numba/tests/test_parfors.py
--- a/numba/tests/test_parfors.py
+++ b/numba/tests/test_parfors.py
@@ -1470,6 +1470,24 @@ def test_impl(a, b):
         msg = ("The reshape API may only include one negative argument.")
         self.assertIn(msg, str(raised.exception))
 
+    @skip_unsupported
+    def test_ndarray_fill(self):
+        def test_impl(x):
+            x.fill(7.0)
+            return x
+        x = np.zeros(10)
+        self.check(test_impl, x)
+        self.assertTrue(countParfors(test_impl, (types.Array(types.float64, 1, 'C'),)) == 1)
+
+    @skip_unsupported
+    def test_ndarray_fill2d(self):
+        def test_impl(x):
+            x.fill(7.0)
+            return x
+        x = np.zeros((2,2))
+        self.check(test_impl, x)
+        self.assertTrue(countParfors(test_impl, (types.Array(types.float64, 2, 'C'),)) == 1)
+
     @skip_unsupported
     def test_0d_array(self):
         def test_impl(n):