From 983e55f47026aa55a84714ade936075936956643 Mon Sep 17 00:00:00 2001 From: boulea7 Date: Mon, 28 Sep 2026 17:20:11 +0800 Subject: [PATCH 1/5] TST: cover reflected scalar operators --- ...est_operators_and_elementwise_functions.py | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/array_api_tests/test_operators_and_elementwise_functions.py b/array_api_tests/test_operators_and_elementwise_functions.py index 6ab7ec34..91ed7378 100644 --- a/array_api_tests/test_operators_and_elementwise_functions.py +++ b/array_api_tests/test_operators_and_elementwise_functions.py @@ -2104,6 +2104,50 @@ def test_subtract(ctx, data): raise +def make_reflected_scalar_params(): + cases = ( + ("__radd__", "add", operator.add, dh.numeric_dtypes, 7, (2, 3)), + ("__rsub__", "subtract", operator.sub, dh.numeric_dtypes, 7, (2, 3)), + ("__rmul__", "multiply", operator.mul, dh.numeric_dtypes, 7, (2, 3)), + ("__rtruediv__", "divide", operator.truediv, dh.all_float_dtypes, 6, (2, 4)), + ("__rfloordiv__", "floor_divide", operator.floordiv, dh.real_dtypes, 7, (2, 3)), + ("__rpow__", "pow", operator.pow, dh.numeric_dtypes, 3, (1, 2)), + ("__rmod__", "remainder", operator.mod, dh.real_dtypes, 7, (2, 3)), + ("__rand__", "bitwise_and", operator.and_, dh.bool_and_all_int_dtypes, 6, (1, 2)), + ("__ror__", "bitwise_or", operator.or_, dh.bool_and_all_int_dtypes, 6, (1, 2)), + ("__rxor__", "bitwise_xor", operator.xor, dh.bool_and_all_int_dtypes, 6, (1, 2)), + ("__rlshift__", "bitwise_left_shift", operator.lshift, dh.all_int_dtypes, 3, (1, 2)), + ("__rrshift__", "bitwise_right_shift", operator.rshift, dh.all_int_dtypes, 8, (1, 2)), + ) + for method, func_name, op, dtypes, scalar, values in cases: + for dtype in dtypes: + if isinstance(dtype, xp._UndefinedStub): + continue + if dtype == xp.bool: + operand, elements = True, (False, True) + else: + scalar_type = dh.get_scalar_type(dtype) + operand = scalar_type(scalar) + elements = tuple(scalar_type(value) for value in values) + yield pytest.param( + method, func_name, op, dtype, operand, elements, + id=f"{method}-{dh.dtype_to_name[dtype]}", + ) + + +@pytest.mark.parametrize( + "method, func_name, op, dtype, scalar, values", make_reflected_scalar_params() +) +def test_reflected_scalar_operator(method, func_name, op, dtype, scalar, values): + x = xp.asarray(values, dtype=dtype) + out = op(scalar, x) + expected = getattr(xp, func_name)(xp.asarray(scalar, dtype=dtype), x) + + ph.assert_dtype(method, in_dtype=dtype, out_dtype=out.dtype) + ph.assert_result_shape(method, in_shapes=[x.shape], out_shape=out.shape) + ph.assert_array_elements(method, out=out, expected=expected) + + @given(hh.arrays(dtype=hh.all_floating_dtypes(), shape=hh.shapes())) def test_tan(x): repro_snippet = ph.format_snippet(f"xp.tan({x!r})") From ffd5e41e8ca36dc852e50126c6d37b56420b39e9 Mon Sep 17 00:00:00 2001 From: boulea7 Date: Wed, 30 Sep 2026 04:01:36 +0800 Subject: [PATCH 2/5] TST: generate reflected operator inputs with Hypothesis --- ...est_operators_and_elementwise_functions.py | 55 +++++++++++-------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/array_api_tests/test_operators_and_elementwise_functions.py b/array_api_tests/test_operators_and_elementwise_functions.py index 91ed7378..f3c29785 100644 --- a/array_api_tests/test_operators_and_elementwise_functions.py +++ b/array_api_tests/test_operators_and_elementwise_functions.py @@ -2106,40 +2106,49 @@ def test_subtract(ctx, data): def make_reflected_scalar_params(): cases = ( - ("__radd__", "add", operator.add, dh.numeric_dtypes, 7, (2, 3)), - ("__rsub__", "subtract", operator.sub, dh.numeric_dtypes, 7, (2, 3)), - ("__rmul__", "multiply", operator.mul, dh.numeric_dtypes, 7, (2, 3)), - ("__rtruediv__", "divide", operator.truediv, dh.all_float_dtypes, 6, (2, 4)), - ("__rfloordiv__", "floor_divide", operator.floordiv, dh.real_dtypes, 7, (2, 3)), - ("__rpow__", "pow", operator.pow, dh.numeric_dtypes, 3, (1, 2)), - ("__rmod__", "remainder", operator.mod, dh.real_dtypes, 7, (2, 3)), - ("__rand__", "bitwise_and", operator.and_, dh.bool_and_all_int_dtypes, 6, (1, 2)), - ("__ror__", "bitwise_or", operator.or_, dh.bool_and_all_int_dtypes, 6, (1, 2)), - ("__rxor__", "bitwise_xor", operator.xor, dh.bool_and_all_int_dtypes, 6, (1, 2)), - ("__rlshift__", "bitwise_left_shift", operator.lshift, dh.all_int_dtypes, 3, (1, 2)), - ("__rrshift__", "bitwise_right_shift", operator.rshift, dh.all_int_dtypes, 8, (1, 2)), + ("__radd__", "add", operator.add, dh.numeric_dtypes), + ("__rsub__", "subtract", operator.sub, dh.numeric_dtypes), + ("__rmul__", "multiply", operator.mul, dh.numeric_dtypes), + ("__rtruediv__", "divide", operator.truediv, dh.all_float_dtypes), + ("__rfloordiv__", "floor_divide", operator.floordiv, dh.real_dtypes), + ("__rpow__", "pow", operator.pow, dh.numeric_dtypes), + ("__rmod__", "remainder", operator.mod, dh.real_dtypes), + ("__rand__", "bitwise_and", operator.and_, dh.bool_and_all_int_dtypes), + ("__ror__", "bitwise_or", operator.or_, dh.bool_and_all_int_dtypes), + ("__rxor__", "bitwise_xor", operator.xor, dh.bool_and_all_int_dtypes), + ("__rlshift__", "bitwise_left_shift", operator.lshift, dh.all_int_dtypes), + ("__rrshift__", "bitwise_right_shift", operator.rshift, dh.all_int_dtypes), ) - for method, func_name, op, dtypes, scalar, values in cases: + for method, func_name, op, dtypes in cases: for dtype in dtypes: if isinstance(dtype, xp._UndefinedStub): continue - if dtype == xp.bool: - operand, elements = True, (False, True) - else: - scalar_type = dh.get_scalar_type(dtype) - operand = scalar_type(scalar) - elements = tuple(scalar_type(value) for value in values) yield pytest.param( - method, func_name, op, dtype, operand, elements, + method, func_name, op, dtype, id=f"{method}-{dh.dtype_to_name[dtype]}", ) @pytest.mark.parametrize( - "method, func_name, op, dtype, scalar, values", make_reflected_scalar_params() + "method, func_name, op, dtype", make_reflected_scalar_params() ) -def test_reflected_scalar_operator(method, func_name, op, dtype, scalar, values): - x = xp.asarray(values, dtype=dtype) +@given(data=st.data()) +def test_reflected_scalar_operator(method, func_name, op, dtype, data): + # Small positive values avoid division by zero, invalid powers, and overflow. + if dtype == xp.bool: + elements = st.booleans() + else: + elements = st.integers(1, 3).map(dh.get_scalar_type(dtype)) + scalar_strategy = elements + if func_name == "subtract" and dtype in dh.uint_dtypes: + scalar_strategy = st.integers(3, 7) + scalar = data.draw(scalar_strategy, label="scalar") + x = data.draw( + hh.arrays( + dtype=dtype, shape=hh.shapes(min_dims=1, min_side=1), elements=elements + ), + label="x", + ) out = op(scalar, x) expected = getattr(xp, func_name)(xp.asarray(scalar, dtype=dtype), x) From a273ba6ce5d4b619f713b8fc5ca55a598211bc8c Mon Sep 17 00:00:00 2001 From: boulea7 Date: Thu, 1 Oct 2026 23:51:00 +0800 Subject: [PATCH 3/5] TST: materialize reflected scalar parameters --- array_api_tests/test_operators_and_elementwise_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/array_api_tests/test_operators_and_elementwise_functions.py b/array_api_tests/test_operators_and_elementwise_functions.py index f3c29785..a1247459 100644 --- a/array_api_tests/test_operators_and_elementwise_functions.py +++ b/array_api_tests/test_operators_and_elementwise_functions.py @@ -2130,7 +2130,7 @@ def make_reflected_scalar_params(): @pytest.mark.parametrize( - "method, func_name, op, dtype", make_reflected_scalar_params() + "method, func_name, op, dtype", list(make_reflected_scalar_params()) ) @given(data=st.data()) def test_reflected_scalar_operator(method, func_name, op, dtype, data): From e0da98d8aaf9f6694800437919683159eff74a2c Mon Sep 17 00:00:00 2001 From: boulea7 Date: Mon, 26 Oct 2026 18:25:10 +0800 Subject: [PATCH 4/5] TST: add reflected scalar failure snippets --- .../test_operators_and_elementwise_functions.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/array_api_tests/test_operators_and_elementwise_functions.py b/array_api_tests/test_operators_and_elementwise_functions.py index a1247459..12246860 100644 --- a/array_api_tests/test_operators_and_elementwise_functions.py +++ b/array_api_tests/test_operators_and_elementwise_functions.py @@ -2149,12 +2149,18 @@ def test_reflected_scalar_operator(method, func_name, op, dtype, data): ), label="x", ) - out = op(scalar, x) - expected = getattr(xp, func_name)(xp.asarray(scalar, dtype=dtype), x) + op_sym = dh.binary_op_to_symbol[func_to_op[func_name]] + repro_snippet = ph.format_snippet(f"{scalar!r} {op_sym} {x!r} # {method}") + try: + out = op(scalar, x) + expected = getattr(xp, func_name)(xp.asarray(scalar, dtype=dtype), x) - ph.assert_dtype(method, in_dtype=dtype, out_dtype=out.dtype) - ph.assert_result_shape(method, in_shapes=[x.shape], out_shape=out.shape) - ph.assert_array_elements(method, out=out, expected=expected) + ph.assert_dtype(method, in_dtype=dtype, out_dtype=out.dtype) + ph.assert_result_shape(method, in_shapes=[x.shape], out_shape=out.shape) + ph.assert_array_elements(method, out=out, expected=expected) + except Exception as exc: + ph.add_note(exc, repro_snippet) + raise @given(hh.arrays(dtype=hh.all_floating_dtypes(), shape=hh.shapes())) From 4bec100b031197381b3f4074699079d8fbb50044 Mon Sep 17 00:00:00 2001 From: Evgeni Burovski Date: Mon, 26 Oct 2026 11:31:24 +0100 Subject: [PATCH 5/5] Apply suggestion from @ev-br --- array_api_tests/test_operators_and_elementwise_functions.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/array_api_tests/test_operators_and_elementwise_functions.py b/array_api_tests/test_operators_and_elementwise_functions.py index 12246860..89480a90 100644 --- a/array_api_tests/test_operators_and_elementwise_functions.py +++ b/array_api_tests/test_operators_and_elementwise_functions.py @@ -2129,6 +2129,8 @@ def make_reflected_scalar_params(): ) +# XXX: this test is parametrized over lists of dtypes not dtype strategies. +# Therefore adding `--max-examples` to the test run does not improve coverage. @pytest.mark.parametrize( "method, func_name, op, dtype", list(make_reflected_scalar_params()) )