diff --git a/array_api_tests/special_cases/test_dunder_abs.py b/array_api_tests/special_cases/test_dunder_abs.py new file mode 100644 index 00000000..5028f1b7 --- /dev/null +++ b/array_api_tests/special_cases/test_dunder_abs.py @@ -0,0 +1,52 @@ +""" +Special cases tests for __abs__. + +These tests are generated from the special cases listed in the spec. + +NOTE: This file is generated automatically by the generate_stubs.py script. Do +not modify it directly. +""" + +from ..array_helpers import NaN, assert_exactly_equal, exactly_equal, infinity, zero +from ..hypothesis_helpers import numeric_arrays + +from hypothesis import given + + +@given(numeric_arrays) +def test_abs_special_cases_one_arg_equal_1(arg1): + """ + Special case test for `__abs__(self, /)`: + + - If `x_i` is `NaN`, the result is `NaN`. + + """ + res = (arg1).__abs__() + mask = exactly_equal(arg1, NaN(arg1.shape, arg1.dtype)) + assert_exactly_equal(res[mask], (NaN(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays) +def test_abs_special_cases_one_arg_equal_2(arg1): + """ + Special case test for `__abs__(self, /)`: + + - If `x_i` is `-0`, the result is `+0`. + + """ + res = (arg1).__abs__() + mask = exactly_equal(arg1, -zero(arg1.shape, arg1.dtype)) + assert_exactly_equal(res[mask], (zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays) +def test_abs_special_cases_one_arg_equal_3(arg1): + """ + Special case test for `__abs__(self, /)`: + + - If `x_i` is `-infinity`, the result is `+infinity`. + + """ + res = (arg1).__abs__() + mask = exactly_equal(arg1, -infinity(arg1.shape, arg1.dtype)) + assert_exactly_equal(res[mask], (infinity(arg1.shape, arg1.dtype))[mask]) diff --git a/array_api_tests/special_cases/test_dunder_add.py b/array_api_tests/special_cases/test_dunder_add.py new file mode 100644 index 00000000..d3b5e169 --- /dev/null +++ b/array_api_tests/special_cases/test_dunder_add.py @@ -0,0 +1,225 @@ +""" +Special cases tests for __add__. + +These tests are generated from the special cases listed in the spec. + +NOTE: This file is generated automatically by the generate_stubs.py script. Do +not modify it directly. +""" + +from ..array_helpers import (NaN, assert_exactly_equal, exactly_equal, infinity, isfinite, + logical_and, logical_or, non_zero, zero) +from ..hypothesis_helpers import numeric_arrays + +from hypothesis import given + + +@given(numeric_arrays, numeric_arrays) +def test_add_special_cases_two_args_either(arg1, arg2): + """ + Special case test for `__add__(self, other, /)`: + + - If either `x1_i` or `x2_i` is `NaN`, the result is `NaN`. + + """ + res = arg1.__add__(arg2) + mask = logical_or(exactly_equal(arg1, NaN(arg1.shape, arg1.dtype)), exactly_equal(arg2, NaN(arg1.shape, arg1.dtype))) + assert_exactly_equal(res[mask], (NaN(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_add_special_cases_two_args_equal__equal_1(arg1, arg2): + """ + Special case test for `__add__(self, other, /)`: + + - If `x1_i` is `+infinity` and `x2_i` is `-infinity`, the result is `NaN`. + + """ + res = arg1.__add__(arg2) + mask = logical_and(exactly_equal(arg1, infinity(arg1.shape, arg1.dtype)), exactly_equal(arg2, -infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (NaN(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_add_special_cases_two_args_equal__equal_2(arg1, arg2): + """ + Special case test for `__add__(self, other, /)`: + + - If `x1_i` is `-infinity` and `x2_i` is `+infinity`, the result is `NaN`. + + """ + res = arg1.__add__(arg2) + mask = logical_and(exactly_equal(arg1, -infinity(arg1.shape, arg1.dtype)), exactly_equal(arg2, infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (NaN(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_add_special_cases_two_args_equal__equal_3(arg1, arg2): + """ + Special case test for `__add__(self, other, /)`: + + - If `x1_i` is `+infinity` and `x2_i` is `+infinity`, the result is `+infinity`. + + """ + res = arg1.__add__(arg2) + mask = logical_and(exactly_equal(arg1, infinity(arg1.shape, arg1.dtype)), exactly_equal(arg2, infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_add_special_cases_two_args_equal__equal_4(arg1, arg2): + """ + Special case test for `__add__(self, other, /)`: + + - If `x1_i` is `-infinity` and `x2_i` is `-infinity`, the result is `-infinity`. + + """ + res = arg1.__add__(arg2) + mask = logical_and(exactly_equal(arg1, -infinity(arg1.shape, arg1.dtype)), exactly_equal(arg2, -infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (-infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_add_special_cases_two_args_equal__equal_5(arg1, arg2): + """ + Special case test for `__add__(self, other, /)`: + + - If `x1_i` is `+infinity` and `x2_i` is a finite number, the result is `+infinity`. + + """ + res = arg1.__add__(arg2) + mask = logical_and(exactly_equal(arg1, infinity(arg1.shape, arg1.dtype)), isfinite(arg2)) + assert_exactly_equal(res[mask], (infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_add_special_cases_two_args_equal__equal_6(arg1, arg2): + """ + Special case test for `__add__(self, other, /)`: + + - If `x1_i` is `-infinity` and `x2_i` is a finite number, the result is `-infinity`. + + """ + res = arg1.__add__(arg2) + mask = logical_and(exactly_equal(arg1, -infinity(arg1.shape, arg1.dtype)), isfinite(arg2)) + assert_exactly_equal(res[mask], (-infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_add_special_cases_two_args_equal__equal_7(arg1, arg2): + """ + Special case test for `__add__(self, other, /)`: + + - If `x1_i` is a finite number and `x2_i` is `+infinity`, the result is `+infinity`. + + """ + res = arg1.__add__(arg2) + mask = logical_and(isfinite(arg1), exactly_equal(arg2, infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_add_special_cases_two_args_equal__equal_8(arg1, arg2): + """ + Special case test for `__add__(self, other, /)`: + + - If `x1_i` is a finite number and `x2_i` is `-infinity`, the result is `-infinity`. + + """ + res = arg1.__add__(arg2) + mask = logical_and(isfinite(arg1), exactly_equal(arg2, -infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (-infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_add_special_cases_two_args_equal__equal_9(arg1, arg2): + """ + Special case test for `__add__(self, other, /)`: + + - If `x1_i` is `-0` and `x2_i` is `-0`, the result is `-0`. + + """ + res = arg1.__add__(arg2) + mask = logical_and(exactly_equal(arg1, -zero(arg1.shape, arg1.dtype)), exactly_equal(arg2, -zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (-zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_add_special_cases_two_args_equal__equal_10(arg1, arg2): + """ + Special case test for `__add__(self, other, /)`: + + - If `x1_i` is `-0` and `x2_i` is `+0`, the result is `+0`. + + """ + res = arg1.__add__(arg2) + mask = logical_and(exactly_equal(arg1, -zero(arg1.shape, arg1.dtype)), exactly_equal(arg2, zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_add_special_cases_two_args_equal__equal_11(arg1, arg2): + """ + Special case test for `__add__(self, other, /)`: + + - If `x1_i` is `+0` and `x2_i` is `-0`, the result is `+0`. + + """ + res = arg1.__add__(arg2) + mask = logical_and(exactly_equal(arg1, zero(arg1.shape, arg1.dtype)), exactly_equal(arg2, -zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_add_special_cases_two_args_equal__equal_12(arg1, arg2): + """ + Special case test for `__add__(self, other, /)`: + + - If `x1_i` is `+0` and `x2_i` is `+0`, the result is `+0`. + + """ + res = arg1.__add__(arg2) + mask = logical_and(exactly_equal(arg1, zero(arg1.shape, arg1.dtype)), exactly_equal(arg2, zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_add_special_cases_two_args_equal__equal_13(arg1, arg2): + """ + Special case test for `__add__(self, other, /)`: + + - If `x1_i` is a nonzero finite number and `x2_i` is `-x1_i`, the result is `+0`. + + """ + res = arg1.__add__(arg2) + mask = logical_and(logical_and(isfinite(arg1), non_zero(arg1)), exactly_equal(arg2, -arg1)) + assert_exactly_equal(res[mask], (zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_add_special_cases_two_args_either__equal(arg1, arg2): + """ + Special case test for `__add__(self, other, /)`: + + - If `x1_i` is either `+0` or `-0` and `x2_i` is a nonzero finite number, the result is `x2_i`. + + """ + res = arg1.__add__(arg2) + mask = logical_and(logical_or(exactly_equal(arg1, zero(arg1.shape, arg1.dtype)), exactly_equal(arg1, -zero(arg1.shape, arg1.dtype))), logical_and(isfinite(arg2), non_zero(arg2))) + assert_exactly_equal(res[mask], (arg2)[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_add_special_cases_two_args_equal__either(arg1, arg2): + """ + Special case test for `__add__(self, other, /)`: + + - If `x1_i` is a nonzero finite number and `x2_i` is either `+0` or `-0`, the result is `x1_i`. + + """ + res = arg1.__add__(arg2) + mask = logical_and(logical_and(isfinite(arg1), non_zero(arg1)), logical_or(exactly_equal(arg2, zero(arg2.shape, arg2.dtype)), exactly_equal(arg2, -zero(arg2.shape, arg2.dtype)))) + assert_exactly_equal(res[mask], (arg1)[mask]) + +# TODO: Implement REMAINING test for: +# - In the remaining cases, when neither `infinity`, `+0`, `-0`, nor a `NaN` is involved, and the operands have the same mathematical sign or have different magnitudes, the sum must be computed and rounded to the nearest representable value according to IEEE 754-2019 and a supported round mode. If the magnitude is too large to represent, the operation overflows and the result is an `infinity` of appropriate mathematical sign. diff --git a/array_api_tests/special_cases/test_dunder_iadd.py b/array_api_tests/special_cases/test_dunder_iadd.py new file mode 100644 index 00000000..692877bf --- /dev/null +++ b/array_api_tests/special_cases/test_dunder_iadd.py @@ -0,0 +1,243 @@ +""" +Special cases tests for __iadd__. + +These tests are generated from the special cases listed in the spec. + +NOTE: This file is generated automatically by the generate_stubs.py script. Do +not modify it directly. +""" + +from operator import iadd + +from ..array_helpers import (NaN, asarray, assert_exactly_equal, exactly_equal, infinity, isfinite, + logical_and, logical_or, non_zero, zero) +from ..hypothesis_helpers import numeric_arrays + +from hypothesis import given + + +@given(numeric_arrays, numeric_arrays) +def test_iadd_special_cases_two_args_either(arg1, arg2): + """ + Special case test for `__iadd__(self, other, /)`: + + - If either `x1_i` or `x2_i` is `NaN`, the result is `NaN`. + + """ + res = asarray(arg1, copy=True) + iadd(res, arg2) + mask = logical_or(exactly_equal(arg1, NaN(arg1.shape, arg1.dtype)), exactly_equal(arg2, NaN(arg1.shape, arg1.dtype))) + assert_exactly_equal(res[mask], (NaN(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_iadd_special_cases_two_args_equal__equal_1(arg1, arg2): + """ + Special case test for `__iadd__(self, other, /)`: + + - If `x1_i` is `+infinity` and `x2_i` is `-infinity`, the result is `NaN`. + + """ + res = asarray(arg1, copy=True) + iadd(res, arg2) + mask = logical_and(exactly_equal(arg1, infinity(arg1.shape, arg1.dtype)), exactly_equal(arg2, -infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (NaN(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_iadd_special_cases_two_args_equal__equal_2(arg1, arg2): + """ + Special case test for `__iadd__(self, other, /)`: + + - If `x1_i` is `-infinity` and `x2_i` is `+infinity`, the result is `NaN`. + + """ + res = asarray(arg1, copy=True) + iadd(res, arg2) + mask = logical_and(exactly_equal(arg1, -infinity(arg1.shape, arg1.dtype)), exactly_equal(arg2, infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (NaN(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_iadd_special_cases_two_args_equal__equal_3(arg1, arg2): + """ + Special case test for `__iadd__(self, other, /)`: + + - If `x1_i` is `+infinity` and `x2_i` is `+infinity`, the result is `+infinity`. + + """ + res = asarray(arg1, copy=True) + iadd(res, arg2) + mask = logical_and(exactly_equal(arg1, infinity(arg1.shape, arg1.dtype)), exactly_equal(arg2, infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_iadd_special_cases_two_args_equal__equal_4(arg1, arg2): + """ + Special case test for `__iadd__(self, other, /)`: + + - If `x1_i` is `-infinity` and `x2_i` is `-infinity`, the result is `-infinity`. + + """ + res = asarray(arg1, copy=True) + iadd(res, arg2) + mask = logical_and(exactly_equal(arg1, -infinity(arg1.shape, arg1.dtype)), exactly_equal(arg2, -infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (-infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_iadd_special_cases_two_args_equal__equal_5(arg1, arg2): + """ + Special case test for `__iadd__(self, other, /)`: + + - If `x1_i` is `+infinity` and `x2_i` is a finite number, the result is `+infinity`. + + """ + res = asarray(arg1, copy=True) + iadd(res, arg2) + mask = logical_and(exactly_equal(arg1, infinity(arg1.shape, arg1.dtype)), isfinite(arg2)) + assert_exactly_equal(res[mask], (infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_iadd_special_cases_two_args_equal__equal_6(arg1, arg2): + """ + Special case test for `__iadd__(self, other, /)`: + + - If `x1_i` is `-infinity` and `x2_i` is a finite number, the result is `-infinity`. + + """ + res = asarray(arg1, copy=True) + iadd(res, arg2) + mask = logical_and(exactly_equal(arg1, -infinity(arg1.shape, arg1.dtype)), isfinite(arg2)) + assert_exactly_equal(res[mask], (-infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_iadd_special_cases_two_args_equal__equal_7(arg1, arg2): + """ + Special case test for `__iadd__(self, other, /)`: + + - If `x1_i` is a finite number and `x2_i` is `+infinity`, the result is `+infinity`. + + """ + res = asarray(arg1, copy=True) + iadd(res, arg2) + mask = logical_and(isfinite(arg1), exactly_equal(arg2, infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_iadd_special_cases_two_args_equal__equal_8(arg1, arg2): + """ + Special case test for `__iadd__(self, other, /)`: + + - If `x1_i` is a finite number and `x2_i` is `-infinity`, the result is `-infinity`. + + """ + res = asarray(arg1, copy=True) + iadd(res, arg2) + mask = logical_and(isfinite(arg1), exactly_equal(arg2, -infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (-infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_iadd_special_cases_two_args_equal__equal_9(arg1, arg2): + """ + Special case test for `__iadd__(self, other, /)`: + + - If `x1_i` is `-0` and `x2_i` is `-0`, the result is `-0`. + + """ + res = asarray(arg1, copy=True) + iadd(res, arg2) + mask = logical_and(exactly_equal(arg1, -zero(arg1.shape, arg1.dtype)), exactly_equal(arg2, -zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (-zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_iadd_special_cases_two_args_equal__equal_10(arg1, arg2): + """ + Special case test for `__iadd__(self, other, /)`: + + - If `x1_i` is `-0` and `x2_i` is `+0`, the result is `+0`. + + """ + res = asarray(arg1, copy=True) + iadd(res, arg2) + mask = logical_and(exactly_equal(arg1, -zero(arg1.shape, arg1.dtype)), exactly_equal(arg2, zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_iadd_special_cases_two_args_equal__equal_11(arg1, arg2): + """ + Special case test for `__iadd__(self, other, /)`: + + - If `x1_i` is `+0` and `x2_i` is `-0`, the result is `+0`. + + """ + res = asarray(arg1, copy=True) + iadd(res, arg2) + mask = logical_and(exactly_equal(arg1, zero(arg1.shape, arg1.dtype)), exactly_equal(arg2, -zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_iadd_special_cases_two_args_equal__equal_12(arg1, arg2): + """ + Special case test for `__iadd__(self, other, /)`: + + - If `x1_i` is `+0` and `x2_i` is `+0`, the result is `+0`. + + """ + res = asarray(arg1, copy=True) + iadd(res, arg2) + mask = logical_and(exactly_equal(arg1, zero(arg1.shape, arg1.dtype)), exactly_equal(arg2, zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_iadd_special_cases_two_args_equal__equal_13(arg1, arg2): + """ + Special case test for `__iadd__(self, other, /)`: + + - If `x1_i` is a nonzero finite number and `x2_i` is `-x1_i`, the result is `+0`. + + """ + res = asarray(arg1, copy=True) + iadd(res, arg2) + mask = logical_and(logical_and(isfinite(arg1), non_zero(arg1)), exactly_equal(arg2, -arg1)) + assert_exactly_equal(res[mask], (zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_iadd_special_cases_two_args_either__equal(arg1, arg2): + """ + Special case test for `__iadd__(self, other, /)`: + + - If `x1_i` is either `+0` or `-0` and `x2_i` is a nonzero finite number, the result is `x2_i`. + + """ + res = asarray(arg1, copy=True) + iadd(res, arg2) + mask = logical_and(logical_or(exactly_equal(arg1, zero(arg1.shape, arg1.dtype)), exactly_equal(arg1, -zero(arg1.shape, arg1.dtype))), logical_and(isfinite(arg2), non_zero(arg2))) + assert_exactly_equal(res[mask], (arg2)[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_iadd_special_cases_two_args_equal__either(arg1, arg2): + """ + Special case test for `__iadd__(self, other, /)`: + + - If `x1_i` is a nonzero finite number and `x2_i` is either `+0` or `-0`, the result is `x1_i`. + + """ + res = asarray(arg1, copy=True) + iadd(res, arg2) + mask = logical_and(logical_and(isfinite(arg1), non_zero(arg1)), logical_or(exactly_equal(arg2, zero(arg2.shape, arg2.dtype)), exactly_equal(arg2, -zero(arg2.shape, arg2.dtype)))) + assert_exactly_equal(res[mask], (arg1)[mask]) + +# TODO: Implement REMAINING test for: +# - In the remaining cases, when neither `infinity`, `+0`, `-0`, nor a `NaN` is involved, and the operands have the same mathematical sign or have different magnitudes, the sum must be computed and rounded to the nearest representable value according to IEEE 754-2019 and a supported round mode. If the magnitude is too large to represent, the operation overflows and the result is an `infinity` of appropriate mathematical sign. diff --git a/array_api_tests/special_cases/test_dunder_imul.py b/array_api_tests/special_cases/test_dunder_imul.py new file mode 100644 index 00000000..a077fb9a --- /dev/null +++ b/array_api_tests/special_cases/test_dunder_imul.py @@ -0,0 +1,133 @@ +""" +Special cases tests for __imul__. + +These tests are generated from the special cases listed in the spec. + +NOTE: This file is generated automatically by the generate_stubs.py script. Do +not modify it directly. +""" + +from operator import imul + +from ..array_helpers import (NaN, asarray, assert_exactly_equal, assert_isinf, + assert_negative_mathematical_sign, assert_positive_mathematical_sign, + exactly_equal, infinity, isfinite, logical_and, logical_not, + logical_or, non_zero, same_sign, zero) +from ..hypothesis_helpers import numeric_arrays + +from hypothesis import given + + +@given(numeric_arrays, numeric_arrays) +def test_imul_special_cases_two_args_either(arg1, arg2): + """ + Special case test for `__imul__(self, other, /)`: + + - If either `x1_i` or `x2_i` is `NaN`, the result is `NaN`. + + """ + res = asarray(arg1, copy=True) + imul(res, arg2) + mask = logical_or(exactly_equal(arg1, NaN(arg1.shape, arg1.dtype)), exactly_equal(arg2, NaN(arg1.shape, arg1.dtype))) + assert_exactly_equal(res[mask], (NaN(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_imul_special_cases_two_args_either__either_1(arg1, arg2): + """ + Special case test for `__imul__(self, other, /)`: + + - If `x1_i` is either `+infinity` or `-infinity` and `x2_i` is either `+0` or `-0`, the result is `NaN`. + + """ + res = asarray(arg1, copy=True) + imul(res, arg2) + mask = logical_and(logical_or(exactly_equal(arg1, infinity(arg1.shape, arg1.dtype)), exactly_equal(arg1, -infinity(arg1.shape, arg1.dtype))), logical_or(exactly_equal(arg2, zero(arg2.shape, arg2.dtype)), exactly_equal(arg2, -zero(arg2.shape, arg2.dtype)))) + assert_exactly_equal(res[mask], (NaN(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_imul_special_cases_two_args_either__either_2(arg1, arg2): + """ + Special case test for `__imul__(self, other, /)`: + + - If `x1_i` is either `+0` or `-0` and `x2_i` is either `+infinity` or `-infinity`, the result is `NaN`. + + """ + res = asarray(arg1, copy=True) + imul(res, arg2) + mask = logical_and(logical_or(exactly_equal(arg1, zero(arg1.shape, arg1.dtype)), exactly_equal(arg1, -zero(arg1.shape, arg1.dtype))), logical_or(exactly_equal(arg2, infinity(arg2.shape, arg2.dtype)), exactly_equal(arg2, -infinity(arg2.shape, arg2.dtype)))) + assert_exactly_equal(res[mask], (NaN(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_imul_special_cases_two_args_either__either_3(arg1, arg2): + """ + Special case test for `__imul__(self, other, /)`: + + - If `x1_i` is either `+infinity` or `-infinity` and `x2_i` is either `+infinity` or `-infinity`, the result is a signed infinity with the mathematical sign determined by the rule already stated above. + + """ + res = asarray(arg1, copy=True) + imul(res, arg2) + mask = logical_and(logical_or(exactly_equal(arg1, infinity(arg1.shape, arg1.dtype)), exactly_equal(arg1, -infinity(arg1.shape, arg1.dtype))), logical_or(exactly_equal(arg2, infinity(arg2.shape, arg2.dtype)), exactly_equal(arg2, -infinity(arg2.shape, arg2.dtype)))) + assert_isinf(res[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_imul_special_cases_two_args_same_sign_except(arg1, arg2): + """ + Special case test for `__imul__(self, other, /)`: + + - If `x1_i` and `x2_i` have the same mathematical sign, the result has a positive mathematical sign, unless the result is `NaN`. If the result is `NaN`, the "sign" of `NaN` is implementation-defined. + + """ + res = asarray(arg1, copy=True) + imul(res, arg2) + mask = logical_and(same_sign(arg1, arg2), logical_not(exactly_equal(res, NaN(res.shape, res.dtype)))) + assert_positive_mathematical_sign(res[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_imul_special_cases_two_args_different_signs_except(arg1, arg2): + """ + Special case test for `__imul__(self, other, /)`: + + - If `x1_i` and `x2_i` have different mathematical signs, the result has a negative mathematical sign, unless the result is `NaN`. If the result is `NaN`, the "sign" of `NaN` is implementation-defined. + + """ + res = asarray(arg1, copy=True) + imul(res, arg2) + mask = logical_and(logical_not(same_sign(arg1, arg2)), logical_not(exactly_equal(res, NaN(res.shape, res.dtype)))) + assert_negative_mathematical_sign(res[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_imul_special_cases_two_args_either__equal(arg1, arg2): + """ + Special case test for `__imul__(self, other, /)`: + + - If `x1_i` is either `+infinity` or `-infinity` and `x2_i` is a nonzero finite number, the result is a signed infinity with the mathematical sign determined by the rule already stated above. + + """ + res = asarray(arg1, copy=True) + imul(res, arg2) + mask = logical_and(logical_or(exactly_equal(arg1, infinity(arg1.shape, arg1.dtype)), exactly_equal(arg1, -infinity(arg1.shape, arg1.dtype))), logical_and(isfinite(arg2), non_zero(arg2))) + assert_isinf(res[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_imul_special_cases_two_args_equal__either(arg1, arg2): + """ + Special case test for `__imul__(self, other, /)`: + + - If `x1_i` is a nonzero finite number and `x2_i` is either `+infinity` or `-infinity`, the result is a signed infinity with the mathematical sign determined by the rule already stated above. + + """ + res = asarray(arg1, copy=True) + imul(res, arg2) + mask = logical_and(logical_and(isfinite(arg1), non_zero(arg1)), logical_or(exactly_equal(arg2, infinity(arg2.shape, arg2.dtype)), exactly_equal(arg2, -infinity(arg2.shape, arg2.dtype)))) + assert_isinf(res[mask]) + +# TODO: Implement REMAINING test for: +# - In the remaining cases, where neither `infinity` nor `NaN` is involved, the product must be computed and rounded to the nearest representable value according to IEEE 754-2019 and a supported rounding mode. If the magnitude is too large to represent, the result is an `infinity` of appropriate mathematical sign. If the magnitude is too small to represent, the result is a zero of appropriate mathematical sign. diff --git a/array_api_tests/special_cases/test_dunder_ipow.py b/array_api_tests/special_cases/test_dunder_ipow.py new file mode 100644 index 00000000..0ca5e705 --- /dev/null +++ b/array_api_tests/special_cases/test_dunder_ipow.py @@ -0,0 +1,353 @@ +""" +Special cases tests for __ipow__. + +These tests are generated from the special cases listed in the spec. + +NOTE: This file is generated automatically by the generate_stubs.py script. Do +not modify it directly. +""" + +from operator import ipow + +from ..array_helpers import (NaN, asarray, assert_exactly_equal, exactly_equal, greater, infinity, + isfinite, isintegral, isodd, less, logical_and, logical_not, notequal, + one, zero) +from ..hypothesis_helpers import numeric_arrays + +from hypothesis import given + + +@given(numeric_arrays, numeric_arrays) +def test_ipow_special_cases_two_args_notequal__equal(arg1, arg2): + """ + Special case test for `__ipow__(self, other, /)`: + + - If `x1_i` is not equal to `1` and `x2_i` is `NaN`, the result is `NaN`. + + """ + res = asarray(arg1, copy=True) + ipow(res, arg2) + mask = logical_and(logical_not(exactly_equal(arg1, one(arg1.shape, arg1.dtype))), exactly_equal(arg2, NaN(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (NaN(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_ipow_special_cases_two_args_even_if_1(arg1, arg2): + """ + Special case test for `__ipow__(self, other, /)`: + + - If `x2_i` is `+0`, the result is `1`, even if `x1_i` is `NaN`. + + """ + res = asarray(arg1, copy=True) + ipow(res, arg2) + mask = exactly_equal(arg2, zero(arg2.shape, arg2.dtype)) + assert_exactly_equal(res[mask], (one(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_ipow_special_cases_two_args_even_if_2(arg1, arg2): + """ + Special case test for `__ipow__(self, other, /)`: + + - If `x2_i` is `-0`, the result is `1`, even if `x1_i` is `NaN`. + + """ + res = asarray(arg1, copy=True) + ipow(res, arg2) + mask = exactly_equal(arg2, -zero(arg2.shape, arg2.dtype)) + assert_exactly_equal(res[mask], (one(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_ipow_special_cases_two_args_equal__notequal_1(arg1, arg2): + """ + Special case test for `__ipow__(self, other, /)`: + + - If `x1_i` is `NaN` and `x2_i` is not equal to `0`, the result is `NaN`. + + """ + res = asarray(arg1, copy=True) + ipow(res, arg2) + mask = logical_and(exactly_equal(arg1, NaN(arg1.shape, arg1.dtype)), notequal(arg2, zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (NaN(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_ipow_special_cases_two_args_equal__notequal_2(arg1, arg2): + """ + Special case test for `__ipow__(self, other, /)`: + + - If `x1_i` is `1` and `x2_i` is not `NaN`, the result is `1`. + + """ + res = asarray(arg1, copy=True) + ipow(res, arg2) + mask = logical_and(exactly_equal(arg1, one(arg1.shape, arg1.dtype)), logical_not(exactly_equal(arg2, NaN(arg2.shape, arg2.dtype)))) + assert_exactly_equal(res[mask], (one(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_ipow_special_cases_two_args_absgreater__equal_1(arg1, arg2): + """ + Special case test for `__ipow__(self, other, /)`: + + - If `abs(x1_i)` is greater than `1` and `x2_i` is `+infinity`, the result is `+infinity`. + + """ + res = asarray(arg1, copy=True) + ipow(res, arg2) + mask = logical_and(greater(abs(arg1), one(arg1.shape, arg1.dtype)), exactly_equal(arg2, infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_ipow_special_cases_two_args_absgreater__equal_2(arg1, arg2): + """ + Special case test for `__ipow__(self, other, /)`: + + - If `abs(x1_i)` is greater than `1` and `x2_i` is `-infinity`, the result is `+0`. + + """ + res = asarray(arg1, copy=True) + ipow(res, arg2) + mask = logical_and(greater(abs(arg1), one(arg1.shape, arg1.dtype)), exactly_equal(arg2, -infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_ipow_special_cases_two_args_absequal__equal_1(arg1, arg2): + """ + Special case test for `__ipow__(self, other, /)`: + + - If `abs(x1_i)` is `1` and `x2_i` is `+infinity`, the result is `1`. + + """ + res = asarray(arg1, copy=True) + ipow(res, arg2) + mask = logical_and(exactly_equal(abs(arg1), one(arg1.shape, arg1.dtype)), exactly_equal(arg2, infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (one(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_ipow_special_cases_two_args_absequal__equal_2(arg1, arg2): + """ + Special case test for `__ipow__(self, other, /)`: + + - If `abs(x1_i)` is `1` and `x2_i` is `-infinity`, the result is `1`. + + """ + res = asarray(arg1, copy=True) + ipow(res, arg2) + mask = logical_and(exactly_equal(abs(arg1), one(arg1.shape, arg1.dtype)), exactly_equal(arg2, -infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (one(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_ipow_special_cases_two_args_absless__equal_1(arg1, arg2): + """ + Special case test for `__ipow__(self, other, /)`: + + - If `abs(x1_i)` is less than `1` and `x2_i` is `+infinity`, the result is `+0`. + + """ + res = asarray(arg1, copy=True) + ipow(res, arg2) + mask = logical_and(less(abs(arg1), one(arg1.shape, arg1.dtype)), exactly_equal(arg2, infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_ipow_special_cases_two_args_absless__equal_2(arg1, arg2): + """ + Special case test for `__ipow__(self, other, /)`: + + - If `abs(x1_i)` is less than `1` and `x2_i` is `-infinity`, the result is `+infinity`. + + """ + res = asarray(arg1, copy=True) + ipow(res, arg2) + mask = logical_and(less(abs(arg1), one(arg1.shape, arg1.dtype)), exactly_equal(arg2, -infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_ipow_special_cases_two_args_equal__greater_1(arg1, arg2): + """ + Special case test for `__ipow__(self, other, /)`: + + - If `x1_i` is `+infinity` and `x2_i` is greater than `0`, the result is `+infinity`. + + """ + res = asarray(arg1, copy=True) + ipow(res, arg2) + mask = logical_and(exactly_equal(arg1, infinity(arg1.shape, arg1.dtype)), greater(arg2, zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_ipow_special_cases_two_args_equal__greater_2(arg1, arg2): + """ + Special case test for `__ipow__(self, other, /)`: + + - If `x1_i` is `+0` and `x2_i` is greater than `0`, the result is `+0`. + + """ + res = asarray(arg1, copy=True) + ipow(res, arg2) + mask = logical_and(exactly_equal(arg1, zero(arg1.shape, arg1.dtype)), greater(arg2, zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_ipow_special_cases_two_args_equal__less_1(arg1, arg2): + """ + Special case test for `__ipow__(self, other, /)`: + + - If `x1_i` is `+infinity` and `x2_i` is less than `0`, the result is `+0`. + + """ + res = asarray(arg1, copy=True) + ipow(res, arg2) + mask = logical_and(exactly_equal(arg1, infinity(arg1.shape, arg1.dtype)), less(arg2, zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_ipow_special_cases_two_args_equal__less_2(arg1, arg2): + """ + Special case test for `__ipow__(self, other, /)`: + + - If `x1_i` is `+0` and `x2_i` is less than `0`, the result is `+infinity`. + + """ + res = asarray(arg1, copy=True) + ipow(res, arg2) + mask = logical_and(exactly_equal(arg1, zero(arg1.shape, arg1.dtype)), less(arg2, zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_ipow_special_cases_two_args_equal__greater_equal_1(arg1, arg2): + """ + Special case test for `__ipow__(self, other, /)`: + + - If `x1_i` is `-infinity`, `x2_i` is greater than `0`, and `x2_i` is an odd integer value, the result is `-infinity`. + + """ + res = asarray(arg1, copy=True) + ipow(res, arg2) + mask = logical_and(exactly_equal(arg1, -infinity(arg1.shape, arg1.dtype)), logical_and(greater(arg2, zero(arg2.shape, arg2.dtype)), isodd(arg2))) + assert_exactly_equal(res[mask], (-infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_ipow_special_cases_two_args_equal__greater_equal_2(arg1, arg2): + """ + Special case test for `__ipow__(self, other, /)`: + + - If `x1_i` is `-0`, `x2_i` is greater than `0`, and `x2_i` is an odd integer value, the result is `-0`. + + """ + res = asarray(arg1, copy=True) + ipow(res, arg2) + mask = logical_and(exactly_equal(arg1, -zero(arg1.shape, arg1.dtype)), logical_and(greater(arg2, zero(arg2.shape, arg2.dtype)), isodd(arg2))) + assert_exactly_equal(res[mask], (-zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_ipow_special_cases_two_args_equal__greater_notequal_1(arg1, arg2): + """ + Special case test for `__ipow__(self, other, /)`: + + - If `x1_i` is `-infinity`, `x2_i` is greater than `0`, and `x2_i` is not an odd integer value, the result is `+infinity`. + + """ + res = asarray(arg1, copy=True) + ipow(res, arg2) + mask = logical_and(exactly_equal(arg1, -infinity(arg1.shape, arg1.dtype)), logical_and(greater(arg2, zero(arg2.shape, arg2.dtype)), logical_not(isodd(arg2)))) + assert_exactly_equal(res[mask], (infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_ipow_special_cases_two_args_equal__greater_notequal_2(arg1, arg2): + """ + Special case test for `__ipow__(self, other, /)`: + + - If `x1_i` is `-0`, `x2_i` is greater than `0`, and `x2_i` is not an odd integer value, the result is `+0`. + + """ + res = asarray(arg1, copy=True) + ipow(res, arg2) + mask = logical_and(exactly_equal(arg1, -zero(arg1.shape, arg1.dtype)), logical_and(greater(arg2, zero(arg2.shape, arg2.dtype)), logical_not(isodd(arg2)))) + assert_exactly_equal(res[mask], (zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_ipow_special_cases_two_args_equal__less_equal_1(arg1, arg2): + """ + Special case test for `__ipow__(self, other, /)`: + + - If `x1_i` is `-infinity`, `x2_i` is less than `0`, and `x2_i` is an odd integer value, the result is `-0`. + + """ + res = asarray(arg1, copy=True) + ipow(res, arg2) + mask = logical_and(exactly_equal(arg1, -infinity(arg1.shape, arg1.dtype)), logical_and(less(arg2, zero(arg2.shape, arg2.dtype)), isodd(arg2))) + assert_exactly_equal(res[mask], (-zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_ipow_special_cases_two_args_equal__less_equal_2(arg1, arg2): + """ + Special case test for `__ipow__(self, other, /)`: + + - If `x1_i` is `-0`, `x2_i` is less than `0`, and `x2_i` is an odd integer value, the result is `-infinity`. + + """ + res = asarray(arg1, copy=True) + ipow(res, arg2) + mask = logical_and(exactly_equal(arg1, -zero(arg1.shape, arg1.dtype)), logical_and(less(arg2, zero(arg2.shape, arg2.dtype)), isodd(arg2))) + assert_exactly_equal(res[mask], (-infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_ipow_special_cases_two_args_equal__less_notequal_1(arg1, arg2): + """ + Special case test for `__ipow__(self, other, /)`: + + - If `x1_i` is `-infinity`, `x2_i` is less than `0`, and `x2_i` is not an odd integer value, the result is `+0`. + + """ + res = asarray(arg1, copy=True) + ipow(res, arg2) + mask = logical_and(exactly_equal(arg1, -infinity(arg1.shape, arg1.dtype)), logical_and(less(arg2, zero(arg2.shape, arg2.dtype)), logical_not(isodd(arg2)))) + assert_exactly_equal(res[mask], (zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_ipow_special_cases_two_args_equal__less_notequal_2(arg1, arg2): + """ + Special case test for `__ipow__(self, other, /)`: + + - If `x1_i` is `-0`, `x2_i` is less than `0`, and `x2_i` is not an odd integer value, the result is `+infinity`. + + """ + res = asarray(arg1, copy=True) + ipow(res, arg2) + mask = logical_and(exactly_equal(arg1, -zero(arg1.shape, arg1.dtype)), logical_and(less(arg2, zero(arg2.shape, arg2.dtype)), logical_not(isodd(arg2)))) + assert_exactly_equal(res[mask], (infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_ipow_special_cases_two_args_less_equal__equal_notequal(arg1, arg2): + """ + Special case test for `__ipow__(self, other, /)`: + + - If `x1_i` is less than `0`, `x1_i` is a finite number, `x2_i` is a finite number, and `x2_i` is not an integer value, the result is `NaN`. + + """ + res = asarray(arg1, copy=True) + ipow(res, arg2) + mask = logical_and(logical_and(less(arg1, zero(arg1.shape, arg1.dtype)), isfinite(arg1)), logical_and(isfinite(arg2), logical_not(isintegral(arg2)))) + assert_exactly_equal(res[mask], (NaN(arg1.shape, arg1.dtype))[mask]) diff --git a/array_api_tests/special_cases/test_dunder_itruediv.py b/array_api_tests/special_cases/test_dunder_itruediv.py new file mode 100644 index 00000000..e6747d40 --- /dev/null +++ b/array_api_tests/special_cases/test_dunder_itruediv.py @@ -0,0 +1,315 @@ +""" +Special cases tests for __itruediv__. + +These tests are generated from the special cases listed in the spec. + +NOTE: This file is generated automatically by the generate_stubs.py script. Do +not modify it directly. +""" + +from operator import itruediv + +from ..array_helpers import (NaN, asarray, assert_exactly_equal, assert_negative_mathematical_sign, + assert_positive_mathematical_sign, exactly_equal, greater, infinity, + isfinite, isnegative, ispositive, less, logical_and, logical_not, + logical_or, non_zero, same_sign, zero) +from ..hypothesis_helpers import numeric_arrays + +from hypothesis import given + + +@given(numeric_arrays, numeric_arrays) +def test_itruediv_special_cases_two_args_either(arg1, arg2): + """ + Special case test for `__itruediv__(self, other, /)`: + + - If either `x1_i` or `x2_i` is `NaN`, the result is `NaN`. + + """ + res = asarray(arg1, copy=True) + itruediv(res, arg2) + mask = logical_or(exactly_equal(arg1, NaN(arg1.shape, arg1.dtype)), exactly_equal(arg2, NaN(arg1.shape, arg1.dtype))) + assert_exactly_equal(res[mask], (NaN(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_itruediv_special_cases_two_args_either__either_1(arg1, arg2): + """ + Special case test for `__itruediv__(self, other, /)`: + + - If `x1_i` is either `+infinity` or `-infinity` and `x2_i` is either `+infinity` or `-infinity`, the result is `NaN`. + + """ + res = asarray(arg1, copy=True) + itruediv(res, arg2) + mask = logical_and(logical_or(exactly_equal(arg1, infinity(arg1.shape, arg1.dtype)), exactly_equal(arg1, -infinity(arg1.shape, arg1.dtype))), logical_or(exactly_equal(arg2, infinity(arg2.shape, arg2.dtype)), exactly_equal(arg2, -infinity(arg2.shape, arg2.dtype)))) + assert_exactly_equal(res[mask], (NaN(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_itruediv_special_cases_two_args_either__either_2(arg1, arg2): + """ + Special case test for `__itruediv__(self, other, /)`: + + - If `x1_i` is either `+0` or `-0` and `x2_i` is either `+0` or `-0`, the result is `NaN`. + + """ + res = asarray(arg1, copy=True) + itruediv(res, arg2) + mask = logical_and(logical_or(exactly_equal(arg1, zero(arg1.shape, arg1.dtype)), exactly_equal(arg1, -zero(arg1.shape, arg1.dtype))), logical_or(exactly_equal(arg2, zero(arg2.shape, arg2.dtype)), exactly_equal(arg2, -zero(arg2.shape, arg2.dtype)))) + assert_exactly_equal(res[mask], (NaN(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_itruediv_special_cases_two_args_equal__greater_1(arg1, arg2): + """ + Special case test for `__itruediv__(self, other, /)`: + + - If `x1_i` is `+0` and `x2_i` is greater than `0`, the result is `+0`. + + """ + res = asarray(arg1, copy=True) + itruediv(res, arg2) + mask = logical_and(exactly_equal(arg1, zero(arg1.shape, arg1.dtype)), greater(arg2, zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_itruediv_special_cases_two_args_equal__greater_2(arg1, arg2): + """ + Special case test for `__itruediv__(self, other, /)`: + + - If `x1_i` is `-0` and `x2_i` is greater than `0`, the result is `-0`. + + """ + res = asarray(arg1, copy=True) + itruediv(res, arg2) + mask = logical_and(exactly_equal(arg1, -zero(arg1.shape, arg1.dtype)), greater(arg2, zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (-zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_itruediv_special_cases_two_args_equal__less_1(arg1, arg2): + """ + Special case test for `__itruediv__(self, other, /)`: + + - If `x1_i` is `+0` and `x2_i` is less than `0`, the result is `-0`. + + """ + res = asarray(arg1, copy=True) + itruediv(res, arg2) + mask = logical_and(exactly_equal(arg1, zero(arg1.shape, arg1.dtype)), less(arg2, zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (-zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_itruediv_special_cases_two_args_equal__less_2(arg1, arg2): + """ + Special case test for `__itruediv__(self, other, /)`: + + - If `x1_i` is `-0` and `x2_i` is less than `0`, the result is `+0`. + + """ + res = asarray(arg1, copy=True) + itruediv(res, arg2) + mask = logical_and(exactly_equal(arg1, -zero(arg1.shape, arg1.dtype)), less(arg2, zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_itruediv_special_cases_two_args_greater__equal_1(arg1, arg2): + """ + Special case test for `__itruediv__(self, other, /)`: + + - If `x1_i` is greater than `0` and `x2_i` is `+0`, the result is `+infinity`. + + """ + res = asarray(arg1, copy=True) + itruediv(res, arg2) + mask = logical_and(greater(arg1, zero(arg1.shape, arg1.dtype)), exactly_equal(arg2, zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_itruediv_special_cases_two_args_greater__equal_2(arg1, arg2): + """ + Special case test for `__itruediv__(self, other, /)`: + + - If `x1_i` is greater than `0` and `x2_i` is `-0`, the result is `-infinity`. + + """ + res = asarray(arg1, copy=True) + itruediv(res, arg2) + mask = logical_and(greater(arg1, zero(arg1.shape, arg1.dtype)), exactly_equal(arg2, -zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (-infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_itruediv_special_cases_two_args_less__equal_1(arg1, arg2): + """ + Special case test for `__itruediv__(self, other, /)`: + + - If `x1_i` is less than `0` and `x2_i` is `+0`, the result is `-infinity`. + + """ + res = asarray(arg1, copy=True) + itruediv(res, arg2) + mask = logical_and(less(arg1, zero(arg1.shape, arg1.dtype)), exactly_equal(arg2, zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (-infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_itruediv_special_cases_two_args_less__equal_2(arg1, arg2): + """ + Special case test for `__itruediv__(self, other, /)`: + + - If `x1_i` is less than `0` and `x2_i` is `-0`, the result is `+infinity`. + + """ + res = asarray(arg1, copy=True) + itruediv(res, arg2) + mask = logical_and(less(arg1, zero(arg1.shape, arg1.dtype)), exactly_equal(arg2, -zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_itruediv_special_cases_two_args_equal__equal_1(arg1, arg2): + """ + Special case test for `__itruediv__(self, other, /)`: + + - If `x1_i` is `+infinity` and `x2_i` is a positive (i.e., greater than `0`) finite number, the result is `+infinity`. + + """ + res = asarray(arg1, copy=True) + itruediv(res, arg2) + mask = logical_and(exactly_equal(arg1, infinity(arg1.shape, arg1.dtype)), logical_and(isfinite(arg2), ispositive(arg2))) + assert_exactly_equal(res[mask], (infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_itruediv_special_cases_two_args_equal__equal_2(arg1, arg2): + """ + Special case test for `__itruediv__(self, other, /)`: + + - If `x1_i` is `+infinity` and `x2_i` is a negative (i.e., less than `0`) finite number, the result is `-infinity`. + + """ + res = asarray(arg1, copy=True) + itruediv(res, arg2) + mask = logical_and(exactly_equal(arg1, infinity(arg1.shape, arg1.dtype)), logical_and(isfinite(arg2), isnegative(arg2))) + assert_exactly_equal(res[mask], (-infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_itruediv_special_cases_two_args_equal__equal_3(arg1, arg2): + """ + Special case test for `__itruediv__(self, other, /)`: + + - If `x1_i` is `-infinity` and `x2_i` is a positive (i.e., greater than `0`) finite number, the result is `-infinity`. + + """ + res = asarray(arg1, copy=True) + itruediv(res, arg2) + mask = logical_and(exactly_equal(arg1, -infinity(arg1.shape, arg1.dtype)), logical_and(isfinite(arg2), ispositive(arg2))) + assert_exactly_equal(res[mask], (-infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_itruediv_special_cases_two_args_equal__equal_4(arg1, arg2): + """ + Special case test for `__itruediv__(self, other, /)`: + + - If `x1_i` is `-infinity` and `x2_i` is a negative (i.e., less than `0`) finite number, the result is `+infinity`. + + """ + res = asarray(arg1, copy=True) + itruediv(res, arg2) + mask = logical_and(exactly_equal(arg1, -infinity(arg1.shape, arg1.dtype)), logical_and(isfinite(arg2), isnegative(arg2))) + assert_exactly_equal(res[mask], (infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_itruediv_special_cases_two_args_equal__equal_5(arg1, arg2): + """ + Special case test for `__itruediv__(self, other, /)`: + + - If `x1_i` is a positive (i.e., greater than `0`) finite number and `x2_i` is `+infinity`, the result is `+0`. + + """ + res = asarray(arg1, copy=True) + itruediv(res, arg2) + mask = logical_and(logical_and(isfinite(arg1), ispositive(arg1)), exactly_equal(arg2, infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_itruediv_special_cases_two_args_equal__equal_6(arg1, arg2): + """ + Special case test for `__itruediv__(self, other, /)`: + + - If `x1_i` is a positive (i.e., greater than `0`) finite number and `x2_i` is `-infinity`, the result is `-0`. + + """ + res = asarray(arg1, copy=True) + itruediv(res, arg2) + mask = logical_and(logical_and(isfinite(arg1), ispositive(arg1)), exactly_equal(arg2, -infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (-zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_itruediv_special_cases_two_args_equal__equal_7(arg1, arg2): + """ + Special case test for `__itruediv__(self, other, /)`: + + - If `x1_i` is a negative (i.e., less than `0`) finite number and `x2_i` is `+infinity`, the result is `-0`. + + """ + res = asarray(arg1, copy=True) + itruediv(res, arg2) + mask = logical_and(logical_and(isfinite(arg1), isnegative(arg1)), exactly_equal(arg2, infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (-zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_itruediv_special_cases_two_args_equal__equal_8(arg1, arg2): + """ + Special case test for `__itruediv__(self, other, /)`: + + - If `x1_i` is a negative (i.e., less than `0`) finite number and `x2_i` is `-infinity`, the result is `+0`. + + """ + res = asarray(arg1, copy=True) + itruediv(res, arg2) + mask = logical_and(logical_and(isfinite(arg1), isnegative(arg1)), exactly_equal(arg2, -infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_itruediv_special_cases_two_args_same_sign_both(arg1, arg2): + """ + Special case test for `__itruediv__(self, other, /)`: + + - If `x1_i` and `x2_i` have the same mathematical sign and are both nonzero finite numbers, the result has a positive mathematical sign. + + """ + res = asarray(arg1, copy=True) + itruediv(res, arg2) + mask = logical_and(same_sign(arg1, arg2), logical_and(logical_and(isfinite(arg1), non_zero(arg1)), logical_and(isfinite(arg2), non_zero(arg2)))) + assert_positive_mathematical_sign(res[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_itruediv_special_cases_two_args_different_signs_both(arg1, arg2): + """ + Special case test for `__itruediv__(self, other, /)`: + + - If `x1_i` and `x2_i` have different mathematical signs and are both nonzero finite numbers, the result has a negative mathematical sign. + + """ + res = asarray(arg1, copy=True) + itruediv(res, arg2) + mask = logical_and(logical_not(same_sign(arg1, arg2)), logical_and(logical_and(isfinite(arg1), non_zero(arg1)), logical_and(isfinite(arg2), non_zero(arg2)))) + assert_negative_mathematical_sign(res[mask]) + +# TODO: Implement REMAINING test for: +# - In the remaining cases, where neither `-infinity`, `+0`, `-0`, nor `NaN` is involved, the quotient must be computed and rounded to the nearest representable value according to IEEE 754-2019 and a supported rounding mode. If the magnitude is too larger to represent, the operation overflows and the result is an `infinity` of appropriate mathematical sign. If the magnitude is too small to represent, the operation underflows and the result is a zero of appropriate mathematical sign. diff --git a/array_api_tests/special_cases/test_dunder_mul.py b/array_api_tests/special_cases/test_dunder_mul.py new file mode 100644 index 00000000..983f3654 --- /dev/null +++ b/array_api_tests/special_cases/test_dunder_mul.py @@ -0,0 +1,123 @@ +""" +Special cases tests for __mul__. + +These tests are generated from the special cases listed in the spec. + +NOTE: This file is generated automatically by the generate_stubs.py script. Do +not modify it directly. +""" + +from ..array_helpers import (NaN, assert_exactly_equal, assert_isinf, + assert_negative_mathematical_sign, assert_positive_mathematical_sign, + exactly_equal, infinity, isfinite, logical_and, logical_not, + logical_or, non_zero, same_sign, zero) +from ..hypothesis_helpers import numeric_arrays + +from hypothesis import given + + +@given(numeric_arrays, numeric_arrays) +def test_mul_special_cases_two_args_either(arg1, arg2): + """ + Special case test for `__mul__(self, other, /)`: + + - If either `x1_i` or `x2_i` is `NaN`, the result is `NaN`. + + """ + res = arg1.__mul__(arg2) + mask = logical_or(exactly_equal(arg1, NaN(arg1.shape, arg1.dtype)), exactly_equal(arg2, NaN(arg1.shape, arg1.dtype))) + assert_exactly_equal(res[mask], (NaN(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_mul_special_cases_two_args_either__either_1(arg1, arg2): + """ + Special case test for `__mul__(self, other, /)`: + + - If `x1_i` is either `+infinity` or `-infinity` and `x2_i` is either `+0` or `-0`, the result is `NaN`. + + """ + res = arg1.__mul__(arg2) + mask = logical_and(logical_or(exactly_equal(arg1, infinity(arg1.shape, arg1.dtype)), exactly_equal(arg1, -infinity(arg1.shape, arg1.dtype))), logical_or(exactly_equal(arg2, zero(arg2.shape, arg2.dtype)), exactly_equal(arg2, -zero(arg2.shape, arg2.dtype)))) + assert_exactly_equal(res[mask], (NaN(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_mul_special_cases_two_args_either__either_2(arg1, arg2): + """ + Special case test for `__mul__(self, other, /)`: + + - If `x1_i` is either `+0` or `-0` and `x2_i` is either `+infinity` or `-infinity`, the result is `NaN`. + + """ + res = arg1.__mul__(arg2) + mask = logical_and(logical_or(exactly_equal(arg1, zero(arg1.shape, arg1.dtype)), exactly_equal(arg1, -zero(arg1.shape, arg1.dtype))), logical_or(exactly_equal(arg2, infinity(arg2.shape, arg2.dtype)), exactly_equal(arg2, -infinity(arg2.shape, arg2.dtype)))) + assert_exactly_equal(res[mask], (NaN(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_mul_special_cases_two_args_either__either_3(arg1, arg2): + """ + Special case test for `__mul__(self, other, /)`: + + - If `x1_i` is either `+infinity` or `-infinity` and `x2_i` is either `+infinity` or `-infinity`, the result is a signed infinity with the mathematical sign determined by the rule already stated above. + + """ + res = arg1.__mul__(arg2) + mask = logical_and(logical_or(exactly_equal(arg1, infinity(arg1.shape, arg1.dtype)), exactly_equal(arg1, -infinity(arg1.shape, arg1.dtype))), logical_or(exactly_equal(arg2, infinity(arg2.shape, arg2.dtype)), exactly_equal(arg2, -infinity(arg2.shape, arg2.dtype)))) + assert_isinf(res[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_mul_special_cases_two_args_same_sign_except(arg1, arg2): + """ + Special case test for `__mul__(self, other, /)`: + + - If `x1_i` and `x2_i` have the same mathematical sign, the result has a positive mathematical sign, unless the result is `NaN`. If the result is `NaN`, the "sign" of `NaN` is implementation-defined. + + """ + res = arg1.__mul__(arg2) + mask = logical_and(same_sign(arg1, arg2), logical_not(exactly_equal(res, NaN(res.shape, res.dtype)))) + assert_positive_mathematical_sign(res[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_mul_special_cases_two_args_different_signs_except(arg1, arg2): + """ + Special case test for `__mul__(self, other, /)`: + + - If `x1_i` and `x2_i` have different mathematical signs, the result has a negative mathematical sign, unless the result is `NaN`. If the result is `NaN`, the "sign" of `NaN` is implementation-defined. + + """ + res = arg1.__mul__(arg2) + mask = logical_and(logical_not(same_sign(arg1, arg2)), logical_not(exactly_equal(res, NaN(res.shape, res.dtype)))) + assert_negative_mathematical_sign(res[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_mul_special_cases_two_args_either__equal(arg1, arg2): + """ + Special case test for `__mul__(self, other, /)`: + + - If `x1_i` is either `+infinity` or `-infinity` and `x2_i` is a nonzero finite number, the result is a signed infinity with the mathematical sign determined by the rule already stated above. + + """ + res = arg1.__mul__(arg2) + mask = logical_and(logical_or(exactly_equal(arg1, infinity(arg1.shape, arg1.dtype)), exactly_equal(arg1, -infinity(arg1.shape, arg1.dtype))), logical_and(isfinite(arg2), non_zero(arg2))) + assert_isinf(res[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_mul_special_cases_two_args_equal__either(arg1, arg2): + """ + Special case test for `__mul__(self, other, /)`: + + - If `x1_i` is a nonzero finite number and `x2_i` is either `+infinity` or `-infinity`, the result is a signed infinity with the mathematical sign determined by the rule already stated above. + + """ + res = arg1.__mul__(arg2) + mask = logical_and(logical_and(isfinite(arg1), non_zero(arg1)), logical_or(exactly_equal(arg2, infinity(arg2.shape, arg2.dtype)), exactly_equal(arg2, -infinity(arg2.shape, arg2.dtype)))) + assert_isinf(res[mask]) + +# TODO: Implement REMAINING test for: +# - In the remaining cases, where neither `infinity` nor `NaN` is involved, the product must be computed and rounded to the nearest representable value according to IEEE 754-2019 and a supported rounding mode. If the magnitude is too large to represent, the result is an `infinity` of appropriate mathematical sign. If the magnitude is too small to represent, the result is a zero of appropriate mathematical sign. diff --git a/array_api_tests/special_cases/test_dunder_pow.py b/array_api_tests/special_cases/test_dunder_pow.py new file mode 100644 index 00000000..b90abd4c --- /dev/null +++ b/array_api_tests/special_cases/test_dunder_pow.py @@ -0,0 +1,326 @@ +""" +Special cases tests for __pow__. + +These tests are generated from the special cases listed in the spec. + +NOTE: This file is generated automatically by the generate_stubs.py script. Do +not modify it directly. +""" + +from ..array_helpers import (NaN, assert_exactly_equal, exactly_equal, greater, infinity, isfinite, + isintegral, isodd, less, logical_and, logical_not, notequal, one, zero) +from ..hypothesis_helpers import numeric_arrays + +from hypothesis import given + + +@given(numeric_arrays, numeric_arrays) +def test_pow_special_cases_two_args_notequal__equal(arg1, arg2): + """ + Special case test for `__pow__(self, other, /)`: + + - If `x1_i` is not equal to `1` and `x2_i` is `NaN`, the result is `NaN`. + + """ + res = arg1.__pow__(arg2) + mask = logical_and(logical_not(exactly_equal(arg1, one(arg1.shape, arg1.dtype))), exactly_equal(arg2, NaN(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (NaN(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_pow_special_cases_two_args_even_if_1(arg1, arg2): + """ + Special case test for `__pow__(self, other, /)`: + + - If `x2_i` is `+0`, the result is `1`, even if `x1_i` is `NaN`. + + """ + res = arg1.__pow__(arg2) + mask = exactly_equal(arg2, zero(arg2.shape, arg2.dtype)) + assert_exactly_equal(res[mask], (one(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_pow_special_cases_two_args_even_if_2(arg1, arg2): + """ + Special case test for `__pow__(self, other, /)`: + + - If `x2_i` is `-0`, the result is `1`, even if `x1_i` is `NaN`. + + """ + res = arg1.__pow__(arg2) + mask = exactly_equal(arg2, -zero(arg2.shape, arg2.dtype)) + assert_exactly_equal(res[mask], (one(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_pow_special_cases_two_args_equal__notequal_1(arg1, arg2): + """ + Special case test for `__pow__(self, other, /)`: + + - If `x1_i` is `NaN` and `x2_i` is not equal to `0`, the result is `NaN`. + + """ + res = arg1.__pow__(arg2) + mask = logical_and(exactly_equal(arg1, NaN(arg1.shape, arg1.dtype)), notequal(arg2, zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (NaN(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_pow_special_cases_two_args_equal__notequal_2(arg1, arg2): + """ + Special case test for `__pow__(self, other, /)`: + + - If `x1_i` is `1` and `x2_i` is not `NaN`, the result is `1`. + + """ + res = arg1.__pow__(arg2) + mask = logical_and(exactly_equal(arg1, one(arg1.shape, arg1.dtype)), logical_not(exactly_equal(arg2, NaN(arg2.shape, arg2.dtype)))) + assert_exactly_equal(res[mask], (one(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_pow_special_cases_two_args_absgreater__equal_1(arg1, arg2): + """ + Special case test for `__pow__(self, other, /)`: + + - If `abs(x1_i)` is greater than `1` and `x2_i` is `+infinity`, the result is `+infinity`. + + """ + res = arg1.__pow__(arg2) + mask = logical_and(greater(abs(arg1), one(arg1.shape, arg1.dtype)), exactly_equal(arg2, infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_pow_special_cases_two_args_absgreater__equal_2(arg1, arg2): + """ + Special case test for `__pow__(self, other, /)`: + + - If `abs(x1_i)` is greater than `1` and `x2_i` is `-infinity`, the result is `+0`. + + """ + res = arg1.__pow__(arg2) + mask = logical_and(greater(abs(arg1), one(arg1.shape, arg1.dtype)), exactly_equal(arg2, -infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_pow_special_cases_two_args_absequal__equal_1(arg1, arg2): + """ + Special case test for `__pow__(self, other, /)`: + + - If `abs(x1_i)` is `1` and `x2_i` is `+infinity`, the result is `1`. + + """ + res = arg1.__pow__(arg2) + mask = logical_and(exactly_equal(abs(arg1), one(arg1.shape, arg1.dtype)), exactly_equal(arg2, infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (one(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_pow_special_cases_two_args_absequal__equal_2(arg1, arg2): + """ + Special case test for `__pow__(self, other, /)`: + + - If `abs(x1_i)` is `1` and `x2_i` is `-infinity`, the result is `1`. + + """ + res = arg1.__pow__(arg2) + mask = logical_and(exactly_equal(abs(arg1), one(arg1.shape, arg1.dtype)), exactly_equal(arg2, -infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (one(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_pow_special_cases_two_args_absless__equal_1(arg1, arg2): + """ + Special case test for `__pow__(self, other, /)`: + + - If `abs(x1_i)` is less than `1` and `x2_i` is `+infinity`, the result is `+0`. + + """ + res = arg1.__pow__(arg2) + mask = logical_and(less(abs(arg1), one(arg1.shape, arg1.dtype)), exactly_equal(arg2, infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_pow_special_cases_two_args_absless__equal_2(arg1, arg2): + """ + Special case test for `__pow__(self, other, /)`: + + - If `abs(x1_i)` is less than `1` and `x2_i` is `-infinity`, the result is `+infinity`. + + """ + res = arg1.__pow__(arg2) + mask = logical_and(less(abs(arg1), one(arg1.shape, arg1.dtype)), exactly_equal(arg2, -infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_pow_special_cases_two_args_equal__greater_1(arg1, arg2): + """ + Special case test for `__pow__(self, other, /)`: + + - If `x1_i` is `+infinity` and `x2_i` is greater than `0`, the result is `+infinity`. + + """ + res = arg1.__pow__(arg2) + mask = logical_and(exactly_equal(arg1, infinity(arg1.shape, arg1.dtype)), greater(arg2, zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_pow_special_cases_two_args_equal__greater_2(arg1, arg2): + """ + Special case test for `__pow__(self, other, /)`: + + - If `x1_i` is `+0` and `x2_i` is greater than `0`, the result is `+0`. + + """ + res = arg1.__pow__(arg2) + mask = logical_and(exactly_equal(arg1, zero(arg1.shape, arg1.dtype)), greater(arg2, zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_pow_special_cases_two_args_equal__less_1(arg1, arg2): + """ + Special case test for `__pow__(self, other, /)`: + + - If `x1_i` is `+infinity` and `x2_i` is less than `0`, the result is `+0`. + + """ + res = arg1.__pow__(arg2) + mask = logical_and(exactly_equal(arg1, infinity(arg1.shape, arg1.dtype)), less(arg2, zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_pow_special_cases_two_args_equal__less_2(arg1, arg2): + """ + Special case test for `__pow__(self, other, /)`: + + - If `x1_i` is `+0` and `x2_i` is less than `0`, the result is `+infinity`. + + """ + res = arg1.__pow__(arg2) + mask = logical_and(exactly_equal(arg1, zero(arg1.shape, arg1.dtype)), less(arg2, zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_pow_special_cases_two_args_equal__greater_equal_1(arg1, arg2): + """ + Special case test for `__pow__(self, other, /)`: + + - If `x1_i` is `-infinity`, `x2_i` is greater than `0`, and `x2_i` is an odd integer value, the result is `-infinity`. + + """ + res = arg1.__pow__(arg2) + mask = logical_and(exactly_equal(arg1, -infinity(arg1.shape, arg1.dtype)), logical_and(greater(arg2, zero(arg2.shape, arg2.dtype)), isodd(arg2))) + assert_exactly_equal(res[mask], (-infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_pow_special_cases_two_args_equal__greater_equal_2(arg1, arg2): + """ + Special case test for `__pow__(self, other, /)`: + + - If `x1_i` is `-0`, `x2_i` is greater than `0`, and `x2_i` is an odd integer value, the result is `-0`. + + """ + res = arg1.__pow__(arg2) + mask = logical_and(exactly_equal(arg1, -zero(arg1.shape, arg1.dtype)), logical_and(greater(arg2, zero(arg2.shape, arg2.dtype)), isodd(arg2))) + assert_exactly_equal(res[mask], (-zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_pow_special_cases_two_args_equal__greater_notequal_1(arg1, arg2): + """ + Special case test for `__pow__(self, other, /)`: + + - If `x1_i` is `-infinity`, `x2_i` is greater than `0`, and `x2_i` is not an odd integer value, the result is `+infinity`. + + """ + res = arg1.__pow__(arg2) + mask = logical_and(exactly_equal(arg1, -infinity(arg1.shape, arg1.dtype)), logical_and(greater(arg2, zero(arg2.shape, arg2.dtype)), logical_not(isodd(arg2)))) + assert_exactly_equal(res[mask], (infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_pow_special_cases_two_args_equal__greater_notequal_2(arg1, arg2): + """ + Special case test for `__pow__(self, other, /)`: + + - If `x1_i` is `-0`, `x2_i` is greater than `0`, and `x2_i` is not an odd integer value, the result is `+0`. + + """ + res = arg1.__pow__(arg2) + mask = logical_and(exactly_equal(arg1, -zero(arg1.shape, arg1.dtype)), logical_and(greater(arg2, zero(arg2.shape, arg2.dtype)), logical_not(isodd(arg2)))) + assert_exactly_equal(res[mask], (zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_pow_special_cases_two_args_equal__less_equal_1(arg1, arg2): + """ + Special case test for `__pow__(self, other, /)`: + + - If `x1_i` is `-infinity`, `x2_i` is less than `0`, and `x2_i` is an odd integer value, the result is `-0`. + + """ + res = arg1.__pow__(arg2) + mask = logical_and(exactly_equal(arg1, -infinity(arg1.shape, arg1.dtype)), logical_and(less(arg2, zero(arg2.shape, arg2.dtype)), isodd(arg2))) + assert_exactly_equal(res[mask], (-zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_pow_special_cases_two_args_equal__less_equal_2(arg1, arg2): + """ + Special case test for `__pow__(self, other, /)`: + + - If `x1_i` is `-0`, `x2_i` is less than `0`, and `x2_i` is an odd integer value, the result is `-infinity`. + + """ + res = arg1.__pow__(arg2) + mask = logical_and(exactly_equal(arg1, -zero(arg1.shape, arg1.dtype)), logical_and(less(arg2, zero(arg2.shape, arg2.dtype)), isodd(arg2))) + assert_exactly_equal(res[mask], (-infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_pow_special_cases_two_args_equal__less_notequal_1(arg1, arg2): + """ + Special case test for `__pow__(self, other, /)`: + + - If `x1_i` is `-infinity`, `x2_i` is less than `0`, and `x2_i` is not an odd integer value, the result is `+0`. + + """ + res = arg1.__pow__(arg2) + mask = logical_and(exactly_equal(arg1, -infinity(arg1.shape, arg1.dtype)), logical_and(less(arg2, zero(arg2.shape, arg2.dtype)), logical_not(isodd(arg2)))) + assert_exactly_equal(res[mask], (zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_pow_special_cases_two_args_equal__less_notequal_2(arg1, arg2): + """ + Special case test for `__pow__(self, other, /)`: + + - If `x1_i` is `-0`, `x2_i` is less than `0`, and `x2_i` is not an odd integer value, the result is `+infinity`. + + """ + res = arg1.__pow__(arg2) + mask = logical_and(exactly_equal(arg1, -zero(arg1.shape, arg1.dtype)), logical_and(less(arg2, zero(arg2.shape, arg2.dtype)), logical_not(isodd(arg2)))) + assert_exactly_equal(res[mask], (infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_pow_special_cases_two_args_less_equal__equal_notequal(arg1, arg2): + """ + Special case test for `__pow__(self, other, /)`: + + - If `x1_i` is less than `0`, `x1_i` is a finite number, `x2_i` is a finite number, and `x2_i` is not an integer value, the result is `NaN`. + + """ + res = arg1.__pow__(arg2) + mask = logical_and(logical_and(less(arg1, zero(arg1.shape, arg1.dtype)), isfinite(arg1)), logical_and(isfinite(arg2), logical_not(isintegral(arg2)))) + assert_exactly_equal(res[mask], (NaN(arg1.shape, arg1.dtype))[mask]) diff --git a/array_api_tests/special_cases/test_dunder_truediv.py b/array_api_tests/special_cases/test_dunder_truediv.py new file mode 100644 index 00000000..d08302f2 --- /dev/null +++ b/array_api_tests/special_cases/test_dunder_truediv.py @@ -0,0 +1,292 @@ +""" +Special cases tests for __truediv__. + +These tests are generated from the special cases listed in the spec. + +NOTE: This file is generated automatically by the generate_stubs.py script. Do +not modify it directly. +""" + +from ..array_helpers import (NaN, assert_exactly_equal, assert_negative_mathematical_sign, + assert_positive_mathematical_sign, exactly_equal, greater, infinity, + isfinite, isnegative, ispositive, less, logical_and, logical_not, + logical_or, non_zero, same_sign, zero) +from ..hypothesis_helpers import numeric_arrays + +from hypothesis import given + + +@given(numeric_arrays, numeric_arrays) +def test_truediv_special_cases_two_args_either(arg1, arg2): + """ + Special case test for `__truediv__(self, other, /)`: + + - If either `x1_i` or `x2_i` is `NaN`, the result is `NaN`. + + """ + res = arg1.__truediv__(arg2) + mask = logical_or(exactly_equal(arg1, NaN(arg1.shape, arg1.dtype)), exactly_equal(arg2, NaN(arg1.shape, arg1.dtype))) + assert_exactly_equal(res[mask], (NaN(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_truediv_special_cases_two_args_either__either_1(arg1, arg2): + """ + Special case test for `__truediv__(self, other, /)`: + + - If `x1_i` is either `+infinity` or `-infinity` and `x2_i` is either `+infinity` or `-infinity`, the result is `NaN`. + + """ + res = arg1.__truediv__(arg2) + mask = logical_and(logical_or(exactly_equal(arg1, infinity(arg1.shape, arg1.dtype)), exactly_equal(arg1, -infinity(arg1.shape, arg1.dtype))), logical_or(exactly_equal(arg2, infinity(arg2.shape, arg2.dtype)), exactly_equal(arg2, -infinity(arg2.shape, arg2.dtype)))) + assert_exactly_equal(res[mask], (NaN(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_truediv_special_cases_two_args_either__either_2(arg1, arg2): + """ + Special case test for `__truediv__(self, other, /)`: + + - If `x1_i` is either `+0` or `-0` and `x2_i` is either `+0` or `-0`, the result is `NaN`. + + """ + res = arg1.__truediv__(arg2) + mask = logical_and(logical_or(exactly_equal(arg1, zero(arg1.shape, arg1.dtype)), exactly_equal(arg1, -zero(arg1.shape, arg1.dtype))), logical_or(exactly_equal(arg2, zero(arg2.shape, arg2.dtype)), exactly_equal(arg2, -zero(arg2.shape, arg2.dtype)))) + assert_exactly_equal(res[mask], (NaN(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_truediv_special_cases_two_args_equal__greater_1(arg1, arg2): + """ + Special case test for `__truediv__(self, other, /)`: + + - If `x1_i` is `+0` and `x2_i` is greater than `0`, the result is `+0`. + + """ + res = arg1.__truediv__(arg2) + mask = logical_and(exactly_equal(arg1, zero(arg1.shape, arg1.dtype)), greater(arg2, zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_truediv_special_cases_two_args_equal__greater_2(arg1, arg2): + """ + Special case test for `__truediv__(self, other, /)`: + + - If `x1_i` is `-0` and `x2_i` is greater than `0`, the result is `-0`. + + """ + res = arg1.__truediv__(arg2) + mask = logical_and(exactly_equal(arg1, -zero(arg1.shape, arg1.dtype)), greater(arg2, zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (-zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_truediv_special_cases_two_args_equal__less_1(arg1, arg2): + """ + Special case test for `__truediv__(self, other, /)`: + + - If `x1_i` is `+0` and `x2_i` is less than `0`, the result is `-0`. + + """ + res = arg1.__truediv__(arg2) + mask = logical_and(exactly_equal(arg1, zero(arg1.shape, arg1.dtype)), less(arg2, zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (-zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_truediv_special_cases_two_args_equal__less_2(arg1, arg2): + """ + Special case test for `__truediv__(self, other, /)`: + + - If `x1_i` is `-0` and `x2_i` is less than `0`, the result is `+0`. + + """ + res = arg1.__truediv__(arg2) + mask = logical_and(exactly_equal(arg1, -zero(arg1.shape, arg1.dtype)), less(arg2, zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_truediv_special_cases_two_args_greater__equal_1(arg1, arg2): + """ + Special case test for `__truediv__(self, other, /)`: + + - If `x1_i` is greater than `0` and `x2_i` is `+0`, the result is `+infinity`. + + """ + res = arg1.__truediv__(arg2) + mask = logical_and(greater(arg1, zero(arg1.shape, arg1.dtype)), exactly_equal(arg2, zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_truediv_special_cases_two_args_greater__equal_2(arg1, arg2): + """ + Special case test for `__truediv__(self, other, /)`: + + - If `x1_i` is greater than `0` and `x2_i` is `-0`, the result is `-infinity`. + + """ + res = arg1.__truediv__(arg2) + mask = logical_and(greater(arg1, zero(arg1.shape, arg1.dtype)), exactly_equal(arg2, -zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (-infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_truediv_special_cases_two_args_less__equal_1(arg1, arg2): + """ + Special case test for `__truediv__(self, other, /)`: + + - If `x1_i` is less than `0` and `x2_i` is `+0`, the result is `-infinity`. + + """ + res = arg1.__truediv__(arg2) + mask = logical_and(less(arg1, zero(arg1.shape, arg1.dtype)), exactly_equal(arg2, zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (-infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_truediv_special_cases_two_args_less__equal_2(arg1, arg2): + """ + Special case test for `__truediv__(self, other, /)`: + + - If `x1_i` is less than `0` and `x2_i` is `-0`, the result is `+infinity`. + + """ + res = arg1.__truediv__(arg2) + mask = logical_and(less(arg1, zero(arg1.shape, arg1.dtype)), exactly_equal(arg2, -zero(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_truediv_special_cases_two_args_equal__equal_1(arg1, arg2): + """ + Special case test for `__truediv__(self, other, /)`: + + - If `x1_i` is `+infinity` and `x2_i` is a positive (i.e., greater than `0`) finite number, the result is `+infinity`. + + """ + res = arg1.__truediv__(arg2) + mask = logical_and(exactly_equal(arg1, infinity(arg1.shape, arg1.dtype)), logical_and(isfinite(arg2), ispositive(arg2))) + assert_exactly_equal(res[mask], (infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_truediv_special_cases_two_args_equal__equal_2(arg1, arg2): + """ + Special case test for `__truediv__(self, other, /)`: + + - If `x1_i` is `+infinity` and `x2_i` is a negative (i.e., less than `0`) finite number, the result is `-infinity`. + + """ + res = arg1.__truediv__(arg2) + mask = logical_and(exactly_equal(arg1, infinity(arg1.shape, arg1.dtype)), logical_and(isfinite(arg2), isnegative(arg2))) + assert_exactly_equal(res[mask], (-infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_truediv_special_cases_two_args_equal__equal_3(arg1, arg2): + """ + Special case test for `__truediv__(self, other, /)`: + + - If `x1_i` is `-infinity` and `x2_i` is a positive (i.e., greater than `0`) finite number, the result is `-infinity`. + + """ + res = arg1.__truediv__(arg2) + mask = logical_and(exactly_equal(arg1, -infinity(arg1.shape, arg1.dtype)), logical_and(isfinite(arg2), ispositive(arg2))) + assert_exactly_equal(res[mask], (-infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_truediv_special_cases_two_args_equal__equal_4(arg1, arg2): + """ + Special case test for `__truediv__(self, other, /)`: + + - If `x1_i` is `-infinity` and `x2_i` is a negative (i.e., less than `0`) finite number, the result is `+infinity`. + + """ + res = arg1.__truediv__(arg2) + mask = logical_and(exactly_equal(arg1, -infinity(arg1.shape, arg1.dtype)), logical_and(isfinite(arg2), isnegative(arg2))) + assert_exactly_equal(res[mask], (infinity(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_truediv_special_cases_two_args_equal__equal_5(arg1, arg2): + """ + Special case test for `__truediv__(self, other, /)`: + + - If `x1_i` is a positive (i.e., greater than `0`) finite number and `x2_i` is `+infinity`, the result is `+0`. + + """ + res = arg1.__truediv__(arg2) + mask = logical_and(logical_and(isfinite(arg1), ispositive(arg1)), exactly_equal(arg2, infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_truediv_special_cases_two_args_equal__equal_6(arg1, arg2): + """ + Special case test for `__truediv__(self, other, /)`: + + - If `x1_i` is a positive (i.e., greater than `0`) finite number and `x2_i` is `-infinity`, the result is `-0`. + + """ + res = arg1.__truediv__(arg2) + mask = logical_and(logical_and(isfinite(arg1), ispositive(arg1)), exactly_equal(arg2, -infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (-zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_truediv_special_cases_two_args_equal__equal_7(arg1, arg2): + """ + Special case test for `__truediv__(self, other, /)`: + + - If `x1_i` is a negative (i.e., less than `0`) finite number and `x2_i` is `+infinity`, the result is `-0`. + + """ + res = arg1.__truediv__(arg2) + mask = logical_and(logical_and(isfinite(arg1), isnegative(arg1)), exactly_equal(arg2, infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (-zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_truediv_special_cases_two_args_equal__equal_8(arg1, arg2): + """ + Special case test for `__truediv__(self, other, /)`: + + - If `x1_i` is a negative (i.e., less than `0`) finite number and `x2_i` is `-infinity`, the result is `+0`. + + """ + res = arg1.__truediv__(arg2) + mask = logical_and(logical_and(isfinite(arg1), isnegative(arg1)), exactly_equal(arg2, -infinity(arg2.shape, arg2.dtype))) + assert_exactly_equal(res[mask], (zero(arg1.shape, arg1.dtype))[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_truediv_special_cases_two_args_same_sign_both(arg1, arg2): + """ + Special case test for `__truediv__(self, other, /)`: + + - If `x1_i` and `x2_i` have the same mathematical sign and are both nonzero finite numbers, the result has a positive mathematical sign. + + """ + res = arg1.__truediv__(arg2) + mask = logical_and(same_sign(arg1, arg2), logical_and(logical_and(isfinite(arg1), non_zero(arg1)), logical_and(isfinite(arg2), non_zero(arg2)))) + assert_positive_mathematical_sign(res[mask]) + + +@given(numeric_arrays, numeric_arrays) +def test_truediv_special_cases_two_args_different_signs_both(arg1, arg2): + """ + Special case test for `__truediv__(self, other, /)`: + + - If `x1_i` and `x2_i` have different mathematical signs and are both nonzero finite numbers, the result has a negative mathematical sign. + + """ + res = arg1.__truediv__(arg2) + mask = logical_and(logical_not(same_sign(arg1, arg2)), logical_and(logical_and(isfinite(arg1), non_zero(arg1)), logical_and(isfinite(arg2), non_zero(arg2)))) + assert_negative_mathematical_sign(res[mask]) + +# TODO: Implement REMAINING test for: +# - In the remaining cases, where neither `-infinity`, `+0`, `-0`, nor `NaN` is involved, the quotient must be computed and rounded to the nearest representable value according to IEEE 754-2019 and a supported rounding mode. If the magnitude is too larger to represent, the operation overflows and the result is an `infinity` of appropriate mathematical sign. If the magnitude is too small to represent, the operation underflows and the result is a zero of appropriate mathematical sign. diff --git a/generate_stubs.py b/generate_stubs.py index 6aa0fba4..857f6d1c 100755 --- a/generate_stubs.py +++ b/generate_stubs.py @@ -11,15 +11,17 @@ """ import argparse import os -import sys import ast import itertools from collections import defaultdict +from typing import DefaultDict, Dict, List +from pathlib import Path import regex from removestar.removestar import fix_code FUNCTION_HEADER_RE = regex.compile(r'\(function-(.*?)\)') +METHOD_HEADER_RE = regex.compile(r'\(method-(.*?)\)') HEADER_RE = regex.compile(r'\((?:function-linalg|function|method|constant|attribute)-(.*?)\)') FUNCTION_RE = regex.compile(r'\(function-.*\)=\n#+ ?(.*\(.*\))') METHOD_RE = regex.compile(r'\(method-.*\)=\n#+ ?(.*\(.*\))') @@ -29,6 +31,50 @@ REFLECTED_OPERATOR_RE = regex.compile(r'- +`__r(.*)__`') ALIAS_RE = regex.compile(r'Alias for {ref}`function-(.*)`.') +OPS = [ + '__abs__', + '__add__', + '__and__', + '__bool__', + '__eq__', + '__float__', + '__floordiv__', + '__ge__', + '__getitem__', + '__gt__', + '__invert__', + '__le__', + '__lshift__', + '__lt__', + '__matmul__', + '__mod__', + '__mul__', + '__ne__', + '__neg__', + '__or__', + '__pos__', + '__pow__', + '__rshift__', + '__sub__', + '__truediv__', + '__xor__' +] +IOPS = [ + '__iadd__', + '__isub__', + '__imul__', + '__itruediv__', + '__ifloordiv__', + '__ipow__', + '__imod__', + '__imatmul__', + '__iand__', + '__ior__', + '__ixor__', + '__ilshift__', + '__irshift__' +] + NAME_RE = regex.compile(r'(.*?)\(.*\)') STUB_FILE_HEADER = '''\ @@ -82,6 +128,44 @@ ''' +OP_SPECIAL_CASES_HEADER = '''\ +""" +Special cases tests for {func}. + +These tests are generated from the special cases listed in the spec. + +NOTE: This file is generated automatically by the generate_stubs.py script. Do +not modify it directly. +""" + +from ..array_helpers import * +from ..hypothesis_helpers import numeric_arrays + +from hypothesis import given + +''' + + +IOP_SPECIAL_CASES_HEADER = '''\ +""" +Special cases tests for {func}. + +These tests are generated from the special cases listed in the spec. + +NOTE: This file is generated automatically by the generate_stubs.py script. Do +not modify it directly. +""" + +from operator import {operator} + +from ..array_helpers import * +from ..hypothesis_helpers import numeric_arrays + +from hypothesis import given + +''' + + TYPES_HEADER = '''\ """ This file defines the types for type annotations. @@ -128,9 +212,8 @@ class iinfo_object: def main(): parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument('array_api_repo', help="Path to clone of the array-api repository") - parser.add_argument('--no-write', help="""Print what it would do but don't - write any files""", action='store_false', dest='write') - parser.add_argument('-v', '--verbose', help="""Print verbose output to the terminal""", action='store_true') + parser.add_argument('--no-write', help="Print what it would do but don't write any files", action='store_false', dest='write') + parser.add_argument('-v', '--verbose', help="Print verbose output to the terminal", action='store_true') args = parser.parse_args() types_path = os.path.join('array_api_tests', 'function_stubs', '_types.py') @@ -138,6 +221,10 @@ def main(): with open(types_path, 'w') as f: f.write(TYPES_HEADER) + special_cases_dir = Path('array_api_tests/special_cases') + special_cases_dir.mkdir(exist_ok=True) + (special_cases_dir / '__init__.py').touch() + spec_dir = os.path.join(args.array_api_repo, 'spec', 'API_specification') extensions_dir = os.path.join(args.array_api_repo, 'spec', 'extensions') files = sorted([os.path.join(spec_dir, f) for f in os.listdir(spec_dir)] @@ -249,26 +336,12 @@ def {annotated_sig}:{doc} if args.write: with open(py_path, 'w') as f: f.write(code) + if filename == 'elementwise_functions.md': special_cases = parse_special_cases(text, verbose=args.verbose) for func in special_cases: py_path = os.path.join('array_api_tests', 'special_cases', f'test_{func}.py') - tests = [] - for typ in special_cases[func]: - multiple = len(special_cases[func][typ]) > 1 - for i, m in enumerate(special_cases[func][typ], 1): - test_name_extra = typ.lower() - if multiple: - test_name_extra += f"_{i}" - try: - test = generate_special_case_test(func, typ, m, - test_name_extra, sigs) - if test is None: - raise NotImplementedError("Special case test not implemented") - tests.append(test) - except: - print(f"Error with {func}() {typ}: {m.group(0)}:\n", file=sys.stderr) - raise + tests = make_special_case_tests(func, special_cases, sigs) if tests: code = SPECIAL_CASES_HEADER.format(func=func) + '\n'.join(tests) # quiet=False will make it print a warning if a name is not found (indicating an error) @@ -276,6 +349,31 @@ def {annotated_sig}:{doc} if args.write: with open(py_path, 'w') as f: f.write(code) + elif filename == 'array_object.md': + op_special_cases = parse_special_cases(text, verbose=args.verbose) + for func in op_special_cases: + py_path = os.path.join('array_api_tests', 'special_cases', f'test_dunder_{func[2:-2]}.py') + tests = make_special_case_tests(func, op_special_cases, sigs) + if tests: + code = OP_SPECIAL_CASES_HEADER.format(func=func) + '\n'.join(tests) + code = fix_code(code, file=py_path, verbose=False, quiet=False) + if args.write: + with open(py_path, 'w') as f: + f.write(code) + iop_special_cases = {} + for name in IN_PLACE_OPERATOR_RE.findall(text): + op = f"__{name}__" + iop = f"__i{name}__" + iop_special_cases[iop] = op_special_cases[op] + for func in iop_special_cases: + py_path = os.path.join('array_api_tests', 'special_cases', f'test_dunder_{func[2:-2]}.py') + tests = make_special_case_tests(func, iop_special_cases, sigs) + if tests: + code = IOP_SPECIAL_CASES_HEADER.format(func=func, operator=func[2:-2]) + '\n'.join(tests) + code = fix_code(code, file=py_path, verbose=False, quiet=False) + if args.write: + with open(py_path, 'w') as f: + f.write(code) init_path = os.path.join('array_api_tests', 'function_stubs', '__init__.py') if args.write: @@ -482,12 +580,39 @@ def test_{func}_special_cases_{test_name_extra}(arg1, arg2): {assertion} """ +OP_ONE_ARG_TEMPLATE = """ +{decorator} +def test_{op}_special_cases_{test_name_extra}(arg1): + {doc} + res = (arg1).{func}() + mask = {mask} + {assertion} +""" + +OP_TWO_ARGS_TEMPLATE = """ +{decorator} +def test_{op}_special_cases_{test_name_extra}(arg1, arg2): + {doc} + res = arg1.{func}(arg2) + mask = {mask} + {assertion} +""" + +IOP_TWO_ARGS_TEMPLATE = """ +{decorator} +def test_{op}_special_cases_{test_name_extra}(arg1, arg2): + {doc} + res = asarray(arg1, copy=True) + {op}(res, arg2) + mask = {mask} + {assertion} +""" + REMAINING_TEMPLATE = """# TODO: Implement REMAINING test for: # {text} """ def generate_special_case_test(func, typ, m, test_name_extra, sigs): - doc = f'''""" Special case test for `{sigs[func]}`: @@ -524,14 +649,25 @@ def generate_special_case_test(func, typ, m, test_name_extra, sigs): else: raise ValueError(f"Unrecognized special value type {typ}") assertion = get_assert("exactly_equal", result) - return ONE_ARG_TEMPLATE.format( - decorator=decorator, - func=func, - test_name_extra=test_name_extra, - doc=doc, - mask=mask, - assertion=assertion, - ) + if func in OPS: + return OP_ONE_ARG_TEMPLATE.format( + decorator=decorator, + func=func, + op=func[2:-2], + test_name_extra=test_name_extra, + doc=doc, + mask=mask, + assertion=assertion, + ) + else: + return ONE_ARG_TEMPLATE.format( + decorator=decorator, + func=func, + test_name_extra=test_name_extra, + doc=doc, + mask=mask, + assertion=assertion, + ) elif typ.startswith("TWO_ARGS"): decorator = "@given(numeric_arrays, numeric_arrays)" @@ -571,9 +707,9 @@ def generate_special_case_test(func, typ, m, test_name_extra, sigs): tomask = lambda t: t.lower().replace("either_equal", "equal").replace("equal", "exactly_equal") value1masks = [get_mask(tomask(t), 'arg1', v) for t, v in - zip(arg1typs, arg1values)] + zip(arg1typs, arg1values)] value2masks = [get_mask(tomask(t), 'arg2', v) for t, v in - zip(arg2typs, arg2values)] + zip(arg2typs, arg2values)] if len(value1masks) > 1: if arg1typs[0] == "EITHER_EQUAL": mask1 = f"logical_or({value1masks[0]}, {value1masks[1]})" @@ -638,27 +774,52 @@ def generate_special_case_test(func, typ, m, test_name_extra, sigs): assertion = get_assert("exactly_equal", result) else: raise ValueError(f"Unrecognized special value type {typ}") - return TWO_ARGS_TEMPLATE.format( - decorator=decorator, - func=func, - test_name_extra=test_name_extra, - doc=doc, - mask=mask, - assertion=assertion, - ) + + if func in OPS: + return OP_TWO_ARGS_TEMPLATE.format( + decorator=decorator, + func=func, + op=func[2:-2], + test_name_extra=test_name_extra, + doc=doc, + mask=mask, + assertion=assertion, + ) + elif func in IOPS: + return IOP_TWO_ARGS_TEMPLATE.format( + decorator=decorator, + func=func, + op=func[2:-2], + test_name_extra=test_name_extra, + doc=doc, + mask=mask, + assertion=assertion, + ) + else: + return TWO_ARGS_TEMPLATE.format( + decorator=decorator, + func=func, + test_name_extra=test_name_extra, + doc=doc, + mask=mask, + assertion=assertion, + ) elif typ == "REMAINING": return REMAINING_TEMPLATE.format(text=m.group(0)) + else: raise RuntimeError(f"Unexpected type {typ}") -def parse_special_cases(spec_text, verbose=False): +def parse_special_cases(spec_text, verbose=False) -> Dict[str, DefaultDict[str, List[regex.Match]]]: special_cases = {} in_block = False + name = None for line in spec_text.splitlines(): - m = FUNCTION_HEADER_RE.match(line) - if m: - name = m.group(1) + func_m = FUNCTION_HEADER_RE.match(line) + meth_m = METHOD_HEADER_RE.match(line) + if func_m or meth_m: + name = func_m.group(1) if func_m else meth_m.group(1) special_cases[name] = defaultdict(list) continue if line == '#### Special Cases': @@ -668,7 +829,7 @@ def parse_special_cases(spec_text, verbose=False): in_block = False continue if in_block: - if '- ' not in line: + if '- ' not in line or name is None: continue for typ, reg in SPECIAL_CASE_REGEXS.items(): m = reg.match(line) @@ -682,6 +843,20 @@ def parse_special_cases(spec_text, verbose=False): return special_cases +def make_special_case_tests(func, special_cases: Dict[str, DefaultDict[str, List[regex.Match]]], sigs) -> List[str]: + tests = [] + for typ in special_cases[func]: + multiple = len(special_cases[func][typ]) > 1 + for i, m in enumerate(special_cases[func][typ], 1): + test_name_extra = typ.lower() + if multiple: + test_name_extra += f"_{i}" + test = generate_special_case_test(func, typ, m, test_name_extra, sigs) + assert test is not None # sanity check + tests.append(test) + return tests + + PARAMETER_RE = regex.compile(r"- +\*\*(.*)\*\*: _(.*)_") def parse_annotations(spec_text, all_annotations, verbose=False): annotations = defaultdict(dict)