Skip to content

Commit

Permalink
Merge branch 'devel' for 3.3 release
Browse files Browse the repository at this point in the history
  • Loading branch information
pavanky committed Mar 20, 2016
2 parents 764f0d9 + 9436ae7 commit 6c34821
Show file tree
Hide file tree
Showing 18 changed files with 704 additions and 77 deletions.
44 changes: 44 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,47 @@
### v3.3.20160320
- Feature parity with Arrayfire 3.3 libs
- Functions to interact with arryafire's internal data structures.
- `Array.offset`
- `Array.strides`
- `Array.is_owner`
- `Array.is_linear`
- `Array.raw_ptr`
- Array constructor now takes `offset` and `strides` as optional parameters.
- New visualization functions: `scatter` and `scatter3`
- OpenCL backend specific functions:
- `get_device_type`
- `get_platform`
- `add_device_context`
- `delete_device_context`
- `set_device_context`
- Functions to allocate and free memory on host and device
- `alloc_host` and `free_host`
- `alloc_pinned` and `free_pinned`
- `alloc_device` and `free_device`
- Function to query which device and backend an array was created on
- `get_device_id`
- `get_backend_id`
- Miscellaneous functions
- `is_lapack_available`
- `is_image_io_available`

- Interopability
- Transfer PyCUDA GPUArrays using `af.pycuda_to_af_array`
- Transfer PyOpenCL Arrays using `af.pyopencl_to_af_array`
- New helper function `af.to_array` added to convert a different `array` to arrayfire Array.
- This function can be used in place of `af.xyz_to_af_array` functions mentioned above.

- Deprecated functions list
- `lock_device_ptr` is deprecated. Use `lock_array` instead.
- `unlock_device_ptr` is deprecated. Use `unlock_array` instead.

- Bug Fixes:
- [Boolean indexing giving faulty results](https://github.com/arrayfire/arrayfire-python/issues/68) for multi dimensional arrays.
- [Enum types comparision failures](https://github.com/arrayfire/arrayfire-python/issues/65) in Python 2.x
- [Support loading SO versioned libraries](https://github.com/arrayfire/arrayfire-python/issues/64) in Linux and OSX.
- Fixed typo that prevented changing backend
- Fixed image processing functions that accepted floating point scalar paramters.
- Affected functions include: `translate`, `scale`, `skew`, `histogram`, `bilateral`, `mean_shift`.
### v3.2.20151224
- Bug fixes:
- A default `AF_PATH` is set if none is found as an environment variable.
Expand Down
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@

[ArrayFire](https://github.com/arrayfire/arrayfire) is a high performance library for parallel computing with an easy-to-use API. It enables users to write scientific computing code that is portable across CUDA, OpenCL and CPU devices. This project provides Python bindings for the ArrayFire library.

## Status
| OS | Tests |
|:-------:|:-------:|
| Linux | [![Build Status](http://ci.arrayfire.org/buildStatus/icon?job=arrayfire-wrappers/python-linux)](http://ci.arrayfire.org/view/All/job/arrayfire-wrappers/job/python-linux/) |
| Windows | [![Build Status](http://ci.arrayfire.org/buildStatus/icon?job=arrayfire-wrappers/python-windows)](http://ci.arrayfire.org/view/All/job/arrayfire-wrappers/job/python-windows/) |
| OSX | [![Build Status](http://ci.arrayfire.org/buildStatus/icon?job=arrayfire-wrappers/python-osx)](http://ci.arrayfire.org/view/All/job/arrayfire-wrappers/job/python-osx/) |

## Example

```
```python
import arrayfire as af

# Display backend information
Expand Down
5 changes: 5 additions & 0 deletions arrayfire/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@
"""

try:
import pycuda.autoinit
except:
pass

from .library import *
from .array import *
from .data import *
Expand Down
127 changes: 119 additions & 8 deletions arrayfire/array.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,27 @@ def _create_array(buf, numdims, idims, dtype, is_device):
numdims, ct.pointer(c_dims), dtype.value))
return out_arr

def _create_strided_array(buf, numdims, idims, dtype, is_device, offset, strides):
out_arr = ct.c_void_p(0)
c_dims = dim4(idims[0], idims[1], idims[2], idims[3])
if offset is None:
offset = 0
offset = ct.c_ulonglong(offset)
if strides is None:
strides = (1, idims[0], idims[0]*idims[1], idims[0]*idims[1]*idims[2])
while len(strides) < 4:
strides = strides + (strides[-1],)
strides = dim4(strides[0], strides[1], strides[2], strides[3])
if is_device:
location = Source.device
else:
location = Source.host
safe_call(backend.get().af_create_strided_array(ct.pointer(out_arr), ct.c_void_p(buf),
offset, numdims, ct.pointer(c_dims),
ct.pointer(strides), dtype.value,
location.value))
return out_arr

def _create_empty_array(numdims, idims, dtype):
out_arr = ct.c_void_p(0)
c_dims = dim4(idims[0], idims[1], idims[2], idims[3])
Expand Down Expand Up @@ -352,7 +373,7 @@ class Array(BaseArray):
"""

def __init__(self, src=None, dims=(0,), dtype=None, is_device=False):
def __init__(self, src=None, dims=(0,), dtype=None, is_device=False, offset=None, strides=None):

super(Array, self).__init__()

Expand Down Expand Up @@ -409,8 +430,10 @@ def __init__(self, src=None, dims=(0,), dtype=None, is_device=False):
if (type_char is not None and
type_char != _type_char):
raise TypeError("Can not create array of requested type from input data type")

self.arr = _create_array(buf, numdims, idims, to_dtype[_type_char], is_device)
if(offset is None and strides is None):
self.arr = _create_array(buf, numdims, idims, to_dtype[_type_char], is_device)
else:
self.arr = _create_strided_array(buf, numdims, idims, to_dtype[_type_char], is_device, offset, strides)

else:

Expand Down Expand Up @@ -454,6 +477,26 @@ def __del__(self):
backend.get().af_release_array(self.arr)

def device_ptr(self):
"""
Return the device pointer exclusively held by the array.
Returns
------
ptr : int
Contains location of the device pointer
Note
----
- This can be used to integrate with custom C code and / or PyCUDA or PyOpenCL.
- No other arrays will share the same device pointer.
- A copy of the memory is done if multiple arrays share the same memory or the array is not the owner of the memory.
- In case of a copy the return value points to the newly allocated memory which is now exclusively owned by the array.
"""
ptr = ct.c_void_p(0)
backend.get().af_get_device_ptr(ct.pointer(ptr), self.arr)
return ptr.value

def raw_ptr(self):
"""
Return the device pointer held by the array.
Expand All @@ -466,11 +509,45 @@ def device_ptr(self):
----
- This can be used to integrate with custom C code and / or PyCUDA or PyOpenCL.
- No mem copy is peformed, this function returns the raw device pointer.
- This pointer may be shared with other arrays. Use this function with caution.
- In particular the JIT compiler will not be aware of the shared arrays.
- This results in JITed operations not being immediately visible through the other array.
"""
ptr = ct.c_void_p(0)
backend.get().af_get_device_ptr(ct.pointer(ptr), self.arr)
backend.get().af_get_raw_ptr(ct.pointer(ptr), self.arr)
return ptr.value

def offset(self):
"""
Return the offset, of the first element relative to the raw pointer.
Returns
------
offset : int
The offset in number of elements
"""
offset = ct.c_longlong(0)
safe_call(backend.get().af_get_offset(ct.pointer(offset), self.arr))
return offset.value

def strides(self):
"""
Return the distance in bytes between consecutive elements for each dimension.
Returns
------
strides : tuple
The strides for each dimension
"""
s0 = ct.c_longlong(0)
s1 = ct.c_longlong(0)
s2 = ct.c_longlong(0)
s3 = ct.c_longlong(0)
safe_call(backend.get().af_get_strides(ct.pointer(s0), ct.pointer(s1),
ct.pointer(s2), ct.pointer(s3), self.arr))
strides = (s0.value,s1.value,s2.value,s3.value)
return strides[:self.numdims()]

def elements(self):
"""
Return the number of elements in the array.
Expand Down Expand Up @@ -622,6 +699,22 @@ def is_bool(self):
safe_call(backend.get().af_is_bool(ct.pointer(res), self.arr))
return res.value

def is_linear(self):
"""
Check if all elements of the array are contiguous.
"""
res = ct.c_bool(False)
safe_call(backend.get().af_is_linear(ct.pointer(res), self.arr))
return res.value

def is_owner(self):
"""
Check if the array owns the raw pointer or is a derived array.
"""
res = ct.c_bool(False)
safe_call(backend.get().af_is_owner(ct.pointer(res), self.arr))
return res.value

def __add__(self, other):
"""
Return self + other.
Expand Down Expand Up @@ -892,6 +985,12 @@ def __getitem__(self, key):
try:
out = Array()
n_dims = self.numdims()

if (isinstance(key, Array) and key.type() == Dtype.b8.value):
n_dims = 1
if (count(key) == 0):
return out

inds = _get_indices(key)

safe_call(backend.get().af_index_gen(ct.pointer(out.arr),
Expand All @@ -912,9 +1011,21 @@ def __setitem__(self, key, val):
try:
n_dims = self.numdims()

is_boolean_idx = isinstance(key, Array) and key.type() == Dtype.b8.value

if (is_boolean_idx):
n_dims = 1
num = count(key)
if (num == 0):
return

if (_is_number(val)):
tdims = _get_assign_dims(key, self.dims())
other_arr = constant_array(val, tdims[0], tdims[1], tdims[2], tdims[3], self.type())
if (is_boolean_idx):
n_dims = 1
other_arr = constant_array(val, int(num), dtype=self.type())
else:
other_arr = constant_array(val, tdims[0] , tdims[1], tdims[2], tdims[3], self.type())
del_other = True
else:
other_arr = val.arr
Expand All @@ -924,8 +1035,8 @@ def __setitem__(self, key, val):
inds = _get_indices(key)

safe_call(backend.get().af_assign_gen(ct.pointer(out_arr),
self.arr, ct.c_longlong(n_dims), inds.pointer,
other_arr))
self.arr, ct.c_longlong(n_dims), inds.pointer,
other_arr))
safe_call(backend.get().af_release_array(self.arr))
if del_other:
safe_call(backend.get().af_release_array(other_arr))
Expand Down Expand Up @@ -1142,5 +1253,5 @@ def read_array(filename, index=None, key=None):

return out

from .algorithm import sum
from .algorithm import (sum, count)
from .arith import cast
68 changes: 65 additions & 3 deletions arrayfire/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,14 @@ def get_device_ptr(a):
return ptr

def lock_device_ptr(a):
"""
This functions is deprecated. Please use lock_array instead.
"""
import warnings
warnings.warn("This function is deprecated. Use lock_array instead.", DeprecationWarning)
lock_array(a)

def lock_array(a):
"""
Ask arrayfire to not perform garbage collection on raw data held by an array.
Expand All @@ -252,10 +260,17 @@ def lock_device_ptr(a):
-----
- The device pointer of `a` is not freed by memory manager until `unlock_device_ptr()` is called.
"""
ptr = ct.c_void_p(0)
safe_call(backend.get().af_lock_device_ptr(a.arr))
safe_call(backend.get().af_lock_array(a.arr))

def unlock_device_ptr(a):
"""
This functions is deprecated. Please use unlock_array instead.
"""
import warnings
warnings.warn("This function is deprecated. Use unlock_array instead.", DeprecationWarning)
unlock_array(a)

def unlock_array(a):
"""
Tell arrayfire to resume garbage collection on raw data held by an array.
Expand All @@ -264,8 +279,55 @@ def unlock_device_ptr(a):
a: af.Array
- A multi dimensional arrayfire array.
"""
safe_call(backend.get().af_unlock_array(a.arr))

def alloc_device(num_bytes):
"""
Allocate a buffer on the device with specified number of bytes.
"""
ptr = ct.c_void_p(0)
c_num_bytes = ct.c_longlong(num_bytes)
safe_call(backend.get().af_alloc_device(ct.pointer(ptr), c_num_bytes))
return ptr.value

def alloc_host(num_bytes):
"""
Allocate a buffer on the host with specified number of bytes.
"""
ptr = ct.c_void_p(0)
safe_call(backend.get().af_unlock_device_ptr(a.arr))
c_num_bytes = ct.c_longlong(num_bytes)
safe_call(backend.get().af_alloc_host(ct.pointer(ptr), c_num_bytes))
return ptr.value

def alloc_pinned(num_bytes):
"""
Allocate a buffer on the host using pinned memory with specified number of bytes.
"""
ptr = ct.c_void_p(0)
c_num_bytes = ct.c_longlong(num_bytes)
safe_call(backend.get().af_alloc_pinned(ct.pointer(ptr), c_num_bytes))
return ptr.value

def free_device(ptr):
"""
Free the device memory allocated by alloc_device
"""
cptr = ct.c_void_p(ptr)
safe_call(backend.get().af_free_device(cptr))

def free_host(ptr):
"""
Free the host memory allocated by alloc_host
"""
cptr = ct.c_void_p(ptr)
safe_call(backend.get().af_free_host(cptr))

def free_pinned(ptr):
"""
Free the pinned memory allocated by alloc_pinned
"""
cptr = ct.c_void_p(ptr)
safe_call(backend.get().af_free_pinned(cptr))

from .array import Array
Loading

0 comments on commit 6c34821

Please sign in to comment.