Thanks to visit codestin.com
Credit goes to github.com

Skip to content

ENH: Create boolean and integer ufuncs for isnan, isinf, and isfinite. #12988

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions doc/release/1.17.0-notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,12 @@ thereby saving a level of indentation
In some cases where ``np.interp`` would previously return ``np.nan``, it now
returns an appropriate infinity.

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


Changes
=======
Expand Down
6 changes: 3 additions & 3 deletions numpy/core/code_generators/generate_umath.py
Original file line number Diff line number Diff line change
Expand Up @@ -827,7 +827,7 @@ def english_upper(s):
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.isnan'),
None,
TD(inexact, out='?'),
TD(nodatetime_or_obj, out='?'),
),
'isnat':
Ufunc(1, 1, None,
Expand All @@ -839,13 +839,13 @@ def english_upper(s):
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.isinf'),
None,
TD(inexact, out='?'),
TD(nodatetime_or_obj, out='?'),
),
'isfinite':
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.isfinite'),
None,
TD(inexact, out='?'),
TD(nodatetime_or_obj, out='?'),
),
'signbit':
Ufunc(1, 1, None,
Expand Down
25 changes: 25 additions & 0 deletions numpy/core/src/umath/fast_loop_macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@
#define IS_UNARY_CONT(tin, tout) (steps[0] == sizeof(tin) && \
steps[1] == sizeof(tout))

#define IS_OUTPUT_CONT(tout) (steps[1] == sizeof(tout))

#define IS_BINARY_REDUCE ((args[0] == args[2])\
&& (steps[0] == steps[2])\
&& (steps[0] == 0))
Expand All @@ -82,6 +84,29 @@
steps[2] == sizeof(tout))


/*
* loop with contiguous specialization
* op should be the code storing the result in `tout * out`
* combine with NPY_GCC_OPT_3 to allow autovectorization
* should only be used where its worthwhile to avoid code bloat
*/
#define BASE_OUTPUT_LOOP(tout, op) \
OUTPUT_LOOP { \
tout * out = (tout *)op1; \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can omit the space after *.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in #13208

op; \
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blank line between macros.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in #13208

#define OUTPUT_LOOP_FAST(tout, op) \
do { \
/* condition allows compiler to optimize the generic macro */ \
if (IS_OUTPUT_CONT(tout)) { \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be indented.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this matches the existing macros, sadly

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see #13208 - I have fixed the indentation for all macros in this file

BASE_OUTPUT_LOOP(tout, op) \
} \
else { \
BASE_OUTPUT_LOOP(tout, op) \
} \
} \
while (0)

/*
* loop with contiguous specialization
* op should be the code working on `tin in` and
Expand Down
25 changes: 25 additions & 0 deletions numpy/core/src/umath/loops.c.src
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,19 @@ BOOL__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UN
}


/**begin repeat
* #kind = isnan, isinf, isfinite#
* #func = npy_isnan, npy_isinf, npy_isfinite#
* #val = NPY_FALSE, NPY_FALSE, NPY_TRUE#
**/
NPY_NO_EXPORT void
BOOL_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func))
{
OUTPUT_LOOP_FAST(npy_bool, *out = @val@);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just pass the value and move the assignment to the macro where out is declared?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was entirely to preserve calling convention to be similar to the other macros - I've implemented your suggestion in #13208

}

/**end repeat**/

/*
*****************************************************************************
** INTEGER LOOPS
Expand Down Expand Up @@ -875,6 +888,18 @@ NPY_NO_EXPORT void
}
}

/**begin repeat1
* #kind = isnan, isinf, isfinite#
* #func = npy_isnan, npy_isinf, npy_isfinite#
* #val = NPY_FALSE, NPY_FALSE, NPY_TRUE#
**/
NPY_NO_EXPORT void
@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func))
{
OUTPUT_LOOP_FAST(npy_bool, *out = @val@);
}
/**end repeat1**/

/**end repeat**/

/**begin repeat
Expand Down
14 changes: 14 additions & 0 deletions numpy/core/src/umath/loops.h.src
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ BOOL_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED
NPY_NO_EXPORT void
BOOL__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data));

/**begin repeat
* #kind = isnan, isinf, isfinite#
**/
NPY_NO_EXPORT void
BOOL_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func));
/**end repeat**/

/*
*****************************************************************************
** INTEGER LOOPS
Expand Down Expand Up @@ -146,6 +153,13 @@ NPY_NO_EXPORT void
NPY_NO_EXPORT void
@S@@TYPE@_lcm(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func));

/**begin repeat2
* #kind = isnan, isinf, isfinite#
**/
NPY_NO_EXPORT void
@S@@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func));
/**end repeat2**/

/**end repeat1**/

/**end repeat**/
Expand Down
21 changes: 21 additions & 0 deletions numpy/core/tests/test_ufunc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1915,3 +1915,24 @@ def test_invalid_args(self):
exc = pytest.raises(TypeError, np.sqrt, None)
# minimally check the exception text
assert 'loop of ufunc does not support' in str(exc)

@pytest.mark.parametrize('nat', [np.datetime64('nat'), np.timedelta64('nat')])
def test_nat_is_not_finite(self, nat):
try:
assert not np.isfinite(nat)
except TypeError:
pass # ok, just not implemented

@pytest.mark.parametrize('nat', [np.datetime64('nat'), np.timedelta64('nat')])
def test_nat_is_nan(self, nat):
try:
assert np.isnan(nat)
except TypeError:
pass # ok, just not implemented

@pytest.mark.parametrize('nat', [np.datetime64('nat'), np.timedelta64('nat')])
def test_nat_is_not_inf(self, nat):
try:
assert not np.isinf(nat)
except TypeError:
pass # ok, just not implemented