From b8494356e99cc8886847a2dab47b42cbc1f54e5f Mon Sep 17 00:00:00 2001 From: Azeez <95100110+AzeezIsh@users.noreply.github.com> Date: Tue, 26 Mar 2024 02:47:30 -0400 Subject: [PATCH 1/3] Arithmetic Operation Testing (#42) * testing on arithmetic functions --------- Co-authored-by: AzeezIsh --- tests/test_arithmetic.py | 152 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 tests/test_arithmetic.py diff --git a/tests/test_arithmetic.py b/tests/test_arithmetic.py new file mode 100644 index 0000000..8f1879d --- /dev/null +++ b/tests/test_arithmetic.py @@ -0,0 +1,152 @@ +import random + +import pytest + +import arrayfire_wrapper.dtypes as dtype +import arrayfire_wrapper.lib as wrapper +from tests.utility_functions import check_type_supported, get_all_types + + +@pytest.mark.parametrize( + "shape", + [ + (), + (random.randint(1, 10),), + (random.randint(1, 10), random.randint(1, 10)), + (random.randint(1, 10), random.randint(1, 10), random.randint(1, 10)), + (random.randint(1, 10), random.randint(1, 10), random.randint(1, 10), random.randint(1, 10)), + ], +) +def test_add_shapes(shape: tuple) -> None: + """Test addition operation between two arrays of the same shape""" + lhs = wrapper.randu(shape, dtype.f16) + rhs = wrapper.randu(shape, dtype.f16) + + result = wrapper.add(lhs, rhs) + + assert wrapper.get_dims(result)[0 : len(shape)] == shape # noqa: E203, W291 + + +def test_add_different_shapes() -> None: + """Test if addition handles arrays of different shapes""" + with pytest.raises(RuntimeError): + lhs_shape = (2, 3) + rhs_shape = (3, 2) + dtypes = dtype.f16 + lhs = wrapper.randu(lhs_shape, dtypes) + rhs = wrapper.randu(rhs_shape, dtypes) + + wrapper.add(lhs, rhs) + + +@pytest.mark.parametrize("dtype_name", get_all_types()) +def test_add_supported_dtypes(dtype_name: dtype.Dtype) -> None: + """Test addition operation across all supported data types.""" + check_type_supported(dtype_name) + shape = (5, 5) # Using a common shape for simplicity + lhs = wrapper.randu(shape, dtype_name) + rhs = wrapper.randu(shape, dtype_name) + result = wrapper.add(lhs, rhs) + assert dtype.c_api_value_to_dtype(wrapper.get_type(result)) == dtype_name, f"Failed for dtype: {dtype_name}" + + +@pytest.mark.parametrize( + "invdtypes", + [ + dtype.c64, + dtype.f64, + ], +) +def test_add_unsupported_dtypes(invdtypes: dtype.Dtype) -> None: + """Test addition operation across all supported data types.""" + with pytest.raises(RuntimeError): + shape = (5, 5) + lhs = wrapper.randu(shape, invdtypes) + rhs = wrapper.randu(shape, invdtypes) + result = wrapper.add(lhs, rhs) + assert dtype.c_api_value_to_dtype(wrapper.get_type(result)) == invdtypes, f"Didn't Fail for Dtype: {invdtypes}" + + +def test_add_zero_sized_arrays() -> None: + """Test addition with arrays where at least one array has zero size.""" + with pytest.raises(RuntimeError): + zero_shape = (0, 5) + normal_shape = (5, 5) + zero_array = wrapper.randu(zero_shape, dtype.f32) + normal_array = wrapper.randu(normal_shape, dtype.f32) + + # Test addition when lhs is zero-sized + result_lhs_zero = wrapper.add(zero_array, normal_array) + assert wrapper.get_dims(result_lhs_zero) == zero_shape + + +@pytest.mark.parametrize( + "shape", + [ + (), + (random.randint(1, 10),), + (random.randint(1, 10), random.randint(1, 10)), + (random.randint(1, 10), random.randint(1, 10), random.randint(1, 10)), + (random.randint(1, 10), random.randint(1, 10), random.randint(1, 10), random.randint(1, 10)), + ], +) +def test_subtract_shapes(shape: tuple) -> None: + """Test subtraction operation between two arrays of the same shape""" + lhs = wrapper.randu(shape, dtype.f16) + rhs = wrapper.randu(shape, dtype.f16) + + result = wrapper.sub(lhs, rhs) + + assert wrapper.get_dims(result)[0 : len(shape)] == shape # noqa: E203, W291 + + +def test_subtract_different_shapes() -> None: + """Test if subtraction handles arrays of different shapes""" + with pytest.raises(RuntimeError): + lhs_shape = (2, 3) + rhs_shape = (3, 2) + dtypes = dtype.f16 + lhs = wrapper.randu(lhs_shape, dtypes) + rhs = wrapper.randu(rhs_shape, dtypes) + + wrapper.sub(lhs, rhs) + + +@pytest.mark.parametrize("dtype_name", get_all_types()) +def test_subtract_supported_dtypes(dtype_name: dtype.Dtype) -> None: + """Test subtraction operation across all supported data types.""" + check_type_supported(dtype_name) + shape = (5, 5) + lhs = wrapper.randu(shape, dtype_name) + rhs = wrapper.randu(shape, dtype_name) + result = wrapper.sub(lhs, rhs) + assert dtype.c_api_value_to_dtype(wrapper.get_type(result)) == dtype_name, f"Failed for dtype: {dtype_name}" + + +@pytest.mark.parametrize( + "invdtypes", + [ + dtype.c64, + dtype.f64, + ], +) +def test_subtract_unsupported_dtypes(invdtypes: dtype.Dtype) -> None: + """Test subtraction operation for unsupported data types.""" + with pytest.raises(RuntimeError): + shape = (5, 5) + lhs = wrapper.randu(shape, invdtypes) + rhs = wrapper.randu(shape, invdtypes) + result = wrapper.sub(lhs, rhs) + assert result == invdtypes, f"Didn't Fail for Dtype: {invdtypes}" + + +def test_subtract_zero_sized_arrays() -> None: + """Test subtraction with arrays where at least one array has zero size.""" + with pytest.raises(RuntimeError): + zero_shape = (0, 5) + normal_shape = (5, 5) + zero_array = wrapper.randu(zero_shape, dtype.f32) + normal_array = wrapper.randu(normal_shape, dtype.f32) + + result_lhs_zero = wrapper.sub(zero_array, normal_array) + assert wrapper.get_dims(result_lhs_zero) == zero_shape From 90b77c62a3931cdd58415dc551d65841e498294a Mon Sep 17 00:00:00 2001 From: Azeez <95100110+AzeezIsh@users.noreply.github.com> Date: Tue, 26 Mar 2024 02:52:22 -0400 Subject: [PATCH 2/3] Trigonometry Testing (#44) * Updated imports and checkstyle changes --------- Co-authored-by: AzeezIsh --- tests/test_trig.py | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/tests/test_trig.py b/tests/test_trig.py index ae62f7b..c81d26e 100644 --- a/tests/test_trig.py +++ b/tests/test_trig.py @@ -4,8 +4,7 @@ import arrayfire_wrapper.dtypes as dtype import arrayfire_wrapper.lib as wrapper - -from . import utility_functions as util +from tests.utility_functions import check_type_supported, get_all_types, get_float_types @pytest.mark.parametrize( @@ -18,10 +17,10 @@ (random.randint(1, 10), random.randint(1, 10), random.randint(1, 10), random.randint(1, 10)), ], ) -@pytest.mark.parametrize("dtype_name", util.get_all_types()) +@pytest.mark.parametrize("dtype_name", get_all_types()) def test_asin_shape_dtypes(shape: tuple, dtype_name: dtype.Dtype) -> None: """Test inverse sine operation across all supported data types.""" - util.check_type_supported(dtype_name) + check_type_supported(dtype_name) values = wrapper.randu(shape, dtype_name) result = wrapper.asin(values) assert wrapper.get_dims(result)[0 : len(shape)] == shape, f"failed for shape: {shape}" # noqa @@ -37,10 +36,10 @@ def test_asin_shape_dtypes(shape: tuple, dtype_name: dtype.Dtype) -> None: (random.randint(1, 10), random.randint(1, 10), random.randint(1, 10), random.randint(1, 10)), ], ) -@pytest.mark.parametrize("dtype_name", util.get_all_types()) +@pytest.mark.parametrize("dtype_name", get_all_types()) def test_acos_shape_dtypes(shape: tuple, dtype_name: dtype.Dtype) -> None: """Test inverse cosine operation across all supported data types.""" - util.check_type_supported(dtype_name) + check_type_supported(dtype_name) values = wrapper.randu(shape, dtype_name) result = wrapper.acos(values) assert wrapper.get_dims(result)[0 : len(shape)] == shape, f"failed for shape: {shape}" # noqa @@ -56,10 +55,10 @@ def test_acos_shape_dtypes(shape: tuple, dtype_name: dtype.Dtype) -> None: (random.randint(1, 10), random.randint(1, 10), random.randint(1, 10), random.randint(1, 10)), ], ) -@pytest.mark.parametrize("dtype_name", util.get_all_types()) +@pytest.mark.parametrize("dtype_name", get_all_types()) def test_atan_shape_dtypes(shape: tuple, dtype_name: dtype.Dtype) -> None: """Test inverse tan operation across all supported data types.""" - util.check_type_supported(dtype_name) + check_type_supported(dtype_name) values = wrapper.randu(shape, dtype_name) result = wrapper.atan(values) assert wrapper.get_dims(result)[0 : len(shape)] == shape, f"failed for shape: {shape}" # noqa @@ -75,10 +74,10 @@ def test_atan_shape_dtypes(shape: tuple, dtype_name: dtype.Dtype) -> None: (random.randint(1, 10), random.randint(1, 10), random.randint(1, 10), random.randint(1, 10)), ], ) -@pytest.mark.parametrize("dtype_name", util.get_float_types()) +@pytest.mark.parametrize("dtype_name", get_float_types()) def test_atan2_shape_dtypes(shape: tuple, dtype_name: dtype.Dtype) -> None: """Test inverse tan operation across all supported data types.""" - util.check_type_supported(dtype_name) + check_type_supported(dtype_name) if dtype_name == dtype.f16: pytest.skip() lhs = wrapper.randu(shape, dtype_name) @@ -110,10 +109,10 @@ def test_atan2_unsupported_dtypes(invdtypes: dtype.Dtype) -> None: (random.randint(1, 10), random.randint(1, 10), random.randint(1, 10), random.randint(1, 10)), ], ) -@pytest.mark.parametrize("dtype_name", util.get_all_types()) +@pytest.mark.parametrize("dtype_name", get_all_types()) def test_cos_shape_dtypes(shape: tuple, dtype_name: dtype.Dtype) -> None: """Test cosine operation across all supported data types.""" - util.check_type_supported(dtype_name) + check_type_supported(dtype_name) values = wrapper.randu(shape, dtype_name) result = wrapper.cos(values) assert wrapper.get_dims(result)[0 : len(shape)] == shape, f"failed for shape: {shape}" # noqa @@ -129,10 +128,10 @@ def test_cos_shape_dtypes(shape: tuple, dtype_name: dtype.Dtype) -> None: (random.randint(1, 10), random.randint(1, 10), random.randint(1, 10), random.randint(1, 10)), ], ) -@pytest.mark.parametrize("dtype_name", util.get_all_types()) +@pytest.mark.parametrize("dtype_name", get_all_types()) def test_sin_shape_dtypes(shape: tuple, dtype_name: dtype.Dtype) -> None: """Test sin operation across all supported data types.""" - util.check_type_supported(dtype_name) + check_type_supported(dtype_name) values = wrapper.randu(shape, dtype_name) result = wrapper.sin(values) assert wrapper.get_dims(result)[0 : len(shape)] == shape, f"failed for shape: {shape}" # noqa @@ -148,10 +147,10 @@ def test_sin_shape_dtypes(shape: tuple, dtype_name: dtype.Dtype) -> None: (random.randint(1, 10), random.randint(1, 10), random.randint(1, 10), random.randint(1, 10)), ], ) -@pytest.mark.parametrize("dtype_name", util.get_all_types()) +@pytest.mark.parametrize("dtype_name", get_all_types()) def test_tan_shape_dtypes(shape: tuple, dtype_name: dtype.Dtype) -> None: """Test tan operation across all supported data types.""" - util.check_type_supported(dtype_name) + check_type_supported(dtype_name) values = wrapper.randu(shape, dtype_name) result = wrapper.tan(values) assert wrapper.get_dims(result)[0 : len(shape)] == shape, f"failed for shape: {shape}" # noqa From b7e42bd93b80afba8875bccab450f3fb5396f39e Mon Sep 17 00:00:00 2001 From: Anton Date: Wed, 27 Mar 2024 05:24:31 +0200 Subject: [PATCH 3/3] Bug fixes. Minor build changes (#46) --- arrayfire_wrapper/_backend.py | 2 +- .../create_array/pad.py | 4 ++-- .../create_and_modify_array/manage_device.py | 2 ++ .../move_and_reorder.py | 18 ++++++++++++------ .../vector_algorithms/reduction_operations.py | 8 ++------ .../lib/vector_algorithms/set_operations.py | 2 +- arrayfire_wrapper/version.py | 2 +- pyproject.toml | 2 +- 8 files changed, 22 insertions(+), 18 deletions(-) diff --git a/arrayfire_wrapper/_backend.py b/arrayfire_wrapper/_backend.py index 2644831..585a92c 100644 --- a/arrayfire_wrapper/_backend.py +++ b/arrayfire_wrapper/_backend.py @@ -1,4 +1,4 @@ -__all__ = ["BackendType"] +__all__ = ["BackendType", "get_backend", "set_backend"] import ctypes import enum diff --git a/arrayfire_wrapper/lib/create_and_modify_array/create_array/pad.py b/arrayfire_wrapper/lib/create_and_modify_array/create_array/pad.py index 7fd8b21..cd9772c 100644 --- a/arrayfire_wrapper/lib/create_and_modify_array/create_array/pad.py +++ b/arrayfire_wrapper/lib/create_and_modify_array/create_array/pad.py @@ -17,9 +17,9 @@ def pad(arr: AFArray, begin_shape: tuple[int, ...], end_shape: tuple[int, ...], ctypes.pointer(out), arr, 4, - begin_c_shape.c_array, + ctypes.pointer(begin_c_shape.c_array), 4, - end_c_shape.c_array, + ctypes.pointer(end_c_shape.c_array), border_type.value, ) return out diff --git a/arrayfire_wrapper/lib/create_and_modify_array/manage_device.py b/arrayfire_wrapper/lib/create_and_modify_array/manage_device.py index 12673f9..49a7614 100644 --- a/arrayfire_wrapper/lib/create_and_modify_array/manage_device.py +++ b/arrayfire_wrapper/lib/create_and_modify_array/manage_device.py @@ -11,6 +11,8 @@ def alloc_host(num_bytes: int, /) -> int: Allocate a buffer on the host with specified number of bytes. """ + # TODO + # Avoid using AFArray and use ctypes.c_void_p to avoid misunderstanding 'coz its not actually an array out = AFArray.create_null_pointer() call_from_clib(alloc_host.__name__, ctypes.pointer(out), CDimT(num_bytes)) return out.value # type: ignore[return-value] diff --git a/arrayfire_wrapper/lib/create_and_modify_array/move_and_reorder.py b/arrayfire_wrapper/lib/create_and_modify_array/move_and_reorder.py index c1e17e1..240412d 100644 --- a/arrayfire_wrapper/lib/create_and_modify_array/move_and_reorder.py +++ b/arrayfire_wrapper/lib/create_and_modify_array/move_and_reorder.py @@ -78,25 +78,31 @@ def replace_scalar(lhs: AFArray, cond_arr: AFArray, rhs: int | float, /) -> None call_from_clib(replace.__name__, lhs, cond_arr, ctypes.c_double(rhs)) -def select(lhs: AFArray, cond_arr: AFArray, rhs: AFArray, /) -> None: +def select(lhs: AFArray, cond_arr: AFArray, rhs: AFArray, /) -> AFArray: """ source: https://arrayfire.org/docs/group__data__func__select.htm#gac4af16e31ddd5ddcf09b670f676fd093 """ - call_from_clib(select.__name__, cond_arr, lhs, rhs) + out = AFArray.create_null_pointer() + call_from_clib(select.__name__, ctypes.pointer(out), cond_arr, lhs, rhs) + return out -def select_scalar_l(lhs: int | float, cond_arr: AFArray, rhs: AFArray, /) -> None: +def select_scalar_l(lhs: int | float, cond_arr: AFArray, rhs: AFArray, /) -> AFArray: """ source: https://arrayfire.org/docs/group__data__func__select.htm#gac4af16e31ddd5ddcf09b670f676fd093 """ - call_from_clib(select_scalar_l.__name__, cond_arr, ctypes.c_double(lhs), rhs) + out = AFArray.create_null_pointer() + call_from_clib(select_scalar_l.__name__, ctypes.pointer(out), cond_arr, ctypes.c_double(lhs), rhs) + return out -def select_scalar_r(lhs: AFArray, cond_arr: AFArray, rhs: int | float, /) -> None: +def select_scalar_r(lhs: AFArray, cond_arr: AFArray, rhs: int | float, /) -> AFArray: """ source: https://arrayfire.org/docs/group__data__func__select.htm#gac4af16e31ddd5ddcf09b670f676fd093 """ - call_from_clib(select_scalar_l.__name__, cond_arr, lhs, ctypes.c_double(rhs)) + out = AFArray.create_null_pointer() + call_from_clib(select_scalar_l.__name__, ctypes.pointer(out), cond_arr, lhs, ctypes.c_double(rhs)) + return out def shift(arr: AFArray, /, d0: int, d1: int = 0, d2: int = 0, d3: int = 0) -> AFArray: diff --git a/arrayfire_wrapper/lib/vector_algorithms/reduction_operations.py b/arrayfire_wrapper/lib/vector_algorithms/reduction_operations.py index 48af56e..3f25e96 100644 --- a/arrayfire_wrapper/lib/vector_algorithms/reduction_operations.py +++ b/arrayfire_wrapper/lib/vector_algorithms/reduction_operations.py @@ -144,9 +144,7 @@ def max_ragged(arr: AFArray, ragged_len: AFArray, dim: int, /) -> tuple[AFArray, """ out_values = AFArray.create_null_pointer() out_idx = AFArray.create_null_pointer() - call_from_clib( - max_ragged.__name__, ctypes.pointer(out_values), ctypes.pointer(out_idx), arr, ragged_len, ctypes.c_int(dim) - ) + call_from_clib(max_ragged.__name__, ctypes.pointer(out_values), ctypes.pointer(out_idx), arr, ragged_len, dim) return (out_values, out_idx) @@ -156,9 +154,7 @@ def max_by_key(keys: AFArray, values: AFArray, dim: int, /) -> tuple[AFArray, AF """ out_keys = AFArray.create_null_pointer() out_values = AFArray.create_null_pointer() - call_from_clib( - max_by_key.__name__, ctypes.pointer(out_keys), ctypes.pointer(out_values), keys, values, ctypes.c_int(dim) - ) + call_from_clib(max_by_key.__name__, ctypes.pointer(out_keys), ctypes.pointer(out_values), keys, values, dim) return (out_keys, out_values) diff --git a/arrayfire_wrapper/lib/vector_algorithms/set_operations.py b/arrayfire_wrapper/lib/vector_algorithms/set_operations.py index 43853e3..09a9f61 100644 --- a/arrayfire_wrapper/lib/vector_algorithms/set_operations.py +++ b/arrayfire_wrapper/lib/vector_algorithms/set_operations.py @@ -27,5 +27,5 @@ def set_unique(arr: AFArray, is_sorted: bool, /) -> AFArray: source: https://arrayfire.org/docs/group__set__func__unique.htm#ga6afa1de48cbbc4b2df530c2530087943 """ out = AFArray.create_null_pointer() - call_from_clib(set_intersect.__name__, ctypes.pointer(out), ctypes.c_bool(is_sorted)) + call_from_clib(set_unique.__name__, ctypes.pointer(out), arr, ctypes.c_bool(is_sorted)) return out diff --git a/arrayfire_wrapper/version.py b/arrayfire_wrapper/version.py index 79ca4ff..5b08d8d 100644 --- a/arrayfire_wrapper/version.py +++ b/arrayfire_wrapper/version.py @@ -1,7 +1,7 @@ import os _MAJOR = "0" -_MINOR = "6" +_MINOR = "7" # On main and in a nightly release the patch should be one ahead of the last # released build. _PATCH = "0" diff --git a/pyproject.toml b/pyproject.toml index 76c3830..d975945 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,7 @@ build-backend = "scikit_build_core.build" [project] name = "arrayfire-binary-python-wrapper" -version = "0.6.0+AF3.9.0" +version = "0.7.0+AF3.9.0" requires-python = ">=3.10" authors = [ { name = "ArrayFire", email = "technical@arrayfire.com"},