Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit db5fcc8

Browse filesBrowse files
authored
Merge pull request #12988 from qwhelan/bool_ufunc
ENH: Create boolean and integer ufuncs for isnan, isinf, and isfinite.
2 parents fd105d1 + 5785ca7 commit db5fcc8
Copy full SHA for db5fcc8

File tree

Expand file treeCollapse file tree

6 files changed

+94
-3
lines changed
Filter options
Expand file treeCollapse file tree

6 files changed

+94
-3
lines changed

‎doc/release/1.17.0-notes.rst

Copy file name to clipboardExpand all lines: doc/release/1.17.0-notes.rst
+6Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,12 @@ concatenation.
191191
In some cases where ``np.interp`` would previously return ``np.nan``, it now
192192
returns an appropriate infinity.
193193

194+
Specialized ``np.isnan``, ``np.isinf``, and ``np.isfinite`` ufuncs for bool and int types
195+
-----------------------------------------------------------------------------------------
196+
The boolean and integer types are incapable of storing ``np.nan`` and ``np.inf`` values,
197+
which allows us to provide specialized ufuncs that are up to 250x faster than the current
198+
approach.
199+
194200

195201
Changes
196202
=======

‎numpy/core/code_generators/generate_umath.py

Copy file name to clipboardExpand all lines: numpy/core/code_generators/generate_umath.py
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -827,7 +827,7 @@ def english_upper(s):
827827
Ufunc(1, 1, None,
828828
docstrings.get('numpy.core.umath.isnan'),
829829
None,
830-
TD(inexact, out='?'),
830+
TD(nodatetime_or_obj, out='?'),
831831
),
832832
'isnat':
833833
Ufunc(1, 1, None,
@@ -839,13 +839,13 @@ def english_upper(s):
839839
Ufunc(1, 1, None,
840840
docstrings.get('numpy.core.umath.isinf'),
841841
None,
842-
TD(inexact, out='?'),
842+
TD(nodatetime_or_obj, out='?'),
843843
),
844844
'isfinite':
845845
Ufunc(1, 1, None,
846846
docstrings.get('numpy.core.umath.isfinite'),
847847
None,
848-
TD(inexact, out='?'),
848+
TD(nodatetime_or_obj, out='?'),
849849
),
850850
'signbit':
851851
Ufunc(1, 1, None,

‎numpy/core/src/umath/fast_loop_macros.h

Copy file name to clipboardExpand all lines: numpy/core/src/umath/fast_loop_macros.h
+25Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@
6464
#define IS_UNARY_CONT(tin, tout) (steps[0] == sizeof(tin) && \
6565
steps[1] == sizeof(tout))
6666

67+
#define IS_OUTPUT_CONT(tout) (steps[1] == sizeof(tout))
68+
6769
#define IS_BINARY_REDUCE ((args[0] == args[2])\
6870
&& (steps[0] == steps[2])\
6971
&& (steps[0] == 0))
@@ -82,6 +84,29 @@
8284
steps[2] == sizeof(tout))
8385

8486

87+
/*
88+
* loop with contiguous specialization
89+
* op should be the code storing the result in `tout * out`
90+
* combine with NPY_GCC_OPT_3 to allow autovectorization
91+
* should only be used where its worthwhile to avoid code bloat
92+
*/
93+
#define BASE_OUTPUT_LOOP(tout, op) \
94+
OUTPUT_LOOP { \
95+
tout * out = (tout *)op1; \
96+
op; \
97+
}
98+
#define OUTPUT_LOOP_FAST(tout, op) \
99+
do { \
100+
/* condition allows compiler to optimize the generic macro */ \
101+
if (IS_OUTPUT_CONT(tout)) { \
102+
BASE_OUTPUT_LOOP(tout, op) \
103+
} \
104+
else { \
105+
BASE_OUTPUT_LOOP(tout, op) \
106+
} \
107+
} \
108+
while (0)
109+
85110
/*
86111
* loop with contiguous specialization
87112
* op should be the code working on `tin in` and

‎numpy/core/src/umath/loops.c.src

Copy file name to clipboardExpand all lines: numpy/core/src/umath/loops.c.src
+25Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,19 @@ BOOL__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UN
644644
}
645645

646646

647+
/**begin repeat
648+
* #kind = isnan, isinf, isfinite#
649+
* #func = npy_isnan, npy_isinf, npy_isfinite#
650+
* #val = NPY_FALSE, NPY_FALSE, NPY_TRUE#
651+
**/
652+
NPY_NO_EXPORT void
653+
BOOL_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func))
654+
{
655+
OUTPUT_LOOP_FAST(npy_bool, *out = @val@);
656+
}
657+
658+
/**end repeat**/
659+
647660
/*
648661
*****************************************************************************
649662
** INTEGER LOOPS
@@ -875,6 +888,18 @@ NPY_NO_EXPORT void
875888
}
876889
}
877890

891+
/**begin repeat1
892+
* #kind = isnan, isinf, isfinite#
893+
* #func = npy_isnan, npy_isinf, npy_isfinite#
894+
* #val = NPY_FALSE, NPY_FALSE, NPY_TRUE#
895+
**/
896+
NPY_NO_EXPORT void
897+
@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func))
898+
{
899+
OUTPUT_LOOP_FAST(npy_bool, *out = @val@);
900+
}
901+
/**end repeat1**/
902+
878903
/**end repeat**/
879904

880905
/**begin repeat

‎numpy/core/src/umath/loops.h.src

Copy file name to clipboardExpand all lines: numpy/core/src/umath/loops.h.src
+14Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ BOOL_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED
3838
NPY_NO_EXPORT void
3939
BOOL__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data));
4040

41+
/**begin repeat
42+
* #kind = isnan, isinf, isfinite#
43+
**/
44+
NPY_NO_EXPORT void
45+
BOOL_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func));
46+
/**end repeat**/
47+
4148
/*
4249
*****************************************************************************
4350
** INTEGER LOOPS
@@ -146,6 +153,13 @@ NPY_NO_EXPORT void
146153
NPY_NO_EXPORT void
147154
@S@@TYPE@_lcm(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func));
148155

156+
/**begin repeat2
157+
* #kind = isnan, isinf, isfinite#
158+
**/
159+
NPY_NO_EXPORT void
160+
@S@@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func));
161+
/**end repeat2**/
162+
149163
/**end repeat1**/
150164

151165
/**end repeat**/

‎numpy/core/tests/test_ufunc.py

Copy file name to clipboardExpand all lines: numpy/core/tests/test_ufunc.py
+21Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1915,3 +1915,24 @@ def test_invalid_args(self):
19151915
exc = pytest.raises(TypeError, np.sqrt, None)
19161916
# minimally check the exception text
19171917
assert 'loop of ufunc does not support' in str(exc)
1918+
1919+
@pytest.mark.parametrize('nat', [np.datetime64('nat'), np.timedelta64('nat')])
1920+
def test_nat_is_not_finite(self, nat):
1921+
try:
1922+
assert not np.isfinite(nat)
1923+
except TypeError:
1924+
pass # ok, just not implemented
1925+
1926+
@pytest.mark.parametrize('nat', [np.datetime64('nat'), np.timedelta64('nat')])
1927+
def test_nat_is_nan(self, nat):
1928+
try:
1929+
assert np.isnan(nat)
1930+
except TypeError:
1931+
pass # ok, just not implemented
1932+
1933+
@pytest.mark.parametrize('nat', [np.datetime64('nat'), np.timedelta64('nat')])
1934+
def test_nat_is_not_inf(self, nat):
1935+
try:
1936+
assert not np.isinf(nat)
1937+
except TypeError:
1938+
pass # ok, just not implemented

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.