From 9d982d850eb4f9db59c9ca80aad79e2b8def6fda Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Mon, 20 Sep 2021 16:33:42 +0100 Subject: [PATCH 01/23] Implemented test_trunc --- array_api_tests/test_elementwise_functions.py | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/array_api_tests/test_elementwise_functions.py b/array_api_tests/test_elementwise_functions.py index 01db8882..b1e9b5ba 100644 --- a/array_api_tests/test_elementwise_functions.py +++ b/array_api_tests/test_elementwise_functions.py @@ -26,7 +26,8 @@ boolean_dtype_objects, floating_dtypes, numeric_dtypes, integer_or_boolean_dtypes, boolean_dtypes, mutually_promotable_dtypes, - array_scalars, shared_arrays1, shared_arrays2) + array_scalars, shared_arrays1, shared_arrays2, + xps) from .array_helpers import (assert_exactly_equal, negative, positive_mathematical_sign, negative_mathematical_sign, logical_not, @@ -37,7 +38,7 @@ ndindex, promote_dtypes, is_integer_dtype, is_float_dtype, not_equal, float64, asarray, dtype_ranges, full, true, false, assert_same_sign, - isnan, less) + isnan, equal, less) # We might as well use this implementation rather than requiring # mod.broadcast_shapes(). See test_equal() and others. from .test_broadcasting import broadcast_shapes @@ -901,7 +902,16 @@ def test_tanh(x): # a = _array_module.tanh(x) pass -@given(numeric_scalars) +@given(xps.arrays(dtype=numeric_dtypes, shape=xps.array_shapes())) def test_trunc(x): - # a = _array_module.trunc(x) - pass + a = _array_module.trunc(x) + assert a.dtype == x.dtype, f"{x.dtype=!s}, but trunc() did not produce a {x.dtype} array - instead was {a.dtype}" + if x.dtype in integer_dtype_objects: + assert array_all(equal(x, a)), f"{x=!s} but trunc(x)={x} - {x.dtype=!s} so trunc(x) should do nothing" + else: + # TODO: a method that generates all indices, so we don't have to flatten first + a = _array_module.reshape(a, a.size) + finite_mask = _array_module.isfinite(a) + for i in range(a.size): + if finite_mask[i]: + assert float(a[i]).is_integer(), f"trunc(x) did not round float {a[i]} to 0 decimals" From 4439c8171718a4d8d6b59128c878338fe139a76d Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Mon, 4 Oct 2021 10:42:09 +0100 Subject: [PATCH 02/23] Improved test_trunc --- array_api_tests/test_elementwise_functions.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/array_api_tests/test_elementwise_functions.py b/array_api_tests/test_elementwise_functions.py index b1e9b5ba..a0dcfcfc 100644 --- a/array_api_tests/test_elementwise_functions.py +++ b/array_api_tests/test_elementwise_functions.py @@ -904,14 +904,13 @@ def test_tanh(x): @given(xps.arrays(dtype=numeric_dtypes, shape=xps.array_shapes())) def test_trunc(x): - a = _array_module.trunc(x) - assert a.dtype == x.dtype, f"{x.dtype=!s}, but trunc() did not produce a {x.dtype} array - instead was {a.dtype}" + out = _array_module.trunc(x) + assert out.dtype == x.dtype, f"{x.dtype=!s} but {out.dtype=!s}" + assert out.shape == x.shape, f"{x.shape} but {out.shape}" if x.dtype in integer_dtype_objects: - assert array_all(equal(x, a)), f"{x=!s} but trunc(x)={x} - {x.dtype=!s} so trunc(x) should do nothing" + assert array_all(equal(x, out)), f"{x=!s} but {out=!s}" else: - # TODO: a method that generates all indices, so we don't have to flatten first - a = _array_module.reshape(a, a.size) - finite_mask = _array_module.isfinite(a) - for i in range(a.size): - if finite_mask[i]: - assert float(a[i]).is_integer(), f"trunc(x) did not round float {a[i]} to 0 decimals" + finite_mask = _array_module.isfinite(out) + for idx in ndindex(out.shape): + if finite_mask[idx]: + assert float(out[idx]).is_integer(), f"x at {idx=} is {x[idx]}, but out at idx is {out[idx]}" From 1ff0af7575167f4531916c1a0ec90912867486aa Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Mon, 4 Oct 2021 10:42:16 +0100 Subject: [PATCH 03/23] Test with ndarrays for test_round --- array_api_tests/test_elementwise_functions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/array_api_tests/test_elementwise_functions.py b/array_api_tests/test_elementwise_functions.py index a0dcfcfc..88514624 100644 --- a/array_api_tests/test_elementwise_functions.py +++ b/array_api_tests/test_elementwise_functions.py @@ -27,7 +27,7 @@ numeric_dtypes, integer_or_boolean_dtypes, boolean_dtypes, mutually_promotable_dtypes, array_scalars, shared_arrays1, shared_arrays2, - xps) + xps, shapes) from .array_helpers import (assert_exactly_equal, negative, positive_mathematical_sign, negative_mathematical_sign, logical_not, @@ -838,7 +838,7 @@ def test_remainder(args): not_nan = logical_not(logical_or(isnan(a), isnan(x2))) assert_same_sign(a[not_nan], x2[not_nan]) -@given(numeric_scalars) +@given(xps.arrays(dtype=xps.numeric_dtypes(), shape=shapes)) def test_round(x): a = _array_module.round(x) From 9f1d58c66196029e4ae6e895957a8d7419784e26 Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Mon, 4 Oct 2021 11:00:52 +0100 Subject: [PATCH 04/23] Use ndarrays for test_remainder --- array_api_tests/hypothesis_helpers.py | 19 ++++++--------- array_api_tests/test_elementwise_functions.py | 23 ++++++++++--------- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/array_api_tests/hypothesis_helpers.py b/array_api_tests/hypothesis_helpers.py index 862a91ef..58683259 100644 --- a/array_api_tests/hypothesis_helpers.py +++ b/array_api_tests/hypothesis_helpers.py @@ -77,10 +77,6 @@ def mutually_promotable_dtypes(draw, dtype_objects=dtype_objects): dtype_pairs = [(i, j) for i, j in dtype_pairs if i in dtype_objects and j in dtype_objects] return draw(sampled_from(dtype_pairs)) -shared_mutually_promotable_dtype_pairs = shared( - mutually_promotable_dtypes(), key="mutually_promotable_dtype_pair" -) - # shared() allows us to draw either the function or the function name and they # will both correspond to the same function. @@ -275,14 +271,13 @@ def multiaxis_indices(draw, shapes): return tuple(res) -shared_arrays1 = xps.arrays( - dtype=shared_mutually_promotable_dtype_pairs.map(lambda pair: pair[0]), - shape=shared(two_mutually_broadcastable_shapes, key="shape_pair").map(lambda pair: pair[0]), -) -shared_arrays2 = xps.arrays( - dtype=shared_mutually_promotable_dtype_pairs.map(lambda pair: pair[1]), - shape=shared(two_mutually_broadcastable_shapes, key="shape_pair").map(lambda pair: pair[1]), -) +@composite +def two_mutual_arrays(draw, dtypes=dtype_objects): + dtype1, dtype2 = draw(mutually_promotable_dtypes(dtypes)) + shape1, shape2 = draw(two_mutually_broadcastable_shapes) + x1 = draw(xps.arrays(dtype=dtype1, shape=shape1)) + x2 = draw(xps.arrays(dtype=dtype2, shape=shape2)) + return x1, x2 @composite diff --git a/array_api_tests/test_elementwise_functions.py b/array_api_tests/test_elementwise_functions.py index 88514624..0f226d34 100644 --- a/array_api_tests/test_elementwise_functions.py +++ b/array_api_tests/test_elementwise_functions.py @@ -26,8 +26,7 @@ boolean_dtype_objects, floating_dtypes, numeric_dtypes, integer_or_boolean_dtypes, boolean_dtypes, mutually_promotable_dtypes, - array_scalars, shared_arrays1, shared_arrays2, - xps, shapes) + array_scalars, two_mutual_arrays, xps, shapes) from .array_helpers import (assert_exactly_equal, negative, positive_mathematical_sign, negative_mathematical_sign, logical_not, @@ -376,8 +375,9 @@ def test_divide(args): # have those sorts in general for this module. -@given(shared_arrays1, shared_arrays2) -def test_equal(x1, x2): +@given(two_mutual_arrays()) +def test_equal(x1_and_x2): + x1, x2 = x1_and_x2 sanity_check(x1, x2) a = _array_module.equal(x1, x2) # NOTE: assert_exactly_equal() itself uses equal(), so we must be careful @@ -827,16 +827,17 @@ def test_pow(args): # numbers. We could test that this does implement IEEE 754 pow, but we # don't yet have those sorts in general for this module. -@given(two_numeric_dtypes.flatmap(lambda i: two_array_scalars(*i))) -def test_remainder(args): - x1, x2 = args +@given(two_mutual_arrays(numeric_dtype_objects)) +def test_remainder(x1_and_x2): + x1, x2 = x1_and_x2 + assume(len(x1.shape) <= len(x2.shape)) # TODO: rework same sign testing below to remove this sanity_check(x1, x2) - a = _array_module.remainder(x1, x2) + out = _array_module.remainder(x1, x2) - # a and x2 should have the same sign. + # out and x2 should have the same sign. # assert_same_sign returns False for nans - not_nan = logical_not(logical_or(isnan(a), isnan(x2))) - assert_same_sign(a[not_nan], x2[not_nan]) + not_nan = logical_not(logical_or(isnan(out), isnan(x2))) + assert_same_sign(out[not_nan], x2[not_nan]) @given(xps.arrays(dtype=xps.numeric_dtypes(), shape=shapes)) def test_round(x): From e00281761e4afcbf59945f40c1489dc4a94135d0 Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Mon, 4 Oct 2021 11:03:20 +0100 Subject: [PATCH 05/23] Use ndarrays for test_pow --- array_api_tests/test_elementwise_functions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/array_api_tests/test_elementwise_functions.py b/array_api_tests/test_elementwise_functions.py index 0f226d34..c4ef2f21 100644 --- a/array_api_tests/test_elementwise_functions.py +++ b/array_api_tests/test_elementwise_functions.py @@ -816,9 +816,9 @@ def test_positive(x): # Positive does nothing assert_exactly_equal(a, x) -@given(two_floating_dtypes.flatmap(lambda i: two_array_scalars(*i))) -def test_pow(args): - x1, x2 = args +@given(two_mutual_arrays(floating_dtype_objects)) +def test_pow(x1_and_x2): + x1, x2 = x1_and_x2 sanity_check(x1, x2) _array_module.pow(x1, x2) # There isn't much we can test here. The spec doesn't require any behavior From f57d8c48905dcc10dc3e6f9335feee69e4a3f09f Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Mon, 4 Oct 2021 11:32:53 +0100 Subject: [PATCH 06/23] Use ndarrays for test_positive --- array_api_tests/test_elementwise_functions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/array_api_tests/test_elementwise_functions.py b/array_api_tests/test_elementwise_functions.py index c4ef2f21..948d6cd8 100644 --- a/array_api_tests/test_elementwise_functions.py +++ b/array_api_tests/test_elementwise_functions.py @@ -810,11 +810,11 @@ def test_not_equal(args): assert aidx.shape == x1idx.shape == x2idx.shape assert bool(aidx) == (scalar_func(x1idx) != scalar_func(x2idx)) -@given(numeric_scalars) +@given(xps.arrays(dtype=xps.numeric_dtypes(), shape=shapes)) def test_positive(x): - a = _array_module.positive(x) + out = _array_module.positive(x) # Positive does nothing - assert_exactly_equal(a, x) + assert_exactly_equal(out, x) @given(two_mutual_arrays(floating_dtype_objects)) def test_pow(x1_and_x2): From 652daa35769d301bef9a8a50118033094f963b09 Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Mon, 4 Oct 2021 11:39:10 +0100 Subject: [PATCH 07/23] Use ndarrays for test_not_equal --- array_api_tests/test_elementwise_functions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/array_api_tests/test_elementwise_functions.py b/array_api_tests/test_elementwise_functions.py index 948d6cd8..659e49ae 100644 --- a/array_api_tests/test_elementwise_functions.py +++ b/array_api_tests/test_elementwise_functions.py @@ -780,9 +780,9 @@ def test_negative(x): ZERO = zero(x[mask].shape, x.dtype) assert_exactly_equal(y, ZERO) -@given(two_any_dtypes.flatmap(lambda i: two_array_scalars(*i))) -def test_not_equal(args): - x1, x2 = args +@given(two_mutual_arrays()) +def test_not_equal(x1_and_x2): + x1, x2 = x1_and_x2 sanity_check(x1, x2) a = _array_module.not_equal(x1, x2) From e73866c97a9752ed026f68b139db27e9dde98038 Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Tue, 5 Oct 2021 09:55:58 +0100 Subject: [PATCH 08/23] Use ndarrays for test_negative --- array_api_tests/test_elementwise_functions.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/array_api_tests/test_elementwise_functions.py b/array_api_tests/test_elementwise_functions.py index 659e49ae..e9ed3545 100644 --- a/array_api_tests/test_elementwise_functions.py +++ b/array_api_tests/test_elementwise_functions.py @@ -759,13 +759,12 @@ def test_multiply(args): # multiply is commutative assert_exactly_equal(a, b) -@given(numeric_scalars) +@given(xps.arrays(dtype=xps.numeric_dtypes(), shape=shapes)) def test_negative(x): - a = _array_module.negative(x) + out = _array_module.negative(x) # Negation is an involution - b = _array_module.negative(a) - assert_exactly_equal(x, b) + assert_exactly_equal(x, _array_module.negative(out)) mask = isfinite(x) if is_integer_dtype(x.dtype): @@ -773,13 +772,13 @@ def test_negative(x): if minval < 0: # negative of the smallest representable negative integer is not defined mask = not_equal(x, full(x.shape, minval, dtype=x.dtype)) - x = x[mask] # Additive inverse - y = _array_module.add(x[mask], a[mask]) + y = _array_module.add(x[mask], out[mask]) ZERO = zero(x[mask].shape, x.dtype) assert_exactly_equal(y, ZERO) + @given(two_mutual_arrays()) def test_not_equal(x1_and_x2): x1, x2 = x1_and_x2 @@ -810,6 +809,7 @@ def test_not_equal(x1_and_x2): assert aidx.shape == x1idx.shape == x2idx.shape assert bool(aidx) == (scalar_func(x1idx) != scalar_func(x2idx)) + @given(xps.arrays(dtype=xps.numeric_dtypes(), shape=shapes)) def test_positive(x): out = _array_module.positive(x) From 88118f05cbb980ccf8cc8847dd883cf0f8ba5d26 Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Tue, 5 Oct 2021 10:16:58 +0100 Subject: [PATCH 09/23] Use ndarrays for test_multiply --- array_api_tests/test_elementwise_functions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/array_api_tests/test_elementwise_functions.py b/array_api_tests/test_elementwise_functions.py index e9ed3545..e7de6100 100644 --- a/array_api_tests/test_elementwise_functions.py +++ b/array_api_tests/test_elementwise_functions.py @@ -749,9 +749,9 @@ def test_logical_xor(args): for idx in ndindex(shape): assert a[idx] == (bool(_x1[idx]) ^ bool(_x2[idx])) -@given(two_numeric_dtypes.flatmap(lambda i: two_array_scalars(*i))) -def test_multiply(args): - x1, x2 = args +@given(two_mutual_arrays(numeric_dtype_objects)) +def test_multiply(x1_and_x2): + x1, x2 = x1_and_x2 sanity_check(x1, x2) a = _array_module.multiply(x1, x2) From 14f1dad2b537392e8a0d31f00efd07246bc22538 Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Tue, 5 Oct 2021 10:25:22 +0100 Subject: [PATCH 10/23] Use ndarrays for logical tests, alias _array_module as xp --- array_api_tests/test_elementwise_functions.py | 224 +++++++++--------- 1 file changed, 112 insertions(+), 112 deletions(-) diff --git a/array_api_tests/test_elementwise_functions.py b/array_api_tests/test_elementwise_functions.py index e7de6100..161eacb1 100644 --- a/array_api_tests/test_elementwise_functions.py +++ b/array_api_tests/test_elementwise_functions.py @@ -42,7 +42,7 @@ # mod.broadcast_shapes(). See test_equal() and others. from .test_broadcasting import broadcast_shapes -from . import _array_module +from . import _array_module as xp # integer_scalars = array_scalars(integer_dtypes) floating_scalars = array_scalars(floating_dtypes) @@ -77,7 +77,7 @@ def test_abs(x): # abs of the smallest representable negative integer is not defined mask = not_equal(x, full(x.shape, minval, dtype=x.dtype)) x = x[mask] - a = _array_module.abs(x) + a = xp.abs(x) assert array_all(logical_not(negative_mathematical_sign(a))), "abs(x) did not have positive sign" less_zero = negative_mathematical_sign(x) negx = negative(x) @@ -88,7 +88,7 @@ def test_abs(x): @given(floating_scalars) def test_acos(x): - a = _array_module.acos(x) + a = xp.acos(x) ONE = one(x.shape, x.dtype) # Here (and elsewhere), should technically be a.dtype, but this is the # same as x.dtype, as tested by the type_promotion tests. @@ -102,7 +102,7 @@ def test_acos(x): @given(floating_scalars) def test_acosh(x): - a = _array_module.acosh(x) + a = xp.acosh(x) ONE = one(x.shape, x.dtype) INFINITY = infinity(x.shape, x.dtype) ZERO = zero(x.shape, x.dtype) @@ -116,16 +116,16 @@ def test_acosh(x): def test_add(args): x1, x2 = args sanity_check(x1, x2) - a = _array_module.add(x1, x2) + a = xp.add(x1, x2) - b = _array_module.add(x2, x1) + b = xp.add(x2, x1) # add is commutative assert_exactly_equal(a, b) # TODO: Test that add is actually addition @given(floating_scalars) def test_asin(x): - a = _array_module.asin(x) + a = xp.asin(x) ONE = one(x.shape, x.dtype) PI = π(x.shape, x.dtype) domain = inrange(x, -ONE, ONE) @@ -136,7 +136,7 @@ def test_asin(x): @given(floating_scalars) def test_asinh(x): - a = _array_module.asinh(x) + a = xp.asinh(x) INFINITY = infinity(x.shape, x.dtype) domain = inrange(x, -INFINITY, INFINITY) codomain = inrange(a, -INFINITY, INFINITY) @@ -146,7 +146,7 @@ def test_asinh(x): @given(floating_scalars) def test_atan(x): - a = _array_module.atan(x) + a = xp.atan(x) INFINITY = infinity(x.shape, x.dtype) PI = π(x.shape, x.dtype) domain = inrange(x, -INFINITY, INFINITY) @@ -159,7 +159,7 @@ def test_atan(x): def test_atan2(args): x1, x2 = args sanity_check(x1, x2) - a = _array_module.atan2(x1, x2) + a = xp.atan2(x1, x2) INFINITY1 = infinity(x1.shape, x1.dtype) INFINITY2 = infinity(x2.shape, x2.dtype) PI = π(a.shape, a.dtype) @@ -194,7 +194,7 @@ def test_atan2(args): @given(floating_scalars) def test_atanh(x): - a = _array_module.atanh(x) + a = xp.atanh(x) ONE = one(x.shape, x.dtype) INFINITY = infinity(x.shape, x.dtype) domain = inrange(x, -ONE, ONE) @@ -208,7 +208,7 @@ def test_bitwise_and(args): from .test_type_promotion import dtype_nbits, dtype_signed x1, x2 = args sanity_check(x1, x2) - a = _array_module.bitwise_and(x1, x2) + a = xp.bitwise_and(x1, x2) # Compare against the Python & operator. # TODO: Generalize this properly for inputs that are arrays. if not (x1.shape == x2.shape == ()): @@ -234,7 +234,7 @@ def test_bitwise_left_shift(args): negative_x2 = isnegative(x2) if array_any(negative_x2): assume(False) - a = _array_module.bitwise_left_shift(x1, x2) + a = xp.bitwise_left_shift(x1, x2) # Compare against the Python << operator. # TODO: Generalize this properly for inputs that are arrays. if not (x1.shape == x2.shape == ()): @@ -253,7 +253,7 @@ def test_bitwise_left_shift(args): @given(integer_or_boolean_scalars) def test_bitwise_invert(x): from .test_type_promotion import dtype_nbits, dtype_signed - a = _array_module.bitwise_invert(x) + a = xp.bitwise_invert(x) # Compare against the Python ~ operator. # TODO: Generalize this properly for inputs that are arrays. if not (x.shape == ()): @@ -273,7 +273,7 @@ def test_bitwise_or(args): from .test_type_promotion import dtype_nbits, dtype_signed x1, x2 = args sanity_check(x1, x2) - a = _array_module.bitwise_or(x1, x2) + a = xp.bitwise_or(x1, x2) # Compare against the Python | operator. # TODO: Generalize this properly for inputs that are arrays. if not (x1.shape == x2.shape == ()): @@ -298,7 +298,7 @@ def test_bitwise_right_shift(args): negative_x2 = isnegative(x2) if array_any(negative_x2): assume(False) - a = _array_module.bitwise_right_shift(x1, x2) + a = xp.bitwise_right_shift(x1, x2) # Compare against the Python >> operator. # TODO: Generalize this properly for inputs that are arrays. if not (x1.shape == x2.shape == ()): @@ -314,7 +314,7 @@ def test_bitwise_xor(args): from .test_type_promotion import dtype_nbits, dtype_signed x1, x2 = args sanity_check(x1, x2) - a = _array_module.bitwise_xor(x1, x2) + a = xp.bitwise_xor(x1, x2) # Compare against the Python ^ operator. # TODO: Generalize this properly for inputs that are arrays. if not (x1.shape == x2.shape == ()): @@ -334,7 +334,7 @@ def test_bitwise_xor(args): @given(numeric_scalars) def test_ceil(x): # This test is almost identical to test_floor() - a = _array_module.ceil(x) + a = xp.ceil(x) finite = isfinite(x) assert_integral(a[finite]) assert array_all(less_equal(x[finite], a[finite])) @@ -344,7 +344,7 @@ def test_ceil(x): @given(floating_scalars) def test_cos(x): - a = _array_module.cos(x) + a = xp.cos(x) ONE = one(x.shape, x.dtype) INFINITY = infinity(x.shape, x.dtype) domain = inrange(x, -INFINITY, INFINITY, open=True) @@ -355,7 +355,7 @@ def test_cos(x): @given(floating_scalars) def test_cosh(x): - a = _array_module.cosh(x) + a = xp.cosh(x) INFINITY = infinity(x.shape, x.dtype) domain = inrange(x, -INFINITY, INFINITY) codomain = inrange(a, -INFINITY, INFINITY) @@ -367,7 +367,7 @@ def test_cosh(x): def test_divide(args): x1, x2 = args sanity_check(x1, x2) - _array_module.divide(x1, x2) + xp.divide(x1, x2) # There isn't much we can test here. The spec doesn't require any behavior # beyond the special cases, and indeed, there aren't many mathematical # properties of division that strictly hold for floating-point numbers. We @@ -379,7 +379,7 @@ def test_divide(args): def test_equal(x1_and_x2): x1, x2 = x1_and_x2 sanity_check(x1, x2) - a = _array_module.equal(x1, x2) + a = xp.equal(x1, x2) # NOTE: assert_exactly_equal() itself uses equal(), so we must be careful # not to use it here. Otherwise, the test would be circular and # meaningless. Instead, we implement this by iterating every element of @@ -393,8 +393,8 @@ def test_equal(x1_and_x2): # indices to x1 and x2 that correspond to the broadcasted shapes. This # would avoid the dependence in this test on broadcast_to(). shape = broadcast_shapes(x1.shape, x2.shape) - _x1 = _array_module.broadcast_to(x1, shape) - _x2 = _array_module.broadcast_to(x2, shape) + _x1 = xp.broadcast_to(x1, shape) + _x2 = xp.broadcast_to(x2, shape) # Second, manually promote the dtypes. This is important. If the internal # type promotion in equal() is wrong, it will not be directly visible in @@ -407,8 +407,8 @@ def test_equal(x1_and_x2): # tested in that file, because doing so requires doing the consistency # check we do here rather than just checking the result dtype. promoted_dtype = promote_dtypes(x1.dtype, x2.dtype) - _x1 = _array_module.asarray(_x1, dtype=promoted_dtype) - _x2 = _array_module.asarray(_x2, dtype=promoted_dtype) + _x1 = xp.asarray(_x1, dtype=promoted_dtype) + _x2 = xp.asarray(_x2, dtype=promoted_dtype) if is_integer_dtype(promoted_dtype): scalar_func = int @@ -426,7 +426,7 @@ def test_equal(x1_and_x2): @given(floating_scalars) def test_exp(x): - a = _array_module.exp(x) + a = xp.exp(x) INFINITY = infinity(x.shape, x.dtype) ZERO = zero(x.shape, x.dtype) domain = inrange(x, -INFINITY, INFINITY) @@ -437,7 +437,7 @@ def test_exp(x): @given(floating_scalars) def test_expm1(x): - a = _array_module.expm1(x) + a = xp.expm1(x) INFINITY = infinity(x.shape, x.dtype) NEGONE = -one(x.shape, x.dtype) domain = inrange(x, -INFINITY, INFINITY) @@ -449,7 +449,7 @@ def test_expm1(x): @given(numeric_scalars) def test_floor(x): # This test is almost identical to test_ceil - a = _array_module.floor(x) + a = xp.floor(x) finite = isfinite(x) assert_integral(a[finite]) assert array_all(less_equal(a[finite], x[finite])) @@ -466,13 +466,13 @@ def test_floor_divide(args): # dtypes. A library may choose to raise an exception in this case, so # we avoid passing it in entirely. nonzero = not_equal(x2, zero(x2.shape, x2.dtype)) - div = _array_module.divide( + div = xp.divide( asarray(x1[nonzero], dtype=float64), asarray(x2[nonzero], dtype=float64)) - a = _array_module.floor_divide(x1[nonzero], x2[nonzero]) + a = xp.floor_divide(x1[nonzero], x2[nonzero]) else: - div = _array_module.divide(x1, x2) - a = _array_module.floor_divide(x1, x2) + div = xp.divide(x1, x2) + a = xp.floor_divide(x1, x2) # TODO: The spec doesn't clearly specify the behavior of floor_divide on # infinities. See https://github.com/data-apis/array-api/issues/199. @@ -485,17 +485,17 @@ def test_floor_divide(args): def test_greater(args): x1, x2 = args sanity_check(x1, x2) - a = _array_module.greater(x1, x2) + a = xp.greater(x1, x2) # See the comments in test_equal() for a description of how this test # works. shape = broadcast_shapes(x1.shape, x2.shape) - _x1 = _array_module.broadcast_to(x1, shape) - _x2 = _array_module.broadcast_to(x2, shape) + _x1 = xp.broadcast_to(x1, shape) + _x2 = xp.broadcast_to(x2, shape) promoted_dtype = promote_dtypes(x1.dtype, x2.dtype) - _x1 = _array_module.asarray(_x1, dtype=promoted_dtype) - _x2 = _array_module.asarray(_x2, dtype=promoted_dtype) + _x1 = xp.asarray(_x1, dtype=promoted_dtype) + _x2 = xp.asarray(_x2, dtype=promoted_dtype) if is_integer_dtype(promoted_dtype): scalar_func = int @@ -515,17 +515,17 @@ def test_greater(args): def test_greater_equal(args): x1, x2 = args sanity_check(x1, x2) - a = _array_module.greater_equal(x1, x2) + a = xp.greater_equal(x1, x2) # See the comments in test_equal() for a description of how this test # works. shape = broadcast_shapes(x1.shape, x2.shape) - _x1 = _array_module.broadcast_to(x1, shape) - _x2 = _array_module.broadcast_to(x2, shape) + _x1 = xp.broadcast_to(x1, shape) + _x2 = xp.broadcast_to(x2, shape) promoted_dtype = promote_dtypes(x1.dtype, x2.dtype) - _x1 = _array_module.asarray(_x1, dtype=promoted_dtype) - _x2 = _array_module.asarray(_x2, dtype=promoted_dtype) + _x1 = xp.asarray(_x1, dtype=promoted_dtype) + _x2 = xp.asarray(_x2, dtype=promoted_dtype) if is_integer_dtype(promoted_dtype): scalar_func = int @@ -543,12 +543,12 @@ def test_greater_equal(args): @given(numeric_scalars) def test_isfinite(x): - a = _array_module.isfinite(x) + a = xp.isfinite(x) TRUE = true(x.shape) if is_integer_dtype(x.dtype): assert_exactly_equal(a, TRUE) # Test that isfinite, isinf, and isnan are self-consistent. - inf = logical_or(_array_module.isinf(x), _array_module.isnan(x)) + inf = logical_or(xp.isinf(x), xp.isnan(x)) assert_exactly_equal(a, logical_not(inf)) # Test the exact value by comparing to the math version @@ -559,11 +559,11 @@ def test_isfinite(x): @given(numeric_scalars) def test_isinf(x): - a = _array_module.isinf(x) + a = xp.isinf(x) FALSE = false(x.shape) if is_integer_dtype(x.dtype): assert_exactly_equal(a, FALSE) - finite_or_nan = logical_or(_array_module.isfinite(x), _array_module.isnan(x)) + finite_or_nan = logical_or(xp.isfinite(x), xp.isnan(x)) assert_exactly_equal(a, logical_not(finite_or_nan)) # Test the exact value by comparing to the math version @@ -574,11 +574,11 @@ def test_isinf(x): @given(numeric_scalars) def test_isnan(x): - a = _array_module.isnan(x) + a = xp.isnan(x) FALSE = false(x.shape) if is_integer_dtype(x.dtype): assert_exactly_equal(a, FALSE) - finite_or_inf = logical_or(_array_module.isfinite(x), _array_module.isinf(x)) + finite_or_inf = logical_or(xp.isfinite(x), xp.isinf(x)) assert_exactly_equal(a, logical_not(finite_or_inf)) # Test the exact value by comparing to the math version @@ -591,17 +591,17 @@ def test_isnan(x): def test_less(args): x1, x2 = args sanity_check(x1, x2) - a = _array_module.less(x1, x2) + a = xp.less(x1, x2) # See the comments in test_equal() for a description of how this test # works. shape = broadcast_shapes(x1.shape, x2.shape) - _x1 = _array_module.broadcast_to(x1, shape) - _x2 = _array_module.broadcast_to(x2, shape) + _x1 = xp.broadcast_to(x1, shape) + _x2 = xp.broadcast_to(x2, shape) promoted_dtype = promote_dtypes(x1.dtype, x2.dtype) - _x1 = _array_module.asarray(_x1, dtype=promoted_dtype) - _x2 = _array_module.asarray(_x2, dtype=promoted_dtype) + _x1 = xp.asarray(_x1, dtype=promoted_dtype) + _x2 = xp.asarray(_x2, dtype=promoted_dtype) if is_integer_dtype(promoted_dtype): scalar_func = int @@ -621,17 +621,17 @@ def test_less(args): def test_less_equal(args): x1, x2 = args sanity_check(x1, x2) - a = _array_module.less_equal(x1, x2) + a = xp.less_equal(x1, x2) # See the comments in test_equal() for a description of how this test # works. shape = broadcast_shapes(x1.shape, x2.shape) - _x1 = _array_module.broadcast_to(x1, shape) - _x2 = _array_module.broadcast_to(x2, shape) + _x1 = xp.broadcast_to(x1, shape) + _x2 = xp.broadcast_to(x2, shape) promoted_dtype = promote_dtypes(x1.dtype, x2.dtype) - _x1 = _array_module.asarray(_x1, dtype=promoted_dtype) - _x2 = _array_module.asarray(_x2, dtype=promoted_dtype) + _x1 = xp.asarray(_x1, dtype=promoted_dtype) + _x2 = xp.asarray(_x2, dtype=promoted_dtype) if is_integer_dtype(promoted_dtype): scalar_func = int @@ -649,7 +649,7 @@ def test_less_equal(args): @given(floating_scalars) def test_log(x): - a = _array_module.log(x) + a = xp.log(x) INFINITY = infinity(x.shape, x.dtype) ZERO = zero(x.shape, x.dtype) domain = inrange(x, ZERO, INFINITY) @@ -660,7 +660,7 @@ def test_log(x): @given(floating_scalars) def test_log1p(x): - a = _array_module.log1p(x) + a = xp.log1p(x) INFINITY = infinity(x.shape, x.dtype) NEGONE = -one(x.shape, x.dtype) codomain = inrange(x, NEGONE, INFINITY) @@ -671,7 +671,7 @@ def test_log1p(x): @given(floating_scalars) def test_log2(x): - a = _array_module.log2(x) + a = xp.log2(x) INFINITY = infinity(x.shape, x.dtype) ZERO = zero(x.shape, x.dtype) domain = inrange(x, ZERO, INFINITY) @@ -682,7 +682,7 @@ def test_log2(x): @given(floating_scalars) def test_log10(x): - a = _array_module.log10(x) + a = xp.log10(x) INFINITY = infinity(x.shape, x.dtype) ZERO = zero(x.shape, x.dtype) domain = inrange(x, ZERO, INFINITY) @@ -695,56 +695,56 @@ def test_log10(x): def test_logaddexp(args): x1, x2 = args sanity_check(x1, x2) - _array_module.logaddexp(x1, x2) + xp.logaddexp(x1, x2) # The spec doesn't require any behavior for this function. We could test # that this is indeed an approximation of log(exp(x1) + exp(x2)), but we # don't have tests for this sort of thing for any functions yet. -@given(two_boolean_dtypes.flatmap(lambda i: two_array_scalars(*i))) -def test_logical_and(args): - x1, x2 = args +@given(two_mutual_arrays([xp.bool])) +def test_logical_and(x1_and_x2): + x1, x2 = x1_and_x2 sanity_check(x1, x2) - a = _array_module.logical_and(x1, x2) + a = xp.logical_and(x1, x2) # See the comments in test_equal shape = broadcast_shapes(x1.shape, x2.shape) - _x1 = _array_module.broadcast_to(x1, shape) - _x2 = _array_module.broadcast_to(x2, shape) + _x1 = xp.broadcast_to(x1, shape) + _x2 = xp.broadcast_to(x2, shape) for idx in ndindex(shape): assert a[idx] == (bool(_x1[idx]) and bool(_x2[idx])) -@given(boolean_scalars) +@given(xps.arrays(dtype=xp.bool, shape=shapes)) def test_logical_not(x): - a = _array_module.logical_not(x) + a = xp.logical_not(x) for idx in ndindex(x.shape): assert a[idx] == (not bool(x[idx])) -@given(two_boolean_dtypes.flatmap(lambda i: two_array_scalars(*i))) -def test_logical_or(args): - x1, x2 = args +@given(two_mutual_arrays([xp.bool])) +def test_logical_or(x1_and_x2): + x1, x2 = x1_and_x2 sanity_check(x1, x2) - a = _array_module.logical_or(x1, x2) + a = xp.logical_or(x1, x2) # See the comments in test_equal shape = broadcast_shapes(x1.shape, x2.shape) - _x1 = _array_module.broadcast_to(x1, shape) - _x2 = _array_module.broadcast_to(x2, shape) + _x1 = xp.broadcast_to(x1, shape) + _x2 = xp.broadcast_to(x2, shape) for idx in ndindex(shape): assert a[idx] == (bool(_x1[idx]) or bool(_x2[idx])) -@given(two_boolean_dtypes.flatmap(lambda i: two_array_scalars(*i))) -def test_logical_xor(args): - x1, x2 = args +@given(two_mutual_arrays([xp.bool])) +def test_logical_xor(x1_and_x2): + x1, x2 = x1_and_x2 sanity_check(x1, x2) - a = _array_module.logical_xor(x1, x2) + a = xp.logical_xor(x1, x2) # See the comments in test_equal shape = broadcast_shapes(x1.shape, x2.shape) - _x1 = _array_module.broadcast_to(x1, shape) - _x2 = _array_module.broadcast_to(x2, shape) + _x1 = xp.broadcast_to(x1, shape) + _x2 = xp.broadcast_to(x2, shape) for idx in ndindex(shape): assert a[idx] == (bool(_x1[idx]) ^ bool(_x2[idx])) @@ -753,18 +753,18 @@ def test_logical_xor(args): def test_multiply(x1_and_x2): x1, x2 = x1_and_x2 sanity_check(x1, x2) - a = _array_module.multiply(x1, x2) + a = xp.multiply(x1, x2) - b = _array_module.multiply(x2, x1) + b = xp.multiply(x2, x1) # multiply is commutative assert_exactly_equal(a, b) @given(xps.arrays(dtype=xps.numeric_dtypes(), shape=shapes)) def test_negative(x): - out = _array_module.negative(x) + out = xp.negative(x) # Negation is an involution - assert_exactly_equal(x, _array_module.negative(out)) + assert_exactly_equal(x, xp.negative(out)) mask = isfinite(x) if is_integer_dtype(x.dtype): @@ -774,7 +774,7 @@ def test_negative(x): mask = not_equal(x, full(x.shape, minval, dtype=x.dtype)) # Additive inverse - y = _array_module.add(x[mask], out[mask]) + y = xp.add(x[mask], out[mask]) ZERO = zero(x[mask].shape, x.dtype) assert_exactly_equal(y, ZERO) @@ -783,17 +783,17 @@ def test_negative(x): def test_not_equal(x1_and_x2): x1, x2 = x1_and_x2 sanity_check(x1, x2) - a = _array_module.not_equal(x1, x2) + a = xp.not_equal(x1, x2) # See the comments in test_equal() for a description of how this test # works. shape = broadcast_shapes(x1.shape, x2.shape) - _x1 = _array_module.broadcast_to(x1, shape) - _x2 = _array_module.broadcast_to(x2, shape) + _x1 = xp.broadcast_to(x1, shape) + _x2 = xp.broadcast_to(x2, shape) promoted_dtype = promote_dtypes(x1.dtype, x2.dtype) - _x1 = _array_module.asarray(_x1, dtype=promoted_dtype) - _x2 = _array_module.asarray(_x2, dtype=promoted_dtype) + _x1 = xp.asarray(_x1, dtype=promoted_dtype) + _x2 = xp.asarray(_x2, dtype=promoted_dtype) if is_integer_dtype(promoted_dtype): scalar_func = int @@ -812,7 +812,7 @@ def test_not_equal(x1_and_x2): @given(xps.arrays(dtype=xps.numeric_dtypes(), shape=shapes)) def test_positive(x): - out = _array_module.positive(x) + out = xp.positive(x) # Positive does nothing assert_exactly_equal(out, x) @@ -820,7 +820,7 @@ def test_positive(x): def test_pow(x1_and_x2): x1, x2 = x1_and_x2 sanity_check(x1, x2) - _array_module.pow(x1, x2) + xp.pow(x1, x2) # There isn't much we can test here. The spec doesn't require any behavior # beyond the special cases, and indeed, there aren't many mathematical # properties of exponentiation that strictly hold for floating-point @@ -832,7 +832,7 @@ def test_remainder(x1_and_x2): x1, x2 = x1_and_x2 assume(len(x1.shape) <= len(x2.shape)) # TODO: rework same sign testing below to remove this sanity_check(x1, x2) - out = _array_module.remainder(x1, x2) + out = xp.remainder(x1, x2) # out and x2 should have the same sign. # assert_same_sign returns False for nans @@ -841,7 +841,7 @@ def test_remainder(x1_and_x2): @given(xps.arrays(dtype=xps.numeric_dtypes(), shape=shapes)) def test_round(x): - a = _array_module.round(x) + a = xp.round(x) # Test that the result is integral finite = isfinite(x) @@ -853,10 +853,10 @@ def test_round(x): # This is the same strategy used in the mask in the # test_round_special_cases_one_arg_two_integers_equally_close special # cases test. - floor = _array_module.floor(x) - ceil = _array_module.ceil(x) - over = _array_module.subtract(x, floor) - under = _array_module.subtract(ceil, x) + floor = xp.floor(x) + ceil = xp.ceil(x) + over = xp.subtract(x, floor) + under = xp.subtract(ceil, x) round_down = less(over, under) round_up = less(under, over) assert_exactly_equal(a[round_down], floor[round_down]) @@ -864,54 +864,54 @@ def test_round(x): @given(numeric_scalars) def test_sign(x): - # a = _array_module.sign(x) + # a = xp.sign(x) pass @given(floating_scalars) def test_sin(x): - # a = _array_module.sin(x) + # a = xp.sin(x) pass @given(floating_scalars) def test_sinh(x): - # a = _array_module.sinh(x) + # a = xp.sinh(x) pass @given(numeric_scalars) def test_square(x): - # a = _array_module.square(x) + # a = xp.square(x) pass @given(floating_scalars) def test_sqrt(x): - # a = _array_module.sqrt(x) + # a = xp.sqrt(x) pass @given(two_numeric_dtypes.flatmap(lambda i: two_array_scalars(*i))) def test_subtract(args): x1, x2 = args sanity_check(x1, x2) - # a = _array_module.subtract(x1, x2) + # a = xp.subtract(x1, x2) @given(floating_scalars) def test_tan(x): - # a = _array_module.tan(x) + # a = xp.tan(x) pass @given(floating_scalars) def test_tanh(x): - # a = _array_module.tanh(x) + # a = xp.tanh(x) pass @given(xps.arrays(dtype=numeric_dtypes, shape=xps.array_shapes())) def test_trunc(x): - out = _array_module.trunc(x) + out = xp.trunc(x) assert out.dtype == x.dtype, f"{x.dtype=!s} but {out.dtype=!s}" assert out.shape == x.shape, f"{x.shape} but {out.shape}" if x.dtype in integer_dtype_objects: assert array_all(equal(x, out)), f"{x=!s} but {out=!s}" else: - finite_mask = _array_module.isfinite(out) + finite_mask = xp.isfinite(out) for idx in ndindex(out.shape): if finite_mask[idx]: assert float(out[idx]).is_integer(), f"x at {idx=} is {x[idx]}, but out at idx is {out[idx]}" From cd0a1083f2570516814055b64c121911d10684b4 Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Tue, 5 Oct 2021 10:40:41 +0100 Subject: [PATCH 11/23] Use ndarrays for log tests --- array_api_tests/test_elementwise_functions.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/array_api_tests/test_elementwise_functions.py b/array_api_tests/test_elementwise_functions.py index 161eacb1..5d28fb2e 100644 --- a/array_api_tests/test_elementwise_functions.py +++ b/array_api_tests/test_elementwise_functions.py @@ -647,7 +647,7 @@ def test_less_equal(args): assert aidx.shape == x1idx.shape == x2idx.shape assert bool(aidx) == (scalar_func(x1idx) <= scalar_func(x2idx)) -@given(floating_scalars) +@given(xps.arrays(dtype=xps.floating_dtypes(), shape=shapes)) def test_log(x): a = xp.log(x) INFINITY = infinity(x.shape, x.dtype) @@ -658,7 +658,7 @@ def test_log(x): # mapped to nan, which is already tested in the special cases. assert_exactly_equal(domain, codomain) -@given(floating_scalars) +@given(xps.arrays(dtype=xps.floating_dtypes(), shape=shapes)) def test_log1p(x): a = xp.log1p(x) INFINITY = infinity(x.shape, x.dtype) @@ -669,7 +669,7 @@ def test_log1p(x): # mapped to nan, which is already tested in the special cases. assert_exactly_equal(domain, codomain) -@given(floating_scalars) +@given(xps.arrays(dtype=xps.floating_dtypes(), shape=shapes)) def test_log2(x): a = xp.log2(x) INFINITY = infinity(x.shape, x.dtype) @@ -680,7 +680,7 @@ def test_log2(x): # mapped to nan, which is already tested in the special cases. assert_exactly_equal(domain, codomain) -@given(floating_scalars) +@given(xps.arrays(dtype=xps.floating_dtypes(), shape=shapes)) def test_log10(x): a = xp.log10(x) INFINITY = infinity(x.shape, x.dtype) @@ -691,9 +691,9 @@ def test_log10(x): # mapped to nan, which is already tested in the special cases. assert_exactly_equal(domain, codomain) -@given(two_floating_dtypes.flatmap(lambda i: two_array_scalars(*i))) -def test_logaddexp(args): - x1, x2 = args +@given(two_mutual_arrays(floating_dtype_objects)) +def test_logaddexp(x1_and_x2): + x1, x2 = x1_and_x2 sanity_check(x1, x2) xp.logaddexp(x1, x2) # The spec doesn't require any behavior for this function. We could test From 6d440af9f60c289ae5edc713e0e186850dffaf2c Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Tue, 5 Oct 2021 11:05:30 +0100 Subject: [PATCH 12/23] Use ndarrays for less/greater tests --- array_api_tests/test_elementwise_functions.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/array_api_tests/test_elementwise_functions.py b/array_api_tests/test_elementwise_functions.py index 5d28fb2e..5745d704 100644 --- a/array_api_tests/test_elementwise_functions.py +++ b/array_api_tests/test_elementwise_functions.py @@ -481,9 +481,9 @@ def test_floor_divide(args): # TODO: Test the exact output for floor_divide. -@given(two_numeric_dtypes.flatmap(lambda i: two_array_scalars(*i))) -def test_greater(args): - x1, x2 = args +@given(two_mutual_arrays(numeric_dtype_objects)) +def test_greater(x1_and_x2): + x1, x2 = x1_and_x2 sanity_check(x1, x2) a = xp.greater(x1, x2) @@ -511,9 +511,9 @@ def test_greater(args): assert aidx.shape == x1idx.shape == x2idx.shape assert bool(aidx) == (scalar_func(x1idx) > scalar_func(x2idx)) -@given(two_numeric_dtypes.flatmap(lambda i: two_array_scalars(*i))) -def test_greater_equal(args): - x1, x2 = args +@given(two_mutual_arrays(numeric_dtype_objects)) +def test_greater_equal(x1_and_x2): + x1, x2 = x1_and_x2 sanity_check(x1, x2) a = xp.greater_equal(x1, x2) @@ -587,9 +587,9 @@ def test_isnan(x): s = float(x[idx]) assert bool(a[idx]) == math.isnan(s) -@given(two_numeric_dtypes.flatmap(lambda i: two_array_scalars(*i))) -def test_less(args): - x1, x2 = args +@given(two_mutual_arrays(numeric_dtype_objects)) +def test_less(x1_and_x2): + x1, x2 = x1_and_x2 sanity_check(x1, x2) a = xp.less(x1, x2) @@ -617,9 +617,9 @@ def test_less(args): assert aidx.shape == x1idx.shape == x2idx.shape assert bool(aidx) == (scalar_func(x1idx) < scalar_func(x2idx)) -@given(two_numeric_dtypes.flatmap(lambda i: two_array_scalars(*i))) -def test_less_equal(args): - x1, x2 = args +@given(two_mutual_arrays(numeric_dtype_objects)) +def test_less_equal(x1_and_x2): + x1, x2 = x1_and_x2 sanity_check(x1, x2) a = xp.less_equal(x1, x2) From 5adab4f5c651f9ea1e87ab55c46dfedc402d2d2a Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Tue, 5 Oct 2021 11:07:05 +0100 Subject: [PATCH 13/23] Use ndarrays for nan and inf tests --- array_api_tests/test_elementwise_functions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/array_api_tests/test_elementwise_functions.py b/array_api_tests/test_elementwise_functions.py index 5745d704..fb6624ee 100644 --- a/array_api_tests/test_elementwise_functions.py +++ b/array_api_tests/test_elementwise_functions.py @@ -541,7 +541,7 @@ def test_greater_equal(x1_and_x2): assert aidx.shape == x1idx.shape == x2idx.shape assert bool(aidx) == (scalar_func(x1idx) >= scalar_func(x2idx)) -@given(numeric_scalars) +@given(xps.arrays(dtype=xps.numeric_dtypes(), shape=shapes)) def test_isfinite(x): a = xp.isfinite(x) TRUE = true(x.shape) @@ -557,7 +557,7 @@ def test_isfinite(x): s = float(x[idx]) assert bool(a[idx]) == math.isfinite(s) -@given(numeric_scalars) +@given(xps.arrays(dtype=xps.numeric_dtypes(), shape=shapes)) def test_isinf(x): a = xp.isinf(x) FALSE = false(x.shape) @@ -572,7 +572,7 @@ def test_isinf(x): s = float(x[idx]) assert bool(a[idx]) == math.isinf(s) -@given(numeric_scalars) +@given(xps.arrays(dtype=xps.numeric_dtypes(), shape=shapes)) def test_isnan(x): a = xp.isnan(x) FALSE = false(x.shape) From 5ec583e0e73496590e7eba69dd83143b1ada1108 Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Tue, 5 Oct 2021 13:05:56 +0100 Subject: [PATCH 14/23] Use ndarrays for floor and exp tests --- array_api_tests/test_elementwise_functions.py | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/array_api_tests/test_elementwise_functions.py b/array_api_tests/test_elementwise_functions.py index fb6624ee..05c6422c 100644 --- a/array_api_tests/test_elementwise_functions.py +++ b/array_api_tests/test_elementwise_functions.py @@ -32,10 +32,10 @@ negative_mathematical_sign, logical_not, logical_or, logical_and, inrange, π, one, zero, infinity, isnegative, all as array_all, any as - array_any, int_to_dtype, bool as bool_dtype, + array_any, int_to_dtype, assert_integral, less_equal, isintegral, isfinite, ndindex, promote_dtypes, is_integer_dtype, - is_float_dtype, not_equal, float64, asarray, + is_float_dtype, not_equal, asarray, dtype_ranges, full, true, false, assert_same_sign, isnan, equal, less) # We might as well use this implementation rather than requiring @@ -214,7 +214,7 @@ def test_bitwise_and(args): if not (x1.shape == x2.shape == ()): raise RuntimeError("Error: test_bitwise_and needs to be updated for nonscalar array inputs") - if a.dtype == bool_dtype: + if a.dtype == xp.bool: x = bool(x1) y = bool(x2) res = bool(a) @@ -258,7 +258,7 @@ def test_bitwise_invert(x): # TODO: Generalize this properly for inputs that are arrays. if not (x.shape == ()): raise RuntimeError("Error: test_bitwise_invert needs to be updated for nonscalar array inputs") - if a.dtype == bool_dtype: + if a.dtype == xp.bool: x = bool(x) res = bool(a) assert (not x) == res @@ -278,7 +278,7 @@ def test_bitwise_or(args): # TODO: Generalize this properly for inputs that are arrays. if not (x1.shape == x2.shape == ()): raise RuntimeError("Error: test_bitwise_or needs to be updated for nonscalar array inputs") - if a.dtype == bool_dtype: + if a.dtype == xp.bool: x = bool(x1) y = bool(x2) res = bool(a) @@ -319,7 +319,7 @@ def test_bitwise_xor(args): # TODO: Generalize this properly for inputs that are arrays. if not (x1.shape == x2.shape == ()): raise RuntimeError("Error: test_bitwise_xor needs to be updated for nonscalar array inputs") - if a.dtype == bool_dtype: + if a.dtype == xp.bool: x = bool(x1) y = bool(x2) res = bool(a) @@ -399,9 +399,9 @@ def test_equal(x1_and_x2): # Second, manually promote the dtypes. This is important. If the internal # type promotion in equal() is wrong, it will not be directly visible in # the output type, but it can lead to wrong answers. For example, - # equal(array(1.0, dtype=float32), array(1.00000001, dtype=float64)) will - # be wrong if the float64 is downcast to float32. # be wrong if the - # float64 is downcast to float32. See the comment on + # equal(array(1.0, dtype=float32), array(1.00000001, dtype=xp.float64)) will + # be wrong if the xp.float64 is downcast to float32. # be wrong if the + # xp.float64 is downcast to float32. See the comment on # test_elementwise_function_two_arg_bool_type_promotion() in # test_type_promotion.py. The type promotion for equal() is not *really* # tested in that file, because doing so requires doing the consistency @@ -424,7 +424,7 @@ def test_equal(x1_and_x2): assert aidx.shape == x1idx.shape == x2idx.shape assert bool(aidx) == (scalar_func(x1idx) == scalar_func(x2idx)) -@given(floating_scalars) +@given(xps.arrays(dtype=xps.floating_dtypes(), shape=shapes)) def test_exp(x): a = xp.exp(x) INFINITY = infinity(x.shape, x.dtype) @@ -435,7 +435,7 @@ def test_exp(x): # mapped to nan, which is already tested in the special cases. assert_exactly_equal(domain, codomain) -@given(floating_scalars) +@given(xps.arrays(dtype=xps.floating_dtypes(), shape=shapes)) def test_expm1(x): a = xp.expm1(x) INFINITY = infinity(x.shape, x.dtype) @@ -446,7 +446,7 @@ def test_expm1(x): # mapped to nan, which is already tested in the special cases. assert_exactly_equal(domain, codomain) -@given(numeric_scalars) +@given(xps.arrays(dtype=xps.numeric_dtypes(), shape=shapes)) def test_floor(x): # This test is almost identical to test_ceil a = xp.floor(x) @@ -457,27 +457,28 @@ def test_floor(x): integers = isintegral(x) assert_exactly_equal(a[integers], x[integers]) -@given(two_numeric_dtypes.flatmap(lambda i: two_array_scalars(*i))) -def test_floor_divide(args): - x1, x2 = args +@given(two_mutual_arrays(numeric_dtype_objects)) +def test_floor_divide(x1_and_x2): + x1, x2 = x1_and_x2 sanity_check(x1, x2) if is_integer_dtype(x1.dtype): # The spec does not specify the behavior for division by 0 for integer # dtypes. A library may choose to raise an exception in this case, so # we avoid passing it in entirely. - nonzero = not_equal(x2, zero(x2.shape, x2.dtype)) + assume(not xp.any(x1 == 0) and not xp.any(x2 == 0)) div = xp.divide( - asarray(x1[nonzero], dtype=float64), - asarray(x2[nonzero], dtype=float64)) - a = xp.floor_divide(x1[nonzero], x2[nonzero]) + asarray(x1, dtype=xp.float64), + asarray(x2, dtype=xp.float64), + ) else: div = xp.divide(x1, x2) - a = xp.floor_divide(x1, x2) + + out = xp.floor_divide(x1, x2) # TODO: The spec doesn't clearly specify the behavior of floor_divide on # infinities. See https://github.com/data-apis/array-api/issues/199. finite = isfinite(div) - assert_integral(a[finite]) + assert_integral(out[finite]) # TODO: Test the exact output for floor_divide. From d6807b0146b6089561da24aca6909c789a495902 Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Tue, 5 Oct 2021 13:14:29 +0100 Subject: [PATCH 15/23] Use ndarrays for cos/ceil/divide tests --- array_api_tests/test_elementwise_functions.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/array_api_tests/test_elementwise_functions.py b/array_api_tests/test_elementwise_functions.py index 05c6422c..fb1b9c6d 100644 --- a/array_api_tests/test_elementwise_functions.py +++ b/array_api_tests/test_elementwise_functions.py @@ -331,7 +331,7 @@ def test_bitwise_xor(args): ans = int_to_dtype(x ^ y, dtype_nbits(a.dtype), dtype_signed(a.dtype)) assert ans == res -@given(numeric_scalars) +@given(xps.arrays(dtype=xps.numeric_dtypes(), shape=shapes)) def test_ceil(x): # This test is almost identical to test_floor() a = xp.ceil(x) @@ -342,7 +342,7 @@ def test_ceil(x): integers = isintegral(x) assert_exactly_equal(a[integers], x[integers]) -@given(floating_scalars) +@given(xps.arrays(dtype=xps.floating_dtypes(), shape=shapes)) def test_cos(x): a = xp.cos(x) ONE = one(x.shape, x.dtype) @@ -353,7 +353,7 @@ def test_cos(x): # to nan, which is already tested in the special cases. assert_exactly_equal(domain, codomain) -@given(floating_scalars) +@given(xps.arrays(dtype=xps.floating_dtypes(), shape=shapes)) def test_cosh(x): a = xp.cosh(x) INFINITY = infinity(x.shape, x.dtype) @@ -363,9 +363,9 @@ def test_cosh(x): # mapped to nan, which is already tested in the special cases. assert_exactly_equal(domain, codomain) -@given(two_floating_dtypes.flatmap(lambda i: two_array_scalars(*i))) -def test_divide(args): - x1, x2 = args +@given(two_mutual_arrays(floating_dtype_objects)) +def test_divide(x1_and_x2): + x1, x2 = x1_and_x2 sanity_check(x1, x2) xp.divide(x1, x2) # There isn't much we can test here. The spec doesn't require any behavior From 14b056b7cf6961b7dc3f824c2498cb4472609e25 Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Tue, 5 Oct 2021 13:20:29 +0100 Subject: [PATCH 16/23] Use ndarrays for abs and some inverse trig tests --- array_api_tests/test_elementwise_functions.py | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/array_api_tests/test_elementwise_functions.py b/array_api_tests/test_elementwise_functions.py index fb1b9c6d..e0d0ca42 100644 --- a/array_api_tests/test_elementwise_functions.py +++ b/array_api_tests/test_elementwise_functions.py @@ -69,7 +69,7 @@ def sanity_check(x1, x2): except ValueError: raise RuntimeError("Error in test generation (probably a bug in the test suite") -@given(numeric_scalars) +@given(xps.arrays(dtype=xps.numeric_dtypes(), shape=shapes)) def test_abs(x): if is_integer_dtype(x.dtype): minval = dtype_ranges[x.dtype][0] @@ -86,7 +86,7 @@ def test_abs(x): # abs(x) = x for x >= 0 assert_exactly_equal(a[logical_not(less_zero)], x[logical_not(less_zero)]) -@given(floating_scalars) +@given(xps.arrays(dtype=xps.floating_dtypes(), shape=shapes)) def test_acos(x): a = xp.acos(x) ONE = one(x.shape, x.dtype) @@ -100,7 +100,7 @@ def test_acos(x): # nan, which is already tested in the special cases. assert_exactly_equal(domain, codomain) -@given(floating_scalars) +@given(xps.arrays(dtype=xps.floating_dtypes(), shape=shapes)) def test_acosh(x): a = xp.acosh(x) ONE = one(x.shape, x.dtype) @@ -112,9 +112,9 @@ def test_acosh(x): # to nan, which is already tested in the special cases. assert_exactly_equal(domain, codomain) -@given(two_numeric_dtypes.flatmap(lambda i: two_array_scalars(*i))) -def test_add(args): - x1, x2 = args +@given(two_mutual_arrays(numeric_dtype_objects)) +def test_add(x1_and_x2): + x1, x2 = x1_and_x2 sanity_check(x1, x2) a = xp.add(x1, x2) @@ -123,7 +123,7 @@ def test_add(args): assert_exactly_equal(a, b) # TODO: Test that add is actually addition -@given(floating_scalars) +@given(xps.arrays(dtype=xps.floating_dtypes(), shape=shapes)) def test_asin(x): a = xp.asin(x) ONE = one(x.shape, x.dtype) @@ -134,7 +134,7 @@ def test_asin(x): # mapped to nan, which is already tested in the special cases. assert_exactly_equal(domain, codomain) -@given(floating_scalars) +@given(xps.arrays(dtype=xps.floating_dtypes(), shape=shapes)) def test_asinh(x): a = xp.asinh(x) INFINITY = infinity(x.shape, x.dtype) @@ -144,7 +144,7 @@ def test_asinh(x): # mapped to nan, which is already tested in the special cases. assert_exactly_equal(domain, codomain) -@given(floating_scalars) +@given(xps.arrays(dtype=xps.floating_dtypes(), shape=shapes)) def test_atan(x): a = xp.atan(x) INFINITY = infinity(x.shape, x.dtype) @@ -155,9 +155,9 @@ def test_atan(x): # mapped to nan, which is already tested in the special cases. assert_exactly_equal(domain, codomain) -@given(two_floating_dtypes.flatmap(lambda i: two_array_scalars(*i))) -def test_atan2(args): - x1, x2 = args +@given(two_mutual_arrays(floating_dtype_objects)) +def test_atan2(x1_and_x2): + x1, x2 = x1_and_x2 sanity_check(x1, x2) a = xp.atan2(x1, x2) INFINITY1 = infinity(x1.shape, x1.dtype) @@ -192,7 +192,7 @@ def test_atan2(args): assert_exactly_equal(logical_or(logical_and(negx1, posx2), logical_and(negx1, negx2)), nega) -@given(floating_scalars) +@given(xps.arrays(dtype=xps.floating_dtypes(), shape=shapes)) def test_atanh(x): a = xp.atanh(x) ONE = one(x.shape, x.dtype) From df5038a1c64a2ff5e950d8c8d56e8a52ba6eb2dc Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Wed, 6 Oct 2021 10:24:48 +0100 Subject: [PATCH 17/23] Alias array module and helper modules --- array_api_tests/test_elementwise_functions.py | 598 +++++++++--------- 1 file changed, 289 insertions(+), 309 deletions(-) diff --git a/array_api_tests/test_elementwise_functions.py b/array_api_tests/test_elementwise_functions.py index e0d0ca42..68e58616 100644 --- a/array_api_tests/test_elementwise_functions.py +++ b/array_api_tests/test_elementwise_functions.py @@ -14,105 +14,87 @@ """ -from hypothesis import given, assume -from hypothesis.strategies import composite, just - import math -from .hypothesis_helpers import (integer_dtype_objects, - floating_dtype_objects, - numeric_dtype_objects, - integer_or_boolean_dtype_objects, - boolean_dtype_objects, floating_dtypes, - numeric_dtypes, integer_or_boolean_dtypes, - boolean_dtypes, mutually_promotable_dtypes, - array_scalars, two_mutual_arrays, xps, shapes) -from .array_helpers import (assert_exactly_equal, negative, - positive_mathematical_sign, - negative_mathematical_sign, logical_not, - logical_or, logical_and, inrange, π, one, zero, - infinity, isnegative, all as array_all, any as - array_any, int_to_dtype, - assert_integral, less_equal, isintegral, isfinite, - ndindex, promote_dtypes, is_integer_dtype, - is_float_dtype, not_equal, asarray, - dtype_ranges, full, true, false, assert_same_sign, - isnan, equal, less) +from hypothesis import assume, given +from hypothesis import strategies as st + +from . import _array_module as xp +from . import array_helpers as ah +from . import hypothesis_helpers as hh # We might as well use this implementation rather than requiring # mod.broadcast_shapes(). See test_equal() and others. from .test_broadcasting import broadcast_shapes -from . import _array_module as xp - -# integer_scalars = array_scalars(integer_dtypes) -floating_scalars = array_scalars(floating_dtypes) -numeric_scalars = array_scalars(numeric_dtypes) -integer_or_boolean_scalars = array_scalars(integer_or_boolean_dtypes) -boolean_scalars = array_scalars(boolean_dtypes) +# integer_scalars = hh.array_scalars(integer_dtypes) +floating_scalars = hh.array_scalars(hh.floating_dtypes) +numeric_scalars = hh.array_scalars(hh.numeric_dtypes) +integer_or_boolean_scalars = hh.array_scalars(hh.integer_or_boolean_dtypes) +boolean_scalars = hh.array_scalars(hh.boolean_dtypes) -two_integer_dtypes = mutually_promotable_dtypes(integer_dtype_objects) -two_floating_dtypes = mutually_promotable_dtypes(floating_dtype_objects) -two_numeric_dtypes = mutually_promotable_dtypes(numeric_dtype_objects) -two_integer_or_boolean_dtypes = mutually_promotable_dtypes(integer_or_boolean_dtype_objects) -two_boolean_dtypes = mutually_promotable_dtypes(boolean_dtype_objects) -two_any_dtypes = mutually_promotable_dtypes() +two_integer_dtypes = hh.mutually_promotable_dtypes(hh.integer_dtype_objects) +two_floating_dtypes = hh.mutually_promotable_dtypes(hh.floating_dtype_objects) +two_numeric_dtypes = hh.mutually_promotable_dtypes(hh.numeric_dtype_objects) +two_integer_or_boolean_dtypes = hh.mutually_promotable_dtypes(hh.integer_or_boolean_dtype_objects) +two_boolean_dtypes = hh.mutually_promotable_dtypes(hh.boolean_dtype_objects) +two_any_dtypes = hh.mutually_promotable_dtypes() -@composite +@st.composite def two_array_scalars(draw, dtype1, dtype2): # two_dtypes should be a strategy that returns two dtypes (like - # mutually_promotable_dtypes()) - return draw(array_scalars(just(dtype1))), draw(array_scalars(just(dtype2))) + # hh.mutually_promotable_dtypes()) + return draw(hh.array_scalars(st.just(dtype1))), draw(hh.array_scalars(st.just(dtype2))) def sanity_check(x1, x2): try: - promote_dtypes(x1.dtype, x2.dtype) + ah.promote_dtypes(x1.dtype, x2.dtype) except ValueError: raise RuntimeError("Error in test generation (probably a bug in the test suite") -@given(xps.arrays(dtype=xps.numeric_dtypes(), shape=shapes)) +@given(hh.xps.arrays(dtype=hh.xps.numeric_dtypes(), shape=hh.shapes)) def test_abs(x): - if is_integer_dtype(x.dtype): - minval = dtype_ranges[x.dtype][0] + if ah.is_integer_dtype(x.dtype): + minval = ah.dtype_ranges[x.dtype][0] if minval < 0: # abs of the smallest representable negative integer is not defined - mask = not_equal(x, full(x.shape, minval, dtype=x.dtype)) + mask = xp.not_equal(x, ah.full(x.shape, minval, dtype=x.dtype)) x = x[mask] a = xp.abs(x) - assert array_all(logical_not(negative_mathematical_sign(a))), "abs(x) did not have positive sign" - less_zero = negative_mathematical_sign(x) - negx = negative(x) + assert ah.all(ah.logical_not(ah.negative_mathematical_sign(a))), "abs(x) did not have positive sign" + less_zero = ah.negative_mathematical_sign(x) + negx = ah.negative(x) # abs(x) = -x for x < 0 - assert_exactly_equal(a[less_zero], negx[less_zero]) + ah.assert_exactly_equal(a[less_zero], negx[less_zero]) # abs(x) = x for x >= 0 - assert_exactly_equal(a[logical_not(less_zero)], x[logical_not(less_zero)]) + ah.assert_exactly_equal(a[ah.logical_not(less_zero)], x[ah.logical_not(less_zero)]) -@given(xps.arrays(dtype=xps.floating_dtypes(), shape=shapes)) +@given(hh.xps.arrays(dtype=hh.xps.floating_dtypes(), shape=hh.shapes)) def test_acos(x): a = xp.acos(x) - ONE = one(x.shape, x.dtype) + ONE = ah.one(x.shape, x.dtype) # Here (and elsewhere), should technically be a.dtype, but this is the # same as x.dtype, as tested by the type_promotion tests. - PI = π(x.shape, x.dtype) - ZERO = zero(x.shape, x.dtype) - domain = inrange(x, -ONE, ONE) - codomain = inrange(a, ZERO, PI) + PI = ah.π(x.shape, x.dtype) + ZERO = ah.zero(x.shape, x.dtype) + domain = ah.inrange(x, -ONE, ONE) + codomain = ah.inrange(a, ZERO, PI) # acos maps [-1, 1] to [0, pi]. Values outside this domain are mapped to # nan, which is already tested in the special cases. - assert_exactly_equal(domain, codomain) + ah.assert_exactly_equal(domain, codomain) -@given(xps.arrays(dtype=xps.floating_dtypes(), shape=shapes)) +@given(hh.xps.arrays(dtype=hh.xps.floating_dtypes(), shape=hh.shapes)) def test_acosh(x): a = xp.acosh(x) - ONE = one(x.shape, x.dtype) - INFINITY = infinity(x.shape, x.dtype) - ZERO = zero(x.shape, x.dtype) - domain = inrange(x, ONE, INFINITY) - codomain = inrange(a, ZERO, INFINITY) + ONE = ah.one(x.shape, x.dtype) + INFINITY = ah.infinity(x.shape, x.dtype) + ZERO = ah.zero(x.shape, x.dtype) + domain = ah.inrange(x, ONE, INFINITY) + codomain = ah.inrange(a, ZERO, INFINITY) # acosh maps [-1, inf] to [0, inf]. Values outside this domain are mapped # to nan, which is already tested in the special cases. - assert_exactly_equal(domain, codomain) + ah.assert_exactly_equal(domain, codomain) -@given(two_mutual_arrays(numeric_dtype_objects)) +@given(hh.two_mutual_arrays(hh.numeric_dtype_objects)) def test_add(x1_and_x2): x1, x2 = x1_and_x2 sanity_check(x1, x2) @@ -120,57 +102,57 @@ def test_add(x1_and_x2): b = xp.add(x2, x1) # add is commutative - assert_exactly_equal(a, b) + ah.assert_exactly_equal(a, b) # TODO: Test that add is actually addition -@given(xps.arrays(dtype=xps.floating_dtypes(), shape=shapes)) +@given(hh.xps.arrays(dtype=hh.xps.floating_dtypes(), shape=hh.shapes)) def test_asin(x): a = xp.asin(x) - ONE = one(x.shape, x.dtype) - PI = π(x.shape, x.dtype) - domain = inrange(x, -ONE, ONE) - codomain = inrange(a, -PI/2, PI/2) + ONE = ah.one(x.shape, x.dtype) + PI = ah.π(x.shape, x.dtype) + domain = ah.inrange(x, -ONE, ONE) + codomain = ah.inrange(a, -PI/2, PI/2) # asin maps [-1, 1] to [-pi/2, pi/2]. Values outside this domain are # mapped to nan, which is already tested in the special cases. - assert_exactly_equal(domain, codomain) + ah.assert_exactly_equal(domain, codomain) -@given(xps.arrays(dtype=xps.floating_dtypes(), shape=shapes)) +@given(hh.xps.arrays(dtype=hh.xps.floating_dtypes(), shape=hh.shapes)) def test_asinh(x): a = xp.asinh(x) - INFINITY = infinity(x.shape, x.dtype) - domain = inrange(x, -INFINITY, INFINITY) - codomain = inrange(a, -INFINITY, INFINITY) + INFINITY = ah.infinity(x.shape, x.dtype) + domain = ah.inrange(x, -INFINITY, INFINITY) + codomain = ah.inrange(a, -INFINITY, INFINITY) # asinh maps [-inf, inf] to [-inf, inf]. Values outside this domain are # mapped to nan, which is already tested in the special cases. - assert_exactly_equal(domain, codomain) + ah.assert_exactly_equal(domain, codomain) -@given(xps.arrays(dtype=xps.floating_dtypes(), shape=shapes)) +@given(hh.xps.arrays(dtype=hh.xps.floating_dtypes(), shape=hh.shapes)) def test_atan(x): a = xp.atan(x) - INFINITY = infinity(x.shape, x.dtype) - PI = π(x.shape, x.dtype) - domain = inrange(x, -INFINITY, INFINITY) - codomain = inrange(a, -PI/2, PI/2) + INFINITY = ah.infinity(x.shape, x.dtype) + PI = ah.π(x.shape, x.dtype) + domain = ah.inrange(x, -INFINITY, INFINITY) + codomain = ah.inrange(a, -PI/2, PI/2) # atan maps [-inf, inf] to [-pi/2, pi/2]. Values outside this domain are # mapped to nan, which is already tested in the special cases. - assert_exactly_equal(domain, codomain) + ah.assert_exactly_equal(domain, codomain) -@given(two_mutual_arrays(floating_dtype_objects)) +@given(hh.two_mutual_arrays(hh.floating_dtype_objects)) def test_atan2(x1_and_x2): x1, x2 = x1_and_x2 sanity_check(x1, x2) a = xp.atan2(x1, x2) - INFINITY1 = infinity(x1.shape, x1.dtype) - INFINITY2 = infinity(x2.shape, x2.dtype) - PI = π(a.shape, a.dtype) - domainx1 = inrange(x1, -INFINITY1, INFINITY1) - domainx2 = inrange(x2, -INFINITY2, INFINITY2) - # codomain = inrange(a, -PI, PI, 1e-5) - codomain = inrange(a, -PI, PI) + INFINITY1 = ah.infinity(x1.shape, x1.dtype) + INFINITY2 = ah.infinity(x2.shape, x2.dtype) + PI = ah.π(a.shape, a.dtype) + domainx1 = ah.inrange(x1, -INFINITY1, INFINITY1) + domainx2 = ah.inrange(x2, -INFINITY2, INFINITY2) + # codomain = ah.inrange(a, -PI, PI, 1e-5) + codomain = ah.inrange(a, -PI, PI) # atan2 maps [-inf, inf] x [-inf, inf] to [-pi, pi]. Values outside # this domain are mapped to nan, which is already tested in the special # cases. - assert_exactly_equal(logical_and(domainx1, domainx2), codomain) + ah.assert_exactly_equal(ah.logical_and(domainx1, domainx2), codomain) # From the spec: # # The mathematical signs of `x1_i` and `x2_i` determine the quadrant of @@ -181,27 +163,27 @@ def test_atan2(x1_and_x2): # This is equivalent to atan2(x1, x2) has the same sign as x1 when x2 is # finite. - posx1 = positive_mathematical_sign(x1) - negx1 = negative_mathematical_sign(x1) - posx2 = positive_mathematical_sign(x2) - negx2 = negative_mathematical_sign(x2) - posa = positive_mathematical_sign(a) - nega = negative_mathematical_sign(a) - assert_exactly_equal(logical_or(logical_and(posx1, posx2), - logical_and(posx1, negx2)), posa) - assert_exactly_equal(logical_or(logical_and(negx1, posx2), - logical_and(negx1, negx2)), nega) - -@given(xps.arrays(dtype=xps.floating_dtypes(), shape=shapes)) + posx1 = ah.positive_mathematical_sign(x1) + negx1 = ah.negative_mathematical_sign(x1) + posx2 = ah.positive_mathematical_sign(x2) + negx2 = ah.negative_mathematical_sign(x2) + posa = ah.positive_mathematical_sign(a) + nega = ah.negative_mathematical_sign(a) + ah.assert_exactly_equal(ah.logical_or(ah.logical_and(posx1, posx2), + ah.logical_and(posx1, negx2)), posa) + ah.assert_exactly_equal(ah.logical_or(ah.logical_and(negx1, posx2), + ah.logical_and(negx1, negx2)), nega) + +@given(hh.xps.arrays(dtype=hh.xps.floating_dtypes(), shape=hh.shapes)) def test_atanh(x): a = xp.atanh(x) - ONE = one(x.shape, x.dtype) - INFINITY = infinity(x.shape, x.dtype) - domain = inrange(x, -ONE, ONE) - codomain = inrange(a, -INFINITY, INFINITY) + ONE = ah.one(x.shape, x.dtype) + INFINITY = ah.infinity(x.shape, x.dtype) + domain = ah.inrange(x, -ONE, ONE) + codomain = ah.inrange(a, -INFINITY, INFINITY) # atanh maps [-1, 1] to [-inf, inf]. Values outside this domain are # mapped to nan, which is already tested in the special cases. - assert_exactly_equal(domain, codomain) + ah.assert_exactly_equal(domain, codomain) @given(two_integer_or_boolean_dtypes.flatmap(lambda i: two_array_scalars(*i))) def test_bitwise_and(args): @@ -223,7 +205,7 @@ def test_bitwise_and(args): x = int(x1) y = int(x2) res = int(a) - ans = int_to_dtype(x & y, dtype_nbits(a.dtype), dtype_signed(a.dtype)) + ans = ah.int_to_dtype(x & y, dtype_nbits(a.dtype), dtype_signed(a.dtype)) assert ans == res @given(two_integer_dtypes.flatmap(lambda i: two_array_scalars(*i))) @@ -231,9 +213,8 @@ def test_bitwise_left_shift(args): from .test_type_promotion import dtype_nbits, dtype_signed x1, x2 = args sanity_check(x1, x2) - negative_x2 = isnegative(x2) - if array_any(negative_x2): - assume(False) + negative_x2 = ah.isnegative(x2) + assume(not xp.any(negative_x2)) a = xp.bitwise_left_shift(x1, x2) # Compare against the Python << operator. # TODO: Generalize this properly for inputs that are arrays. @@ -246,7 +227,7 @@ def test_bitwise_left_shift(args): ans = 0 else: ans = x << y - ans = int_to_dtype(ans, dtype_nbits(a.dtype), dtype_signed(a.dtype)) + ans = ah.int_to_dtype(ans, dtype_nbits(a.dtype), dtype_signed(a.dtype)) res = int(a) assert ans == res @@ -265,7 +246,7 @@ def test_bitwise_invert(x): else: x = int(x) res = int(a) - ans = int_to_dtype(~x, dtype_nbits(a.dtype), dtype_signed(a.dtype)) + ans = ah.int_to_dtype(~x, dtype_nbits(a.dtype), dtype_signed(a.dtype)) assert ans == res @given(two_integer_or_boolean_dtypes.flatmap(lambda i: two_array_scalars(*i))) @@ -287,7 +268,7 @@ def test_bitwise_or(args): x = int(x1) y = int(x2) res = int(a) - ans = int_to_dtype(x | y, dtype_nbits(a.dtype), dtype_signed(a.dtype)) + ans = ah.int_to_dtype(x | y, dtype_nbits(a.dtype), dtype_signed(a.dtype)) assert ans == res @given(two_integer_dtypes.flatmap(lambda i: two_array_scalars(*i))) @@ -295,9 +276,8 @@ def test_bitwise_right_shift(args): from .test_type_promotion import dtype_nbits, dtype_signed x1, x2 = args sanity_check(x1, x2) - negative_x2 = isnegative(x2) - if array_any(negative_x2): - assume(False) + negative_x2 = ah.isnegative(x2) + assume(not xp.any(negative_x2)) a = xp.bitwise_right_shift(x1, x2) # Compare against the Python >> operator. # TODO: Generalize this properly for inputs that are arrays. @@ -305,7 +285,7 @@ def test_bitwise_right_shift(args): raise RuntimeError("Error: test_bitwise_right_shift needs to be updated for nonscalar array inputs") x = int(x1) y = int(x2) - ans = int_to_dtype(x >> y, dtype_nbits(a.dtype), dtype_signed(a.dtype)) + ans = ah.int_to_dtype(x >> y, dtype_nbits(a.dtype), dtype_signed(a.dtype)) res = int(a) assert ans == res @@ -328,42 +308,42 @@ def test_bitwise_xor(args): x = int(x1) y = int(x2) res = int(a) - ans = int_to_dtype(x ^ y, dtype_nbits(a.dtype), dtype_signed(a.dtype)) + ans = ah.int_to_dtype(x ^ y, dtype_nbits(a.dtype), dtype_signed(a.dtype)) assert ans == res -@given(xps.arrays(dtype=xps.numeric_dtypes(), shape=shapes)) +@given(hh.xps.arrays(dtype=hh.xps.numeric_dtypes(), shape=hh.shapes)) def test_ceil(x): # This test is almost identical to test_floor() a = xp.ceil(x) - finite = isfinite(x) - assert_integral(a[finite]) - assert array_all(less_equal(x[finite], a[finite])) - assert array_all(less_equal(a[finite] - x[finite], one(x[finite].shape, x.dtype))) - integers = isintegral(x) - assert_exactly_equal(a[integers], x[integers]) - -@given(xps.arrays(dtype=xps.floating_dtypes(), shape=shapes)) + finite = ah.isfinite(x) + ah.assert_integral(a[finite]) + assert ah.all(ah.less_equal(x[finite], a[finite])) + assert ah.all(ah.less_equal(a[finite] - x[finite], ah.one(x[finite].shape, x.dtype))) + integers = ah.isintegral(x) + ah.assert_exactly_equal(a[integers], x[integers]) + +@given(hh.xps.arrays(dtype=hh.xps.floating_dtypes(), shape=hh.shapes)) def test_cos(x): a = xp.cos(x) - ONE = one(x.shape, x.dtype) - INFINITY = infinity(x.shape, x.dtype) - domain = inrange(x, -INFINITY, INFINITY, open=True) - codomain = inrange(a, -ONE, ONE) + ONE = ah.one(x.shape, x.dtype) + INFINITY = ah.infinity(x.shape, x.dtype) + domain = ah.inrange(x, -INFINITY, INFINITY, open=True) + codomain = ah.inrange(a, -ONE, ONE) # cos maps (-inf, inf) to [-1, 1]. Values outside this domain are mapped # to nan, which is already tested in the special cases. - assert_exactly_equal(domain, codomain) + ah.assert_exactly_equal(domain, codomain) -@given(xps.arrays(dtype=xps.floating_dtypes(), shape=shapes)) +@given(hh.xps.arrays(dtype=hh.xps.floating_dtypes(), shape=hh.shapes)) def test_cosh(x): a = xp.cosh(x) - INFINITY = infinity(x.shape, x.dtype) - domain = inrange(x, -INFINITY, INFINITY) - codomain = inrange(a, -INFINITY, INFINITY) + INFINITY = ah.infinity(x.shape, x.dtype) + domain = ah.inrange(x, -INFINITY, INFINITY) + codomain = ah.inrange(a, -INFINITY, INFINITY) # cosh maps [-inf, inf] to [-inf, inf]. Values outside this domain are # mapped to nan, which is already tested in the special cases. - assert_exactly_equal(domain, codomain) + ah.assert_exactly_equal(domain, codomain) -@given(two_mutual_arrays(floating_dtype_objects)) +@given(hh.two_mutual_arrays(hh.floating_dtype_objects)) def test_divide(x1_and_x2): x1, x2 = x1_and_x2 sanity_check(x1, x2) @@ -375,12 +355,12 @@ def test_divide(x1_and_x2): # have those sorts in general for this module. -@given(two_mutual_arrays()) +@given(hh.two_mutual_arrays()) def test_equal(x1_and_x2): x1, x2 = x1_and_x2 sanity_check(x1, x2) - a = xp.equal(x1, x2) - # NOTE: assert_exactly_equal() itself uses equal(), so we must be careful + a = ah.equal(x1, x2) + # NOTE: ah.assert_exactly_equal() itself uses ah.equal(), so we must be careful # not to use it here. Otherwise, the test would be circular and # meaningless. Instead, we implement this by iterating every element of # the arrays and comparing them. The logic here is also used for the tests @@ -390,33 +370,33 @@ def test_equal(x1_and_x2): # First we broadcast the arrays so that they can be indexed uniformly. # TODO: it should be possible to skip this step if we instead generate - # indices to x1 and x2 that correspond to the broadcasted shapes. This + # indices to x1 and x2 that correspond to the broadcasted hh.shapes. This # would avoid the dependence in this test on broadcast_to(). shape = broadcast_shapes(x1.shape, x2.shape) _x1 = xp.broadcast_to(x1, shape) _x2 = xp.broadcast_to(x2, shape) # Second, manually promote the dtypes. This is important. If the internal - # type promotion in equal() is wrong, it will not be directly visible in + # type promotion in ah.equal() is wrong, it will not be directly visible in # the output type, but it can lead to wrong answers. For example, - # equal(array(1.0, dtype=float32), array(1.00000001, dtype=xp.float64)) will - # be wrong if the xp.float64 is downcast to float32. # be wrong if the + # ah.equal(array(1.0, dtype=xp.float32), array(1.00000001, dtype=xp.float64)) will + # be wrong if the float64 is downcast to float32. # be wrong if the # xp.float64 is downcast to float32. See the comment on # test_elementwise_function_two_arg_bool_type_promotion() in - # test_type_promotion.py. The type promotion for equal() is not *really* + # test_type_promotion.py. The type promotion for ah.equal() is not *really* # tested in that file, because doing so requires doing the consistency - # check we do here rather than just checking the result dtype. - promoted_dtype = promote_dtypes(x1.dtype, x2.dtype) - _x1 = xp.asarray(_x1, dtype=promoted_dtype) - _x2 = xp.asarray(_x2, dtype=promoted_dtype) + # check we do here rather than st.just checking the result dtype. + promoted_dtype = ah.promote_dtypes(x1.dtype, x2.dtype) + _x1 = ah.asarray(_x1, dtype=promoted_dtype) + _x2 = ah.asarray(_x2, dtype=promoted_dtype) - if is_integer_dtype(promoted_dtype): + if ah.is_integer_dtype(promoted_dtype): scalar_func = int - elif is_float_dtype(promoted_dtype): + elif ah.is_float_dtype(promoted_dtype): scalar_func = float else: scalar_func = bool - for idx in ndindex(shape): + for idx in ah.ndindex(shape): aidx = a[idx] x1idx = _x1[idx] x2idx = _x2[idx] @@ -424,51 +404,51 @@ def test_equal(x1_and_x2): assert aidx.shape == x1idx.shape == x2idx.shape assert bool(aidx) == (scalar_func(x1idx) == scalar_func(x2idx)) -@given(xps.arrays(dtype=xps.floating_dtypes(), shape=shapes)) +@given(hh.xps.arrays(dtype=hh.xps.floating_dtypes(), shape=hh.shapes)) def test_exp(x): a = xp.exp(x) - INFINITY = infinity(x.shape, x.dtype) - ZERO = zero(x.shape, x.dtype) - domain = inrange(x, -INFINITY, INFINITY) - codomain = inrange(a, ZERO, INFINITY) + INFINITY = ah.infinity(x.shape, x.dtype) + ZERO = ah.zero(x.shape, x.dtype) + domain = ah.inrange(x, -INFINITY, INFINITY) + codomain = ah.inrange(a, ZERO, INFINITY) # exp maps [-inf, inf] to [0, inf]. Values outside this domain are # mapped to nan, which is already tested in the special cases. - assert_exactly_equal(domain, codomain) + ah.assert_exactly_equal(domain, codomain) -@given(xps.arrays(dtype=xps.floating_dtypes(), shape=shapes)) +@given(hh.xps.arrays(dtype=hh.xps.floating_dtypes(), shape=hh.shapes)) def test_expm1(x): a = xp.expm1(x) - INFINITY = infinity(x.shape, x.dtype) - NEGONE = -one(x.shape, x.dtype) - domain = inrange(x, -INFINITY, INFINITY) - codomain = inrange(a, NEGONE, INFINITY) + INFINITY = ah.infinity(x.shape, x.dtype) + NEGONE = -ah.one(x.shape, x.dtype) + domain = ah.inrange(x, -INFINITY, INFINITY) + codomain = ah.inrange(a, NEGONE, INFINITY) # expm1 maps [-inf, inf] to [1, inf]. Values outside this domain are # mapped to nan, which is already tested in the special cases. - assert_exactly_equal(domain, codomain) + ah.assert_exactly_equal(domain, codomain) -@given(xps.arrays(dtype=xps.numeric_dtypes(), shape=shapes)) +@given(hh.xps.arrays(dtype=hh.xps.numeric_dtypes(), shape=hh.shapes)) def test_floor(x): # This test is almost identical to test_ceil a = xp.floor(x) - finite = isfinite(x) - assert_integral(a[finite]) - assert array_all(less_equal(a[finite], x[finite])) - assert array_all(less_equal(x[finite] - a[finite], one(x[finite].shape, x.dtype))) - integers = isintegral(x) - assert_exactly_equal(a[integers], x[integers]) - -@given(two_mutual_arrays(numeric_dtype_objects)) + finite = ah.isfinite(x) + ah.assert_integral(a[finite]) + assert ah.all(ah.less_equal(a[finite], x[finite])) + assert ah.all(ah.less_equal(x[finite] - a[finite], ah.one(x[finite].shape, x.dtype))) + integers = ah.isintegral(x) + ah.assert_exactly_equal(a[integers], x[integers]) + +@given(hh.two_mutual_arrays(hh.numeric_dtype_objects)) def test_floor_divide(x1_and_x2): x1, x2 = x1_and_x2 sanity_check(x1, x2) - if is_integer_dtype(x1.dtype): + if ah.is_integer_dtype(x1.dtype): # The spec does not specify the behavior for division by 0 for integer # dtypes. A library may choose to raise an exception in this case, so # we avoid passing it in entirely. assume(not xp.any(x1 == 0) and not xp.any(x2 == 0)) div = xp.divide( - asarray(x1, dtype=xp.float64), - asarray(x2, dtype=xp.float64), + ah.asarray(x1, dtype=xp.float64), + ah.asarray(x2, dtype=xp.float64), ) else: div = xp.divide(x1, x2) @@ -477,12 +457,12 @@ def test_floor_divide(x1_and_x2): # TODO: The spec doesn't clearly specify the behavior of floor_divide on # infinities. See https://github.com/data-apis/array-api/issues/199. - finite = isfinite(div) - assert_integral(out[finite]) + finite = ah.isfinite(div) + ah.assert_integral(out[finite]) # TODO: Test the exact output for floor_divide. -@given(two_mutual_arrays(numeric_dtype_objects)) +@given(hh.two_mutual_arrays(hh.numeric_dtype_objects)) def test_greater(x1_and_x2): x1, x2 = x1_and_x2 sanity_check(x1, x2) @@ -494,17 +474,17 @@ def test_greater(x1_and_x2): _x1 = xp.broadcast_to(x1, shape) _x2 = xp.broadcast_to(x2, shape) - promoted_dtype = promote_dtypes(x1.dtype, x2.dtype) - _x1 = xp.asarray(_x1, dtype=promoted_dtype) - _x2 = xp.asarray(_x2, dtype=promoted_dtype) + promoted_dtype = ah.promote_dtypes(x1.dtype, x2.dtype) + _x1 = ah.asarray(_x1, dtype=promoted_dtype) + _x2 = ah.asarray(_x2, dtype=promoted_dtype) - if is_integer_dtype(promoted_dtype): + if ah.is_integer_dtype(promoted_dtype): scalar_func = int - elif is_float_dtype(promoted_dtype): + elif ah.is_float_dtype(promoted_dtype): scalar_func = float else: scalar_func = bool - for idx in ndindex(shape): + for idx in ah.ndindex(shape): aidx = a[idx] x1idx = _x1[idx] x2idx = _x2[idx] @@ -512,7 +492,7 @@ def test_greater(x1_and_x2): assert aidx.shape == x1idx.shape == x2idx.shape assert bool(aidx) == (scalar_func(x1idx) > scalar_func(x2idx)) -@given(two_mutual_arrays(numeric_dtype_objects)) +@given(hh.two_mutual_arrays(hh.numeric_dtype_objects)) def test_greater_equal(x1_and_x2): x1, x2 = x1_and_x2 sanity_check(x1, x2) @@ -524,17 +504,17 @@ def test_greater_equal(x1_and_x2): _x1 = xp.broadcast_to(x1, shape) _x2 = xp.broadcast_to(x2, shape) - promoted_dtype = promote_dtypes(x1.dtype, x2.dtype) - _x1 = xp.asarray(_x1, dtype=promoted_dtype) - _x2 = xp.asarray(_x2, dtype=promoted_dtype) + promoted_dtype = ah.promote_dtypes(x1.dtype, x2.dtype) + _x1 = ah.asarray(_x1, dtype=promoted_dtype) + _x2 = ah.asarray(_x2, dtype=promoted_dtype) - if is_integer_dtype(promoted_dtype): + if ah.is_integer_dtype(promoted_dtype): scalar_func = int - elif is_float_dtype(promoted_dtype): + elif ah.is_float_dtype(promoted_dtype): scalar_func = float else: scalar_func = bool - for idx in ndindex(shape): + for idx in ah.ndindex(shape): aidx = a[idx] x1idx = _x1[idx] x2idx = _x2[idx] @@ -542,57 +522,57 @@ def test_greater_equal(x1_and_x2): assert aidx.shape == x1idx.shape == x2idx.shape assert bool(aidx) == (scalar_func(x1idx) >= scalar_func(x2idx)) -@given(xps.arrays(dtype=xps.numeric_dtypes(), shape=shapes)) +@given(hh.xps.arrays(dtype=hh.xps.numeric_dtypes(), shape=hh.shapes)) def test_isfinite(x): - a = xp.isfinite(x) - TRUE = true(x.shape) - if is_integer_dtype(x.dtype): - assert_exactly_equal(a, TRUE) + a = ah.isfinite(x) + TRUE = ah.true(x.shape) + if ah.is_integer_dtype(x.dtype): + ah.assert_exactly_equal(a, TRUE) # Test that isfinite, isinf, and isnan are self-consistent. - inf = logical_or(xp.isinf(x), xp.isnan(x)) - assert_exactly_equal(a, logical_not(inf)) + inf = ah.logical_or(xp.isinf(x), ah.isnan(x)) + ah.assert_exactly_equal(a, ah.logical_not(inf)) # Test the exact value by comparing to the math version - if is_float_dtype(x.dtype): - for idx in ndindex(x.shape): + if ah.is_float_dtype(x.dtype): + for idx in ah.ndindex(x.shape): s = float(x[idx]) assert bool(a[idx]) == math.isfinite(s) -@given(xps.arrays(dtype=xps.numeric_dtypes(), shape=shapes)) +@given(hh.xps.arrays(dtype=hh.xps.numeric_dtypes(), shape=hh.shapes)) def test_isinf(x): a = xp.isinf(x) - FALSE = false(x.shape) - if is_integer_dtype(x.dtype): - assert_exactly_equal(a, FALSE) - finite_or_nan = logical_or(xp.isfinite(x), xp.isnan(x)) - assert_exactly_equal(a, logical_not(finite_or_nan)) + FALSE = ah.false(x.shape) + if ah.is_integer_dtype(x.dtype): + ah.assert_exactly_equal(a, FALSE) + finite_or_nan = ah.logical_or(ah.isfinite(x), ah.isnan(x)) + ah.assert_exactly_equal(a, ah.logical_not(finite_or_nan)) # Test the exact value by comparing to the math version - if is_float_dtype(x.dtype): - for idx in ndindex(x.shape): + if ah.is_float_dtype(x.dtype): + for idx in ah.ndindex(x.shape): s = float(x[idx]) assert bool(a[idx]) == math.isinf(s) -@given(xps.arrays(dtype=xps.numeric_dtypes(), shape=shapes)) +@given(hh.xps.arrays(dtype=hh.xps.numeric_dtypes(), shape=hh.shapes)) def test_isnan(x): - a = xp.isnan(x) - FALSE = false(x.shape) - if is_integer_dtype(x.dtype): - assert_exactly_equal(a, FALSE) - finite_or_inf = logical_or(xp.isfinite(x), xp.isinf(x)) - assert_exactly_equal(a, logical_not(finite_or_inf)) + a = ah.isnan(x) + FALSE = ah.false(x.shape) + if ah.is_integer_dtype(x.dtype): + ah.assert_exactly_equal(a, FALSE) + finite_or_inf = ah.logical_or(ah.isfinite(x), xp.isinf(x)) + ah.assert_exactly_equal(a, ah.logical_not(finite_or_inf)) # Test the exact value by comparing to the math version - if is_float_dtype(x.dtype): - for idx in ndindex(x.shape): + if ah.is_float_dtype(x.dtype): + for idx in ah.ndindex(x.shape): s = float(x[idx]) assert bool(a[idx]) == math.isnan(s) -@given(two_mutual_arrays(numeric_dtype_objects)) +@given(hh.two_mutual_arrays(hh.numeric_dtype_objects)) def test_less(x1_and_x2): x1, x2 = x1_and_x2 sanity_check(x1, x2) - a = xp.less(x1, x2) + a = ah.less(x1, x2) # See the comments in test_equal() for a description of how this test # works. @@ -600,17 +580,17 @@ def test_less(x1_and_x2): _x1 = xp.broadcast_to(x1, shape) _x2 = xp.broadcast_to(x2, shape) - promoted_dtype = promote_dtypes(x1.dtype, x2.dtype) - _x1 = xp.asarray(_x1, dtype=promoted_dtype) - _x2 = xp.asarray(_x2, dtype=promoted_dtype) + promoted_dtype = ah.promote_dtypes(x1.dtype, x2.dtype) + _x1 = ah.asarray(_x1, dtype=promoted_dtype) + _x2 = ah.asarray(_x2, dtype=promoted_dtype) - if is_integer_dtype(promoted_dtype): + if ah.is_integer_dtype(promoted_dtype): scalar_func = int - elif is_float_dtype(promoted_dtype): + elif ah.is_float_dtype(promoted_dtype): scalar_func = float else: scalar_func = bool - for idx in ndindex(shape): + for idx in ah.ndindex(shape): aidx = a[idx] x1idx = _x1[idx] x2idx = _x2[idx] @@ -618,11 +598,11 @@ def test_less(x1_and_x2): assert aidx.shape == x1idx.shape == x2idx.shape assert bool(aidx) == (scalar_func(x1idx) < scalar_func(x2idx)) -@given(two_mutual_arrays(numeric_dtype_objects)) +@given(hh.two_mutual_arrays(hh.numeric_dtype_objects)) def test_less_equal(x1_and_x2): x1, x2 = x1_and_x2 sanity_check(x1, x2) - a = xp.less_equal(x1, x2) + a = ah.less_equal(x1, x2) # See the comments in test_equal() for a description of how this test # works. @@ -630,17 +610,17 @@ def test_less_equal(x1_and_x2): _x1 = xp.broadcast_to(x1, shape) _x2 = xp.broadcast_to(x2, shape) - promoted_dtype = promote_dtypes(x1.dtype, x2.dtype) - _x1 = xp.asarray(_x1, dtype=promoted_dtype) - _x2 = xp.asarray(_x2, dtype=promoted_dtype) + promoted_dtype = ah.promote_dtypes(x1.dtype, x2.dtype) + _x1 = ah.asarray(_x1, dtype=promoted_dtype) + _x2 = ah.asarray(_x2, dtype=promoted_dtype) - if is_integer_dtype(promoted_dtype): + if ah.is_integer_dtype(promoted_dtype): scalar_func = int - elif is_float_dtype(promoted_dtype): + elif ah.is_float_dtype(promoted_dtype): scalar_func = float else: scalar_func = bool - for idx in ndindex(shape): + for idx in ah.ndindex(shape): aidx = a[idx] x1idx = _x1[idx] x2idx = _x2[idx] @@ -648,51 +628,51 @@ def test_less_equal(x1_and_x2): assert aidx.shape == x1idx.shape == x2idx.shape assert bool(aidx) == (scalar_func(x1idx) <= scalar_func(x2idx)) -@given(xps.arrays(dtype=xps.floating_dtypes(), shape=shapes)) +@given(hh.xps.arrays(dtype=hh.xps.floating_dtypes(), shape=hh.shapes)) def test_log(x): a = xp.log(x) - INFINITY = infinity(x.shape, x.dtype) - ZERO = zero(x.shape, x.dtype) - domain = inrange(x, ZERO, INFINITY) - codomain = inrange(a, -INFINITY, INFINITY) + INFINITY = ah.infinity(x.shape, x.dtype) + ZERO = ah.zero(x.shape, x.dtype) + domain = ah.inrange(x, ZERO, INFINITY) + codomain = ah.inrange(a, -INFINITY, INFINITY) # log maps [0, inf] to [-inf, inf]. Values outside this domain are # mapped to nan, which is already tested in the special cases. - assert_exactly_equal(domain, codomain) + ah.assert_exactly_equal(domain, codomain) -@given(xps.arrays(dtype=xps.floating_dtypes(), shape=shapes)) +@given(hh.xps.arrays(dtype=hh.xps.floating_dtypes(), shape=hh.shapes)) def test_log1p(x): a = xp.log1p(x) - INFINITY = infinity(x.shape, x.dtype) - NEGONE = -one(x.shape, x.dtype) - codomain = inrange(x, NEGONE, INFINITY) - domain = inrange(a, -INFINITY, INFINITY) + INFINITY = ah.infinity(x.shape, x.dtype) + NEGONE = -ah.one(x.shape, x.dtype) + codomain = ah.inrange(x, NEGONE, INFINITY) + domain = ah.inrange(a, -INFINITY, INFINITY) # log1p maps [1, inf] to [-inf, inf]. Values outside this domain are # mapped to nan, which is already tested in the special cases. - assert_exactly_equal(domain, codomain) + ah.assert_exactly_equal(domain, codomain) -@given(xps.arrays(dtype=xps.floating_dtypes(), shape=shapes)) +@given(hh.xps.arrays(dtype=hh.xps.floating_dtypes(), shape=hh.shapes)) def test_log2(x): a = xp.log2(x) - INFINITY = infinity(x.shape, x.dtype) - ZERO = zero(x.shape, x.dtype) - domain = inrange(x, ZERO, INFINITY) - codomain = inrange(a, -INFINITY, INFINITY) + INFINITY = ah.infinity(x.shape, x.dtype) + ZERO = ah.zero(x.shape, x.dtype) + domain = ah.inrange(x, ZERO, INFINITY) + codomain = ah.inrange(a, -INFINITY, INFINITY) # log2 maps [0, inf] to [-inf, inf]. Values outside this domain are # mapped to nan, which is already tested in the special cases. - assert_exactly_equal(domain, codomain) + ah.assert_exactly_equal(domain, codomain) -@given(xps.arrays(dtype=xps.floating_dtypes(), shape=shapes)) +@given(hh.xps.arrays(dtype=hh.xps.floating_dtypes(), shape=hh.shapes)) def test_log10(x): a = xp.log10(x) - INFINITY = infinity(x.shape, x.dtype) - ZERO = zero(x.shape, x.dtype) - domain = inrange(x, ZERO, INFINITY) - codomain = inrange(a, -INFINITY, INFINITY) + INFINITY = ah.infinity(x.shape, x.dtype) + ZERO = ah.zero(x.shape, x.dtype) + domain = ah.inrange(x, ZERO, INFINITY) + codomain = ah.inrange(a, -INFINITY, INFINITY) # log10 maps [0, inf] to [-inf, inf]. Values outside this domain are # mapped to nan, which is already tested in the special cases. - assert_exactly_equal(domain, codomain) + ah.assert_exactly_equal(domain, codomain) -@given(two_mutual_arrays(floating_dtype_objects)) +@given(hh.two_mutual_arrays(hh.floating_dtype_objects)) def test_logaddexp(x1_and_x2): x1, x2 = x1_and_x2 sanity_check(x1, x2) @@ -701,42 +681,42 @@ def test_logaddexp(x1_and_x2): # that this is indeed an approximation of log(exp(x1) + exp(x2)), but we # don't have tests for this sort of thing for any functions yet. -@given(two_mutual_arrays([xp.bool])) +@given(hh.two_mutual_arrays([xp.bool])) def test_logical_and(x1_and_x2): x1, x2 = x1_and_x2 sanity_check(x1, x2) - a = xp.logical_and(x1, x2) + a = ah.logical_and(x1, x2) # See the comments in test_equal shape = broadcast_shapes(x1.shape, x2.shape) _x1 = xp.broadcast_to(x1, shape) _x2 = xp.broadcast_to(x2, shape) - for idx in ndindex(shape): + for idx in ah.ndindex(shape): assert a[idx] == (bool(_x1[idx]) and bool(_x2[idx])) -@given(xps.arrays(dtype=xp.bool, shape=shapes)) +@given(hh.xps.arrays(dtype=xp.bool, shape=hh.shapes)) def test_logical_not(x): - a = xp.logical_not(x) + a = ah.logical_not(x) - for idx in ndindex(x.shape): + for idx in ah.ndindex(x.shape): assert a[idx] == (not bool(x[idx])) -@given(two_mutual_arrays([xp.bool])) +@given(hh.two_mutual_arrays([xp.bool])) def test_logical_or(x1_and_x2): x1, x2 = x1_and_x2 sanity_check(x1, x2) - a = xp.logical_or(x1, x2) + a = ah.logical_or(x1, x2) # See the comments in test_equal shape = broadcast_shapes(x1.shape, x2.shape) _x1 = xp.broadcast_to(x1, shape) _x2 = xp.broadcast_to(x2, shape) - for idx in ndindex(shape): + for idx in ah.ndindex(shape): assert a[idx] == (bool(_x1[idx]) or bool(_x2[idx])) -@given(two_mutual_arrays([xp.bool])) +@given(hh.two_mutual_arrays([xp.bool])) def test_logical_xor(x1_and_x2): x1, x2 = x1_and_x2 sanity_check(x1, x2) @@ -747,10 +727,10 @@ def test_logical_xor(x1_and_x2): _x1 = xp.broadcast_to(x1, shape) _x2 = xp.broadcast_to(x2, shape) - for idx in ndindex(shape): + for idx in ah.ndindex(shape): assert a[idx] == (bool(_x1[idx]) ^ bool(_x2[idx])) -@given(two_mutual_arrays(numeric_dtype_objects)) +@given(hh.two_mutual_arrays(hh.numeric_dtype_objects)) def test_multiply(x1_and_x2): x1, x2 = x1_and_x2 sanity_check(x1, x2) @@ -758,29 +738,29 @@ def test_multiply(x1_and_x2): b = xp.multiply(x2, x1) # multiply is commutative - assert_exactly_equal(a, b) + ah.assert_exactly_equal(a, b) -@given(xps.arrays(dtype=xps.numeric_dtypes(), shape=shapes)) +@given(hh.xps.arrays(dtype=hh.xps.numeric_dtypes(), shape=hh.shapes)) def test_negative(x): - out = xp.negative(x) + out = ah.negative(x) # Negation is an involution - assert_exactly_equal(x, xp.negative(out)) + ah.assert_exactly_equal(x, ah.negative(out)) - mask = isfinite(x) - if is_integer_dtype(x.dtype): - minval = dtype_ranges[x.dtype][0] + mask = ah.isfinite(x) + if ah.is_integer_dtype(x.dtype): + minval = ah.dtype_ranges[x.dtype][0] if minval < 0: # negative of the smallest representable negative integer is not defined - mask = not_equal(x, full(x.shape, minval, dtype=x.dtype)) + mask = xp.not_equal(x, ah.full(x.shape, minval, dtype=x.dtype)) # Additive inverse y = xp.add(x[mask], out[mask]) - ZERO = zero(x[mask].shape, x.dtype) - assert_exactly_equal(y, ZERO) + ZERO = ah.zero(x[mask].shape, x.dtype) + ah.assert_exactly_equal(y, ZERO) -@given(two_mutual_arrays()) +@given(hh.two_mutual_arrays()) def test_not_equal(x1_and_x2): x1, x2 = x1_and_x2 sanity_check(x1, x2) @@ -792,17 +772,17 @@ def test_not_equal(x1_and_x2): _x1 = xp.broadcast_to(x1, shape) _x2 = xp.broadcast_to(x2, shape) - promoted_dtype = promote_dtypes(x1.dtype, x2.dtype) - _x1 = xp.asarray(_x1, dtype=promoted_dtype) - _x2 = xp.asarray(_x2, dtype=promoted_dtype) + promoted_dtype = ah.promote_dtypes(x1.dtype, x2.dtype) + _x1 = ah.asarray(_x1, dtype=promoted_dtype) + _x2 = ah.asarray(_x2, dtype=promoted_dtype) - if is_integer_dtype(promoted_dtype): + if ah.is_integer_dtype(promoted_dtype): scalar_func = int - elif is_float_dtype(promoted_dtype): + elif ah.is_float_dtype(promoted_dtype): scalar_func = float else: scalar_func = bool - for idx in ndindex(shape): + for idx in ah.ndindex(shape): aidx = a[idx] x1idx = _x1[idx] x2idx = _x2[idx] @@ -811,13 +791,13 @@ def test_not_equal(x1_and_x2): assert bool(aidx) == (scalar_func(x1idx) != scalar_func(x2idx)) -@given(xps.arrays(dtype=xps.numeric_dtypes(), shape=shapes)) +@given(hh.xps.arrays(dtype=hh.xps.numeric_dtypes(), shape=hh.shapes)) def test_positive(x): out = xp.positive(x) # Positive does nothing - assert_exactly_equal(out, x) + ah.assert_exactly_equal(out, x) -@given(two_mutual_arrays(floating_dtype_objects)) +@given(hh.two_mutual_arrays(hh.floating_dtype_objects)) def test_pow(x1_and_x2): x1, x2 = x1_and_x2 sanity_check(x1, x2) @@ -828,7 +808,7 @@ def test_pow(x1_and_x2): # numbers. We could test that this does implement IEEE 754 pow, but we # don't yet have those sorts in general for this module. -@given(two_mutual_arrays(numeric_dtype_objects)) +@given(hh.two_mutual_arrays(hh.numeric_dtype_objects)) def test_remainder(x1_and_x2): x1, x2 = x1_and_x2 assume(len(x1.shape) <= len(x2.shape)) # TODO: rework same sign testing below to remove this @@ -836,17 +816,17 @@ def test_remainder(x1_and_x2): out = xp.remainder(x1, x2) # out and x2 should have the same sign. - # assert_same_sign returns False for nans - not_nan = logical_not(logical_or(isnan(out), isnan(x2))) - assert_same_sign(out[not_nan], x2[not_nan]) + # ah.assert_same_sign returns False for nans + not_nan = ah.logical_not(ah.logical_or(ah.isnan(out), ah.isnan(x2))) + ah.assert_same_sign(out[not_nan], x2[not_nan]) -@given(xps.arrays(dtype=xps.numeric_dtypes(), shape=shapes)) +@given(hh.xps.arrays(dtype=hh.xps.numeric_dtypes(), shape=hh.shapes)) def test_round(x): a = xp.round(x) # Test that the result is integral - finite = isfinite(x) - assert_integral(a[finite]) + finite = ah.isfinite(x) + ah.assert_integral(a[finite]) # round(x) should be the nearest integer to x. The case where there is a # tie (round to even) is already handled by the special cases tests. @@ -858,10 +838,10 @@ def test_round(x): ceil = xp.ceil(x) over = xp.subtract(x, floor) under = xp.subtract(ceil, x) - round_down = less(over, under) - round_up = less(under, over) - assert_exactly_equal(a[round_down], floor[round_down]) - assert_exactly_equal(a[round_up], ceil[round_up]) + round_down = ah.less(over, under) + round_up = ah.less(under, over) + ah.assert_exactly_equal(a[round_down], floor[round_down]) + ah.assert_exactly_equal(a[round_up], ceil[round_up]) @given(numeric_scalars) def test_sign(x): @@ -904,15 +884,15 @@ def test_tanh(x): # a = xp.tanh(x) pass -@given(xps.arrays(dtype=numeric_dtypes, shape=xps.array_shapes())) +@given(hh.xps.arrays(dtype=hh.numeric_dtypes, shape=hh.xps.array_shapes())) def test_trunc(x): out = xp.trunc(x) assert out.dtype == x.dtype, f"{x.dtype=!s} but {out.dtype=!s}" assert out.shape == x.shape, f"{x.shape} but {out.shape}" - if x.dtype in integer_dtype_objects: - assert array_all(equal(x, out)), f"{x=!s} but {out=!s}" + if x.dtype in hh.integer_dtype_objects: + assert ah.all(ah.equal(x, out)), f"{x=!s} but {out=!s}" else: - finite_mask = xp.isfinite(out) - for idx in ndindex(out.shape): + finite_mask = ah.isfinite(out) + for idx in ah.ndindex(out.shape): if finite_mask[idx]: assert float(out[idx]).is_integer(), f"x at {idx=} is {x[idx]}, but out at idx is {out[idx]}" From fb3ddb7fed717a67a25c20904c2f2bafe23386e0 Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Wed, 6 Oct 2021 10:30:01 +0100 Subject: [PATCH 18/23] Define `xps` in `__init__.py` --- array_api_tests/__init__.py | 6 +++ array_api_tests/hypothesis_helpers.py | 10 ++-- array_api_tests/test_creation_functions.py | 3 +- array_api_tests/test_elementwise_functions.py | 51 ++++++++++--------- 4 files changed, 37 insertions(+), 33 deletions(-) diff --git a/array_api_tests/__init__.py b/array_api_tests/__init__.py index e69de29b..785aa43b 100644 --- a/array_api_tests/__init__.py +++ b/array_api_tests/__init__.py @@ -0,0 +1,6 @@ +from hypothesis.extra.array_api import make_strategies_namespace + +from . import _array_module as xp + + +xps = make_strategies_namespace(xp) diff --git a/array_api_tests/hypothesis_helpers.py b/array_api_tests/hypothesis_helpers.py index 58683259..1d9b27bf 100644 --- a/array_api_tests/hypothesis_helpers.py +++ b/array_api_tests/hypothesis_helpers.py @@ -6,7 +6,6 @@ from hypothesis.strategies import (lists, integers, sampled_from, shared, floats, just, composite, one_of, none, booleans) -from hypothesis.extra.array_api import make_strategies_namespace from .pytest_helpers import nargs from .array_helpers import (dtype_ranges, integer_dtype_objects, @@ -15,15 +14,12 @@ integer_or_boolean_dtype_objects, dtype_objects) from ._array_module import (full, float32, float64, bool as bool_dtype, _UndefinedStub, eye, broadcast_to) -from . import _array_module from . import _array_module as xp +from . import xps from .function_stubs import elementwise_functions -xps = make_strategies_namespace(xp) - - # Set this to True to not fail tests just because a dtype isn't implemented. # If no compatible dtype is implemented for a given test, the test will fail # with a hypothesis health check error. Note that this functionality will not @@ -87,10 +83,10 @@ def mutually_promotable_dtypes(draw, dtype_objects=dtype_objects): lambda func_name: nargs(func_name) > 1) elementwise_function_objects = elementwise_functions_names.map( - lambda i: getattr(_array_module, i)) + lambda i: getattr(xp, i)) array_functions = elementwise_function_objects multiarg_array_functions = multiarg_array_functions_names.map( - lambda i: getattr(_array_module, i)) + lambda i: getattr(xp, i)) # Limit the total size of an array shape MAX_ARRAY_SIZE = 10000 diff --git a/array_api_tests/test_creation_functions.py b/array_api_tests/test_creation_functions.py index b4566902..91b93882 100644 --- a/array_api_tests/test_creation_functions.py +++ b/array_api_tests/test_creation_functions.py @@ -8,7 +8,8 @@ assert_exactly_equal, isintegral, is_float_dtype) from .hypothesis_helpers import (numeric_dtypes, dtypes, MAX_ARRAY_SIZE, shapes, sizes, sqrt_sizes, shared_dtypes, - scalars, xps, kwargs) + scalars, kwargs) +from . import xps from hypothesis import assume, given from hypothesis.strategies import integers, floats, one_of, none, booleans, just, shared, composite diff --git a/array_api_tests/test_elementwise_functions.py b/array_api_tests/test_elementwise_functions.py index 68e58616..819f9f97 100644 --- a/array_api_tests/test_elementwise_functions.py +++ b/array_api_tests/test_elementwise_functions.py @@ -22,6 +22,7 @@ from . import _array_module as xp from . import array_helpers as ah from . import hypothesis_helpers as hh +from . import xps # We might as well use this implementation rather than requiring # mod.broadcast_shapes(). See test_equal() and others. from .test_broadcasting import broadcast_shapes @@ -51,7 +52,7 @@ def sanity_check(x1, x2): except ValueError: raise RuntimeError("Error in test generation (probably a bug in the test suite") -@given(hh.xps.arrays(dtype=hh.xps.numeric_dtypes(), shape=hh.shapes)) +@given(xps.arrays(dtype=xps.numeric_dtypes(), shape=hh.shapes)) def test_abs(x): if ah.is_integer_dtype(x.dtype): minval = ah.dtype_ranges[x.dtype][0] @@ -68,7 +69,7 @@ def test_abs(x): # abs(x) = x for x >= 0 ah.assert_exactly_equal(a[ah.logical_not(less_zero)], x[ah.logical_not(less_zero)]) -@given(hh.xps.arrays(dtype=hh.xps.floating_dtypes(), shape=hh.shapes)) +@given(xps.arrays(dtype=xps.floating_dtypes(), shape=hh.shapes)) def test_acos(x): a = xp.acos(x) ONE = ah.one(x.shape, x.dtype) @@ -82,7 +83,7 @@ def test_acos(x): # nan, which is already tested in the special cases. ah.assert_exactly_equal(domain, codomain) -@given(hh.xps.arrays(dtype=hh.xps.floating_dtypes(), shape=hh.shapes)) +@given(xps.arrays(dtype=xps.floating_dtypes(), shape=hh.shapes)) def test_acosh(x): a = xp.acosh(x) ONE = ah.one(x.shape, x.dtype) @@ -105,7 +106,7 @@ def test_add(x1_and_x2): ah.assert_exactly_equal(a, b) # TODO: Test that add is actually addition -@given(hh.xps.arrays(dtype=hh.xps.floating_dtypes(), shape=hh.shapes)) +@given(xps.arrays(dtype=xps.floating_dtypes(), shape=hh.shapes)) def test_asin(x): a = xp.asin(x) ONE = ah.one(x.shape, x.dtype) @@ -116,7 +117,7 @@ def test_asin(x): # mapped to nan, which is already tested in the special cases. ah.assert_exactly_equal(domain, codomain) -@given(hh.xps.arrays(dtype=hh.xps.floating_dtypes(), shape=hh.shapes)) +@given(xps.arrays(dtype=xps.floating_dtypes(), shape=hh.shapes)) def test_asinh(x): a = xp.asinh(x) INFINITY = ah.infinity(x.shape, x.dtype) @@ -126,7 +127,7 @@ def test_asinh(x): # mapped to nan, which is already tested in the special cases. ah.assert_exactly_equal(domain, codomain) -@given(hh.xps.arrays(dtype=hh.xps.floating_dtypes(), shape=hh.shapes)) +@given(xps.arrays(dtype=xps.floating_dtypes(), shape=hh.shapes)) def test_atan(x): a = xp.atan(x) INFINITY = ah.infinity(x.shape, x.dtype) @@ -174,7 +175,7 @@ def test_atan2(x1_and_x2): ah.assert_exactly_equal(ah.logical_or(ah.logical_and(negx1, posx2), ah.logical_and(negx1, negx2)), nega) -@given(hh.xps.arrays(dtype=hh.xps.floating_dtypes(), shape=hh.shapes)) +@given(xps.arrays(dtype=xps.floating_dtypes(), shape=hh.shapes)) def test_atanh(x): a = xp.atanh(x) ONE = ah.one(x.shape, x.dtype) @@ -311,7 +312,7 @@ def test_bitwise_xor(args): ans = ah.int_to_dtype(x ^ y, dtype_nbits(a.dtype), dtype_signed(a.dtype)) assert ans == res -@given(hh.xps.arrays(dtype=hh.xps.numeric_dtypes(), shape=hh.shapes)) +@given(xps.arrays(dtype=xps.numeric_dtypes(), shape=hh.shapes)) def test_ceil(x): # This test is almost identical to test_floor() a = xp.ceil(x) @@ -322,7 +323,7 @@ def test_ceil(x): integers = ah.isintegral(x) ah.assert_exactly_equal(a[integers], x[integers]) -@given(hh.xps.arrays(dtype=hh.xps.floating_dtypes(), shape=hh.shapes)) +@given(xps.arrays(dtype=xps.floating_dtypes(), shape=hh.shapes)) def test_cos(x): a = xp.cos(x) ONE = ah.one(x.shape, x.dtype) @@ -333,7 +334,7 @@ def test_cos(x): # to nan, which is already tested in the special cases. ah.assert_exactly_equal(domain, codomain) -@given(hh.xps.arrays(dtype=hh.xps.floating_dtypes(), shape=hh.shapes)) +@given(xps.arrays(dtype=xps.floating_dtypes(), shape=hh.shapes)) def test_cosh(x): a = xp.cosh(x) INFINITY = ah.infinity(x.shape, x.dtype) @@ -404,7 +405,7 @@ def test_equal(x1_and_x2): assert aidx.shape == x1idx.shape == x2idx.shape assert bool(aidx) == (scalar_func(x1idx) == scalar_func(x2idx)) -@given(hh.xps.arrays(dtype=hh.xps.floating_dtypes(), shape=hh.shapes)) +@given(xps.arrays(dtype=xps.floating_dtypes(), shape=hh.shapes)) def test_exp(x): a = xp.exp(x) INFINITY = ah.infinity(x.shape, x.dtype) @@ -415,7 +416,7 @@ def test_exp(x): # mapped to nan, which is already tested in the special cases. ah.assert_exactly_equal(domain, codomain) -@given(hh.xps.arrays(dtype=hh.xps.floating_dtypes(), shape=hh.shapes)) +@given(xps.arrays(dtype=xps.floating_dtypes(), shape=hh.shapes)) def test_expm1(x): a = xp.expm1(x) INFINITY = ah.infinity(x.shape, x.dtype) @@ -426,7 +427,7 @@ def test_expm1(x): # mapped to nan, which is already tested in the special cases. ah.assert_exactly_equal(domain, codomain) -@given(hh.xps.arrays(dtype=hh.xps.numeric_dtypes(), shape=hh.shapes)) +@given(xps.arrays(dtype=xps.numeric_dtypes(), shape=hh.shapes)) def test_floor(x): # This test is almost identical to test_ceil a = xp.floor(x) @@ -522,7 +523,7 @@ def test_greater_equal(x1_and_x2): assert aidx.shape == x1idx.shape == x2idx.shape assert bool(aidx) == (scalar_func(x1idx) >= scalar_func(x2idx)) -@given(hh.xps.arrays(dtype=hh.xps.numeric_dtypes(), shape=hh.shapes)) +@given(xps.arrays(dtype=xps.numeric_dtypes(), shape=hh.shapes)) def test_isfinite(x): a = ah.isfinite(x) TRUE = ah.true(x.shape) @@ -538,7 +539,7 @@ def test_isfinite(x): s = float(x[idx]) assert bool(a[idx]) == math.isfinite(s) -@given(hh.xps.arrays(dtype=hh.xps.numeric_dtypes(), shape=hh.shapes)) +@given(xps.arrays(dtype=xps.numeric_dtypes(), shape=hh.shapes)) def test_isinf(x): a = xp.isinf(x) FALSE = ah.false(x.shape) @@ -553,7 +554,7 @@ def test_isinf(x): s = float(x[idx]) assert bool(a[idx]) == math.isinf(s) -@given(hh.xps.arrays(dtype=hh.xps.numeric_dtypes(), shape=hh.shapes)) +@given(xps.arrays(dtype=xps.numeric_dtypes(), shape=hh.shapes)) def test_isnan(x): a = ah.isnan(x) FALSE = ah.false(x.shape) @@ -628,7 +629,7 @@ def test_less_equal(x1_and_x2): assert aidx.shape == x1idx.shape == x2idx.shape assert bool(aidx) == (scalar_func(x1idx) <= scalar_func(x2idx)) -@given(hh.xps.arrays(dtype=hh.xps.floating_dtypes(), shape=hh.shapes)) +@given(xps.arrays(dtype=xps.floating_dtypes(), shape=hh.shapes)) def test_log(x): a = xp.log(x) INFINITY = ah.infinity(x.shape, x.dtype) @@ -639,7 +640,7 @@ def test_log(x): # mapped to nan, which is already tested in the special cases. ah.assert_exactly_equal(domain, codomain) -@given(hh.xps.arrays(dtype=hh.xps.floating_dtypes(), shape=hh.shapes)) +@given(xps.arrays(dtype=xps.floating_dtypes(), shape=hh.shapes)) def test_log1p(x): a = xp.log1p(x) INFINITY = ah.infinity(x.shape, x.dtype) @@ -650,7 +651,7 @@ def test_log1p(x): # mapped to nan, which is already tested in the special cases. ah.assert_exactly_equal(domain, codomain) -@given(hh.xps.arrays(dtype=hh.xps.floating_dtypes(), shape=hh.shapes)) +@given(xps.arrays(dtype=xps.floating_dtypes(), shape=hh.shapes)) def test_log2(x): a = xp.log2(x) INFINITY = ah.infinity(x.shape, x.dtype) @@ -661,7 +662,7 @@ def test_log2(x): # mapped to nan, which is already tested in the special cases. ah.assert_exactly_equal(domain, codomain) -@given(hh.xps.arrays(dtype=hh.xps.floating_dtypes(), shape=hh.shapes)) +@given(xps.arrays(dtype=xps.floating_dtypes(), shape=hh.shapes)) def test_log10(x): a = xp.log10(x) INFINITY = ah.infinity(x.shape, x.dtype) @@ -695,7 +696,7 @@ def test_logical_and(x1_and_x2): for idx in ah.ndindex(shape): assert a[idx] == (bool(_x1[idx]) and bool(_x2[idx])) -@given(hh.xps.arrays(dtype=xp.bool, shape=hh.shapes)) +@given(xps.arrays(dtype=xp.bool, shape=hh.shapes)) def test_logical_not(x): a = ah.logical_not(x) @@ -740,7 +741,7 @@ def test_multiply(x1_and_x2): # multiply is commutative ah.assert_exactly_equal(a, b) -@given(hh.xps.arrays(dtype=hh.xps.numeric_dtypes(), shape=hh.shapes)) +@given(xps.arrays(dtype=xps.numeric_dtypes(), shape=hh.shapes)) def test_negative(x): out = ah.negative(x) @@ -791,7 +792,7 @@ def test_not_equal(x1_and_x2): assert bool(aidx) == (scalar_func(x1idx) != scalar_func(x2idx)) -@given(hh.xps.arrays(dtype=hh.xps.numeric_dtypes(), shape=hh.shapes)) +@given(xps.arrays(dtype=xps.numeric_dtypes(), shape=hh.shapes)) def test_positive(x): out = xp.positive(x) # Positive does nothing @@ -820,7 +821,7 @@ def test_remainder(x1_and_x2): not_nan = ah.logical_not(ah.logical_or(ah.isnan(out), ah.isnan(x2))) ah.assert_same_sign(out[not_nan], x2[not_nan]) -@given(hh.xps.arrays(dtype=hh.xps.numeric_dtypes(), shape=hh.shapes)) +@given(xps.arrays(dtype=xps.numeric_dtypes(), shape=hh.shapes)) def test_round(x): a = xp.round(x) @@ -884,7 +885,7 @@ def test_tanh(x): # a = xp.tanh(x) pass -@given(hh.xps.arrays(dtype=hh.numeric_dtypes, shape=hh.xps.array_shapes())) +@given(xps.arrays(dtype=hh.numeric_dtypes, shape=xps.array_shapes())) def test_trunc(x): out = xp.trunc(x) assert out.dtype == x.dtype, f"{x.dtype=!s} but {out.dtype=!s}" From 5531563f39074e6e7fc720f6e7a0d849a1671b1e Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Wed, 6 Oct 2021 10:31:34 +0100 Subject: [PATCH 19/23] Force high max examples for `test_kwargs` Prevents the test failing when using `pytest --max-examples=1` --- array_api_tests/meta_tests/test_hypothesis_helpers.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/array_api_tests/meta_tests/test_hypothesis_helpers.py b/array_api_tests/meta_tests/test_hypothesis_helpers.py index 706d1ae5..bb4630e5 100644 --- a/array_api_tests/meta_tests/test_hypothesis_helpers.py +++ b/array_api_tests/meta_tests/test_hypothesis_helpers.py @@ -1,7 +1,7 @@ from math import prod import pytest -from hypothesis import given, strategies as st +from hypothesis import given, strategies as st, settings from .. import _array_module as xp from .._array_module import _UndefinedStub @@ -54,10 +54,11 @@ def test_kwargs(): results = [] @given(hh.kwargs(n=st.integers(0, 10), c=st.from_regex("[a-f]"))) + @settings(max_examples=100) def run(kw): results.append(kw) - run() + assert all(isinstance(kw, dict) for kw in results) for size in [0, 1, 2]: assert any(len(kw) == size for kw in results) From 0bb4d65a58ec799d01f0b8260a37c51b3d84d5e6 Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Wed, 6 Oct 2021 11:08:04 +0100 Subject: [PATCH 20/23] Use `ah.assert_integral` in `test_trunc` --- array_api_tests/test_elementwise_functions.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/array_api_tests/test_elementwise_functions.py b/array_api_tests/test_elementwise_functions.py index 819f9f97..7a96870c 100644 --- a/array_api_tests/test_elementwise_functions.py +++ b/array_api_tests/test_elementwise_functions.py @@ -885,15 +885,18 @@ def test_tanh(x): # a = xp.tanh(x) pass -@given(xps.arrays(dtype=hh.numeric_dtypes, shape=xps.array_shapes())) +@given( + xps.arrays( + dtype=hh.numeric_dtypes, + shape=xps.array_shapes(), + elements={"allow_nan": False, "allow_infinity": False}, + ) +) def test_trunc(x): out = xp.trunc(x) assert out.dtype == x.dtype, f"{x.dtype=!s} but {out.dtype=!s}" - assert out.shape == x.shape, f"{x.shape} but {out.shape}" + assert out.shape == x.shape, f"{x.shape=} but {out.shape=}" if x.dtype in hh.integer_dtype_objects: assert ah.all(ah.equal(x, out)), f"{x=!s} but {out=!s}" else: - finite_mask = ah.isfinite(out) - for idx in ah.ndindex(out.shape): - if finite_mask[idx]: - assert float(out[idx]).is_integer(), f"x at {idx=} is {x[idx]}, but out at idx is {out[idx]}" + ah.assert_integral(out) From f7838d3734b60581bf9f8d2594744cb02c2fb25b Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Wed, 6 Oct 2021 15:04:47 +0100 Subject: [PATCH 21/23] Support and use ndarrays for bitwise tests --- array_api_tests/test_elementwise_functions.py | 244 ++++++++++-------- 1 file changed, 136 insertions(+), 108 deletions(-) diff --git a/array_api_tests/test_elementwise_functions.py b/array_api_tests/test_elementwise_functions.py index 7a96870c..f7d74cc9 100644 --- a/array_api_tests/test_elementwise_functions.py +++ b/array_api_tests/test_elementwise_functions.py @@ -157,8 +157,8 @@ def test_atan2(x1_and_x2): # From the spec: # # The mathematical signs of `x1_i` and `x2_i` determine the quadrant of - # each element-wise result. The quadrant (i.e., branch) is chosen such - # that each element-wise result is the signed angle in radians between the + # each element-wise res. The quadrant (i.e., branch) is chosen such + # that each element-wise res is the signed angle in radians between the # ray ending at the origin and passing through the point `(1,0)` and the # ray ending at the origin and passing through the point `(x2_i, x1_i)`. @@ -186,131 +186,159 @@ def test_atanh(x): # mapped to nan, which is already tested in the special cases. ah.assert_exactly_equal(domain, codomain) -@given(two_integer_or_boolean_dtypes.flatmap(lambda i: two_array_scalars(*i))) -def test_bitwise_and(args): +@given(hh.two_mutual_arrays(ah.integer_or_boolean_dtype_objects)) +def test_bitwise_and(x1_and_x2): from .test_type_promotion import dtype_nbits, dtype_signed - x1, x2 = args + x1, x2 = x1_and_x2 sanity_check(x1, x2) - a = xp.bitwise_and(x1, x2) + out = xp.bitwise_and(x1, x2) + + # TODO: generate indices without broadcasting arrays (see test_equal comment) + shape = broadcast_shapes(x1.shape, x2.shape) + assert out.shape == shape + _x1 = xp.broadcast_to(x1, shape) + _x2 = xp.broadcast_to(x2, shape) + # Compare against the Python & operator. - # TODO: Generalize this properly for inputs that are arrays. - if not (x1.shape == x2.shape == ()): - raise RuntimeError("Error: test_bitwise_and needs to be updated for nonscalar array inputs") - - if a.dtype == xp.bool: - x = bool(x1) - y = bool(x2) - res = bool(a) - assert (x and y) == res + if out.dtype == xp.bool: + for idx in ah.ndindex(out.shape): + val1 = bool(_x1[idx]) + val2 = bool(_x2[idx]) + res = bool(out[idx]) + assert (val1 and val2) == res else: - x = int(x1) - y = int(x2) - res = int(a) - ans = ah.int_to_dtype(x & y, dtype_nbits(a.dtype), dtype_signed(a.dtype)) - assert ans == res - -@given(two_integer_dtypes.flatmap(lambda i: two_array_scalars(*i))) -def test_bitwise_left_shift(args): + for idx in ah.ndindex(out.shape): + val1 = int(_x1[idx]) + val2 = int(_x2[idx]) + res = int(out[idx]) + vals_and = val1 & val2 + vals_and = ah.int_to_dtype(vals_and, dtype_nbits(out.dtype), dtype_signed(out.dtype)) + assert vals_and == res + + +@given(hh.two_mutual_arrays(ah.integer_dtype_objects)) +def test_bitwise_left_shift(x1_and_x2): from .test_type_promotion import dtype_nbits, dtype_signed - x1, x2 = args + x1, x2 = x1_and_x2 sanity_check(x1, x2) - negative_x2 = ah.isnegative(x2) - assume(not xp.any(negative_x2)) - a = xp.bitwise_left_shift(x1, x2) - # Compare against the Python << operator. - # TODO: Generalize this properly for inputs that are arrays. - if not (x1.shape == x2.shape == ()): - raise RuntimeError("Error: test_bitwise_left_shift needs to be updated for nonscalar array inputs") - x = int(x1) - y = int(x2) - if y >= dtype_nbits(a.dtype): - # Avoid shifting very large y in Python ints - ans = 0 - else: - ans = x << y - ans = ah.int_to_dtype(ans, dtype_nbits(a.dtype), dtype_signed(a.dtype)) - res = int(a) - assert ans == res + assume(not ah.any(ah.isnegative(x2))) + out = xp.bitwise_left_shift(x1, x2) + + # TODO: generate indices without broadcasting arrays (see test_equal comment) + shape = broadcast_shapes(x1.shape, x2.shape) + assert out.shape == shape + _x1 = xp.broadcast_to(x1, shape) + _x2 = xp.broadcast_to(x2, shape) -@given(integer_or_boolean_scalars) + # Compare against the Python << operator. + for idx in ah.ndindex(out.shape): + val1 = int(_x1[idx]) + val2 = int(_x2[idx]) + res = int(out[idx]) + # We avoid shifting very large ints + vals_shift = val1 << val2 if val2 < dtype_nbits(out.dtype) else 0 + vals_shift = ah.int_to_dtype(vals_shift, dtype_nbits(out.dtype), dtype_signed(out.dtype)) + assert vals_shift == res + +@given(xps.arrays(dtype=hh.integer_or_boolean_dtypes, shape=hh.shapes)) def test_bitwise_invert(x): from .test_type_promotion import dtype_nbits, dtype_signed - a = xp.bitwise_invert(x) + out = xp.bitwise_invert(x) + # Compare against the Python ~ operator. - # TODO: Generalize this properly for inputs that are arrays. - if not (x.shape == ()): - raise RuntimeError("Error: test_bitwise_invert needs to be updated for nonscalar array inputs") - if a.dtype == xp.bool: - x = bool(x) - res = bool(a) - assert (not x) == res + if out.dtype == xp.bool: + for idx in ah.ndindex(out.shape): + val = bool(x[idx]) + res = bool(out[idx]) + assert (not val) == res else: - x = int(x) - res = int(a) - ans = ah.int_to_dtype(~x, dtype_nbits(a.dtype), dtype_signed(a.dtype)) - assert ans == res - -@given(two_integer_or_boolean_dtypes.flatmap(lambda i: two_array_scalars(*i))) -def test_bitwise_or(args): + for idx in ah.ndindex(out.shape): + val = int(x[idx]) + res = int(out[idx]) + val_invert = ~val + val_invert = ah.int_to_dtype(val_invert, dtype_nbits(out.dtype), dtype_signed(out.dtype)) + assert val_invert == res + +@given(hh.two_mutual_arrays(ah.integer_or_boolean_dtype_objects)) +def test_bitwise_or(x1_and_x2): from .test_type_promotion import dtype_nbits, dtype_signed - x1, x2 = args + x1, x2 = x1_and_x2 sanity_check(x1, x2) - a = xp.bitwise_or(x1, x2) + out = xp.bitwise_or(x1, x2) + + # TODO: generate indices without broadcasting arrays (see test_equal comment) + shape = broadcast_shapes(x1.shape, x2.shape) + assert out.shape == shape + _x1 = xp.broadcast_to(x1, shape) + _x2 = xp.broadcast_to(x2, shape) + # Compare against the Python | operator. - # TODO: Generalize this properly for inputs that are arrays. - if not (x1.shape == x2.shape == ()): - raise RuntimeError("Error: test_bitwise_or needs to be updated for nonscalar array inputs") - if a.dtype == xp.bool: - x = bool(x1) - y = bool(x2) - res = bool(a) - assert (x or y) == res + if out.dtype == xp.bool: + for idx in ah.ndindex(out.shape): + val1 = bool(_x1[idx]) + val2 = bool(_x2[idx]) + res = bool(out[idx]) + assert (val1 or val2) == res else: - x = int(x1) - y = int(x2) - res = int(a) - ans = ah.int_to_dtype(x | y, dtype_nbits(a.dtype), dtype_signed(a.dtype)) - assert ans == res - -@given(two_integer_dtypes.flatmap(lambda i: two_array_scalars(*i))) -def test_bitwise_right_shift(args): + for idx in ah.ndindex(out.shape): + val1 = int(_x1[idx]) + val2 = int(_x2[idx]) + res = int(out[idx]) + vals_or = val1 | val2 + vals_or = ah.int_to_dtype(vals_or, dtype_nbits(out.dtype), dtype_signed(out.dtype)) + assert vals_or == res + +@given(hh.two_mutual_arrays(ah.integer_dtype_objects)) +def test_bitwise_right_shift(x1_and_x2): from .test_type_promotion import dtype_nbits, dtype_signed - x1, x2 = args + x1, x2 = x1_and_x2 sanity_check(x1, x2) - negative_x2 = ah.isnegative(x2) - assume(not xp.any(negative_x2)) - a = xp.bitwise_right_shift(x1, x2) + assume(not ah.any(ah.isnegative(x2))) + out = xp.bitwise_right_shift(x1, x2) + + # TODO: generate indices without broadcasting arrays (see test_equal comment) + shape = broadcast_shapes(x1.shape, x2.shape) + assert out.shape == shape + _x1 = xp.broadcast_to(x1, shape) + _x2 = xp.broadcast_to(x2, shape) + # Compare against the Python >> operator. - # TODO: Generalize this properly for inputs that are arrays. - if not (x1.shape == x2.shape == ()): - raise RuntimeError("Error: test_bitwise_right_shift needs to be updated for nonscalar array inputs") - x = int(x1) - y = int(x2) - ans = ah.int_to_dtype(x >> y, dtype_nbits(a.dtype), dtype_signed(a.dtype)) - res = int(a) - assert ans == res - -@given(two_integer_or_boolean_dtypes.flatmap(lambda i: two_array_scalars(*i))) -def test_bitwise_xor(args): + for idx in ah.ndindex(out.shape): + val1 = int(_x1[idx]) + val2 = int(_x2[idx]) + res = int(out[idx]) + vals_shift = val1 >> val2 + vals_shift = ah.int_to_dtype(vals_shift, dtype_nbits(out.dtype), dtype_signed(out.dtype)) + assert vals_shift == res + +@given(hh.two_mutual_arrays(ah.integer_or_boolean_dtype_objects)) +def test_bitwise_xor(x1_and_x2): from .test_type_promotion import dtype_nbits, dtype_signed - x1, x2 = args + x1, x2 = x1_and_x2 sanity_check(x1, x2) - a = xp.bitwise_xor(x1, x2) + out = xp.bitwise_xor(x1, x2) + + # TODO: generate indices without broadcasting arrays (see test_equal comment) + shape = broadcast_shapes(x1.shape, x2.shape) + assert out.shape == shape + _x1 = xp.broadcast_to(x1, shape) + _x2 = xp.broadcast_to(x2, shape) + # Compare against the Python ^ operator. - # TODO: Generalize this properly for inputs that are arrays. - if not (x1.shape == x2.shape == ()): - raise RuntimeError("Error: test_bitwise_xor needs to be updated for nonscalar array inputs") - if a.dtype == xp.bool: - x = bool(x1) - y = bool(x2) - res = bool(a) - assert (x ^ y) == res + if out.dtype == xp.bool: + for idx in ah.ndindex(out.shape): + val1 = bool(_x1[idx]) + val2 = bool(_x2[idx]) + res = bool(out[idx]) + assert (val1 ^ val2) == res else: - x = int(x1) - y = int(x2) - res = int(a) - ans = ah.int_to_dtype(x ^ y, dtype_nbits(a.dtype), dtype_signed(a.dtype)) - assert ans == res + for idx in ah.ndindex(out.shape): + val1 = int(_x1[idx]) + val2 = int(_x2[idx]) + res = int(out[idx]) + vals_xor = val1 ^ val2 + vals_xor = ah.int_to_dtype(vals_xor, dtype_nbits(out.dtype), dtype_signed(out.dtype)) + assert vals_xor == res @given(xps.arrays(dtype=xps.numeric_dtypes(), shape=hh.shapes)) def test_ceil(x): @@ -386,7 +414,7 @@ def test_equal(x1_and_x2): # test_elementwise_function_two_arg_bool_type_promotion() in # test_type_promotion.py. The type promotion for ah.equal() is not *really* # tested in that file, because doing so requires doing the consistency - # check we do here rather than st.just checking the result dtype. + # check we do here rather than st.just checking the res dtype. promoted_dtype = ah.promote_dtypes(x1.dtype, x2.dtype) _x1 = ah.asarray(_x1, dtype=promoted_dtype) _x2 = ah.asarray(_x2, dtype=promoted_dtype) @@ -446,7 +474,7 @@ def test_floor_divide(x1_and_x2): # The spec does not specify the behavior for division by 0 for integer # dtypes. A library may choose to raise an exception in this case, so # we avoid passing it in entirely. - assume(not xp.any(x1 == 0) and not xp.any(x2 == 0)) + assume(not ah.any(x1 == 0) and not ah.any(x2 == 0)) div = xp.divide( ah.asarray(x1, dtype=xp.float64), ah.asarray(x2, dtype=xp.float64), @@ -825,7 +853,7 @@ def test_remainder(x1_and_x2): def test_round(x): a = xp.round(x) - # Test that the result is integral + # Test that the res is integral finite = ah.isfinite(x) ah.assert_integral(a[finite]) From 5b0c49a25e6f0f1aa593f7aa215a8067d6da3405 Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Thu, 7 Oct 2021 09:29:24 +0100 Subject: [PATCH 22/23] Use finite mask for `test_trunc` --- array_api_tests/test_elementwise_functions.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/array_api_tests/test_elementwise_functions.py b/array_api_tests/test_elementwise_functions.py index f7d74cc9..d4482020 100644 --- a/array_api_tests/test_elementwise_functions.py +++ b/array_api_tests/test_elementwise_functions.py @@ -7,11 +7,6 @@ spec does not make any accuracy requirements for functions, so this does not test that. Tests for the special cases are generated and tested separately in special_cases/ - -Note: Due to current limitations in Hypothesis, the tests below only test -arrays of shape (). In the future, the tests should be updated to test -arrays of any shape, using masking patterns (similar to the tests in special_cases/ - """ import math @@ -913,13 +908,7 @@ def test_tanh(x): # a = xp.tanh(x) pass -@given( - xps.arrays( - dtype=hh.numeric_dtypes, - shape=xps.array_shapes(), - elements={"allow_nan": False, "allow_infinity": False}, - ) -) +@given(xps.arrays(dtype=hh.numeric_dtypes, shape=xps.array_shapes())) def test_trunc(x): out = xp.trunc(x) assert out.dtype == x.dtype, f"{x.dtype=!s} but {out.dtype=!s}" @@ -927,4 +916,5 @@ def test_trunc(x): if x.dtype in hh.integer_dtype_objects: assert ah.all(ah.equal(x, out)), f"{x=!s} but {out=!s}" else: - ah.assert_integral(out) + finite = ah.isfinite(x) + ah.assert_integral(out[finite]) From 3315e1813b178bdff7478a0a3da65b0b4816e719 Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Thu, 7 Oct 2021 10:10:08 +0100 Subject: [PATCH 23/23] `two_mutual_arrays()` returns two strategies --- array_api_tests/hypothesis_helpers.py | 20 ++-- .../meta_tests/test_hypothesis_helpers.py | 9 +- array_api_tests/test_elementwise_functions.py | 111 +++++++----------- 3 files changed, 64 insertions(+), 76 deletions(-) diff --git a/array_api_tests/hypothesis_helpers.py b/array_api_tests/hypothesis_helpers.py index 1d9b27bf..b105b547 100644 --- a/array_api_tests/hypothesis_helpers.py +++ b/array_api_tests/hypothesis_helpers.py @@ -156,7 +156,6 @@ def two_broadcastable_shapes(draw): sizes = integers(0, MAX_ARRAY_SIZE) sqrt_sizes = integers(0, SQRT_MAX_ARRAY_SIZE) -# TODO: Generate general arrays here, rather than just scalars. numeric_arrays = xps.arrays( dtype=shared(xps.floating_dtypes(), key='dtypes'), shape=shared(xps.array_shapes(), key='shapes'), @@ -267,13 +266,18 @@ def multiaxis_indices(draw, shapes): return tuple(res) -@composite -def two_mutual_arrays(draw, dtypes=dtype_objects): - dtype1, dtype2 = draw(mutually_promotable_dtypes(dtypes)) - shape1, shape2 = draw(two_mutually_broadcastable_shapes) - x1 = draw(xps.arrays(dtype=dtype1, shape=shape1)) - x2 = draw(xps.arrays(dtype=dtype2, shape=shape2)) - return x1, x2 +def two_mutual_arrays(dtypes=dtype_objects): + mutual_dtypes = shared(mutually_promotable_dtypes(dtypes)) + mutual_shapes = shared(two_mutually_broadcastable_shapes) + arrays1 = xps.arrays( + dtype=mutual_dtypes.map(lambda pair: pair[0]), + shape=mutual_shapes.map(lambda pair: pair[0]), + ) + arrays2 = xps.arrays( + dtype=mutual_dtypes.map(lambda pair: pair[1]), + shape=mutual_shapes.map(lambda pair: pair[1]), + ) + return arrays1, arrays2 @composite diff --git a/array_api_tests/meta_tests/test_hypothesis_helpers.py b/array_api_tests/meta_tests/test_hypothesis_helpers.py index bb4630e5..6af43a26 100644 --- a/array_api_tests/meta_tests/test_hypothesis_helpers.py +++ b/array_api_tests/meta_tests/test_hypothesis_helpers.py @@ -7,6 +7,8 @@ from .._array_module import _UndefinedStub from .. import array_helpers as ah from .. import hypothesis_helpers as hh +from ..test_broadcasting import broadcast_shapes +from ..test_elementwise_functions import sanity_check UNDEFINED_DTYPES = any(isinstance(d, _UndefinedStub) for d in ah.dtype_objects) pytestmark = [pytest.mark.skipif(UNDEFINED_DTYPES, reason="undefined dtypes")] @@ -44,10 +46,13 @@ def test_two_mutually_broadcastable_shapes(pair): def test_two_broadcastable_shapes(pair): for shape in pair: assert valid_shape(shape) + assert broadcast_shapes(pair[0], pair[1]) == pair[0] - from ..test_broadcasting import broadcast_shapes - assert broadcast_shapes(pair[0], pair[1]) == pair[0] +@given(*hh.two_mutual_arrays()) +def test_two_mutual_arrays(x1, x2): + sanity_check(x1, x2) + assert broadcast_shapes(x1.shape, x2.shape) in (x1.shape, x2.shape) def test_kwargs(): diff --git a/array_api_tests/test_elementwise_functions.py b/array_api_tests/test_elementwise_functions.py index d4482020..70c69f99 100644 --- a/array_api_tests/test_elementwise_functions.py +++ b/array_api_tests/test_elementwise_functions.py @@ -41,6 +41,7 @@ def two_array_scalars(draw, dtype1, dtype2): # hh.mutually_promotable_dtypes()) return draw(hh.array_scalars(st.just(dtype1))), draw(hh.array_scalars(st.just(dtype2))) +# TODO: refactor this into dtype_helpers.py, see https://github.com/data-apis/array-api-tests/pull/26 def sanity_check(x1, x2): try: ah.promote_dtypes(x1.dtype, x2.dtype) @@ -90,9 +91,8 @@ def test_acosh(x): # to nan, which is already tested in the special cases. ah.assert_exactly_equal(domain, codomain) -@given(hh.two_mutual_arrays(hh.numeric_dtype_objects)) -def test_add(x1_and_x2): - x1, x2 = x1_and_x2 +@given(*hh.two_mutual_arrays(hh.numeric_dtype_objects)) +def test_add(x1, x2): sanity_check(x1, x2) a = xp.add(x1, x2) @@ -133,9 +133,8 @@ def test_atan(x): # mapped to nan, which is already tested in the special cases. ah.assert_exactly_equal(domain, codomain) -@given(hh.two_mutual_arrays(hh.floating_dtype_objects)) -def test_atan2(x1_and_x2): - x1, x2 = x1_and_x2 +@given(*hh.two_mutual_arrays(hh.floating_dtype_objects)) +def test_atan2(x1, x2): sanity_check(x1, x2) a = xp.atan2(x1, x2) INFINITY1 = ah.infinity(x1.shape, x1.dtype) @@ -181,10 +180,9 @@ def test_atanh(x): # mapped to nan, which is already tested in the special cases. ah.assert_exactly_equal(domain, codomain) -@given(hh.two_mutual_arrays(ah.integer_or_boolean_dtype_objects)) -def test_bitwise_and(x1_and_x2): +@given(*hh.two_mutual_arrays(ah.integer_or_boolean_dtype_objects)) +def test_bitwise_and(x1, x2): from .test_type_promotion import dtype_nbits, dtype_signed - x1, x2 = x1_and_x2 sanity_check(x1, x2) out = xp.bitwise_and(x1, x2) @@ -211,10 +209,9 @@ def test_bitwise_and(x1_and_x2): assert vals_and == res -@given(hh.two_mutual_arrays(ah.integer_dtype_objects)) -def test_bitwise_left_shift(x1_and_x2): +@given(*hh.two_mutual_arrays(ah.integer_dtype_objects)) +def test_bitwise_left_shift(x1, x2): from .test_type_promotion import dtype_nbits, dtype_signed - x1, x2 = x1_and_x2 sanity_check(x1, x2) assume(not ah.any(ah.isnegative(x2))) out = xp.bitwise_left_shift(x1, x2) @@ -254,10 +251,9 @@ def test_bitwise_invert(x): val_invert = ah.int_to_dtype(val_invert, dtype_nbits(out.dtype), dtype_signed(out.dtype)) assert val_invert == res -@given(hh.two_mutual_arrays(ah.integer_or_boolean_dtype_objects)) -def test_bitwise_or(x1_and_x2): +@given(*hh.two_mutual_arrays(ah.integer_or_boolean_dtype_objects)) +def test_bitwise_or(x1, x2): from .test_type_promotion import dtype_nbits, dtype_signed - x1, x2 = x1_and_x2 sanity_check(x1, x2) out = xp.bitwise_or(x1, x2) @@ -283,10 +279,9 @@ def test_bitwise_or(x1_and_x2): vals_or = ah.int_to_dtype(vals_or, dtype_nbits(out.dtype), dtype_signed(out.dtype)) assert vals_or == res -@given(hh.two_mutual_arrays(ah.integer_dtype_objects)) -def test_bitwise_right_shift(x1_and_x2): +@given(*hh.two_mutual_arrays(ah.integer_dtype_objects)) +def test_bitwise_right_shift(x1, x2): from .test_type_promotion import dtype_nbits, dtype_signed - x1, x2 = x1_and_x2 sanity_check(x1, x2) assume(not ah.any(ah.isnegative(x2))) out = xp.bitwise_right_shift(x1, x2) @@ -306,10 +301,9 @@ def test_bitwise_right_shift(x1_and_x2): vals_shift = ah.int_to_dtype(vals_shift, dtype_nbits(out.dtype), dtype_signed(out.dtype)) assert vals_shift == res -@given(hh.two_mutual_arrays(ah.integer_or_boolean_dtype_objects)) -def test_bitwise_xor(x1_and_x2): +@given(*hh.two_mutual_arrays(ah.integer_or_boolean_dtype_objects)) +def test_bitwise_xor(x1, x2): from .test_type_promotion import dtype_nbits, dtype_signed - x1, x2 = x1_and_x2 sanity_check(x1, x2) out = xp.bitwise_xor(x1, x2) @@ -367,9 +361,8 @@ def test_cosh(x): # mapped to nan, which is already tested in the special cases. ah.assert_exactly_equal(domain, codomain) -@given(hh.two_mutual_arrays(hh.floating_dtype_objects)) -def test_divide(x1_and_x2): - x1, x2 = x1_and_x2 +@given(*hh.two_mutual_arrays(hh.floating_dtype_objects)) +def test_divide(x1, x2): sanity_check(x1, x2) xp.divide(x1, x2) # There isn't much we can test here. The spec doesn't require any behavior @@ -379,9 +372,8 @@ def test_divide(x1_and_x2): # have those sorts in general for this module. -@given(hh.two_mutual_arrays()) -def test_equal(x1_and_x2): - x1, x2 = x1_and_x2 +@given(*hh.two_mutual_arrays()) +def test_equal(x1, x2): sanity_check(x1, x2) a = ah.equal(x1, x2) # NOTE: ah.assert_exactly_equal() itself uses ah.equal(), so we must be careful @@ -461,9 +453,8 @@ def test_floor(x): integers = ah.isintegral(x) ah.assert_exactly_equal(a[integers], x[integers]) -@given(hh.two_mutual_arrays(hh.numeric_dtype_objects)) -def test_floor_divide(x1_and_x2): - x1, x2 = x1_and_x2 +@given(*hh.two_mutual_arrays(hh.numeric_dtype_objects)) +def test_floor_divide(x1, x2): sanity_check(x1, x2) if ah.is_integer_dtype(x1.dtype): # The spec does not specify the behavior for division by 0 for integer @@ -486,9 +477,8 @@ def test_floor_divide(x1_and_x2): # TODO: Test the exact output for floor_divide. -@given(hh.two_mutual_arrays(hh.numeric_dtype_objects)) -def test_greater(x1_and_x2): - x1, x2 = x1_and_x2 +@given(*hh.two_mutual_arrays(hh.numeric_dtype_objects)) +def test_greater(x1, x2): sanity_check(x1, x2) a = xp.greater(x1, x2) @@ -516,9 +506,8 @@ def test_greater(x1_and_x2): assert aidx.shape == x1idx.shape == x2idx.shape assert bool(aidx) == (scalar_func(x1idx) > scalar_func(x2idx)) -@given(hh.two_mutual_arrays(hh.numeric_dtype_objects)) -def test_greater_equal(x1_and_x2): - x1, x2 = x1_and_x2 +@given(*hh.two_mutual_arrays(hh.numeric_dtype_objects)) +def test_greater_equal(x1, x2): sanity_check(x1, x2) a = xp.greater_equal(x1, x2) @@ -592,9 +581,8 @@ def test_isnan(x): s = float(x[idx]) assert bool(a[idx]) == math.isnan(s) -@given(hh.two_mutual_arrays(hh.numeric_dtype_objects)) -def test_less(x1_and_x2): - x1, x2 = x1_and_x2 +@given(*hh.two_mutual_arrays(hh.numeric_dtype_objects)) +def test_less(x1, x2): sanity_check(x1, x2) a = ah.less(x1, x2) @@ -622,9 +610,8 @@ def test_less(x1_and_x2): assert aidx.shape == x1idx.shape == x2idx.shape assert bool(aidx) == (scalar_func(x1idx) < scalar_func(x2idx)) -@given(hh.two_mutual_arrays(hh.numeric_dtype_objects)) -def test_less_equal(x1_and_x2): - x1, x2 = x1_and_x2 +@given(*hh.two_mutual_arrays(hh.numeric_dtype_objects)) +def test_less_equal(x1, x2): sanity_check(x1, x2) a = ah.less_equal(x1, x2) @@ -696,18 +683,16 @@ def test_log10(x): # mapped to nan, which is already tested in the special cases. ah.assert_exactly_equal(domain, codomain) -@given(hh.two_mutual_arrays(hh.floating_dtype_objects)) -def test_logaddexp(x1_and_x2): - x1, x2 = x1_and_x2 +@given(*hh.two_mutual_arrays(hh.floating_dtype_objects)) +def test_logaddexp(x1, x2): sanity_check(x1, x2) xp.logaddexp(x1, x2) # The spec doesn't require any behavior for this function. We could test # that this is indeed an approximation of log(exp(x1) + exp(x2)), but we # don't have tests for this sort of thing for any functions yet. -@given(hh.two_mutual_arrays([xp.bool])) -def test_logical_and(x1_and_x2): - x1, x2 = x1_and_x2 +@given(*hh.two_mutual_arrays([xp.bool])) +def test_logical_and(x1, x2): sanity_check(x1, x2) a = ah.logical_and(x1, x2) @@ -726,9 +711,8 @@ def test_logical_not(x): for idx in ah.ndindex(x.shape): assert a[idx] == (not bool(x[idx])) -@given(hh.two_mutual_arrays([xp.bool])) -def test_logical_or(x1_and_x2): - x1, x2 = x1_and_x2 +@given(*hh.two_mutual_arrays([xp.bool])) +def test_logical_or(x1, x2): sanity_check(x1, x2) a = ah.logical_or(x1, x2) @@ -740,9 +724,8 @@ def test_logical_or(x1_and_x2): for idx in ah.ndindex(shape): assert a[idx] == (bool(_x1[idx]) or bool(_x2[idx])) -@given(hh.two_mutual_arrays([xp.bool])) -def test_logical_xor(x1_and_x2): - x1, x2 = x1_and_x2 +@given(*hh.two_mutual_arrays([xp.bool])) +def test_logical_xor(x1, x2): sanity_check(x1, x2) a = xp.logical_xor(x1, x2) @@ -754,9 +737,8 @@ def test_logical_xor(x1_and_x2): for idx in ah.ndindex(shape): assert a[idx] == (bool(_x1[idx]) ^ bool(_x2[idx])) -@given(hh.two_mutual_arrays(hh.numeric_dtype_objects)) -def test_multiply(x1_and_x2): - x1, x2 = x1_and_x2 +@given(*hh.two_mutual_arrays(hh.numeric_dtype_objects)) +def test_multiply(x1, x2): sanity_check(x1, x2) a = xp.multiply(x1, x2) @@ -784,9 +766,8 @@ def test_negative(x): ah.assert_exactly_equal(y, ZERO) -@given(hh.two_mutual_arrays()) -def test_not_equal(x1_and_x2): - x1, x2 = x1_and_x2 +@given(*hh.two_mutual_arrays()) +def test_not_equal(x1, x2): sanity_check(x1, x2) a = xp.not_equal(x1, x2) @@ -821,9 +802,8 @@ def test_positive(x): # Positive does nothing ah.assert_exactly_equal(out, x) -@given(hh.two_mutual_arrays(hh.floating_dtype_objects)) -def test_pow(x1_and_x2): - x1, x2 = x1_and_x2 +@given(*hh.two_mutual_arrays(hh.floating_dtype_objects)) +def test_pow(x1, x2): sanity_check(x1, x2) xp.pow(x1, x2) # There isn't much we can test here. The spec doesn't require any behavior @@ -832,9 +812,8 @@ def test_pow(x1_and_x2): # numbers. We could test that this does implement IEEE 754 pow, but we # don't yet have those sorts in general for this module. -@given(hh.two_mutual_arrays(hh.numeric_dtype_objects)) -def test_remainder(x1_and_x2): - x1, x2 = x1_and_x2 +@given(*hh.two_mutual_arrays(hh.numeric_dtype_objects)) +def test_remainder(x1, x2): assume(len(x1.shape) <= len(x2.shape)) # TODO: rework same sign testing below to remove this sanity_check(x1, x2) out = xp.remainder(x1, x2)