Skip to content

Commit

Permalink
Merge pull request #22861 from Developer-Ecosystem-Engineering/conver…
Browse files Browse the repository at this point in the history
…t_non_bool_to_bool

BUG, SIMD: Restore behavior converting non bool input to 0x00/0xff
  • Loading branch information
mattip committed Dec 23, 2022
2 parents 85b26f4 + b7ca02f commit 2b9851b
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 4 deletions.
32 changes: 28 additions & 4 deletions numpy/core/src/umath/loops_logical.dispatch.c.src
Expand Up @@ -44,6 +44,30 @@ NPY_FINLINE npyv_u8 mask_to_true(npyv_b8 v)
const npyv_u8 truemask = npyv_setall_u8(1 == 1);
return npyv_and_u8(truemask, npyv_cvt_u8_b8(v));
}
/*
* For logical_and, we have to be careful to handle non-bool inputs where
* bits of each operand might not overlap. Example: a = 0x01, b = 0x80
* Both evaluate to boolean true, however, a & b is false. Return value
* should be consistent with byte_to_true().
*/
NPY_FINLINE npyv_u8 simd_logical_and_u8(npyv_u8 a, npyv_u8 b)
{
const npyv_u8 zero = npyv_zero_u8();
const npyv_u8 truemask = npyv_setall_u8(1 == 1);
npyv_b8 ma = npyv_cmpeq_u8(a, zero);
npyv_b8 mb = npyv_cmpeq_u8(b, zero);
npyv_u8 r = npyv_cvt_u8_b8(npyv_or_b8(ma, mb));
return npyv_andc_u8(truemask, r);
}
/*
* We don't really need the following, but it simplifies the templating code
* below since it is paired with simd_logical_and_u8() above.
*/
NPY_FINLINE npyv_u8 simd_logical_or_u8(npyv_u8 a, npyv_u8 b)
{
npyv_u8 r = npyv_or_u8(a, b);
return byte_to_true(r);
}


/**begin repeat
Expand Down Expand Up @@ -71,8 +95,8 @@ simd_binary_@kind@_BOOL(npy_bool * op, npy_bool * ip1, npy_bool * ip2, npy_intp
#if UNROLL > @unroll@
npyv_u8 a@unroll@ = npyv_load_u8(ip1 + vstep * @unroll@);
npyv_u8 b@unroll@ = npyv_load_u8(ip2 + vstep * @unroll@);
npyv_u8 r@unroll@ = npyv_@intrin@_u8(a@unroll@, b@unroll@);
npyv_store_u8(op + vstep * @unroll@, byte_to_true(r@unroll@));
npyv_u8 r@unroll@ = simd_logical_@intrin@_u8(a@unroll@, b@unroll@);
npyv_store_u8(op + vstep * @unroll@, r@unroll@);
#endif
/**end repeat1**/
}
Expand All @@ -82,8 +106,8 @@ simd_binary_@kind@_BOOL(npy_bool * op, npy_bool * ip1, npy_bool * ip2, npy_intp
for (; len >= vstep; len -= vstep, ip1 += vstep, ip2 += vstep, op += vstep) {
npyv_u8 a = npyv_load_u8(ip1);
npyv_u8 b = npyv_load_u8(ip2);
npyv_u8 r = npyv_@intrin@_u8(a, b);
npyv_store_u8(op, byte_to_true(r));
npyv_u8 r = simd_logical_@intrin@_u8(a, b);
npyv_store_u8(op, r);
}

// Scalar loop to finish off
Expand Down
11 changes: 11 additions & 0 deletions numpy/core/tests/test_regression.py
Expand Up @@ -2553,3 +2553,14 @@ def get_idx(string, str_lst):
f"Unexpected types order of ufunc in {operation}"
f"for {order}. Possible fix: Use signed before unsigned"
"in generate_umath.py")

def test_nonbool_logical(self):
# gh-22845
# create two arrays with bit patterns that do not overlap.
# needs to be large enough to test both SIMD and scalar paths
size = 100
a = np.frombuffer(b'\x01' * size, dtype=np.bool_)
b = np.frombuffer(b'\x80' * size, dtype=np.bool_)
expected = np.ones(size, dtype=np.bool_)
assert_array_equal(np.logical_and(a, b), expected)

0 comments on commit 2b9851b

Please sign in to comment.