From f28ea06577653d46c22261afd6fe3af28a193d14 Mon Sep 17 00:00:00 2001 From: Evgeni Burovski Date: Sat, 17 Jan 2026 20:11:21 +0100 Subject: [PATCH 1/3] MAINT: clean up scalars/array_and_py_scalar strategies Get rid of ad hoc mM and positive arguments. Use the same `min_value`, `max_value` kwargs for scalars and arrays of varying types/dtypes. --- array_api_tests/hypothesis_helpers.py | 32 +++++++++++-------- ...est_operators_and_elementwise_functions.py | 2 +- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/array_api_tests/hypothesis_helpers.py b/array_api_tests/hypothesis_helpers.py index e1df108c..0da2da8d 100644 --- a/array_api_tests/hypothesis_helpers.py +++ b/array_api_tests/hypothesis_helpers.py @@ -457,13 +457,12 @@ def scalars(draw, dtypes, finite=False, **kwds): dtypes should be one of the shared_* dtypes strategies. """ dtype = draw(dtypes) - mM = kwds.pop('mM', None) if dh.is_int_dtype(dtype): - if mM is None: - m, M = dh.dtype_ranges[dtype] - else: - m, M = mM - return draw(integers(m, M)) + m, M = dh.dtype_ranges[dtype] + min_value = kwds.get('min_value', m) + max_value = kwds.get('max_value', M) + + return draw(integers(min_value, max_value)) elif dtype == bool_dtype: return draw(booleans()) elif dtype == float64: @@ -593,20 +592,25 @@ def two_mutual_arrays( @composite -def array_and_py_scalar(draw, dtypes, mM=None, positive=False): +def array_and_py_scalar(draw, dtypes, **kwds): """Draw a pair: (array, scalar) or (scalar, array).""" dtype = draw(sampled_from(dtypes)) - scalar_var = draw(scalars(just(dtype), finite=True, mM=mM)) - if positive: - assume (scalar_var > 0) + scalar_var = draw(scalars(just(dtype), finite=True, **kwds)) elements={} if dtype in dh.real_float_dtypes: - elements = {'allow_nan': False, 'allow_infinity': False, - 'min_value': 1.0 / (2<<5), 'max_value': 2<<5} - if positive: - elements = {'min_value': 0} + elements = { + 'allow_nan': False, + 'allow_infinity': False, + 'min_value': kwds.get('min_value', 1.0 / (2<<5)), + 'max_value': kwds.get('max_value', 2<<5) + } + elif dtype in dh.int_dtypes: + elements = { + 'min_value': kwds.get('min_value', None), + 'max_value': kwds.get('max_value', None) + } array_var = draw(arrays(dtype, shape=shapes(min_dims=1), elements=elements)) if draw(booleans()): diff --git a/array_api_tests/test_operators_and_elementwise_functions.py b/array_api_tests/test_operators_and_elementwise_functions.py index f074bd8e..c19fe3cd 100644 --- a/array_api_tests/test_operators_and_elementwise_functions.py +++ b/array_api_tests/test_operators_and_elementwise_functions.py @@ -2246,7 +2246,7 @@ def test_binary_with_scalars_bitwise(func_data, x1x2): ], ids=lambda func_data: func_data[0] # use names for test IDs ) -@given(x1x2=hh.array_and_py_scalar([xp.int32], positive=True, mM=(1, 3))) +@given(x1x2=hh.array_and_py_scalar([xp.int32], min_value=1, max_value=3)) def test_binary_with_scalars_bitwise_shifts(func_data, x1x2): func_name, refimpl, kwargs, expected = func_data # repack the refimpl From 7cb96ccfdc8a97ca0446dd95d4868882e7312d70 Mon Sep 17 00:00:00 2001 From: Evgeni Burovski Date: Sat, 17 Jan 2026 20:46:18 +0100 Subject: [PATCH 2/3] ENH: test func(float_array, int_scalar) Previously, we only tested the matching type/dtype combinations, i.e. - float_array, float_scalar - int_array, int_scalar - bool_array, bool_scalar --- array_api_tests/hypothesis_helpers.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/array_api_tests/hypothesis_helpers.py b/array_api_tests/hypothesis_helpers.py index 0da2da8d..25d68324 100644 --- a/array_api_tests/hypothesis_helpers.py +++ b/array_api_tests/hypothesis_helpers.py @@ -596,8 +596,15 @@ def array_and_py_scalar(draw, dtypes, **kwds): """Draw a pair: (array, scalar) or (scalar, array).""" dtype = draw(sampled_from(dtypes)) - scalar_var = draw(scalars(just(dtype), finite=True, **kwds)) + # draw the scalar: for float arrays, draw a float or an int + if dtype in dh.real_float_dtypes: + scalar_strategy = sampled_from([xp.int32, dtype]) + else: + scalar_strategy = just(dtype) + scalar_var = draw(scalars(scalar_strategy, finite=True, **kwds)) + # draw the array. + # XXX artificially limit the range of values for floats, otherwise value testing is flaky elements={} if dtype in dh.real_float_dtypes: elements = { From c435e4b6a71230aab14e5f4191f968a4250d8986 Mon Sep 17 00:00:00 2001 From: Evgeni Burovski Date: Sat, 17 Jan 2026 21:32:00 +0100 Subject: [PATCH 3/3] ENH: test clip(float_array, min=int_scalar, max=int_scalar) --- .../test_operators_and_elementwise_functions.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/array_api_tests/test_operators_and_elementwise_functions.py b/array_api_tests/test_operators_and_elementwise_functions.py index c19fe3cd..1652a9d9 100644 --- a/array_api_tests/test_operators_and_elementwise_functions.py +++ b/array_api_tests/test_operators_and_elementwise_functions.py @@ -1067,14 +1067,20 @@ def test_clip(x, data): base_shape=x.shape), label="min.shape, max.shape") + # for min,max being scalars: clip(float_array, min=int_scalar, max=int_scalar) + if x.dtype in dh.real_float_dtypes: + scalar_strategy = st.sampled_from([xp.int32, x.dtype]) + else: + scalar_strategy = st.just(x.dtype) + min = data.draw(st.one_of( st.none(), - hh.scalars(dtypes=st.just(x.dtype)), + hh.scalars(dtypes=scalar_strategy), hh.arrays(dtype=st.just(x.dtype), shape=shape1), ), label="min") max = data.draw(st.one_of( st.none(), - hh.scalars(dtypes=st.just(x.dtype)), + hh.scalars(dtypes=scalar_strategy), hh.arrays(dtype=st.just(x.dtype), shape=shape2), ), label="max")