Skip to content
6 changes: 3 additions & 3 deletions numba/core/typing/builtins.py
Original file line number Diff line number Diff line change
Expand Up @@ -881,7 +881,7 @@ class MinMaxBase(AbstractTemplate):

def _unify_minmax(self, tys):
for ty in tys:
if not isinstance(ty, types.Number):
if not isinstance(ty, (types.Number, types.NPDatetime, types.NPTimedelta)):
return
return self.context.unify_types(*tys)

Expand Down Expand Up @@ -1103,8 +1103,8 @@ class MinValInfer(AbstractTemplate):
def generic(self, args, kws):
assert not kws
assert len(args) == 1
assert isinstance(args[0], (types.DType, types.NumberClass))
return signature(args[0].dtype, *args)
if isinstance(args[0], (types.DType, types.NumberClass)):
return signature(args[0].dtype, *args)


#------------------------------------------------------------------------------
Expand Down
17 changes: 17 additions & 0 deletions numba/core/typing/npdatetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,3 +266,20 @@ class DatetimeCmpGt(DatetimeCmpOp):
@infer_global(operator.ge)
class DatetimeCmpGE(DatetimeCmpOp):
key = operator.ge


@infer_global(npdatetime_helpers.datetime_minimum)
@infer_global(npdatetime_helpers.datetime_maximum)
class DatetimeMinMax(AbstractTemplate):
def generic(self, args, kws):
assert not kws
assert len(args) == 2
error_msg = "DatetimeMinMax requires both arguments to be NPDatetime type or both arguments to be NPTimedelta types"
assert isinstance(args[0], (types.NPDatetime, types.NPTimedelta)), error_msg
if isinstance(args[0], types.NPDatetime):
if not isinstance(args[1], types.NPDatetime):
raise TypeError(error_msg)
else:
if not isinstance(args[1], types.NPTimedelta):
raise TypeError(error_msg)
return signature(args[0], *args)
16 changes: 14 additions & 2 deletions numba/cpython/builtins.py
Original file line number Diff line number Diff line change
Expand Up @@ -481,13 +481,14 @@ def get_type_min_value(typ):
@lower_builtin(get_type_min_value, types.DType)
def lower_get_type_min_value(context, builder, sig, args):
typ = sig.args[0].dtype
bw = typ.bitwidth

if isinstance(typ, types.Integer):
bw = typ.bitwidth
lty = ir.IntType(bw)
val = typ.minval
res = ir.Constant(lty, val)
elif isinstance(typ, types.Float):
bw = typ.bitwidth
if bw == 32:
lty = ir.FloatType()
elif bw == 64:
Expand All @@ -496,19 +497,25 @@ def lower_get_type_min_value(context, builder, sig, args):
raise NotImplementedError("llvmlite only supports 32 and 64 bit floats")
npty = getattr(np, 'float{}'.format(bw))
res = ir.Constant(lty, -np.inf)
elif isinstance(typ, (types.NPDatetime, types.NPTimedelta)):
bw = 64
lty = ir.IntType(bw)
val = types.int64.minval + 1 # minval is NaT, so minval + 1 is the smallest value
res = ir.Constant(lty, val)
return impl_ret_untracked(context, builder, lty, res)

@lower_builtin(get_type_max_value, types.NumberClass)
@lower_builtin(get_type_max_value, types.DType)
def lower_get_type_max_value(context, builder, sig, args):
typ = sig.args[0].dtype
bw = typ.bitwidth

if isinstance(typ, types.Integer):
bw = typ.bitwidth
lty = ir.IntType(bw)
val = typ.maxval
res = ir.Constant(lty, val)
elif isinstance(typ, types.Float):
bw = typ.bitwidth
if bw == 32:
lty = ir.FloatType()
elif bw == 64:
Expand All @@ -517,6 +524,11 @@ def lower_get_type_max_value(context, builder, sig, args):
raise NotImplementedError("llvmlite only supports 32 and 64 bit floats")
npty = getattr(np, 'float{}'.format(bw))
res = ir.Constant(lty, np.inf)
elif isinstance(typ, (types.NPDatetime, types.NPTimedelta)):
bw = 64
lty = ir.IntType(bw)
val = types.int64.maxval
res = ir.Constant(lty, val)
return impl_ret_untracked(context, builder, lty, res)

# -----------------------------------------------------------------------------
Expand Down
6 changes: 6 additions & 0 deletions numba/np/npdatetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -847,3 +847,9 @@ def impl(x):
return np.int64(-2)
return np.int64(x)
return impl


lower_builtin(npdatetime_helpers.datetime_minimum, types.NPDatetime, types.NPDatetime)(datetime_minimum_impl)
lower_builtin(npdatetime_helpers.datetime_minimum, types.NPTimedelta, types.NPTimedelta)(datetime_minimum_impl)
lower_builtin(npdatetime_helpers.datetime_maximum, types.NPDatetime, types.NPDatetime)(datetime_maximum_impl)
lower_builtin(npdatetime_helpers.datetime_maximum, types.NPTimedelta, types.NPTimedelta)(datetime_maximum_impl)
8 changes: 8 additions & 0 deletions numba/np/npdatetime_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,11 @@ def get_best_unit(unit_a, unit_b):
if b > a:
return unit_b
return unit_a


def datetime_minimum(a, b):
pass


def datetime_maximum(a, b):
pass
63 changes: 46 additions & 17 deletions numba/parfors/parfor.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,16 @@
from contextlib import contextmanager
import operator

from llvmlite import ir as lir
from numba.core.imputils import impl_ret_untracked
import numba.core.ir
from numba.core import types, typing, utils, errors, ir, analysis, postproc, rewrites, typeinfer, config, ir_utils
from numba import prange, pndindex
from numba.np.numpy_support import as_dtype
from numba.np.npdatetime_helpers import datetime_minimum, datetime_maximum
from numba.np.numpy_support import as_dtype, numpy_version
from numba.core.typing.templates import infer_global, AbstractTemplate
from numba.stencils.stencilparfor import StencilPass
from numba.core.extending import register_jitable
from numba.core.extending import register_jitable, lower_builtin

from numba.core.ir_utils import (
mk_unique_var,
Expand Down Expand Up @@ -121,20 +124,31 @@ class internal_prange(object):
def __new__(cls, *args):
return range(*args)


def min_parallel_impl(return_type, arg):
# XXX: use prange for 1D arrays since pndindex returns a 1-tuple instead of
# integer. This causes type and fusion issues.
if arg.ndim == 0:
def min_1(in_arr):
return in_arr[()]
elif arg.ndim == 1:
def min_1(in_arr):
numba.parfors.parfor.init_prange()
min_checker(len(in_arr))
val = numba.cpython.builtins.get_type_max_value(in_arr.dtype)
for i in numba.parfors.parfor.internal_prange(len(in_arr)):
val = min(val, in_arr[i])
return val
if isinstance(arg.dtype, (types.NPDatetime, types.NPTimedelta)):
# NaT is always returned if it is in the array
def min_1(in_arr):
numba.parfors.parfor.init_prange()
min_checker(len(in_arr))
val = numba.cpython.builtins.get_type_max_value(in_arr.dtype)
for i in numba.parfors.parfor.internal_prange(len(in_arr)):
val = datetime_minimum(val, in_arr[i])
return val
else:
def min_1(in_arr):
numba.parfors.parfor.init_prange()
min_checker(len(in_arr))
val = numba.cpython.builtins.get_type_max_value(in_arr.dtype)
for i in numba.parfors.parfor.internal_prange(len(in_arr)):
val = min(val, in_arr[i])
return val
else:
def min_1(in_arr):
numba.parfors.parfor.init_prange()
Expand All @@ -150,13 +164,23 @@ def max_parallel_impl(return_type, arg):
def max_1(in_arr):
return in_arr[()]
elif arg.ndim == 1:
def max_1(in_arr):
numba.parfors.parfor.init_prange()
max_checker(len(in_arr))
val = numba.cpython.builtins.get_type_min_value(in_arr.dtype)
for i in numba.parfors.parfor.internal_prange(len(in_arr)):
val = max(val, in_arr[i])
return val
if isinstance(arg.dtype, (types.NPDatetime, types.NPTimedelta)):
# NaT is always returned if it is in the array
def max_1(in_arr):
numba.parfors.parfor.init_prange()
max_checker(len(in_arr))
val = numba.cpython.builtins.get_type_min_value(in_arr.dtype)
for i in numba.parfors.parfor.internal_prange(len(in_arr)):
val = datetime_maximum(val, in_arr[i])
return val
else:
def max_1(in_arr):
numba.parfors.parfor.init_prange()
max_checker(len(in_arr))
val = numba.cpython.builtins.get_type_min_value(in_arr.dtype)
for i in numba.parfors.parfor.internal_prange(len(in_arr)):
val = max(val, in_arr[i])
return val
else:
def max_1(in_arr):
numba.parfors.parfor.init_prange()
Expand Down Expand Up @@ -3608,7 +3632,12 @@ def supported_reduction(x, func_ir):
return True
if x.op == 'call':
callname = guard(find_callname, func_ir, x)
if callname == ('max', 'builtins') or callname == ('min', 'builtins'):
if callname in [
('max', 'builtins'),
('min', 'builtins'),
('datetime_minimum', 'numba.np.npdatetime_helpers'),
('datetime_maximum', 'numba.np.npdatetime_helpers'),
]:
return True
return False

Expand Down
Loading