diff --git a/arrayfire_wrapper/dtypes.py b/arrayfire_wrapper/dtypes.py index 0c8c1b1..31b7af1 100644 --- a/arrayfire_wrapper/dtypes.py +++ b/arrayfire_wrapper/dtypes.py @@ -108,7 +108,7 @@ def c_api_value_to_dtype(value: int) -> Dtype: def str_to_dtype(value: str) -> Dtype: for dtype in supported_dtypes: - if value == dtype.typecode or value == dtype.typename or value == dtype.name: + if value == dtype.typecode or value == dtype.typename or value == dtype.name or value == dtype.c_type: return dtype raise TypeError("There is no supported dtype that matches passed dtype typecode.") diff --git a/arrayfire_wrapper/lib/interface_functions/interop.py b/arrayfire_wrapper/lib/interface_functions/interop.py new file mode 100644 index 0000000..3fb8110 --- /dev/null +++ b/arrayfire_wrapper/lib/interface_functions/interop.py @@ -0,0 +1,60 @@ +import ctypes + +import numpy as np +import pyopencl.array as cl # type: ignore[import-untyped] + +from arrayfire_wrapper.defines import AFArray, CShape +from arrayfire_wrapper.dtypes import c_api_value_to_dtype, str_to_dtype +from arrayfire_wrapper.lib._utility import call_from_clib +from arrayfire_wrapper.lib.create_and_modify_array.manage_array import create_array, get_data_ptr, get_dims, get_type + + +def numpy_to_af_array(np_arr: np.ndarray) -> AFArray: + out = AFArray(0) + shape = np_arr.shape + c_shape = CShape(*shape) + + c_type = np.ctypeslib.as_ctypes_type(np_arr.dtype) + dtype = str_to_dtype(c_type) + + call_from_clib( + create_array.__name__, + ctypes.pointer(out), + np_arr.ctypes.data_as(ctypes.c_void_p), + c_shape.original_shape, + ctypes.pointer(c_shape.c_array), + dtype.c_api_value, + ) + return out + + +def af_to_numpy_array(af_arr: AFArray) -> np.ndarray: + shape = get_dims(af_arr) + dtype = c_api_value_to_dtype(get_type(af_arr)) + typecode = dtype.typecode + + out = np.empty(shape, typecode, "F") + call_from_clib(get_data_ptr.__name__, ctypes.c_void_p(out.ctypes.data), af_arr) + return out + + +def pyopencl_to_af_array(pycl_arr: cl.Array) -> AFArray: + out = AFArray(0) + np_arr = pycl_arr.get() + + shape = np_arr.shape + c_shape = CShape(*shape) + + c_type = np.ctypeslib.as_ctypes_type(np_arr.dtype) + dtype = str_to_dtype(c_type) + + call_from_clib( + create_array.__name__, + ctypes.pointer(out), + np_arr.ctypes.data_as(ctypes.c_void_p), + c_shape.original_shape, + ctypes.pointer(c_shape.c_array), + dtype.c_api_value, + ) + + return out diff --git a/arrayfire_wrapper/lib/interface_functions/opencl.py b/arrayfire_wrapper/lib/interface_functions/opencl.py index 4640904..9dd8296 100644 --- a/arrayfire_wrapper/lib/interface_functions/opencl.py +++ b/arrayfire_wrapper/lib/interface_functions/opencl.py @@ -1 +1,100 @@ -# TODO +import ctypes +from enum import Enum + +from arrayfire_wrapper.lib._utility import call_from_clib + + +class DeviceType(Enum): + CPU = 2 + GPU = 4 + ACC = 8 + UNKNOWN = -1 + + +class PlatformType(Enum): + AMD = 0 + APPLE = 1 + INTEL = 2 + NVIDIA = 3 + BEIGNET = 4 + POCL = 5 + UNKNOWN = -1 + + +def get_context(retain: bool = False) -> ctypes.c_void_p: + """ + source: https://arrayfire.org/docs/group__opencl__mat.htm#gad42de383f405b3e38d6eb669c0cbe2e3 + """ + out = ctypes.c_void_p() + call_from_clib(get_context.__name__, ctypes.pointer(out), retain, clib_prefix="afcl") + return out # type: ignore[return-value] + + +def get_queue(retain: bool = False) -> ctypes.c_void_p: + """ + source: https://arrayfire.org/docs/group__opencl__mat.htm#gab1701ef4f2b68429eb31c1e21c88d0bc + """ + out = ctypes.c_void_p() + call_from_clib(get_queue.__name__, ctypes.pointer(out), retain, clib_prefix="afcl") + return out # type: ignore[return-value] + + +def get_device_id() -> int: + """ + source: https://arrayfire.org/docs/group__opencl__mat.htm#gaf7258055284e65a8647a49c3f3b9feee + """ + out = ctypes.c_void_p() + call_from_clib(get_device_id.__name__, ctypes.pointer(out), clib_prefix="afcl") + return out # type: ignore[return-value] + + +def set_device_id(idx: int) -> None: + """ + source: https://arrayfire.org/docs/group__opencl__mat.htm#ga600361a20ceac2a65590b67fc0366314 + """ + call_from_clib(set_device_id.__name__, ctypes.c_int64(idx), clib_prefix="afcl") + return None + + +def add_device_context(dev: int, ctx: int, que: int) -> None: + """ + source: https://arrayfire.org/docs/group__opencl__mat.htm#ga49f596a4041fb757f1f5a75999cf8858 + """ + call_from_clib( + add_device_context.__name__, ctypes.c_int64(dev), ctypes.c_int64(ctx), ctypes.c_int64(que), clib_prefix="afcl" + ) + return None + + +def set_device_context(dev: int, ctx: int) -> None: + """ + source: https://arrayfire.org/docs/group__opencl__mat.htm#ga975661f2b06dddb125c5d1757160b02c + """ + call_from_clib(set_device_context.__name__, ctypes.c_int64(dev), ctypes.c_int64(ctx), clib_prefix="afcl") + return None + + +def delete_device_context(dev: int, ctx: int) -> None: + """ + source: https://arrayfire.org/docs/group__opencl__mat.htm#ga1a56dcf05099d6ac0a3b7701f7cb23f8 + """ + call_from_clib(delete_device_context.__name__, ctypes.c_int64(dev), ctypes.c_int64(ctx), clib_prefix="afcl") + return None + + +def get_device_type() -> DeviceType: + """ + source: https://arrayfire.org/docs/group__opencl__mat.htm#ga5e360e0fe0eb55d0046191bc3fd6f81d + """ + res = ctypes.c_void_p() + call_from_clib(get_device_type.__name__, ctypes.pointer(res), clib_prefix="afcl") + return DeviceType(res.value) + + +def get_platform() -> PlatformType: + """ + source: https://arrayfire.org/docs/group__opencl__mat.htm#ga5e360e0fe0eb55d0046191bc3fd6f81d&gsc.tab=0 + """ + res = ctypes.c_void_p() + call_from_clib(get_platform.__name__, ctypes.pointer(res), clib_prefix="afcl") + return PlatformType(res.value) diff --git a/tests/test_interop.py b/tests/test_interop.py new file mode 100644 index 0000000..80c4329 --- /dev/null +++ b/tests/test_interop.py @@ -0,0 +1,72 @@ +import numpy as np +import pyopencl as cl # type: ignore +import pyopencl.array as cl_array # type: ignore + +import arrayfire_wrapper.lib as wrapper +from arrayfire_wrapper.defines import AFArray +from arrayfire_wrapper.dtypes import int16 +from arrayfire_wrapper.lib.create_and_modify_array.manage_array import get_dims, get_numdims +from arrayfire_wrapper.lib.interface_functions.interop import ( # noqa: E501 + af_to_numpy_array, + numpy_to_af_array, + pyopencl_to_af_array, +) + +# flake8: noqa: E203 + + +def test_numpy_to_af_array_type() -> None: + arr = np.array([1, 2, 3, 4]) + + af_array = numpy_to_af_array(arr) + + assert isinstance(af_array, AFArray) + + +def test_af_to_numpy_array_type() -> None: + arr = wrapper.constant(2, (5, 5), int16) + + np_arr = af_to_numpy_array(arr) + + assert isinstance(np_arr, np.ndarray) + + +def test_pyopencl_to_af_array_type() -> None: + ctx = cl.create_some_context() + queue = cl.CommandQueue(ctx) + + host_array = np.array([1, 2, 3, 4]) + + cl_array_device = cl_array.to_device(queue, host_array) + + af_array = pyopencl_to_af_array(cl_array_device) + + assert isinstance(af_array, AFArray) + + +def test_numpy_to_af_array_shape() -> None: + np_arr = np.array([1, 2, 3, 4]) + + af_arr = numpy_to_af_array(np_arr) + + assert get_dims(af_arr)[0 : get_numdims(af_arr)] == np_arr.shape[0 : get_numdims(af_arr)] + + +def test_af_to_numpy_array_shape() -> None: + af_arr = wrapper.constant(2, (5, 5), int16) + + np_arr = af_to_numpy_array(af_arr) + assert np_arr.shape[0 : get_numdims(af_arr)] == get_dims(af_arr)[0 : get_numdims(af_arr)] + + +def test_pyopencl_to_af_array_shape() -> None: + ctx = cl.create_some_context() + queue = cl.CommandQueue(ctx) + + host_array = np.array([1, 2, 3, 4]) + + cl_arr = cl_array.to_device(queue, host_array) + + af_arr = pyopencl_to_af_array(cl_arr) + + assert cl_arr.shape[0 : get_numdims(af_arr)] == get_dims(af_arr)[0 : get_numdims(af_arr)] diff --git a/tests/test_opencl.py b/tests/test_opencl.py new file mode 100644 index 0000000..63aa9da --- /dev/null +++ b/tests/test_opencl.py @@ -0,0 +1,29 @@ +import ctypes + +import arrayfire_wrapper.lib.interface_functions.opencl as cl + + +def test_get_context_type() -> None: + ptr = cl.get_context() + assert isinstance(ptr, ctypes.c_void_p) + + +def test_get_queue_type() -> None: + assert isinstance(cl.get_queue(), ctypes.c_void_p) + + +def test_get_device_id() -> None: + assert isinstance(cl.get_device_id(), int) + + +def test_set_device_id() -> None: + cl.set_device_id(0) + assert cl.get_device_id() == 0 + + +def test_get_device_type() -> None: + assert cl.get_device_type() == cl.DeviceType.GPU # change according to device + + +def test_get_platform() -> None: + assert cl.get_platform() == cl.PlatformType.INTEL # change according to platform