Skip to content
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

CLN: Add float and double versions of BN_NAN #373

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
CLN: Add float and double versions of BN_NAN
qwhelan committed Jan 24, 2021
commit 58463a00cac849c448154b79977f582b911c3ab6
10 changes: 6 additions & 4 deletions bottleneck/include/bottleneck.h
Original file line number Diff line number Diff line change
@@ -87,10 +87,12 @@ static inline float __bn_nanf(void) {
return __bint.__f;
}

#define BN_INFINITYF __bn_inff()
#define BN_NANF __bn_nanf()
#define BN_INFINITY ((npy_double)BN_INFINITYF)
#define BN_NAN ((npy_double)BN_NANF)
#define BN_INFINITYF __bn_inff()
#define BN_NANF __bn_nanf()
#define BN_INFINITY ((npy_double)BN_INFINITYF)
#define BN_NAN ((npy_double)BN_NANF)
#define BN_NAN_float32 BN_NANF
#define BN_NAN_float64 BN_NAN

/* WIRTH ----------------------------------------------------------------- */

42 changes: 21 additions & 21 deletions bottleneck/src/move_template.c
Original file line number Diff line number Diff line change
@@ -83,15 +83,15 @@ MOVE(move_sum, DTYPE0) {
asum += ai;
count += 1;
}
YI(DTYPE0) = BN_NAN;
YI(DTYPE0) = BN_NAN_DTYPE0;
}
WHILE1 {
const npy_DTYPE0 ai = AI(DTYPE0);
if (!bn_isnan(ai)) {
asum += ai;
count += 1;
}
YI(DTYPE0) = count >= min_count ? asum : BN_NAN;
YI(DTYPE0) = count >= min_count ? asum : BN_NAN_DTYPE0;
}
WHILE2 {
const npy_DTYPE0 ai = AI(DTYPE0);
@@ -109,7 +109,7 @@ MOVE(move_sum, DTYPE0) {
count--;
}
}
YI(DTYPE0) = count >= min_count ? asum : BN_NAN;
YI(DTYPE0) = count >= min_count ? asum : BN_NAN_DTYPE0;
}
NEXT2
}
@@ -127,7 +127,7 @@ MOVE(move_sum, DTYPE0) {
asum = 0;
WHILE0 {
asum += AI(DTYPE0);
YI(DTYPE1) = BN_NAN;
YI(DTYPE1) = BN_NAN_DTYPE1;
}
WHILE1 {
asum += AI(DTYPE0);
@@ -162,15 +162,15 @@ MOVE(move_mean, DTYPE0) {
asum += ai;
count += 1;
}
YI(DTYPE0) = BN_NAN;
YI(DTYPE0) = BN_NAN_DTYPE0;
}
WHILE1 {
const npy_DTYPE0 ai = AI(DTYPE0);
if (!bn_isnan(ai)) {
asum += ai;
count += 1;
}
YI(DTYPE0) = count >= min_count ? asum / count : BN_NAN;
YI(DTYPE0) = count >= min_count ? asum / count : BN_NAN_DTYPE0;
}
count_inv = 1.0 / count;
WHILE2 {
@@ -191,7 +191,7 @@ MOVE(move_mean, DTYPE0) {
count_inv = 1.0 / count;
}
}
YI(DTYPE0) = count >= min_count ? asum * count_inv : BN_NAN;
YI(DTYPE0) = count >= min_count ? asum * count_inv : BN_NAN_DTYPE0;
}
NEXT2
}
@@ -209,7 +209,7 @@ MOVE(move_mean, DTYPE0) {
asum = 0;
WHILE0 {
asum += AI(DTYPE0);
YI(DTYPE1) = BN_NAN;
YI(DTYPE1) = BN_NAN_DTYPE1;
}
WHILE1 {
asum += AI(DTYPE0);
@@ -249,7 +249,7 @@ MOVE(NAME, DTYPE0) {
amean += delta / count;
assqdm += delta * (ai - amean);
}
YI(DTYPE0) = BN_NAN;
YI(DTYPE0) = BN_NAN_DTYPE0;
}
WHILE1 {
const npy_DTYPE0 ai = AI(DTYPE0);
@@ -265,7 +265,7 @@ MOVE(NAME, DTYPE0) {
}
yi = FUNC(assqdm / (count - ddof));
} else {
yi = BN_NAN;
yi = BN_NAN_DTYPE0;
}
YI(DTYPE0) = yi;
}
@@ -310,7 +310,7 @@ MOVE(NAME, DTYPE0) {
}
yi = FUNC(assqdm * ddof_inv);
} else {
yi = BN_NAN;
yi = BN_NAN_DTYPE0;
}
YI(DTYPE0) = yi;
}
@@ -335,7 +335,7 @@ MOVE(NAME, DTYPE0) {
const npy_DTYPE1 delta = ai - amean;
amean += delta / (INDEX + 1);
assqdm += delta * (ai - amean);
YI(DTYPE1) = BN_NAN;
YI(DTYPE1) = BN_NAN_DTYPE1;
}
WHILE1 {
const npy_DTYPE1 ai = AI(DTYPE0);
@@ -457,16 +457,16 @@ MOVE(NAME, DTYPE0) {
extreme_pair->death = window;
WHILE0 {
MACRO_FLOAT(DTYPE0,
BN_NAN, )
BN_NAN_DTYPE0, )
}
WHILE1 {
MACRO_FLOAT(DTYPE0,
count >= min_count ? VALUE : BN_NAN, )
count >= min_count ? VALUE : BN_NAN_DTYPE0, )
}
WHILE2 {
MACRO_FLOAT(
DTYPE0,
count >= min_count ? VALUE : BN_NAN,
count >= min_count ? VALUE : BN_NAN_DTYPE0,
const npy_DTYPE0 aold = AOLD(DTYPE0);
if (!bn_isnan(aold)) count--;
if (extreme_pair->death == INDEX) {
@@ -501,7 +501,7 @@ MOVE(NAME, DTYPE0) {
WHILE0 {
MACRO_INT(DTYPE0,
DTYPE1,
BN_NAN, )
BN_NAN_DTYPE1, )
}
WHILE1 {
MACRO_INT(DTYPE0,
@@ -625,7 +625,7 @@ MOVE_MAIN(move_median, 0)
} \
} \
if (n < min_count) { \
r = BN_NAN; \
r = BN_NAN_##dtype1; \
} else if (n == 1) { \
r = 0.0; \
} else { \
@@ -634,7 +634,7 @@ MOVE_MAIN(move_median, 0)
r = 2.0 * (r - 0.5); \
} \
} else { \
r = BN_NAN; \
r = BN_NAN_##dtype1; \
}

/* dtype = [['float64', 'float64'], ['float32', 'float32']] */
@@ -643,7 +643,7 @@ MOVE(move_rank, DTYPE0) {
BN_BEGIN_ALLOW_THREADS
WHILE {
WHILE0 {
YI(DTYPE1) = BN_NAN;
YI(DTYPE1) = BN_NAN_DTYPE1;
}
WHILE1 {
MOVE_RANK(DTYPE0, DTYPE1, 0)
@@ -668,7 +668,7 @@ MOVE(move_rank, DTYPE0) {
BN_BEGIN_ALLOW_THREADS
WHILE {
WHILE0 {
YI(DTYPE1) = BN_NAN;
YI(DTYPE1) = BN_NAN_DTYPE1;
}
WHILE1 {
const npy_DTYPE0 ai = AI(DTYPE0);
@@ -683,7 +683,7 @@ MOVE(move_rank, DTYPE0) {
}
}
if (INDEX < min_count - 1) {
r = BN_NAN;
r = BN_NAN_DTYPE1;
} else if (INDEX == 0) {
r = 0.0;
} else {
2 changes: 1 addition & 1 deletion bottleneck/src/nonreduce_axis_template.c
Original file line number Diff line number Diff line change
@@ -363,7 +363,7 @@ NRA(push, DTYPE0) {
BN_BEGIN_ALLOW_THREADS
WHILE {
index = 0;
ai_last = BN_NAN;
ai_last = BN_NAN_DTYPE0;
FOR {
ai = AI(DTYPE0);
if (!bn_isnan(ai)) {
48 changes: 25 additions & 23 deletions bottleneck/src/reduce_template.c
Original file line number Diff line number Diff line change
@@ -228,15 +228,15 @@ REDUCE_ALL(nanmean, DTYPE0) {
if (count > 0) {
return PyFloat_FromDouble(asum / count);
} else {
return PyFloat_FromDouble(BN_NAN);
return PyFloat_FromDouble(BN_NAN_DTYPE0);
}
}

REDUCE_ONE(nanmean, DTYPE0) {
INIT_ONE(DTYPE0, DTYPE0)
BN_BEGIN_ALLOW_THREADS
if (LENGTH == 0) {
FILL_Y(BN_NAN)
FILL_Y(BN_NAN_DTYPE0)
} else {
WHILE {
Py_ssize_t count = 0;
@@ -251,7 +251,7 @@ REDUCE_ONE(nanmean, DTYPE0) {
if (count > 0) {
asum /= count;
} else {
asum = BN_NAN;
asum = BN_NAN_DTYPE0;
}
YPP = asum;
NEXT
@@ -279,15 +279,15 @@ REDUCE_ALL(nanmean, DTYPE0) {
if (total_length > 0) {
return PyFloat_FromDouble(asum / total_length);
} else {
return PyFloat_FromDouble(BN_NAN);
return PyFloat_FromDouble(BN_NAN_DTYPE1);
}
}

REDUCE_ONE(nanmean, DTYPE0) {
INIT_ONE(DTYPE1, DTYPE1)
BN_BEGIN_ALLOW_THREADS
if (LENGTH == 0) {
FILL_Y(BN_NAN)
FILL_Y(BN_NAN_DTYPE1)
} else {
WHILE {
npy_DTYPE1 asum = 0;
@@ -297,7 +297,7 @@ REDUCE_ONE(nanmean, DTYPE0) {
if (LENGTH > 0) {
asum /= LENGTH;
} else {
asum = BN_NAN;
asum = BN_NAN_DTYPE1;
}
YPP = asum;
NEXT
@@ -346,7 +346,7 @@ REDUCE_ALL(NAME, DTYPE0) {
}
out = FUNC(asum / (count - ddof));
} else {
out = BN_NAN;
out = BN_NAN_DTYPE0;
}
BN_END_ALLOW_THREADS
return PyFloat_FromDouble(out);
@@ -356,7 +356,7 @@ REDUCE_ONE(NAME, DTYPE0) {
INIT_ONE(DTYPE0, DTYPE0)
BN_BEGIN_ALLOW_THREADS
if (LENGTH == 0) {
FILL_Y(BN_NAN)
FILL_Y(BN_NAN_DTYPE0)
} else {
WHILE {
Py_ssize_t count = 0;
@@ -380,7 +380,7 @@ REDUCE_ONE(NAME, DTYPE0) {
}
asum = FUNC(asum / (count - ddof));
} else {
asum = BN_NAN;
asum = BN_NAN_DTYPE0;
}
YPP = asum;
NEXT
@@ -418,7 +418,7 @@ REDUCE_ALL(NAME, DTYPE0) {
}
out = FUNC(asum / (size - ddof));
} else {
out = BN_NAN;
out = BN_NAN_DTYPE1;
}
BN_END_ALLOW_THREADS
return PyFloat_FromDouble(out);
@@ -430,7 +430,7 @@ REDUCE_ONE(NAME, DTYPE0) {
const npy_DTYPE1 length_inv = 1.0 / LENGTH;
const npy_DTYPE1 length_ddof_inv = 1.0 / (LENGTH - ddof);
if (LENGTH == 0) {
FILL_Y(BN_NAN)
FILL_Y(BN_NAN_DTYPE1)
} else {
WHILE {
npy_DTYPE1 asum = 0;
@@ -446,7 +446,7 @@ REDUCE_ONE(NAME, DTYPE0) {
}
asum = FUNC(asum * length_ddof_inv);
} else {
asum = BN_NAN;
asum = BN_NAN_DTYPE1;
}
YPP = asum;
NEXT
@@ -537,7 +537,7 @@ REDUCE_ALL(NAME, DTYPE0) {
}
}
if (is_allnan) {
extreme = BN_NAN;
extreme = BN_NAN_DTYPE0;
}
BN_END_ALLOW_THREADS
return PyFloat_FromDouble(extreme);
@@ -591,9 +591,9 @@ REDUCE_ONE(NAME, DTYPE0) {
}
for (npy_intp k = 0; k < LOOP_SIZE; k++) {
if (allnans[k]) {
py[k] = BN_NAN;
py[k] = BN_NAN_DTYPE0;
} else {
py[k] = extremes[k];
py[k] = extremes[i];
}
}
free(extremes);
@@ -642,7 +642,7 @@ REDUCE_ONE(NAME, DTYPE0) {
}

if (is_allnan) {
extreme = BN_NAN;
extreme = BN_NAN_DTYPE0;
}
YPP = extreme;
NEXT
@@ -658,7 +658,9 @@ REDUCE_ONE(NAME, DTYPE0) {
is_allnan = 0;
}
}
if (is_allnan) extreme = BN_NAN;
if (is_allnan) {
extreme = BN_NAN_DTYPE0;
}
YPP = extreme;
NEXT
}
@@ -1012,7 +1014,7 @@ REDUCE_MAIN(ss, 0)
} \
} \
if (l != LENGTH) { \
med = BN_NAN; \
med = BN_NAN_##dtype; \
goto done; \
} \
k = LENGTH >> 1; \
@@ -1055,7 +1057,7 @@ REDUCE_MAIN(ss, 0)
l = 0; \
r = n - 1; \
if (n == 0) { \
med = BN_NAN; \
med = BN_NAN_##dtype; \
goto done; \
} \
PARTITION(dtype) \
@@ -1077,7 +1079,7 @@ REDUCE_ALL(NAME, DTYPE0) {
INIT_ALL_RAVEL_ANY_ORDER
BN_BEGIN_ALLOW_THREADS
if (LENGTH == 0) {
med = BN_NAN;
med = BN_NAN_DTYPE1;
} else {
BUFFER_NEW(DTYPE0, LENGTH)
FUNC(DTYPE0)
@@ -1093,7 +1095,7 @@ REDUCE_ONE(NAME, DTYPE0) {
INIT_ONE(DTYPE1, DTYPE1)
BN_BEGIN_ALLOW_THREADS
if (LENGTH == 0) {
FILL_Y(BN_NAN)
FILL_Y(BN_NAN_DTYPE1)
} else {
npy_DTYPE1 med = BN_NAN;
BUFFER_NEW(DTYPE0, LENGTH)
@@ -1118,7 +1120,7 @@ REDUCE_ALL(median, DTYPE0) {
INIT_ALL_RAVEL_ANY_ORDER
BN_BEGIN_ALLOW_THREADS
if (LENGTH == 0) {
med = BN_NAN;
med = BN_NAN_DTYPE1;
} else {
BUFFER_NEW(DTYPE0, LENGTH)
MEDIAN_INT(DTYPE0)
@@ -1134,7 +1136,7 @@ REDUCE_ONE(median, DTYPE0) {
INIT_ONE(DTYPE1, DTYPE1)
BN_BEGIN_ALLOW_THREADS
if (LENGTH == 0) {
FILL_Y(BN_NAN)
FILL_Y(BN_NAN_DTYPE1)
} else {
npy_DTYPE1 med;
BUFFER_NEW(DTYPE0, LENGTH)
54 changes: 38 additions & 16 deletions bottleneck/tests/list_input_test.py
Original file line number Diff line number Diff line change
@@ -2,12 +2,13 @@

import warnings

import hypothesis
import numpy as np
import pytest
from numpy.testing import assert_array_almost_equal

import bottleneck as bn
from .util import DTYPES
from .util import DTYPES, get_functions, hy_lists


def lists(dtypes=DTYPES):
@@ -27,25 +28,46 @@ def lists(dtypes=DTYPES):
yield a.astype(dtype).tolist()


@pytest.mark.parametrize("func", bn.get_functions("all"), ids=lambda x: x.__name__)
def test_list_input(func):
@hypothesis.given(input_list=hy_lists())
@pytest.mark.parametrize(
"func", get_functions("all"), ids=lambda x: x.__name__,
)
def test_list_input(func, input_list) -> None:
"""Test that bn.xxx gives the same output as bn.slow.xxx for list input."""
msg = "\nfunc %s | input %s (%s) | shape %s\n"
msg += "\nInput array:\n%s\n"
name = func.__name__
if name == "replace":
return
func0 = eval("bn.slow.%s" % name)
for i, a in enumerate(lists()):
with warnings.catch_warnings():
warnings.simplefilter("ignore")
try:
actual = func(a)
desired = func0(a)
except TypeError:
actual = func(a, 2)
desired = func0(a, 2)
a = np.array(a)
tup = (name, "a" + str(i), str(a.dtype), str(a.shape), a)
err_msg = msg % tup
assert_array_almost_equal(actual, desired, err_msg=err_msg)
with warnings.catch_warnings():
warnings.simplefilter("ignore")

actual_raised = False
desired_raised = False

try:
if any(x in func.__name__ for x in ["move", "partition"]):
actual = func(input_list, 2)
else:
actual = func(input_list)
except ValueError:
actual_raised = True

try:
if any(x in func.__name__ for x in ["move", "partition"]):
desired = func0(input_list, 2)
else:
desired = func0(input_list)
except ValueError:
desired_raised = True

if actual_raised and desired_raised:
return

assert not (actual_raised or desired_raised)

a = np.array(input_list)
tup = (name, "a", str(a.dtype), str(a.shape), a)
err_msg = msg % tup
assert_array_almost_equal(actual, desired, err_msg=err_msg)
18 changes: 11 additions & 7 deletions bottleneck/tests/nonreduce_axis_test.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import numpy as np
import pytest
import hypothesis
from numpy.testing import (
assert_array_almost_equal,
assert_array_equal,
@@ -10,7 +11,7 @@
import bottleneck as bn
from .reduce_test import unit_maker as reduce_unit_maker
from .reduce_test import unit_maker_argparse as unit_maker_parse_rankdata
from .util import DTYPES, array_order, arrays
from .util import DTYPES, array_order, arrays, hy_array_gen

# ---------------------------------------------------------------------------
# partition, argpartition
@@ -62,8 +63,8 @@ def test_partition_and_argpartition(func) -> None:
assert_array_equal(s1, s0, err_msg)


def complete_the_partition(a, n, axis):
def func1d(a, n):
def complete_the_partition(a: np.ndarray, n: int, axis: int) -> np.ndarray:
def func1d(a: np.ndarray, n: int) -> np.ndarray:
a[:n] = np.sort(a[:n])
a[n + 1 :] = np.sort(a[n + 1 :])
return a
@@ -127,11 +128,14 @@ def complete_the_argpartition(index, a, n, axis):
return a


def test_transpose() -> None:
@hypothesis.given(array=hy_array_gen)
def test_transpose(array) -> None:
"""partition transpose test"""
a = np.arange(12).reshape(4, 3)
actual = bn.partition(a.T, 2, -1).T
desired = bn.slow.partition(a.T, 2, -1).T
if min(array.shape) < 3:
return

actual = bn.partition(array.T, 2, -1).T
desired = bn.slow.partition(array.T, 2, -1).T
assert_equal(actual, desired, "partition transpose test")


12 changes: 8 additions & 4 deletions bottleneck/tests/nonreduce_test.py
Original file line number Diff line number Diff line change
@@ -80,10 +80,14 @@ def test_replace_unsafe_cast() -> None:

def test_non_array() -> None:
"""Test that non-array input raises"""
a = [1, 2, 3]
assert_raises(TypeError, bn.replace, a, 0, 1)
a = (1, 2, 3)
assert_raises(TypeError, bn.replace, a, 0, 1)
list_input = [1, 2, 3]
assert_raises(TypeError, bn.replace, list_input, 0, 1)

tuple_input = (1, 2, 3)
assert_raises(TypeError, bn.replace, tuple_input, 0, 1)

integer_input = 1
assert_raises(TypeError, bn.replace, integer_input, 0, 1)


# ---------------------------------------------------------------------------