From 14ac81a35c00ca3c71839146281a41e384f19c15 Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Thu, 21 Mar 2024 17:02:20 +0000 Subject: [PATCH 01/10] Add `unvectorized` markers to `test_linalg.py` --- array_api_tests/test_linalg.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/array_api_tests/test_linalg.py b/array_api_tests/test_linalg.py index 1cd57000..76dc08dd 100644 --- a/array_api_tests/test_linalg.py +++ b/array_api_tests/test_linalg.py @@ -119,6 +119,7 @@ def _test_namedtuple(res, fields, func_name): assert hasattr(res, field), f"{func_name}() result namedtuple doesn't have the '{field}' field" assert res[i] is getattr(res, field), f"{func_name}() result namedtuple '{field}' field is not in position {i}" +@pytest.mark.unvectorized @pytest.mark.xp_extension('linalg') @given( x=positive_definite_matrices(), @@ -175,6 +176,7 @@ def cross_args(draw, dtype_objects=dh.real_dtypes): ) return draw(arrays1), draw(arrays2), kw +@pytest.mark.unvectorized @pytest.mark.xp_extension('linalg') @given( cross_args() @@ -209,6 +211,7 @@ def exact_cross(a, b): # vectors. _test_stacks(linalg.cross, x1, x2, dims=1, matrix_axes=(axis,), res=res, true_val=exact_cross) +@pytest.mark.unvectorized @pytest.mark.xp_extension('linalg') @given( x=arrays(dtype=all_floating_dtypes(), shape=square_matrix_shapes), @@ -224,6 +227,7 @@ def test_det(x): # TODO: Test that res actually corresponds to the determinant of x +@pytest.mark.unvectorized @pytest.mark.xp_extension('linalg') @given( x=arrays(dtype=xps.scalar_dtypes(), shape=matrix_shapes()), @@ -261,6 +265,7 @@ def true_diag(x_stack, offset=0): _test_stacks(linalg.diagonal, x, **kw, res=res, dims=1, true_val=true_diag) +@pytest.mark.unvectorized @pytest.mark.xp_extension('linalg') @given(x=symmetric_matrices(finite=True)) def test_eigh(x): @@ -299,6 +304,7 @@ def test_eigh(x): # TODO: Test that res actually corresponds to the eigenvalues and # eigenvectors of x +@pytest.mark.unvectorized @pytest.mark.xp_extension('linalg') @given(x=symmetric_matrices(finite=True)) def test_eigvalsh(x): @@ -319,6 +325,7 @@ def test_eigvalsh(x): # TODO: Test that res actually corresponds to the eigenvalues of x +@pytest.mark.unvectorized @pytest.mark.xp_extension('linalg') @given(x=invertible_matrices()) def test_inv(x): @@ -372,6 +379,7 @@ def _test_matmul(namespace, x1, x2): expected=stack_shape + (x1.shape[-2], x2.shape[-1])) _test_stacks(matmul, x1, x2, res=res) +@pytest.mark.unvectorized @pytest.mark.xp_extension('linalg') @given( *two_mutual_arrays(dh.real_dtypes) @@ -379,12 +387,14 @@ def _test_matmul(namespace, x1, x2): def test_linalg_matmul(x1, x2): return _test_matmul(linalg, x1, x2) +@pytest.mark.unvectorized @given( *two_mutual_arrays(dh.real_dtypes) ) def test_matmul(x1, x2): return _test_matmul(_array_module, x1, x2) +@pytest.mark.unvectorized @pytest.mark.xp_extension('linalg') @given( x=finite_matrices(), @@ -410,6 +420,7 @@ def test_matrix_norm(x, kw): res=res) matrix_power_n = shared(integers(-100, 100), key='matrix_power n') +@pytest.mark.unvectorized @pytest.mark.xp_extension('linalg') @given( # Generate any square matrix if n >= 0 but only invertible matrices if n < 0 @@ -433,6 +444,7 @@ def test_matrix_power(x, n): func = lambda x: linalg.matrix_power(x, n) _test_stacks(func, x, res=res, true_val=true_val) +@pytest.mark.unvectorized @pytest.mark.xp_extension('linalg') @given( x=finite_matrices(shape=rtol_shared_matrix_shapes), @@ -457,6 +469,7 @@ def _test_matrix_transpose(namespace, x): _test_stacks(matrix_transpose, x, res=res, true_val=true_val) +@pytest.mark.unvectorized @pytest.mark.xp_extension('linalg') @given( x=arrays(dtype=xps.scalar_dtypes(), shape=matrix_shapes()), @@ -464,6 +477,7 @@ def _test_matrix_transpose(namespace, x): def test_linalg_matrix_transpose(x): return _test_matrix_transpose(linalg, x) +@pytest.mark.unvectorized @given( x=arrays(dtype=xps.scalar_dtypes(), shape=matrix_shapes()), ) @@ -503,6 +517,7 @@ def test_outer(x1, x2): def test_pinv(x, kw): linalg.pinv(x, **kw) +@pytest.mark.unvectorized @pytest.mark.xp_extension('linalg') @given( x=arrays(dtype=all_floating_dtypes(), shape=matrix_shapes()), @@ -545,6 +560,7 @@ def test_qr(x, kw): # Check that R is upper-triangular. assert_exactly_equal(R, _array_module.triu(R)) +@pytest.mark.unvectorized @pytest.mark.xp_extension('linalg') @given( x=arrays(dtype=all_floating_dtypes(), shape=square_matrix_shapes), @@ -617,6 +633,7 @@ def _x2_shapes(draw): x2 = arrays(shape=x2_shapes, dtype=mutual_dtypes.map(lambda pair: pair[1])) return x1, x2 +@pytest.mark.unvectorized @pytest.mark.xp_extension('linalg') @given(*solve_args()) def test_solve(x1, x2): @@ -635,6 +652,7 @@ def test_solve(x1, x2): ph.assert_result_shape("solve", in_shapes=[x1.shape, x2.shape], out_shape=res.shape, expected=expected_shape) +@pytest.mark.unvectorized @pytest.mark.xp_extension('linalg') @given( x=finite_matrices(), @@ -685,6 +703,7 @@ def test_svd(x, kw): _test_stacks(lambda x: linalg.svd(x, **kw).S, x, dims=1, res=S) _test_stacks(lambda x: linalg.svd(x, **kw).Vh, x, res=Vh) +@pytest.mark.unvectorized @pytest.mark.xp_extension('linalg') @given( x=finite_matrices(), @@ -818,6 +837,7 @@ def _test_tensordot(namespace, x1, x2, kw): expected=result_shape) _test_tensordot_stacks(x1, x2, kw, res) +@pytest.mark.unvectorized @pytest.mark.xp_extension('linalg') @given( *two_mutual_arrays(dh.numeric_dtypes, two_shapes=tensordot_shapes()), @@ -826,6 +846,7 @@ def _test_tensordot(namespace, x1, x2, kw): def test_linalg_tensordot(x1, x2, kw): _test_tensordot(linalg, x1, x2, kw) +@pytest.mark.unvectorized @given( *two_mutual_arrays(dh.numeric_dtypes, two_shapes=tensordot_shapes()), tensordot_kw, @@ -833,6 +854,7 @@ def test_linalg_tensordot(x1, x2, kw): def test_tensordot(x1, x2, kw): _test_tensordot(_array_module, x1, x2, kw) +@pytest.mark.unvectorized @pytest.mark.xp_extension('linalg') @given( x=arrays(dtype=xps.numeric_dtypes(), shape=matrix_shapes()), @@ -910,6 +932,7 @@ def true_val(x, y, axis=-1): matrix_axes=(axis,), true_val=true_val) +@pytest.mark.unvectorized @pytest.mark.xp_extension('linalg') @given( *two_mutual_arrays(dh.numeric_dtypes, mutually_broadcastable_shapes(2, min_dims=1)), @@ -918,6 +941,7 @@ def true_val(x, y, axis=-1): def test_linalg_vecdot(x1, x2, data): _test_vecdot(linalg, x1, x2, data) +@pytest.mark.unvectorized @given( *two_mutual_arrays(dh.numeric_dtypes, mutually_broadcastable_shapes(2, min_dims=1)), data(), @@ -929,6 +953,7 @@ def test_vecdot(x1, x2, data): # spec, so we just limit to reasonable values here. max_ord = 100 +@pytest.mark.unvectorized @pytest.mark.xp_extension('linalg') @given( x=arrays(dtype=all_floating_dtypes(), shape=shapes(min_side=1)), From 66fe25e68d932802bb7265e635339805745a08a5 Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Thu, 21 Mar 2024 17:22:06 +0000 Subject: [PATCH 02/10] Add `unvectorized` marker to `test_concat` --- array_api_tests/test_manipulation_functions.py | 1 + 1 file changed, 1 insertion(+) diff --git a/array_api_tests/test_manipulation_functions.py b/array_api_tests/test_manipulation_functions.py index 045b5153..cb16de95 100644 --- a/array_api_tests/test_manipulation_functions.py +++ b/array_api_tests/test_manipulation_functions.py @@ -46,6 +46,7 @@ def assert_array_ndindex( assert out[out_idx] == x[x_idx], msg +@pytest.mark.unvectorized @given( dtypes=hh.mutually_promotable_dtypes(None, dtypes=dh.numeric_dtypes), base_shape=hh.shapes(), From 1d82bd919de0ce10db69be1a1e87e07a205eab90 Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Fri, 22 Mar 2024 11:18:07 +0000 Subject: [PATCH 03/10] Limit main special case tests to one example for now --- array_api_tests/test_special_cases.py | 169 +++++++------------------- 1 file changed, 41 insertions(+), 128 deletions(-) diff --git a/array_api_tests/test_special_cases.py b/array_api_tests/test_special_cases.py index bd088676..a21d9475 100644 --- a/array_api_tests/test_special_cases.py +++ b/array_api_tests/test_special_cases.py @@ -23,7 +23,7 @@ from warnings import warn import pytest -from hypothesis import assume, given, note +from hypothesis import given, note, settings from hypothesis import strategies as st from array_api_tests.typing import Array, DataType @@ -31,7 +31,6 @@ from . import dtype_helpers as dh from . import hypothesis_helpers as hh from . import pytest_helpers as ph -from . import shape_helpers as sh from . import xp, xps from .stubs import category_to_funcs @@ -1210,143 +1209,57 @@ def parse_binary_case_block(case_block: str) -> List[BinaryCase]: assert len(iop_params) != 0 -@pytest.mark.unvectorized @pytest.mark.parametrize("func_name, func, case", unary_params) -@given( - x=hh.arrays(dtype=xps.floating_dtypes(), shape=hh.shapes(min_side=1)), - data=st.data(), -) -def test_unary(func_name, func, case, x, data): - set_idx = data.draw( - xps.indices(x.shape, max_dims=0, allow_ellipsis=False), label="set idx" +def test_unary(func_name, func, case): + in_value = case.cond_from_dtype(xp.float64).example() + x = xp.asarray(in_value, dtype=xp.float64) + out = func(x) + out_value = float(out) + assert case.check_result(in_value, out_value), ( + f"out={out_value}, but should be {case.result_expr} [{func_name}()]\n" ) - set_value = data.draw(case.cond_from_dtype(x.dtype), label="set value") - x[set_idx] = set_value - note(f"{x=}") - - res = func(x) - - good_example = False - for idx in sh.ndindex(res.shape): - in_ = float(x[idx]) - if case.cond(in_): - good_example = True - out = float(res[idx]) - f_in = f"{sh.fmt_idx('x', idx)}={in_}" - f_out = f"{sh.fmt_idx('out', idx)}={out}" - assert case.check_result(in_, out), ( - f"{f_out}, but should be {case.result_expr} [{func_name}()]\n" - f"condition: {case.cond_expr}\n" - f"{f_in}" - ) - break - assume(good_example) - - -x1_strat, x2_strat = hh.two_mutual_arrays( - dtypes=dh.real_float_dtypes, - two_shapes=hh.mutually_broadcastable_shapes(2, min_side=1), -) -@pytest.mark.unvectorized @pytest.mark.parametrize("func_name, func, case", binary_params) -@given(x1=x1_strat, x2=x2_strat, data=st.data()) -def test_binary(func_name, func, case, x1, x2, data): - result_shape = sh.broadcast_shapes(x1.shape, x2.shape) - all_indices = list(sh.iter_indices(x1.shape, x2.shape, result_shape)) - - indices_strat = st.shared(st.sampled_from(all_indices)) - set_x1_idx = data.draw(indices_strat.map(lambda t: t[0]), label="set x1 idx") - set_x1_value = data.draw(case.x1_cond_from_dtype(x1.dtype), label="set x1 value") - x1[set_x1_idx] = set_x1_value - note(f"{x1=}") - set_x2_idx = data.draw(indices_strat.map(lambda t: t[1]), label="set x2 idx") - set_x2_value = data.draw(case.x2_cond_from_dtype(x2.dtype), label="set x2 value") - x2[set_x2_idx] = set_x2_value - note(f"{x2=}") - - res = func(x1, x2) - # sanity check - ph.assert_result_shape( - func_name, - in_shapes=[x1.shape, x2.shape], - out_shape=res.shape, - expected=result_shape, +@settings(max_examples=1) +@given(data=st.data()) +def test_binary(func_name, func, case, data): + # We don't use example() like in test_unary because the same internal shared + # strategies used in both x1's and x2's don't "sync" with example() draws. + x1_value = data.draw(case.x1_cond_from_dtype(xp.float64), label="x1_value") + x2_value = data.draw(case.x2_cond_from_dtype(xp.float64), label="x2_value") + x1 = xp.asarray(x1_value, dtype=xp.float64) + x2 = xp.asarray(x2_value, dtype=xp.float64) + + out = func(x1, x2) + out_value = float(out) + + assert case.check_result(x1_value, x2_value, out_value), ( + f"out={out_value}, but should be {case.result_expr} [{func_name}()]\n" + f"condition: {case}\n" + f"x1={x1_value}, x2={x2_value}" ) - good_example = False - for l_idx, r_idx, o_idx in all_indices: - l = float(x1[l_idx]) - r = float(x2[r_idx]) - if case.cond(l, r): - good_example = True - o = float(res[o_idx]) - f_left = f"{sh.fmt_idx('x1', l_idx)}={l}" - f_right = f"{sh.fmt_idx('x2', r_idx)}={r}" - f_out = f"{sh.fmt_idx('out', o_idx)}={o}" - assert case.check_result(l, r, o), ( - f"{f_out}, but should be {case.result_expr} [{func_name}()]\n" - f"condition: {case}\n" - f"{f_left}, {f_right}" - ) - break - assume(good_example) -@pytest.mark.unvectorized @pytest.mark.parametrize("iop_name, iop, case", iop_params) -@given( - oneway_dtypes=hh.oneway_promotable_dtypes(dh.real_float_dtypes), - oneway_shapes=hh.oneway_broadcastable_shapes(), - data=st.data(), -) -def test_iop(iop_name, iop, case, oneway_dtypes, oneway_shapes, data): - x1 = data.draw( - hh.arrays(dtype=oneway_dtypes.result_dtype, shape=oneway_shapes.result_shape), - label="x1", +@settings(max_examples=1) +@given(data=st.data()) +def test_iop(iop_name, iop, case, data): + # See test_binary comment + x1_value = data.draw(case.x1_cond_from_dtype(xp.float64), label="x1_value") + x2_value = data.draw(case.x2_cond_from_dtype(xp.float64), label="x2_value") + x1 = xp.asarray(x1_value, dtype=xp.float64) + x2 = xp.asarray(x2_value, dtype=xp.float64) + + res = iop(x1, x2) + res_value = float(res) + + assert case.check_result(x1_value, x2_value, res_value), ( + f"x1={res}, but should be {case.result_expr} [{func_name}()]\n" + f"condition: {case}\n" + f"x1={x1_value}, x2={x2_value}" ) - x2 = data.draw( - hh.arrays(dtype=oneway_dtypes.input_dtype, shape=oneway_shapes.input_shape), - label="x2", - ) - - all_indices = list(sh.iter_indices(x1.shape, x2.shape, x1.shape)) - - indices_strat = st.shared(st.sampled_from(all_indices)) - set_x1_idx = data.draw(indices_strat.map(lambda t: t[0]), label="set x1 idx") - set_x1_value = data.draw(case.x1_cond_from_dtype(x1.dtype), label="set x1 value") - x1[set_x1_idx] = set_x1_value - note(f"{x1=}") - set_x2_idx = data.draw(indices_strat.map(lambda t: t[1]), label="set x2 idx") - set_x2_value = data.draw(case.x2_cond_from_dtype(x2.dtype), label="set x2 value") - x2[set_x2_idx] = set_x2_value - note(f"{x2=}") - - res = xp.asarray(x1, copy=True) - res = iop(res, x2) - # sanity check - ph.assert_result_shape( - iop_name, in_shapes=[x1.shape, x2.shape], out_shape=res.shape - ) - - good_example = False - for l_idx, r_idx, o_idx in all_indices: - l = float(x1[l_idx]) - r = float(x2[r_idx]) - if case.cond(l, r): - good_example = True - o = float(res[o_idx]) - f_left = f"{sh.fmt_idx('x1', l_idx)}={l}" - f_right = f"{sh.fmt_idx('x2', r_idx)}={r}" - f_out = f"{sh.fmt_idx('out', o_idx)}={o}" - assert case.check_result(l, r, o), ( - f"{f_out}, but should be {case.result_expr} [{iop_name}()]\n" - f"condition: {case}\n" - f"{f_left}, {f_right}" - ) - break - assume(good_example) @pytest.mark.parametrize( From 07c8cd0dfa0753c0746fddaa4e1f36fdb42ab4ef Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Thu, 21 Mar 2024 17:12:55 +0000 Subject: [PATCH 04/10] Remove unreachable loading default settings path And set max examples default to 20 --- conftest.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/conftest.py b/conftest.py index 74f8a948..7cf5f9d7 100644 --- a/conftest.py +++ b/conftest.py @@ -24,7 +24,8 @@ def pytest_addoption(parser): "--hypothesis-max-examples", "--max-examples", action="store", - default=100, + default=20, + type=int, help="set the Hypothesis max_examples setting", ) # Hypothesis deadline From 36f4c1eecbbda7b1b60a5ade7fe216071935b081 Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Fri, 22 Mar 2024 11:30:51 +0000 Subject: [PATCH 05/10] `conftest.py` updates * Set max examples default to 20 * For faster default runs, over more test quality * Remove unreachable loading default settings path --- .github/workflows/test.yml | 2 +- conftest.py | 24 ++++++++---------------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f1a91300..220471d4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,6 +28,6 @@ jobs: env: ARRAY_API_TESTS_MODULE: array_api_strict run: | - pytest -v -rxXfE --skips-file array-api-strict-skips.txt array_api_tests/ + pytest -v -rxXfE --skips-file array-api-strict-skips.txt array_api_tests/ --hypothesis-disable-deadline # We also have internal tests that isn't really necessary for adopters pytest -v -rxXfE meta_tests/ diff --git a/conftest.py b/conftest.py index 7cf5f9d7..fb5e83f8 100644 --- a/conftest.py +++ b/conftest.py @@ -15,7 +15,6 @@ from reporting import pytest_metadata, pytest_json_modifyreport, add_extra_json_metadata # noqa -settings.register_profile("xp_default", deadline=800) def pytest_addoption(parser): # Hypothesis max examples @@ -87,21 +86,14 @@ def pytest_configure(config): "unvectorized: asserts against values via element-wise iteration (not performative!)", ) # Hypothesis - hypothesis_max_examples = config.getoption("--hypothesis-max-examples") - disable_deadline = config.getoption("--hypothesis-disable-deadline") - derandomize = config.getoption("--hypothesis-derandomize") - profile_settings = {} - if hypothesis_max_examples is not None: - profile_settings["max_examples"] = int(hypothesis_max_examples) - if disable_deadline: + profile_settings = { + "max_examples": config.getoption("--hypothesis-max-examples"), + "derandomize": config.getoption("--hypothesis-derandomize"), + } + if config.getoption("--hypothesis-disable-deadline"): profile_settings["deadline"] = None - if derandomize: - profile_settings["derandomize"] = True - if profile_settings: - settings.register_profile("xp_override", **profile_settings) - settings.load_profile("xp_override") - else: - settings.load_profile("xp_default") + settings.register_profile("array-api-tests", **profile_settings) + settings.load_profile("array-api-tests") # CI if config.getoption("--ci"): warnings.warn( @@ -156,7 +148,7 @@ def pytest_collection_modifyitems(config, items): disabled_exts = config.getoption("--disable-extension") disabled_dds = config.getoption("--disable-data-dependent-shapes") - unvectorized_max_examples = math.ceil(math.log(int(config.getoption("--hypothesis-max-examples")) or 50)) + unvectorized_max_examples = math.ceil(math.log(config.getoption("--hypothesis-max-examples"))) # 2. Iterate through items and apply markers accordingly # ------------------------------------------------------ From 4b12b0e63dba79f2973e0cce91796a63b5f85774 Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Mon, 25 Mar 2024 12:10:01 +0000 Subject: [PATCH 06/10] Default Hypothesis deadline to `800` again --- conftest.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/conftest.py b/conftest.py index fb5e83f8..4952bcee 100644 --- a/conftest.py +++ b/conftest.py @@ -86,13 +86,13 @@ def pytest_configure(config): "unvectorized: asserts against values via element-wise iteration (not performative!)", ) # Hypothesis - profile_settings = { - "max_examples": config.getoption("--hypothesis-max-examples"), - "derandomize": config.getoption("--hypothesis-derandomize"), - } - if config.getoption("--hypothesis-disable-deadline"): - profile_settings["deadline"] = None - settings.register_profile("array-api-tests", **profile_settings) + deadline = None if config.getoption("--hypothesis-disable-deadline") else 800 + settings.register_profile( + "array-api-tests", + max_examples=config.getoption("--hypothesis-max-examples"), + derandomize=config.getoption("--hypothesis-derandomize"), + deadline=deadline, + ) settings.load_profile("array-api-tests") # CI if config.getoption("--ci"): From cfd9601bf7472f7ed66ccc05f8d2cdc72544835a Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Mon, 25 Mar 2024 12:32:15 +0000 Subject: [PATCH 07/10] Update max example defaults in README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 63484bcb..a5f43d4d 100644 --- a/README.md +++ b/README.md @@ -254,7 +254,7 @@ jobs: > There are several ways to avoid this problem: > > - Increase the maximum number of examples, e.g., by adding `--max-examples -> 200` to the test command (the default is `100`, see below). This will +> 200` to the test command (the default is `20`, see below). This will > make it more likely that the failing case will be found, but it will also > make the tests take longer to run. > - Don't use `-o xfail_strict=True`. This will make it so that if an XFAIL @@ -275,7 +275,7 @@ jobs: The tests make heavy use [Hypothesis](https://hypothesis.readthedocs.io/en/latest/). You can configure how many examples are generated using the `--max-examples` flag, which -defaults to `100`. Lower values can be useful for quick checks, and larger +defaults to `20`. Lower values can be useful for quick checks, and larger values should result in more rigorous runs. For example, `--max-examples 10_000` may find bugs where default runs don't but will take much longer to run. From fa5b9a71a3ce0fc46803064e45714623672dee80 Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Thu, 28 Mar 2024 10:42:17 +0000 Subject: [PATCH 08/10] Store original special case text in processed case objects --- array_api_tests/test_special_cases.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/array_api_tests/test_special_cases.py b/array_api_tests/test_special_cases.py index a21d9475..3a4af0e5 100644 --- a/array_api_tests/test_special_cases.py +++ b/array_api_tests/test_special_cases.py @@ -494,6 +494,7 @@ def check_result(result: float) -> bool: class Case(Protocol): cond_expr: str result_expr: str + raw_case: Optional[str] def cond(self, *args) -> bool: ... @@ -532,6 +533,7 @@ class UnaryCase(Case): cond_from_dtype: FromDtypeFunc cond: UnaryCheck check_result: UnaryResultCheck + raw_case: Optional[str] = field(default=None) r_unary_case = re.compile("If ``x_i`` is (.+), the result is (.+)") @@ -674,6 +676,7 @@ def parse_unary_case_block(case_block: str) -> List[UnaryCase]: cond_from_dtype=cond_from_dtype, result_expr=result_expr, check_result=check_result, + raw_case=case_str, ) cases.append(case) else: @@ -700,6 +703,7 @@ class BinaryCase(Case): x2_cond_from_dtype: FromDtypeFunc cond: BinaryCond check_result: BinaryResultCheck + raw_case: Optional[str] = field(default=None) r_binary_case = re.compile("If (.+), the result (.+)") @@ -1058,6 +1062,7 @@ def cond(i1: float, i2: float) -> bool: x2_cond_from_dtype=x2_cond_from_dtype, result_expr=result_expr, check_result=check_result, + raw_case=case_str, ) From 5aace72e8e8f2d61669b32bb361e9cc1b535dbc5 Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Tue, 2 Apr 2024 11:59:28 +0100 Subject: [PATCH 09/10] Add missing bound from dtypes for sign special cases --- array_api_tests/test_special_cases.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/array_api_tests/test_special_cases.py b/array_api_tests/test_special_cases.py index 3a4af0e5..e2481229 100644 --- a/array_api_tests/test_special_cases.py +++ b/array_api_tests/test_special_cases.py @@ -941,12 +941,18 @@ def _x2_cond_from_dtype(dtype, **kw) -> st.SearchStrategy[float]: def partial_cond(i1: float, i2: float) -> bool: return math.copysign(1, i1) == math.copysign(1, i2) + x1_cond_from_dtypes.append(BoundFromDtype(kwargs={"min_value": 1})) + x2_cond_from_dtypes.append(BoundFromDtype(kwargs={"min_value": 1})) + elif value_str == "different mathematical signs": partial_expr = "copysign(1, x1_i) != copysign(1, x2_i)" def partial_cond(i1: float, i2: float) -> bool: return math.copysign(1, i1) != math.copysign(1, i2) + x1_cond_from_dtypes.append(BoundFromDtype(kwargs={"min_value": 1})) + x2_cond_from_dtypes.append(BoundFromDtype(kwargs={"max_value": -1})) + else: unary_cond, expr_template, cond_from_dtype = parse_cond(value_str) # Do not define partial_cond via the def keyword or lambda From ae1605f15b9a2a9c7d689ff660752fa8e20e49a1 Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Wed, 3 Apr 2024 09:13:08 +0100 Subject: [PATCH 10/10] Remove disable deadline flag for CI --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 220471d4..f1a91300 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,6 +28,6 @@ jobs: env: ARRAY_API_TESTS_MODULE: array_api_strict run: | - pytest -v -rxXfE --skips-file array-api-strict-skips.txt array_api_tests/ --hypothesis-disable-deadline + pytest -v -rxXfE --skips-file array-api-strict-skips.txt array_api_tests/ # We also have internal tests that isn't really necessary for adopters pytest -v -rxXfE meta_tests/