Skip to content

Commit ff7a689

Browse files
committed
cleaning up interop code and adding tests
1 parent 02efc47 commit ff7a689

File tree

4 files changed

+214
-79
lines changed

4 files changed

+214
-79
lines changed

arrayfire/interop.py

Lines changed: 85 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,54 @@
1010
"""
1111
Interop with other python packages.
1212
13-
This module provides interoperability with the following python packages.
13+
This module provides helper functions to copy data to arrayfire from the following modules:
14+
15+
1. numpy - numpy.ndarray
16+
2. pycuda - pycuda.gpuarray
17+
3. pyopencl - pyopencl.array
1418
15-
1. numpy
16-
2. pycuda
17-
3. pyopencl
1819
"""
1920

2021
from .array import *
2122
from .device import *
2223

24+
25+
def _fc_to_af_array(in_ptr, in_shape, in_dtype, is_device=False, copy = True):
26+
"""
27+
Fortran Contiguous to af array
28+
"""
29+
res = Array(in_ptr, in_shape, in_dtype, is_device=is_device)
30+
31+
if is_device:
32+
lock_array(res)
33+
pass
34+
35+
return res.copy() if copy else res
36+
37+
def _cc_to_af_array(in_ptr, ndim, in_shape, in_dtype, is_device=False, copy = True):
38+
"""
39+
C Contiguous to af array
40+
"""
41+
if ndim == 1:
42+
return _fc_to_af_array(in_ptr, in_shape, in_dtype, is_device, copy)
43+
elif ndim == 2:
44+
shape = (in_shape[1], in_shape[0])
45+
res = Array(in_ptr, shape, in_dtype, is_device=is_device)
46+
if is_device: lock_array(res)
47+
return reorder(res, 1, 0)
48+
elif ndim == 3:
49+
shape = (in_shape[2], in_shape[1], in_shape[0])
50+
res = Array(in_ptr, shape, in_dtype, is_device=is_device)
51+
if is_device: lock_array(res)
52+
return reorder(res, 2, 1, 0)
53+
elif ndim == 4:
54+
shape = (in_shape[3], in_shape[2], in_shape[1], in_shape[0])
55+
res = Array(in_ptr, shape, in_dtype, is_device=is_device)
56+
if is_device: lock_array(res)
57+
return reorder(res, 3, 2, 1, 0)
58+
else:
59+
raise RuntimeError("Unsupported ndim")
60+
2361
try:
2462
import numpy as np
2563
from numpy import ndarray as NumpyArray
@@ -40,14 +78,18 @@
4078
'f8' : Dtype.f64,
4179
'c16' : Dtype.c64}
4280

43-
def np_to_af_array(np_arr):
81+
def np_to_af_array(np_arr, copy=True):
4482
"""
4583
Convert numpy.ndarray to arrayfire.Array.
4684
4785
Parameters
4886
----------
4987
np_arr : numpy.ndarray()
5088
89+
copy : Bool specifying if array is to be copied.
90+
Default is true.
91+
Can only be False if array is fortran contiguous.
92+
5193
Returns
5294
---------
5395
af_arr : arrayfire.Array()
@@ -57,27 +99,15 @@ def np_to_af_array(np_arr):
5799
in_ptr = np_arr.ctypes.data_as(c_void_ptr_t)
58100
in_dtype = _nptype_to_aftype[np_arr.dtype.str[1:]]
59101

102+
if not copy:
103+
raise RuntimeError("Copy can not be False for numpy arrays")
104+
60105
if (np_arr.flags['F_CONTIGUOUS']):
61-
return Array(in_ptr, in_shape, in_dtype)
106+
return _fc_to_af_array(in_ptr, in_shape, in_dtype)
62107
elif (np_arr.flags['C_CONTIGUOUS']):
63-
if np_arr.ndim == 1:
64-
return Array(in_ptr, in_shape, in_dtype)
65-
elif np_arr.ndim == 2:
66-
shape = (in_shape[1], in_shape[0])
67-
res = Array(in_ptr, shape, in_dtype)
68-
return reorder(res, 1, 0)
69-
elif np_arr.ndim == 3:
70-
shape = (in_shape[2], in_shape[1], in_shape[0])
71-
res = Array(in_ptr, shape, in_dtype)
72-
return reorder(res, 2, 1, 0)
73-
elif np_arr.ndim == 4:
74-
shape = (in_shape[3], in_shape[2], in_shape[1], in_shape[0])
75-
res = Array(in_ptr, shape, in_dtype)
76-
return reorder(res, 3, 2, 1, 0)
77-
else:
78-
raise RuntimeError("Unsupported ndim")
108+
return _cc_to_af_array(in_ptr, np_arr.ndim, in_shape, in_dtype)
79109
else:
80-
return np_to_af_array(np.asfortranarray(np_arr))
110+
return np_to_af_array(np_arr.copy())
81111

82112
from_ndarray = np_to_af_array
83113
except:
@@ -88,14 +118,18 @@ def np_to_af_array(np_arr):
88118
from pycuda.gpuarray import GPUArray as CudaArray
89119
AF_PYCUDA_FOUND=True
90120

91-
def pycuda_to_af_array(pycu_arr):
121+
def pycuda_to_af_array(pycu_arr, copy=True):
92122
"""
93123
Convert pycuda.gpuarray to arrayfire.Array
94124
95125
Parameters
96126
-----------
97127
pycu_arr : pycuda.GPUArray()
98128
129+
copy : Bool specifying if array is to be copied.
130+
Default is true.
131+
Can only be False if array is fortran contiguous.
132+
99133
Returns
100134
----------
101135
af_arr : arrayfire.Array()
@@ -109,31 +143,13 @@ def pycuda_to_af_array(pycu_arr):
109143
in_shape = pycu_arr.shape
110144
in_dtype = pycu_arr.dtype.char
111145

146+
if not copy and not pycu_arr.flags.f_contiguous:
147+
raise RuntimeError("Copy can only be False when arr.flags.f_contiguous is True")
148+
112149
if (pycu_arr.flags.f_contiguous):
113-
res = Array(in_ptr, in_shape, in_dtype, is_device=True)
114-
lock_array(res)
115-
res = res.copy()
116-
return res
150+
return _fc_to_af_array(in_ptr, in_shape, in_dtype, True, copy)
117151
elif (pycu_arr.flags.c_contiguous):
118-
if pycu_arr.ndim == 1:
119-
return Array(in_ptr, in_shape, in_dtype, is_device=True)
120-
elif pycu_arr.ndim == 2:
121-
shape = (in_shape[1], in_shape[0])
122-
res = Array(in_ptr, shape, in_dtype, is_device=True)
123-
lock_array(res)
124-
return reorder(res, 1, 0)
125-
elif pycu_arr.ndim == 3:
126-
shape = (in_shape[2], in_shape[1], in_shape[0])
127-
res = Array(in_ptr, shape, in_dtype, is_device=True)
128-
lock_array(res)
129-
return reorder(res, 2, 1, 0)
130-
elif pycu_arr.ndim == 4:
131-
shape = (in_shape[3], in_shape[2], in_shape[1], in_shape[0])
132-
res = Array(in_ptr, shape, in_dtype, is_device=True)
133-
lock_array(res)
134-
return reorder(res, 3, 2, 1, 0)
135-
else:
136-
raise RuntimeError("Unsupported ndim")
152+
return _cc_to_af_array(in_ptr, pycu_arr.ndim, in_shape, in_dtype, True, copy)
137153
else:
138154
return pycuda_to_af_array(pycu_arr.copy())
139155
except:
@@ -147,14 +163,18 @@ def pycuda_to_af_array(pycu_arr):
147163
from .opencl import get_context as _get_context
148164
AF_PYOPENCL_FOUND=True
149165

150-
def pyopencl_to_af_array(pycl_arr):
166+
def pyopencl_to_af_array(pycl_arr, copy=True):
151167
"""
152168
Convert pyopencl.gpuarray to arrayfire.Array
153169
154170
Parameters
155171
-----------
156172
pycl_arr : pyopencl.Array()
157173
174+
copy : Bool specifying if array is to be copied.
175+
Default is true.
176+
Can only be False if array is fortran contiguous.
177+
158178
Returns
159179
----------
160180
af_arr : arrayfire.Array()
@@ -179,44 +199,31 @@ def pyopencl_to_af_array(pycl_arr):
179199

180200
if (dev_idx == None or ctx_idx == None or
181201
dev_idx != dev or ctx_idx != ctx):
202+
print("Adding context and queue")
182203
_add_device_context(dev, ctx, que)
183204
_set_device_context(dev, ctx)
184205

206+
info()
185207
in_ptr = pycl_arr.base_data.int_ptr
186208
in_shape = pycl_arr.shape
187209
in_dtype = pycl_arr.dtype.char
188210

211+
if not copy and not pycl_arr.flags.f_contiguous:
212+
raise RuntimeError("Copy can only be False when arr.flags.f_contiguous is True")
213+
214+
print("Copying array")
215+
print(pycl_arr.base_data.int_ptr)
189216
if (pycl_arr.flags.f_contiguous):
190-
res = Array(in_ptr, in_shape, in_dtype, is_device=True)
191-
lock_array(res)
192-
return res
217+
return _fc_to_af_array(in_ptr, in_shape, in_dtype, True, copy)
193218
elif (pycl_arr.flags.c_contiguous):
194-
if pycl_arr.ndim == 1:
195-
return Array(in_ptr, in_shape, in_dtype, is_device=True)
196-
elif pycl_arr.ndim == 2:
197-
shape = (in_shape[1], in_shape[0])
198-
res = Array(in_ptr, shape, in_dtype, is_device=True)
199-
lock_array(res)
200-
return reorder(res, 1, 0)
201-
elif pycl_arr.ndim == 3:
202-
shape = (in_shape[2], in_shape[1], in_shape[0])
203-
res = Array(in_ptr, shape, in_dtype, is_device=True)
204-
lock_array(res)
205-
return reorder(res, 2, 1, 0)
206-
elif pycl_arr.ndim == 4:
207-
shape = (in_shape[3], in_shape[2], in_shape[1], in_shape[0])
208-
res = Array(in_ptr, shape, in_dtype, is_device=True)
209-
lock_array(res)
210-
return reorder(res, 3, 2, 1, 0)
211-
else:
212-
raise RuntimeError("Unsupported ndim")
219+
return _cc_to_af_array(in_ptr, pycl_arr.ndim, in_shape, in_dtype, True, copy)
213220
else:
214221
return pyopencl_to_af_array(pycl_arr.copy())
215222
except:
216223
AF_PYOPENCL_FOUND=False
217224

218225

219-
def to_array(in_array):
226+
def to_array(in_array, copy = True):
220227
"""
221228
Helper function to convert input from a different module to af.Array
222229
@@ -226,16 +233,19 @@ def to_array(in_array):
226233
in_array : array like object
227234
Can be one of numpy.ndarray, pycuda.GPUArray, pyopencl.Array, array.array, list
228235
236+
copy : Bool specifying if array is to be copied.
237+
Default is true.
238+
Can only be False if array is fortran contiguous.
239+
229240
Returns
230241
--------------
231242
af.Array of same dimensions as input after copying the data from the input
232243
233-
234244
"""
235245
if AF_NUMPY_FOUND and isinstance(in_array, NumpyArray):
236-
return np_to_af_array(in_array)
246+
return np_to_af_array(in_array, copy)
237247
if AF_PYCUDA_FOUND and isinstance(in_array, CudaArray):
238-
return pycuda_to_af_array(in_array)
248+
return pycuda_to_af_array(in_array, copy)
239249
if AF_PYOPENCL_FOUND and isinstance(in_array, OpenclArray):
240-
return pyopencl_to_af_array(in_array)
250+
return pyopencl_to_af_array(in_array, copy)
241251
return Array(src=in_array)

arrayfire/opencl.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def get_context(retain=False):
5454

5555
import ctypes as ct
5656
from .util import safe_call as safe_call
57-
from .library import backend as backend
57+
from .library import backend
5858

5959
if (backend.name() != "opencl"):
6060
raise RuntimeError("Invalid backend loaded")
@@ -80,7 +80,7 @@ def get_queue(retain):
8080

8181
import ctypes as ct
8282
from .util import safe_call as safe_call
83-
from .library import backend as backend
83+
from .library import backend
8484

8585
if (backend.name() != "opencl"):
8686
raise RuntimeError("Invalid backend loaded")
@@ -102,7 +102,7 @@ def get_device_id():
102102

103103
import ctypes as ct
104104
from .util import safe_call as safe_call
105-
from .library import backend as backend
105+
from .library import backend
106106

107107
if (backend.name() != "opencl"):
108108
raise RuntimeError("Invalid backend loaded")
@@ -124,7 +124,7 @@ def set_device_id(idx):
124124

125125
import ctypes as ct
126126
from .util import safe_call as safe_call
127-
from .library import backend as backend
127+
from .library import backend
128128

129129
if (backend.name() != "opencl"):
130130
raise RuntimeError("Invalid backend loaded")
@@ -146,6 +146,10 @@ def add_device_context(dev, ctx, que):
146146
que : cl_command_queue
147147
148148
"""
149+
import ctypes as ct
150+
from .util import safe_call as safe_call
151+
from .library import backend
152+
149153
if (backend.name() != "opencl"):
150154
raise RuntimeError("Invalid backend loaded")
151155

@@ -163,6 +167,10 @@ def set_device_context(dev, ctx):
163167
ctx : cl_context
164168
165169
"""
170+
import ctypes as ct
171+
from .util import safe_call as safe_call
172+
from .library import backend
173+
166174
if (backend.name() != "opencl"):
167175
raise RuntimeError("Invalid backend loaded")
168176

@@ -180,6 +188,10 @@ def delete_device_context(dev, ctx):
180188
ctx : cl_context
181189
182190
"""
191+
import ctypes as ct
192+
from .util import safe_call as safe_call
193+
from .library import backend
194+
183195
if (backend.name() != "opencl"):
184196
raise RuntimeError("Invalid backend loaded")
185197

@@ -204,6 +216,13 @@ def get_device_type():
204216
"""
205217
Get opencl device type
206218
"""
219+
import ctypes as ct
220+
from .util import safe_call as safe_call
221+
from .library import backend
222+
223+
if (backend.name() != "opencl"):
224+
raise RuntimeError("Invalid backend loaded")
225+
207226
res = c_int_t(DEVICE_TYPE.UNKNOWN.value)
208227
safe_call(backend.get().afcl_get_device_type(c_pointer(res)))
209228
return _to_device_type[res.value]
@@ -212,6 +231,13 @@ def get_platform():
212231
"""
213232
Get opencl platform
214233
"""
234+
import ctypes as ct
235+
from .util import safe_call as safe_call
236+
from .library import backend
237+
238+
if (backend.name() != "opencl"):
239+
raise RuntimeError("Invalid backend loaded")
240+
215241
res = c_int_t(PLATFORM.UNKNOWN.value)
216242
safe_call(backend.get().afcl_get_platform(c_pointer(res)))
217243
return _to_platform[res.value]

arrayfire/tests/simple/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from .device import *
1616
from .image import *
1717
from .index import *
18+
from .interop import *
1819
from .lapack import *
1920
from .signal import *
2021
from .statistics import *

0 commit comments

Comments
 (0)