Repository: numba/numba

Issue:
  Unexpected behavior taking the minimum of an array of `np.inf` when `parallel=True`.
## Reporting a bug

- [x] I am using the latest released version of Numba (most recent is visible in
 the change log (https://github.com/numba/numba/blob/master/CHANGE_LOG).
- [x] I have included below a minimal working reproducer (if you are unsure how
 to write one see http://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports).

When using `np.min` on an array with all infinite values, I expect `np.inf` to be returned. This happens correctly when the function is compiled in nopython mode. When `parallel=True` however, the minimum is returned as the largest `float64` value  

In the code snippet below the first two assertion pass on my machine but the third fails.

```python
import numba
import numpy as np

@numba.njit()
def min(a):
    return a.min()

@numba.njit(parallel=True)
def pmin(a):
    return a.min()

infs = np.array([np.inf, np.inf])

assert min(infs) == np.inf
assert pmin(infs) == np.finfo(np.float64).max
assert min(infs) == pmin(infs)
```





Discussion:
@mbattifarano thanks for reporting this, I can reproduce this on the current master `0.50.0dev0-179-g86e600d0ec` and this might be a bug related to the parallel accelerator. @DrTodd13 could you have a look?
It's a simple impl bug:
https://github.com/numba/numba/blob/86e600d0ec867402cb4f41100295878586f2a019/numba/parfors/parfor.py#L123-L145
@stuartarchibald how about we initialize `val` then to `inf` in the `float` case? What about the `max` impl.?
@esc @stuartarchibald It seems like initializing val to inf for float should work.  Can you try that Val?

Resolving patch:
diff --git a/numba/cpython/builtins.py b/numba/cpython/builtins.py
--- a/numba/cpython/builtins.py
+++ b/numba/cpython/builtins.py
@@ -415,24 +415,14 @@ def bool_none(x):
 
 def get_type_max_value(typ):
     if isinstance(typ, types.Float):
-        bw = typ.bitwidth
-        if bw == 32:
-            return np.finfo(np.float32).max
-        if bw == 64:
-            return np.finfo(np.float64).max
-        raise NotImplementedError("Unsupported floating point type")
+        return np.inf
     if isinstance(typ, types.Integer):
         return typ.maxval
     raise NotImplementedError("Unsupported type")
 
 def get_type_min_value(typ):
     if isinstance(typ, types.Float):
-        bw = typ.bitwidth
-        if bw == 32:
-            return np.finfo(np.float32).min
-        if bw == 64:
-            return np.finfo(np.float64).min
-        raise NotImplementedError("Unsupported floating point type")
+        return -np.inf
     if isinstance(typ, types.Integer):
         return typ.minval
     raise NotImplementedError("Unsupported type")
@@ -455,7 +445,7 @@ def lower_get_type_min_value(context, builder, sig, args):
         else:
             raise NotImplementedError("llvmlite only supports 32 and 64 bit floats")
         npty = getattr(np, 'float{}'.format(bw))
-        res = ir.Constant(lty, np.finfo(npty).min)
+        res = ir.Constant(lty, -np.inf)
     return impl_ret_untracked(context, builder, lty, res)
 
 @lower_builtin(get_type_max_value, types.NumberClass)
@@ -476,7 +466,7 @@ def lower_get_type_max_value(context, builder, sig, args):
         else:
             raise NotImplementedError("llvmlite only supports 32 and 64 bit floats")
         npty = getattr(np, 'float{}'.format(bw))
-        res = ir.Constant(lty, np.finfo(npty).max)
+        res = ir.Constant(lty, np.inf)
     return impl_ret_untracked(context, builder, lty, res)
 
 # -----------------------------------------------------------------------------
diff --git a/numba/parfors/parfor_lowering.py b/numba/parfors/parfor_lowering.py
--- a/numba/parfors/parfor_lowering.py
+++ b/numba/parfors/parfor_lowering.py
@@ -90,6 +90,9 @@ def _lower_parfor_parallel(lowerer, parfor):
         parfor, parfor.params)
     parfor_redvars, parfor_reddict = numba.parfors.parfor.get_parfor_reductions(
         lowerer.func_ir, parfor, parfor.params, lowerer.fndesc.calltypes)
+    if config.DEBUG_ARRAY_OPT:
+        print("parfor_redvars:", parfor_redvars)
+        print("parfor_reddict:", parfor_reddict)
 
     # init reduction array allocation here.
     nredvars = len(parfor_redvars)
@@ -136,7 +139,6 @@ def _lower_parfor_parallel(lowerer, parfor):
                 name='num_threads',
             )
 
-
             size_var_list = [num_threads_var]
 
             # If this is a reduction over an array...
@@ -213,6 +215,25 @@ def _lower_parfor_parallel(lowerer, parfor):
             else:
                 redtoset = redvar
 
+                if config.DEBUG_ARRAY_OPT_RUNTIME:
+                    res_print_str = "res_print1 for redvar " + str(redvar) + ":"
+                    strconsttyp = types.StringLiteral(res_print_str)
+
+                    lhs = pfbdr.make_const_variable(
+                        cval=res_print_str,
+                        typ=strconsttyp,
+                        name="str_const",
+                    )
+
+                    res_print = ir.Print(args=[lhs, redvar],
+                                         vararg=None, loc=loc)
+                    lowerer.fndesc.calltypes[res_print] = signature(types.none,
+                                                             typemap[lhs.name],
+                                                             typemap[redvar.name])
+                    print("res_print_redvar", res_print)
+                    lowerer.lower_inst(res_print)
+
+
             # For each thread, initialize the per-worker reduction array to the current reduction array value.
             for j in range(thread_count):
                 index_var = pfbdr.make_const_variable(
@@ -304,7 +325,7 @@ def _lower_parfor_parallel(lowerer, parfor):
                 res_print_str = "res_print"
                 strconsttyp = types.StringLiteral(res_print_str)
 
-                lhs = pfbldr.make_const_variable(
+                lhs = pfbdr.make_const_variable(
                     cval=res_print_str,
                     typ=strconsttyp,
                     name="str_const",
@@ -1236,7 +1257,7 @@ def _create_gufunc_for_parfor_body(
                     # Make print node
                     print_node = ir.Print(args=[lhs, inst.target], vararg=None, loc=loc)
                     new_block.append(print_node)
-                    sig = numba.typing.signature(types.none,
+                    sig = numba.core.typing.signature(types.none,
                                            typemap[lhs.name],
                                            typemap[inst.target.name])
                     lowerer.fndesc.calltypes[print_node] = sig


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
@@ -1014,12 +1014,15 @@ def test_impl2(A):
         A = np.random.ranf(n)
         B = np.random.randint(10, size=n).astype(np.int32)
         C = np.random.ranf((n, n))  # test multi-dimensional array
+        D = np.array([np.inf, np.inf])
         self.check(test_impl1, A)
         self.check(test_impl1, B)
         self.check(test_impl1, C)
+        self.check(test_impl1, D)
         self.check(test_impl2, A)
         self.check(test_impl2, B)
         self.check(test_impl2, C)
+        self.check(test_impl2, D)
 
         # checks that 0d array input raises
         msg = ("zero-size array to reduction operation "
@@ -1042,12 +1045,15 @@ def test_impl2(A):
         A = np.random.ranf(n)
         B = np.random.randint(10, size=n).astype(np.int32)
         C = np.random.ranf((n, n))  # test multi-dimensional array
+        D = np.array([-np.inf, -np.inf])
         self.check(test_impl1, A)
         self.check(test_impl1, B)
         self.check(test_impl1, C)
+        self.check(test_impl1, D)
         self.check(test_impl2, A)
         self.check(test_impl2, B)
         self.check(test_impl2, C)
+        self.check(test_impl2, D)
 
         # checks that 0d array input raises
         msg = ("zero-size array to reduction operation "