Skip to content

Commit 32196a0

Browse files
committedFeb 22, 2017
Merge branch 'master' into devel
·
3.8.03.4.20170222
2 parents ced607b + 3cf70d8 commit 32196a0

File tree

4 files changed

+308
-93
lines changed

4 files changed

+308
-93
lines changed
 

‎arrayfire/interop.py

Lines changed: 150 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -10,44 +10,88 @@
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
18+
4. numba - numba.cuda.cudadrv.devicearray.DeviceNDArray
1419
15-
1. numpy
16-
2. pycuda
17-
3. pyopencl
1820
"""
1921

2022
from .array import *
2123
from .device import *
2224

25+
26+
def _fc_to_af_array(in_ptr, in_shape, in_dtype, is_device=False, copy = True):
27+
"""
28+
Fortran Contiguous to af array
29+
"""
30+
res = Array(in_ptr, in_shape, in_dtype, is_device=is_device)
31+
32+
if is_device:
33+
lock_array(res)
34+
pass
35+
36+
return res.copy() if copy else res
37+
38+
def _cc_to_af_array(in_ptr, ndim, in_shape, in_dtype, is_device=False, copy = True):
39+
"""
40+
C Contiguous to af array
41+
"""
42+
if ndim == 1:
43+
return _fc_to_af_array(in_ptr, in_shape, in_dtype, is_device, copy)
44+
elif ndim == 2:
45+
shape = (in_shape[1], in_shape[0])
46+
res = Array(in_ptr, shape, in_dtype, is_device=is_device)
47+
if is_device: lock_array(res)
48+
return reorder(res, 1, 0)
49+
elif ndim == 3:
50+
shape = (in_shape[2], in_shape[1], in_shape[0])
51+
res = Array(in_ptr, shape, in_dtype, is_device=is_device)
52+
if is_device: lock_array(res)
53+
return reorder(res, 2, 1, 0)
54+
elif ndim == 4:
55+
shape = (in_shape[3], in_shape[2], in_shape[1], in_shape[0])
56+
res = Array(in_ptr, shape, in_dtype, is_device=is_device)
57+
if is_device: lock_array(res)
58+
return reorder(res, 3, 2, 1, 0)
59+
else:
60+
raise RuntimeError("Unsupported ndim")
61+
62+
63+
_nptype_to_aftype = {'b1' : Dtype.b8,
64+
'u1' : Dtype.u8,
65+
'u2' : Dtype.u16,
66+
'i2' : Dtype.s16,
67+
's4' : Dtype.u32,
68+
'i4' : Dtype.s32,
69+
'f4' : Dtype.f32,
70+
'c8' : Dtype.c32,
71+
's8' : Dtype.u64,
72+
'i8' : Dtype.s64,
73+
'f8' : Dtype.f64,
74+
'c16' : Dtype.c64}
75+
2376
try:
2477
import numpy as np
2578
from numpy import ndarray as NumpyArray
2679
from .data import reorder
2780

2881
AF_NUMPY_FOUND=True
2982

30-
_nptype_to_aftype = {'b1' : Dtype.b8,
31-
'u1' : Dtype.u8,
32-
'u2' : Dtype.u16,
33-
'i2' : Dtype.s16,
34-
's4' : Dtype.u32,
35-
'i4' : Dtype.s32,
36-
'f4' : Dtype.f32,
37-
'c8' : Dtype.c32,
38-
's8' : Dtype.u64,
39-
'i8' : Dtype.s64,
40-
'f8' : Dtype.f64,
41-
'c16' : Dtype.c64}
42-
43-
def np_to_af_array(np_arr):
83+
def np_to_af_array(np_arr, copy=True):
4484
"""
4585
Convert numpy.ndarray to arrayfire.Array.
4686
4787
Parameters
4888
----------
4989
np_arr : numpy.ndarray()
5090
91+
copy : Bool specifying if array is to be copied.
92+
Default is true.
93+
Can only be False if array is fortran contiguous.
94+
5195
Returns
5296
---------
5397
af_arr : arrayfire.Array()
@@ -57,27 +101,15 @@ def np_to_af_array(np_arr):
57101
in_ptr = np_arr.ctypes.data_as(c_void_ptr_t)
58102
in_dtype = _nptype_to_aftype[np_arr.dtype.str[1:]]
59103

104+
if not copy:
105+
raise RuntimeError("Copy can not be False for numpy arrays")
106+
60107
if (np_arr.flags['F_CONTIGUOUS']):
61-
return Array(in_ptr, in_shape, in_dtype)
108+
return _fc_to_af_array(in_ptr, in_shape, in_dtype)
62109
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")
110+
return _cc_to_af_array(in_ptr, np_arr.ndim, in_shape, in_dtype)
79111
else:
80-
return np_to_af_array(np.asfortranarray(np_arr))
112+
return np_to_af_array(np_arr.copy())
81113

82114
from_ndarray = np_to_af_array
83115
except:
@@ -88,14 +120,18 @@ def np_to_af_array(np_arr):
88120
from pycuda.gpuarray import GPUArray as CudaArray
89121
AF_PYCUDA_FOUND=True
90122

91-
def pycuda_to_af_array(pycu_arr):
123+
def pycuda_to_af_array(pycu_arr, copy=True):
92124
"""
93125
Convert pycuda.gpuarray to arrayfire.Array
94126
95127
Parameters
96128
-----------
97129
pycu_arr : pycuda.GPUArray()
98130
131+
copy : Bool specifying if array is to be copied.
132+
Default is true.
133+
Can only be False if array is fortran contiguous.
134+
99135
Returns
100136
----------
101137
af_arr : arrayfire.Array()
@@ -109,31 +145,13 @@ def pycuda_to_af_array(pycu_arr):
109145
in_shape = pycu_arr.shape
110146
in_dtype = pycu_arr.dtype.char
111147

148+
if not copy and not pycu_arr.flags.f_contiguous:
149+
raise RuntimeError("Copy can only be False when arr.flags.f_contiguous is True")
150+
112151
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
152+
return _fc_to_af_array(in_ptr, in_shape, in_dtype, True, copy)
117153
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")
154+
return _cc_to_af_array(in_ptr, pycu_arr.ndim, in_shape, in_dtype, True, copy)
137155
else:
138156
return pycuda_to_af_array(pycu_arr.copy())
139157
except:
@@ -147,14 +165,18 @@ def pycuda_to_af_array(pycu_arr):
147165
from .opencl import get_context as _get_context
148166
AF_PYOPENCL_FOUND=True
149167

150-
def pyopencl_to_af_array(pycl_arr):
168+
def pyopencl_to_af_array(pycl_arr, copy=True):
151169
"""
152170
Convert pyopencl.gpuarray to arrayfire.Array
153171
154172
Parameters
155173
-----------
156174
pycl_arr : pyopencl.Array()
157175
176+
copy : Bool specifying if array is to be copied.
177+
Default is true.
178+
Can only be False if array is fortran contiguous.
179+
158180
Returns
159181
----------
160182
af_arr : arrayfire.Array()
@@ -179,63 +201,102 @@ def pyopencl_to_af_array(pycl_arr):
179201

180202
if (dev_idx == None or ctx_idx == None or
181203
dev_idx != dev or ctx_idx != ctx):
204+
print("Adding context and queue")
182205
_add_device_context(dev, ctx, que)
183206
_set_device_context(dev, ctx)
184207

208+
info()
185209
in_ptr = pycl_arr.base_data.int_ptr
186210
in_shape = pycl_arr.shape
187211
in_dtype = pycl_arr.dtype.char
188212

213+
if not copy and not pycl_arr.flags.f_contiguous:
214+
raise RuntimeError("Copy can only be False when arr.flags.f_contiguous is True")
215+
216+
print("Copying array")
217+
print(pycl_arr.base_data.int_ptr)
189218
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
219+
return _fc_to_af_array(in_ptr, in_shape, in_dtype, True, copy)
193220
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")
221+
return _cc_to_af_array(in_ptr, pycl_arr.ndim, in_shape, in_dtype, True, copy)
213222
else:
214223
return pyopencl_to_af_array(pycl_arr.copy())
215224
except:
216225
AF_PYOPENCL_FOUND=False
217226

227+
try:
228+
import numba
229+
from numba import cuda
230+
NumbaCudaArray = cuda.cudadrv.devicearray.DeviceNDArray
231+
AF_NUMBA_FOUND=True
232+
233+
def numba_to_af_array(nb_arr, copy=True):
234+
"""
235+
Convert numba.gpuarray to arrayfire.Array
236+
237+
Parameters
238+
-----------
239+
nb_arr : numba.cuda.cudadrv.devicearray.DeviceNDArray()
240+
241+
copy : Bool specifying if array is to be copied.
242+
Default is true.
243+
Can only be False if array is fortran contiguous.
244+
245+
Returns
246+
----------
247+
af_arr : arrayfire.Array()
218248
219-
def to_array(in_array):
249+
Note
250+
----------
251+
The input array is copied to af.Array
252+
"""
253+
254+
in_ptr = nb_arr.device_ctypes_pointer.value
255+
in_shape = nb_arr.shape
256+
in_dtype = _nptype_to_aftype[nb_arr.dtype.str[1:]]
257+
258+
if not copy and not nb_arr.flags.f_contiguous:
259+
raise RuntimeError("Copy can only be False when arr.flags.f_contiguous is True")
260+
261+
if (nb_arr.is_f_contiguous()):
262+
return _fc_to_af_array(in_ptr, in_shape, in_dtype, True, copy)
263+
elif (nb_arr.is_c_contiguous()):
264+
return _cc_to_af_array(in_ptr, nb_arr.ndim, in_shape, in_dtype, True, copy)
265+
else:
266+
return numba_to_af_array(nb_arr.copy())
267+
except:
268+
AF_NUMBA_FOUND=False
269+
270+
def to_array(in_array, copy = True):
220271
"""
221272
Helper function to convert input from a different module to af.Array
222273
223274
Parameters
224275
-------------
225276
226277
in_array : array like object
227-
Can be one of numpy.ndarray, pycuda.GPUArray, pyopencl.Array, array.array, list
278+
Can be one of the following:
279+
- numpy.ndarray
280+
- pycuda.GPUArray
281+
- pyopencl.Array
282+
- numba.cuda.cudadrv.devicearray.DeviceNDArray
283+
- array.array
284+
- list
285+
copy : Bool specifying if array is to be copied.
286+
Default is true.
287+
Can only be False if array is fortran contiguous.
228288
229289
Returns
230290
--------------
231291
af.Array of same dimensions as input after copying the data from the input
232292
233-
234293
"""
235294
if AF_NUMPY_FOUND and isinstance(in_array, NumpyArray):
236-
return np_to_af_array(in_array)
295+
return np_to_af_array(in_array, copy)
237296
if AF_PYCUDA_FOUND and isinstance(in_array, CudaArray):
238-
return pycuda_to_af_array(in_array)
297+
return pycuda_to_af_array(in_array, copy)
239298
if AF_PYOPENCL_FOUND and isinstance(in_array, OpenclArray):
240-
return pyopencl_to_af_array(in_array)
299+
return pyopencl_to_af_array(in_array, copy)
300+
if AF_NUMBA_FOUND and isinstance(in_array, NumbaCudaArray):
301+
return numba_to_af_array(in_array, copy)
241302
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 *

‎arrayfire/tests/simple/interop.py

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
#!/usr/bin/python
2+
#######################################################
3+
# Copyright (c) 2015, ArrayFire
4+
# All rights reserved.
5+
#
6+
# This file is distributed under 3-clause BSD license.
7+
# The complete license agreement can be obtained at:
8+
# http://arrayfire.com/licenses/BSD-3-Clause
9+
########################################################
10+
11+
import arrayfire as af
12+
from . import _util
13+
14+
def simple_interop(verbose = False):
15+
if af.AF_NUMPY_FOUND:
16+
import numpy as np
17+
n = np.random.random((5,))
18+
a = af.to_array(n)
19+
n2 = np.array(a)
20+
assert((n==n2).all())
21+
22+
n = np.random.random((5,3))
23+
a = af.to_array(n)
24+
n2 = np.array(a)
25+
assert((n==n2).all())
26+
27+
n = np.random.random((5,3,2))
28+
a = af.to_array(n)
29+
n2 = np.array(a)
30+
assert((n==n2).all())
31+
32+
n = np.random.random((5,3,2, 2))
33+
a = af.to_array(n)
34+
n2 = np.array(a)
35+
assert((n==n2).all())
36+
37+
if af.AF_PYCUDA_FOUND and af.get_active_backend() == 'cuda':
38+
import pycuda.autoinit
39+
import pycuda.gpuarray as cudaArray
40+
n = np.random.random((5,))
41+
c = cudaArray.to_gpu(n)
42+
a = af.to_array(c)
43+
n2 = np.array(a)
44+
assert((n==n2).all())
45+
46+
n = np.random.random((5,3))
47+
c = cudaArray.to_gpu(n)
48+
a = af.to_array(c)
49+
n2 = np.array(a)
50+
assert((n==n2).all())
51+
52+
n = np.random.random((5,3,2))
53+
c = cudaArray.to_gpu(n)
54+
a = af.to_array(c)
55+
n2 = np.array(a)
56+
assert((n==n2).all())
57+
58+
n = np.random.random((5,3,2,2))
59+
c = cudaArray.to_gpu(n)
60+
a = af.to_array(c)
61+
n2 = np.array(a)
62+
assert((n==n2).all())
63+
64+
if af.AF_PYOPENCL_FOUND and af.backend.name() == 'opencl':
65+
# This needs fixing upstream
66+
# https://github.com/arrayfire/arrayfire/issues/1728
67+
68+
# import pyopencl as cl
69+
# import pyopencl.array as clArray
70+
# ctx = cl.create_some_context()
71+
# queue = cl.CommandQueue(ctx)
72+
73+
# n = np.random.random((5,))
74+
# c = cl.array.to_device(queue, n)
75+
# a = af.to_array(c)
76+
# n2 = np.array(a)
77+
# assert((n==n2).all())
78+
79+
# n = np.random.random((5,3))
80+
# c = cl.array.to_device(queue, n)
81+
# a = af.to_array(c)
82+
# n2 = np.array(a)
83+
# assert((n==n2).all())
84+
85+
# n = np.random.random((5,3,2))
86+
# c = cl.array.to_device(queue, n)
87+
# a = af.to_array(c)
88+
# n2 = np.array(a)
89+
# assert((n==n2).all())
90+
91+
# n = np.random.random((5,3,2,2))
92+
# c = cl.array.to_device(queue, n)
93+
# a = af.to_array(c)
94+
# n2 = np.array(a)
95+
# assert((n==n2).all())
96+
pass
97+
98+
if af.AF_NUMBA_FOUND and af.get_active_backend() == 'cuda':
99+
100+
import numba
101+
from numba import cuda
102+
103+
n = np.random.random((5,))
104+
c = cuda.to_device(n)
105+
a = af.to_array(c)
106+
n2 = np.array(a)
107+
assert((n==n2).all())
108+
109+
n = np.random.random((5,3))
110+
c = cuda.to_device(n)
111+
a = af.to_array(c)
112+
n2 = np.array(a)
113+
assert((n==n2).all())
114+
115+
n = np.random.random((5,3,2))
116+
c = cuda.to_device(n)
117+
a = af.to_array(c)
118+
n2 = np.array(a)
119+
assert((n==n2).all())
120+
121+
n = np.random.random((5,3,2,2))
122+
c = cuda.to_device(n)
123+
a = af.to_array(c)
124+
n2 = np.array(a)
125+
assert((n==n2).all())
126+
127+
_util.tests['interop'] = simple_interop

0 commit comments

Comments
 (0)
Please sign in to comment.