+#######################################################
+# Copyright (c) 2015, ArrayFire
+# All rights reserved.
+#
+# This file is distributed under 3-clause BSD license.
+# The complete license agreement can be obtained at:
+# http://arrayfire.com/licenses/BSD-3-Clause
+########################################################
+
+"""
+Array class and helper functions.
+"""
+
+import inspect
+import os
+from .library import *
+from .util import *
+from .util import _is_number
+from .bcast import _bcast_var
+from .base import *
+from .index import *
+from .index import _Index4
+
+_is_running_in_py_charm = "PYCHARM_HOSTED" in os.environ
+
+_display_dims_limit = None
+
+[docs]def set_display_dims_limit(*dims):
+
"""
+
Sets the dimension limit after which array's data won't get
+
presented to the result of str(arr).
+
+
Default is None, which means there is no limit.
+
+
Parameters
+
----------
+
*dims : dimension limit args
+
+
Example
+
-------
+
set_display_dims_limit(10, 10, 10, 10)
+
+
"""
+
global _display_dims_limit
+
_display_dims_limit = dims
+
+[docs]def get_display_dims_limit():
+
"""
+
Gets the dimension limit after which array's data won't get
+
presented to the result of str(arr).
+
+
Default is None, which means there is no limit.
+
+
Returns
+
-----------
+
- tuple of the current limit
+
- None is there is no limit
+
+
Example
+
-------
+
get_display_dims_limit()
+
# None
+
set_display_dims_limit(10, 10, 10, 10)
+
get_display_dims_limit()
+
# (10, 10, 10, 10)
+
+
"""
+
return _display_dims_limit
+
+def _in_display_dims_limit(dims):
+ if _is_running_in_py_charm:
+ return False
+ if _display_dims_limit is not None:
+ limit_len = len(_display_dims_limit)
+ dim_len = len(dims)
+ if dim_len > limit_len:
+ return False
+ for i in range(dim_len):
+ if dims[i] > _display_dims_limit[i]:
+ return False
+ return True
+
+def _create_array(buf, numdims, idims, dtype, is_device):
+ out_arr = c_void_ptr_t(0)
+ c_dims = dim4(idims[0], idims[1], idims[2], idims[3])
+ if (not is_device):
+ safe_call(backend.get().af_create_array(c_pointer(out_arr), c_void_ptr_t(buf),
+ numdims, c_pointer(c_dims), dtype.value))
+ else:
+ safe_call(backend.get().af_device_array(c_pointer(out_arr), c_void_ptr_t(buf),
+ numdims, c_pointer(c_dims), dtype.value))
+ return out_arr
+
+def _create_strided_array(buf, numdims, idims, dtype, is_device, offset, strides):
+ out_arr = c_void_ptr_t(0)
+ c_dims = dim4(idims[0], idims[1], idims[2], idims[3])
+ if offset is None:
+ offset = 0
+ offset = c_dim_t(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(c_pointer(out_arr), c_void_ptr_t(buf),
+ offset, numdims, c_pointer(c_dims),
+ c_pointer(strides), dtype.value,
+ location.value))
+ return out_arr
+
+def _create_empty_array(numdims, idims, dtype):
+ out_arr = c_void_ptr_t(0)
+
+ if numdims == 0: return out_arr
+
+ c_dims = dim4(idims[0], idims[1], idims[2], idims[3])
+ safe_call(backend.get().af_create_handle(c_pointer(out_arr),
+ numdims, c_pointer(c_dims), dtype.value))
+ return out_arr
+
+[docs]def constant_array(val, d0, d1=None, d2=None, d3=None, dtype=Dtype.f32):
+
"""
+
Internal function to create a C array. Should not be used externall.
+
"""
+
+
if not isinstance(dtype, c_int_t):
+
if isinstance(dtype, int):
+
dtype = c_int_t(dtype)
+
elif isinstance(dtype, Dtype):
+
dtype = c_int_t(dtype.value)
+
else:
+
raise TypeError("Invalid dtype")
+
+
out = c_void_ptr_t(0)
+
dims = dim4(d0, d1, d2, d3)
+
+
if isinstance(val, complex):
+
c_real = c_double_t(val.real)
+
c_imag = c_double_t(val.imag)
+
+
if (dtype.value != Dtype.c32.value and dtype.value != Dtype.c64.value):
+
dtype = Dtype.c32.value
+
+
safe_call(backend.get().af_constant_complex(c_pointer(out), c_real, c_imag,
+
4, c_pointer(dims), dtype))
+
elif dtype.value == Dtype.s64.value:
+
c_val = c_longlong_t(val.real)
+
safe_call(backend.get().af_constant_long(c_pointer(out), c_val, 4, c_pointer(dims)))
+
elif dtype.value == Dtype.u64.value:
+
c_val = c_ulonglong_t(val.real)
+
safe_call(backend.get().af_constant_ulong(c_pointer(out), c_val, 4, c_pointer(dims)))
+
else:
+
c_val = c_double_t(val)
+
safe_call(backend.get().af_constant(c_pointer(out), c_val, 4, c_pointer(dims), dtype))
+
+
return out
+
+
+def _binary_func(lhs, rhs, c_func):
+ out = Array()
+ other = rhs
+
+ if (_is_number(rhs)):
+ ldims = dim4_to_tuple(lhs.dims())
+ rty = implicit_dtype(rhs, lhs.type())
+ other = Array()
+ other.arr = constant_array(rhs, ldims[0], ldims[1], ldims[2], ldims[3], rty.value)
+ elif not isinstance(rhs, Array):
+ raise TypeError("Invalid parameter to binary function")
+
+ safe_call(c_func(c_pointer(out.arr), lhs.arr, other.arr, _bcast_var.get()))
+
+ return out
+
+def _binary_funcr(lhs, rhs, c_func):
+ out = Array()
+ other = lhs
+
+ if (_is_number(lhs)):
+ rdims = dim4_to_tuple(rhs.dims())
+ lty = implicit_dtype(lhs, rhs.type())
+ other = Array()
+ other.arr = constant_array(lhs, rdims[0], rdims[1], rdims[2], rdims[3], lty.value)
+ elif not isinstance(lhs, Array):
+ raise TypeError("Invalid parameter to binary function")
+
+ c_func(c_pointer(out.arr), other.arr, rhs.arr, _bcast_var.get())
+
+ return out
+
+def _ctype_to_lists(ctype_arr, dim, shape, offset=0):
+ if (dim == 0):
+ return list(ctype_arr[offset : offset + shape[0]])
+ else:
+ dim_len = shape[dim]
+ res = [[]] * dim_len
+ for n in range(dim_len):
+ res[n] = _ctype_to_lists(ctype_arr, dim - 1, shape, offset)
+ offset += shape[0]
+ return res
+
+def _slice_to_length(key, dim):
+ tkey = [key.start, key.stop, key.step]
+
+ if tkey[0] is None:
+ tkey[0] = 0
+ elif tkey[0] < 0:
+ tkey[0] = dim - tkey[0]
+
+ if tkey[1] is None:
+ tkey[1] = dim
+ elif tkey[1] < 0:
+ tkey[1] = dim - tkey[1]
+
+ if tkey[2] is None:
+ tkey[2] = 1
+
+ return int(((tkey[1] - tkey[0] - 1) / tkey[2]) + 1)
+
+def _get_info(dims, buf_len):
+ elements = 1
+ numdims = 0
+ if dims:
+ numdims = len(dims)
+ idims = [1]*4
+ for i in range(numdims):
+ elements *= dims[i]
+ idims[i] = dims[i]
+ elif (buf_len != 0):
+ idims = [buf_len, 1, 1, 1]
+ numdims = 1
+ else:
+ raise RuntimeError("Invalid size")
+
+ return numdims, idims
+
+
+def _get_indices(key):
+ inds = _Index4()
+ if isinstance(key, tuple):
+ n_idx = len(key)
+ for n in range(n_idx):
+ inds[n] = Index(key[n])
+ else:
+ inds[0] = Index(key)
+
+ return inds
+
+def _get_assign_dims(key, idims):
+
+ dims = [1]*4
+
+ for n in range(len(idims)):
+ dims[n] = idims[n]
+
+ if _is_number(key):
+ dims[0] = 1
+ return dims
+ elif isinstance(key, slice):
+ dims[0] = _slice_to_length(key, idims[0])
+ return dims
+ elif isinstance(key, ParallelRange):
+ dims[0] = _slice_to_length(key.S, idims[0])
+ return dims
+ elif isinstance(key, BaseArray):
+ # If the array is boolean take only the number of nonzeros
+ if(key.dtype() is Dtype.b8):
+ dims[0] = int(sum(key))
+ else:
+ dims[0] = key.elements()
+ return dims
+ elif isinstance(key, tuple):
+ n_inds = len(key)
+
+ for n in range(n_inds):
+ if (_is_number(key[n])):
+ dims[n] = 1
+ elif (isinstance(key[n], BaseArray)):
+ # If the array is boolean take only the number of nonzeros
+ if(key[n].dtype() is Dtype.b8):
+ dims[n] = int(sum(key[n]))
+ else:
+ dims[n] = key[n].elements()
+ elif (isinstance(key[n], slice)):
+ dims[n] = _slice_to_length(key[n], idims[n])
+ elif (isinstance(key[n], ParallelRange)):
+ dims[n] = _slice_to_length(key[n].S, idims[n])
+ else:
+ raise IndexError("Invalid type while assigning to arrayfire.array")
+
+ return dims
+ else:
+ raise IndexError("Invalid type while assigning to arrayfire.array")
+
+[docs]def transpose(a, conj=False):
+
"""
+
Perform the transpose on an input.
+
+
Parameters
+
-----------
+
a : af.Array
+
Multi dimensional arrayfire array.
+
+
conj : optional: bool. default: False.
+
Flag to specify if a complex conjugate needs to applied for complex inputs.
+
+
Returns
+
--------
+
out : af.Array
+
Containing the tranpose of `a` for all batches.
+
+
"""
+
out = Array()
+
safe_call(backend.get().af_transpose(c_pointer(out.arr), a.arr, conj))
+
return out
+
+[docs]def transpose_inplace(a, conj=False):
+
"""
+
Perform inplace transpose on an input.
+
+
Parameters
+
-----------
+
a : af.Array
+
- Multi dimensional arrayfire array.
+
- Contains transposed values on exit.
+
+
conj : optional: bool. default: False.
+
Flag to specify if a complex conjugate needs to applied for complex inputs.
+
+
Note
+
-------
+
Input `a` needs to be a square matrix or a batch of square matrices.
+
+
"""
+
safe_call(backend.get().af_transpose_inplace(a.arr, conj))
+
+[docs]class Array(BaseArray):
+
+
"""
+
A multi dimensional array container.
+
+
Parameters
+
----------
+
src : optional: array.array, list or C buffer. default: None.
+
- When `src` is `array.array` or `list`, the data is copied to create the Array()
+
- When `src` is None, an empty buffer is created.
+
+
dims : optional: tuple of ints. default: (0,)
+
- When using the default values of `dims`, the dims are caclulated as `len(src)`
+
+
dtype: optional: str or arrayfire.Dtype. default: None.
+
- if str, must be one of the following:
+
- 'f' for float
+
- 'd' for double
+
- 'b' for bool
+
- 'B' for unsigned char
+
- 'h' for signed 16 bit integer
+
- 'H' for unsigned 16 bit integer
+
- 'i' for signed 32 bit integer
+
- 'I' for unsigned 32 bit integer
+
- 'l' for signed 64 bit integer
+
- 'L' for unsigned 64 bit integer
+
- 'F' for 32 bit complex number
+
- 'D' for 64 bit complex number
+
+
- if arrayfire.Dtype, must be one of the following:
+
- Dtype.f32 for float
+
- Dtype.f64 for double
+
- Dtype.b8 for bool
+
- Dtype.u8 for unsigned char
+
- Dtype.s16 for signed 16 bit integer
+
- Dtype.u16 for unsigned 16 bit integer
+
- Dtype.s32 for signed 32 bit integer
+
- Dtype.u32 for unsigned 32 bit integer
+
- Dtype.s64 for signed 64 bit integer
+
- Dtype.u64 for unsigned 64 bit integer
+
- Dtype.c32 for 32 bit complex number
+
- Dtype.c64 for 64 bit complex number
+
+
- if None, Dtype.f32 is assumed
+
+
Attributes
+
-----------
+
arr: ctypes.c_void_p
+
ctypes variable containing af_array from arrayfire library.
+
+
Examples
+
--------
+
+
Creating an af.Array() from array.array()
+
+
>>> import arrayfire as af
+
>>> import array
+
>>> a = array.array('f', (1, 2, 3, 4))
+
>>> b = af.Array(a, (2,2))
+
>>> af.display(b)
+
[2 2 1 1]
+
1.0000 3.0000
+
2.0000 4.0000
+
+
Creating an af.Array() from a list
+
+
>>> import arrayfire as af
+
>>> import array
+
>>> a = [1, 2, 3, 4]
+
>>> b = af.Array(a)
+
>>> af.display(b)
+
[4 1 1 1]
+
1.0000
+
2.0000
+
3.0000
+
4.0000
+
+
Creating an af.Array() from numpy.array()
+
+
>>> import numpy as np
+
>>> import arrayfire as af
+
>>> a = np.random.random((2,2))
+
>>> a
+
array([[ 0.33042524, 0.36135449],
+
[ 0.86748649, 0.42199135]])
+
>>> b = af.Array(a.ctypes.data, a.shape, a.dtype.char)
+
>>> af.display(b)
+
[2 2 1 1]
+
0.3304 0.8675
+
0.3614 0.4220
+
+
Note
+
-----
+
- The class is currently limited to 4 dimensions.
+
- arrayfire.Array() uses column major format.
+
- numpy uses row major format by default which can cause issues during conversion
+
+
"""
+
+
# Numpy checks this attribute to know which class handles binary builtin operations, such as __add__.
+
# Setting to such a high value should make sure that arrayfire has priority over
+
# other classes, ensuring that e.g. numpy.float32(1)*arrayfire.randu(3) is handled by
+
# arrayfire's __radd__() instead of numpy's __add__()
+
__array_priority__ = 30
+
+
def __init__(self, src=None, dims=None, dtype=None, is_device=False, offset=None, strides=None):
+
+
super(Array, self).__init__()
+
+
buf=None
+
buf_len=0
+
+
if dtype is not None:
+
if isinstance(dtype, str):
+
type_char = dtype
+
else:
+
type_char = to_typecode[dtype.value]
+
else:
+
type_char = None
+
+
_type_char='f'
+
+
if src is not None:
+
+
if (isinstance(src, Array)):
+
safe_call(backend.get().af_retain_array(c_pointer(self.arr), src.arr))
+
return
+
+
host = __import__("array")
+
+
if isinstance(src, host.array):
+
buf,buf_len = src.buffer_info()
+
_type_char = src.typecode
+
numdims, idims = _get_info(dims, buf_len)
+
elif isinstance(src, list):
+
tmp = host.array('f', src)
+
buf,buf_len = tmp.buffer_info()
+
_type_char = tmp.typecode
+
numdims, idims = _get_info(dims, buf_len)
+
elif isinstance(src, int) or isinstance(src, c_void_ptr_t):
+
buf = src if not isinstance(src, c_void_ptr_t) else src.value
+
+
numdims, idims = _get_info(dims, buf_len)
+
+
elements = 1
+
for dim in idims:
+
elements *= dim
+
+
if (elements == 0):
+
raise RuntimeError("Expected dims when src is data pointer")
+
+
if (type_char is None):
+
raise TypeError("Expected type_char when src is data pointer")
+
+
_type_char = type_char
+
+
else:
+
raise TypeError("src is an object of unsupported class")
+
+
if (type_char is not None and
+
type_char != _type_char):
+
raise TypeError("Can not create array of requested type from input data type")
+
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:
+
+
if type_char is None:
+
type_char = 'f'
+
+
numdims = len(dims) if dims else 0
+
+
idims = [1] * 4
+
for n in range(numdims):
+
idims[n] = dims[n]
+
+
self.arr = _create_empty_array(numdims, idims, to_dtype[type_char])
+
+
[docs] def as_type(self, ty):
+
"""
+
Cast current array to a specified data type
+
+
Parameters
+
----------
+
ty : Return data type
+
"""
+
return cast(self, ty)
+
+
[docs] def copy(self):
+
"""
+
Performs a deep copy of the array.
+
+
Returns
+
-------
+
out: af.Array()
+
An identical copy of self.
+
"""
+
out = Array()
+
safe_call(backend.get().af_copy_array(c_pointer(out.arr), self.arr))
+
return out
+
+
def __del__(self):
+
"""
+
Release the C array when going out of scope
+
"""
+
if self.arr.value:
+
backend.get().af_release_array(self.arr)
+
self.arr.value = 0
+
+
[docs] 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.
+
- Implies `af.device.lock_array()`. The device pointer of `a` is not freed by memory manager until `unlock_device_ptr()` is called.
+
- 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 = c_void_ptr_t(0)
+
backend.get().af_get_device_ptr(c_pointer(ptr), self.arr)
+
return ptr.value
+
+
[docs] def raw_ptr(self):
+
"""
+
Return the device pointer 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 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 = c_void_ptr_t(0)
+
backend.get().af_get_raw_ptr(c_pointer(ptr), self.arr)
+
return ptr.value
+
+
[docs] 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 = c_dim_t(0)
+
safe_call(backend.get().af_get_offset(c_pointer(offset), self.arr))
+
return offset.value
+
+
[docs] def strides(self):
+
"""
+
Return the distance in bytes between consecutive elements for each dimension.
+
+
Returns
+
--------
+
strides : tuple
+
The strides for each dimension
+
"""
+
s0 = c_dim_t(0)
+
s1 = c_dim_t(0)
+
s2 = c_dim_t(0)
+
s3 = c_dim_t(0)
+
safe_call(backend.get().af_get_strides(c_pointer(s0), c_pointer(s1),
+
c_pointer(s2), c_pointer(s3), self.arr))
+
strides = (s0.value,s1.value,s2.value,s3.value)
+
return strides[:self.numdims()]
+
+
[docs] def elements(self):
+
"""
+
Return the number of elements in the array.
+
"""
+
num = c_dim_t(0)
+
safe_call(backend.get().af_get_elements(c_pointer(num), self.arr))
+
return num.value
+
+
def __len__(self):
+
return(self.elements())
+
+
[docs] def allocated(self):
+
"""
+
Returns the number of bytes allocated by the memory manager for the array.
+
"""
+
num = c_size_t(0)
+
safe_call(backend.get().af_get_allocated_bytes(c_pointer(num), self.arr))
+
return num.value
+
+
[docs] def dtype(self):
+
"""
+
Return the data type as a arrayfire.Dtype enum value.
+
"""
+
dty = c_int_t(Dtype.f32.value)
+
safe_call(backend.get().af_get_type(c_pointer(dty), self.arr))
+
return to_dtype[to_typecode[dty.value]]
+
+
[docs] def type(self):
+
"""
+
Return the data type as an int.
+
"""
+
return self.dtype().value
+
+
@property
+
def T(self):
+
"""
+
Return the transpose of the array
+
"""
+
return transpose(self, False)
+
+
@property
+
def H(self):
+
"""
+
Return the hermitian transpose of the array
+
"""
+
return transpose(self, True)
+
+
[docs] def dims(self):
+
"""
+
Return the shape of the array as a tuple.
+
"""
+
d0 = c_dim_t(0)
+
d1 = c_dim_t(0)
+
d2 = c_dim_t(0)
+
d3 = c_dim_t(0)
+
safe_call(backend.get().af_get_dims(c_pointer(d0), c_pointer(d1),
+
c_pointer(d2), c_pointer(d3), self.arr))
+
dims = (d0.value,d1.value,d2.value,d3.value)
+
return dims[:self.numdims()]
+
+
@property
+
def shape(self):
+
"""
+
The shape of the array
+
"""
+
return self.dims()
+
+
[docs] def numdims(self):
+
"""
+
Return the number of dimensions of the array.
+
"""
+
nd = c_uint_t(0)
+
safe_call(backend.get().af_get_numdims(c_pointer(nd), self.arr))
+
return nd.value
+
+
[docs] def is_empty(self):
+
"""
+
Check if the array is empty i.e. it has no elements.
+
"""
+
res = c_bool_t(False)
+
safe_call(backend.get().af_is_empty(c_pointer(res), self.arr))
+
return res.value
+
+
[docs] def is_scalar(self):
+
"""
+
Check if the array is scalar i.e. it has only one element.
+
"""
+
res = c_bool_t(False)
+
safe_call(backend.get().af_is_scalar(c_pointer(res), self.arr))
+
return res.value
+
+
[docs] def is_row(self):
+
"""
+
Check if the array is a row i.e. it has a shape of (1, cols).
+
"""
+
res = c_bool_t(False)
+
safe_call(backend.get().af_is_row(c_pointer(res), self.arr))
+
return res.value
+
+
[docs] def is_column(self):
+
"""
+
Check if the array is a column i.e. it has a shape of (rows, 1).
+
"""
+
res = c_bool_t(False)
+
safe_call(backend.get().af_is_column(c_pointer(res), self.arr))
+
return res.value
+
+
[docs] def is_vector(self):
+
"""
+
Check if the array is a vector i.e. it has a shape of one of the following:
+
- (rows, 1)
+
- (1, cols)
+
- (1, 1, vols)
+
- (1, 1, 1, batch)
+
"""
+
res = c_bool_t(False)
+
safe_call(backend.get().af_is_vector(c_pointer(res), self.arr))
+
return res.value
+
+
[docs] def is_sparse(self):
+
"""
+
Check if the array is a sparse matrix.
+
"""
+
res = c_bool_t(False)
+
safe_call(backend.get().af_is_sparse(c_pointer(res), self.arr))
+
return res.value
+
+
[docs] def is_complex(self):
+
"""
+
Check if the array is of complex type.
+
"""
+
res = c_bool_t(False)
+
safe_call(backend.get().af_is_complex(c_pointer(res), self.arr))
+
return res.value
+
+
[docs] def is_real(self):
+
"""
+
Check if the array is not of complex type.
+
"""
+
res = c_bool_t(False)
+
safe_call(backend.get().af_is_real(c_pointer(res), self.arr))
+
return res.value
+
+
[docs] def is_double(self):
+
"""
+
Check if the array is of double precision floating point type.
+
"""
+
res = c_bool_t(False)
+
safe_call(backend.get().af_is_double(c_pointer(res), self.arr))
+
return res.value
+
+
[docs] def is_single(self):
+
"""
+
Check if the array is of single precision floating point type.
+
"""
+
res = c_bool_t(False)
+
safe_call(backend.get().af_is_single(c_pointer(res), self.arr))
+
return res.value
+
+
[docs] def is_half(self):
+
"""
+
Check if the array is of half floating point type (fp16).
+
"""
+
res = c_bool_t(False)
+
safe_call(backend.get().af_is_half(c_pointer(res), self.arr))
+
return res.value
+
+
[docs] def is_real_floating(self):
+
"""
+
Check if the array is real and of floating point type.
+
"""
+
res = c_bool_t(False)
+
safe_call(backend.get().af_is_realfloating(c_pointer(res), self.arr))
+
return res.value
+
+
[docs] def is_floating(self):
+
"""
+
Check if the array is of floating point type.
+
"""
+
res = c_bool_t(False)
+
safe_call(backend.get().af_is_floating(c_pointer(res), self.arr))
+
return res.value
+
+
[docs] def is_integer(self):
+
"""
+
Check if the array is of integer type.
+
"""
+
res = c_bool_t(False)
+
safe_call(backend.get().af_is_integer(c_pointer(res), self.arr))
+
return res.value
+
+
[docs] def is_bool(self):
+
"""
+
Check if the array is of type b8.
+
"""
+
res = c_bool_t(False)
+
safe_call(backend.get().af_is_bool(c_pointer(res), self.arr))
+
return res.value
+
+
[docs] def is_linear(self):
+
"""
+
Check if all elements of the array are contiguous.
+
"""
+
res = c_bool_t(False)
+
safe_call(backend.get().af_is_linear(c_pointer(res), self.arr))
+
return res.value
+
+
[docs] def is_owner(self):
+
"""
+
Check if the array owns the raw pointer or is a derived array.
+
"""
+
res = c_bool_t(False)
+
safe_call(backend.get().af_is_owner(c_pointer(res), self.arr))
+
return res.value
+
+
def __add__(self, other):
+
"""
+
Return self + other.
+
"""
+
return _binary_func(self, other, backend.get().af_add)
+
+
def __iadd__(self, other):
+
"""
+
Perform self += other.
+
"""
+
self = _binary_func(self, other, backend.get().af_add)
+
return self
+
+
def __radd__(self, other):
+
"""
+
Return other + self.
+
"""
+
return _binary_funcr(other, self, backend.get().af_add)
+
+
def __sub__(self, other):
+
"""
+
Return self - other.
+
"""
+
return _binary_func(self, other, backend.get().af_sub)
+
+
def __isub__(self, other):
+
"""
+
Perform self -= other.
+
"""
+
self = _binary_func(self, other, backend.get().af_sub)
+
return self
+
+
def __rsub__(self, other):
+
"""
+
Return other - self.
+
"""
+
return _binary_funcr(other, self, backend.get().af_sub)
+
+
def __mul__(self, other):
+
"""
+
Return self * other.
+
"""
+
return _binary_func(self, other, backend.get().af_mul)
+
+
def __imul__(self, other):
+
"""
+
Perform self *= other.
+
"""
+
self = _binary_func(self, other, backend.get().af_mul)
+
return self
+
+
def __rmul__(self, other):
+
"""
+
Return other * self.
+
"""
+
return _binary_funcr(other, self, backend.get().af_mul)
+
+
def __truediv__(self, other):
+
"""
+
Return self / other.
+
"""
+
return _binary_func(self, other, backend.get().af_div)
+
+
def __itruediv__(self, other):
+
"""
+
Perform self /= other.
+
"""
+
self = _binary_func(self, other, backend.get().af_div)
+
return self
+
+
def __rtruediv__(self, other):
+
"""
+
Return other / self.
+
"""
+
return _binary_funcr(other, self, backend.get().af_div)
+
+
def __div__(self, other):
+
"""
+
Return self / other.
+
"""
+
return _binary_func(self, other, backend.get().af_div)
+
+
def __idiv__(self, other):
+
"""
+
Perform other / self.
+
"""
+
self = _binary_func(self, other, backend.get().af_div)
+
return self
+
+
def __rdiv__(self, other):
+
"""
+
Return other / self.
+
"""
+
return _binary_funcr(other, self, backend.get().af_div)
+
+
def __mod__(self, other):
+
"""
+
Return self % other.
+
"""
+
return _binary_func(self, other, backend.get().af_mod)
+
+
def __imod__(self, other):
+
"""
+
Perform self %= other.
+
"""
+
self = _binary_func(self, other, backend.get().af_mod)
+
return self
+
+
def __rmod__(self, other):
+
"""
+
Return other % self.
+
"""
+
return _binary_funcr(other, self, backend.get().af_mod)
+
+
def __pow__(self, other):
+
"""
+
Return self ** other.
+
"""
+
return _binary_func(self, other, backend.get().af_pow)
+
+
def __ipow__(self, other):
+
"""
+
Perform self **= other.
+
"""
+
self = _binary_func(self, other, backend.get().af_pow)
+
return self
+
+
def __rpow__(self, other):
+
"""
+
Return other ** self.
+
"""
+
return _binary_funcr(other, self, backend.get().af_pow)
+
+
def __lt__(self, other):
+
"""
+
Return self < other.
+
"""
+
return _binary_func(self, other, backend.get().af_lt)
+
+
def __gt__(self, other):
+
"""
+
Return self > other.
+
"""
+
return _binary_func(self, other, backend.get().af_gt)
+
+
def __le__(self, other):
+
"""
+
Return self <= other.
+
"""
+
return _binary_func(self, other, backend.get().af_le)
+
+
def __ge__(self, other):
+
"""
+
Return self >= other.
+
"""
+
return _binary_func(self, other, backend.get().af_ge)
+
+
def __eq__(self, other):
+
"""
+
Return self == other.
+
"""
+
return _binary_func(self, other, backend.get().af_eq)
+
+
def __ne__(self, other):
+
"""
+
Return self != other.
+
"""
+
return _binary_func(self, other, backend.get().af_neq)
+
+
def __and__(self, other):
+
"""
+
Return self & other.
+
"""
+
return _binary_func(self, other, backend.get().af_bitand)
+
+
def __iand__(self, other):
+
"""
+
Perform self &= other.
+
"""
+
self = _binary_func(self, other, backend.get().af_bitand)
+
return self
+
+
def __or__(self, other):
+
"""
+
Return self | other.
+
"""
+
return _binary_func(self, other, backend.get().af_bitor)
+
+
def __ior__(self, other):
+
"""
+
Perform self |= other.
+
"""
+
self = _binary_func(self, other, backend.get().af_bitor)
+
return self
+
+
def __xor__(self, other):
+
"""
+
Return self ^ other.
+
"""
+
return _binary_func(self, other, backend.get().af_bitxor)
+
+
def __ixor__(self, other):
+
"""
+
Perform self ^= other.
+
"""
+
self = _binary_func(self, other, backend.get().af_bitxor)
+
return self
+
+
def __lshift__(self, other):
+
"""
+
Return self << other.
+
"""
+
return _binary_func(self, other, backend.get().af_bitshiftl)
+
+
def __ilshift__(self, other):
+
"""
+
Perform self <<= other.
+
"""
+
self = _binary_func(self, other, backend.get().af_bitshiftl)
+
return self
+
+
def __rshift__(self, other):
+
"""
+
Return self >> other.
+
"""
+
return _binary_func(self, other, backend.get().af_bitshiftr)
+
+
def __irshift__(self, other):
+
"""
+
Perform self >>= other.
+
"""
+
self = _binary_func(self, other, backend.get().af_bitshiftr)
+
return self
+
+
def __neg__(self):
+
"""
+
Return -self
+
"""
+
return 0 - self
+
+
def __pos__(self):
+
"""
+
Return +self
+
"""
+
return self
+
+
def __invert__(self):
+
"""
+
Return ~self
+
"""
+
out = Array()
+
safe_call(backend.get().af_bitnot(c_pointer(out.arr), self.arr))
+
return out
+
+
[docs] def logical_not(self):
+
"""
+
Return ~self
+
"""
+
out = Array()
+
safe_call(backend.get().af_not(c_pointer(out.arr), self.arr))
+
return out
+
+
[docs] def logical_and(self, other):
+
"""
+
Return self && other.
+
"""
+
out = Array()
+
safe_call(backend.get().af_and(c_pointer(out.arr), self.arr, other.arr)) #TODO: bcast var?
+
return out
+
+
[docs] def logical_or(self, other):
+
"""
+
Return self || other.
+
"""
+
out = Array()
+
safe_call(backend.get().af_or(c_pointer(out.arr), self.arr, other.arr)) #TODO: bcast var?
+
return out
+
+
def __nonzero__(self):
+
return self != 0
+
+
# TODO:
+
# def __abs__(self):
+
# return self
+
+
def __getitem__(self, key):
+
"""
+
Return self[key]
+
+
Note
+
----
+
Ellipsis not supported as 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(c_pointer(out.arr),
+
self.arr, c_dim_t(n_dims), inds.pointer))
+
return out
+
except RuntimeError as e:
+
raise IndexError(str(e))
+
+
+
def __setitem__(self, key, val):
+
"""
+
Perform self[key] = val
+
+
Note
+
----
+
Ellipsis not supported as key
+
"""
+
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())
+
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
+
del_other = False
+
+
out_arr = c_void_ptr_t(0)
+
inds = _get_indices(key)
+
+
safe_call(backend.get().af_assign_gen(c_pointer(out_arr),
+
self.arr, c_dim_t(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))
+
self.arr = out_arr
+
+
except RuntimeError as e:
+
raise IndexError(str(e))
+
+
def _reorder(self):
+
"""
+
Returns a reordered array to help interoperate with row major formats.
+
"""
+
ndims = self.numdims()
+
if (ndims == 1):
+
return self
+
+
rdims = tuple(reversed(range(ndims))) + tuple(range(ndims, 4))
+
out = Array()
+
safe_call(backend.get().af_reorder(c_pointer(out.arr), self.arr, *rdims))
+
return out
+
+
[docs] def to_ctype(self, row_major=False, return_shape=False):
+
"""
+
Return the data as a ctype C array after copying to host memory
+
+
Parameters
+
-----------
+
+
row_major: optional: bool. default: False.
+
Specifies if a transpose needs to occur before copying to host memory.
+
+
return_shape: optional: bool. default: False.
+
Specifies if the shape of the array needs to be returned.
+
+
Returns
+
-------
+
+
If return_shape is False:
+
res: The ctypes array of the appropriate type and length.
+
else :
+
(res, dims): tuple of the ctypes array and the shape of the array
+
+
"""
+
if (self.arr.value == 0):
+
raise RuntimeError("Can not call to_ctype on empty array")
+
+
tmp = self._reorder() if (row_major) else self
+
+
ctype_type = to_c_type[self.type()] * self.elements()
+
res = ctype_type()
+
+
safe_call(backend.get().af_get_data_ptr(c_pointer(res), self.arr))
+
if (return_shape):
+
return res, self.dims()
+
else:
+
return res
+
+
[docs] def to_array(self, row_major=False, return_shape=False):
+
"""
+
Return the data as array.array
+
+
Parameters
+
-----------
+
+
row_major: optional: bool. default: False.
+
Specifies if a transpose needs to occur before copying to host memory.
+
+
return_shape: optional: bool. default: False.
+
Specifies if the shape of the array needs to be returned.
+
+
Returns
+
-------
+
+
If return_shape is False:
+
res: array.array of the appropriate type and length.
+
else :
+
(res, dims): array.array and the shape of the array
+
+
"""
+
if (self.arr.value == 0):
+
raise RuntimeError("Can not call to_array on empty array")
+
+
res = self.to_ctype(row_major, return_shape)
+
+
host = __import__("array")
+
h_type = to_typecode[self.type()]
+
+
if (return_shape):
+
return host.array(h_type, res[0]), res[1]
+
else:
+
return host.array(h_type, res)
+
+
[docs] def to_list(self, row_major=False):
+
"""
+
Return the data as list
+
+
Parameters
+
-----------
+
+
row_major: optional: bool. default: False.
+
Specifies if a transpose needs to occur before copying to host memory.
+
+
return_shape: optional: bool. default: False.
+
Specifies if the shape of the array needs to be returned.
+
+
Returns
+
-------
+
+
If return_shape is False:
+
res: list of the appropriate type and length.
+
else :
+
(res, dims): list and the shape of the array
+
+
"""
+
ct_array, shape = self.to_ctype(row_major, True)
+
return _ctype_to_lists(ct_array, len(shape) - 1, shape)
+
+
[docs] def scalar(self):
+
"""
+
Return the first element of the array
+
"""
+
+
if (self.arr.value == 0):
+
raise RuntimeError("Can not call to_ctype on empty array")
+
+
ctype_type = to_c_type[self.type()]
+
res = ctype_type()
+
safe_call(backend.get().af_get_scalar(c_pointer(res), self.arr))
+
return res.value
+
+
def __str__(self):
+
"""
+
Converts the arrayfire array to string showing its meta data and contents.
+
+
Note
+
----
+
You can also use af.display(a, pres) to display the contents of the array with better precision.
+
"""
+
+
if not _in_display_dims_limit(self.dims()):
+
return self._get_metadata_str()
+
+
return self._get_metadata_str(dims=False) + self._as_str()
+
+
def __repr__(self):
+
"""
+
Displays the meta data of the arrayfire array.
+
+
Note
+
----
+
You can use af.display(a, pres) to display the contents of the array.
+
"""
+
+
return self._get_metadata_str()
+
+
def _get_metadata_str(self, dims=True):
+
return 'arrayfire.Array()\nType: {}\n{}' \
+
.format(to_typename[self.type()], 'Dims: {}'.format(str(self.dims())) if dims else '')
+
+
def _as_str(self):
+
arr_str = c_char_ptr_t(0)
+
be = backend.get()
+
safe_call(be.af_array_to_string(c_pointer(arr_str), "", self.arr, 4, True))
+
py_str = to_str(arr_str)
+
safe_call(be.af_free_host(arr_str))
+
return py_str
+
+
def __array__(self):
+
"""
+
Constructs a numpy.array from arrayfire.Array
+
"""
+
import numpy as np
+
res = np.empty(self.dims(), dtype=np.dtype(to_typecode[self.type()]), order='F')
+
safe_call(backend.get().af_get_data_ptr(c_void_ptr_t(res.ctypes.data), self.arr))
+
return res
+
+
[docs] def to_ndarray(self, output=None):
+
"""
+
Parameters
+
-----------
+
output: optional: numpy. default: None
+
+
Returns
+
----------
+
If output is None: Constructs a numpy.array from arrayfire.Array
+
If output is not None: copies content of af.array into numpy array.
+
+
Note
+
------
+
+
- An exception is thrown when output is not None and it is not contiguous.
+
- When output is None, The returned array is in fortran contiguous order.
+
"""
+
if output is None:
+
return self.__array__()
+
+
if (output.dtype != to_typecode[self.type()]):
+
raise TypeError("Output is not the same type as the array")
+
+
if (output.size != self.elements()):
+
raise RuntimeError("Output size does not match that of input")
+
+
flags = output.flags
+
tmp = None
+
if flags['F_CONTIGUOUS']:
+
tmp = self
+
elif flags['C_CONTIGUOUS']:
+
tmp = self._reorder()
+
else:
+
raise RuntimeError("When output is not None, it must be contiguous")
+
+
safe_call(backend.get().af_get_data_ptr(c_void_ptr_t(output.ctypes.data), tmp.arr))
+
return output
+
+[docs]def display(a, precision=4):
+
"""
+
Displays the contents of an array.
+
+
Parameters
+
----------
+
a : af.Array
+
Multi dimensional arrayfire array
+
precision: int. optional.
+
Specifies the number of precision bits to display
+
"""
+
expr = inspect.stack()[1][-2]
+
name = ""
+
+
try:
+
if (expr is not None):
+
st = expr[0].find('(') + 1
+
en = expr[0].rfind(')')
+
name = expr[0][st:en]
+
except IndexError:
+
pass
+
+
safe_call(backend.get().af_print_array_gen(name.encode('utf-8'),
+
a.arr, c_int_t(precision)))
+
+[docs]def save_array(key, a, filename, append=False):
+
"""
+
Save an array to disk.
+
+
Parameters
+
----------
+
key : str
+
A name / key associated with the array
+
+
a : af.Array
+
The array to be stored to disk
+
+
filename : str
+
Location of the data file.
+
+
append : Boolean. optional. default: False.
+
If the file already exists, specifies if the data should be appended or overwritten.
+
+
Returns
+
---------
+
index : int
+
The index of the array stored in the file.
+
"""
+
index = c_int_t(-1)
+
safe_call(backend.get().af_save_array(c_pointer(index),
+
key.encode('utf-8'),
+
a.arr,
+
filename.encode('utf-8'),
+
append))
+
return index.value
+
+[docs]def read_array(filename, index=None, key=None):
+
"""
+
Read an array from disk.
+
+
Parameters
+
----------
+
+
filename : str
+
Location of the data file.
+
+
index : int. Optional. Default: None.
+
- The index of the array stored in the file.
+
- If None, key is used.
+
+
key : str. Optional. Default: None.
+
- A name / key associated with the array
+
- If None, index is used.
+
+
Returns
+
---------
+
"""
+
assert((index is not None) or (key is not None))
+
out = Array()
+
if (index is not None):
+
safe_call(backend.get().af_read_array_index(c_pointer(out.arr),
+
filename.encode('utf-8'),
+
index))
+
elif (key is not None):
+
safe_call(backend.get().af_read_array_key(c_pointer(out.arr),
+
filename.encode('utf-8'),
+
key.encode('utf-8')))
+
+
return out
+
+from .algorithm import (sum, count)
+from .arith import cast
+