/******************************************************* * Copyright (c) 2014, 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 ********************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include "error.hpp" #include #include namespace af { static int gforDim(af_index_t *indices) { for (int i = 0; i < AF_MAX_DIMS; i++) { if (indices[i].isBatch) return i; } return -1; } static af_array gforReorder(const af_array in, unsigned dim) { // This is here to stop gcc from complaining if (dim > 3) AF_THROW_ERR("GFor: Dimension is invalid", AF_ERR_SIZE); unsigned order[AF_MAX_DIMS] = {0, 1, 2, dim}; order[dim] = 3; af_array out; AF_THROW(af_reorder(&out, in, order[0], order[1], order[2], order[3])); return out; } static af::dim4 seqToDims(af_index_t *indices, af::dim4 parentDims, bool reorder = true) { try { af::dim4 odims(1); for (int i = 0; i < AF_MAX_DIMS; i++) { if (indices[i].isSeq) { odims[i] = calcDim(indices[i].idx.seq, parentDims[i]); } else { dim_t elems = 0; AF_THROW(af_get_elements(&elems, indices[i].idx.arr)); odims[i] = elems; } } // Change the dimensions if inside GFOR if (reorder) { for (int i = 0; i < AF_MAX_DIMS; i++) { if (indices[i].isBatch) { int tmp = odims[i]; odims[i] = odims[3]; odims[3] = tmp; break; } } } return odims; } catch(std::logic_error &err) { AF_THROW_ERR(err.what(), AF_ERR_SIZE); } } static unsigned numDims(const af_array arr) { unsigned nd; AF_THROW(af_get_numdims(&nd, arr)); return nd; } static dim4 getDims(const af_array arr) { dim_t d0, d1, d2, d3; AF_THROW(af_get_dims(&d0, &d1, &d2, &d3, arr)); return dim4(d0, d1, d2, d3); } struct array::array_proxy::array_proxy_impl { array * parent_; //< The original array af_index_t indices_[4]; //< Indexing array or seq objects bool is_linear_; // if true the parent_ object will be deleted on distruction. This is // necessary only when calling indexing functions in array_proxy objects. bool delete_on_destruction_; array_proxy_impl(array &parent, af_index_t *idx, bool linear) : parent_(&parent) , indices_() , is_linear_(linear) , delete_on_destruction_(false) { std::copy(idx, idx + AF_MAX_DIMS, indices_); } void delete_on_destruction(bool val) { delete_on_destruction_ = val; } ~array_proxy_impl() { if (delete_on_destruction_) delete parent_; } private: array_proxy_impl(const array_proxy_impl&); array_proxy_impl(const array_proxy_impl&&); array_proxy_impl operator=(const array_proxy_impl&); array_proxy_impl operator=(const array_proxy_impl&&); }; array::array(const af_array handle): arr(handle) { } static void initEmptyArray(af_array *arr, af::dtype ty, dim_t d0, dim_t d1=1, dim_t d2=1, dim_t d3=1) { dim_t my_dims[] = {d0, d1, d2, d3}; AF_THROW(af_create_handle(arr, AF_MAX_DIMS, my_dims, ty)); } template static void initDataArray(af_array *arr, const T *ptr, af::source src, dim_t d0, dim_t d1=1, dim_t d2=1, dim_t d3=1) { af::dtype ty = (af::dtype)dtype_traits::af_type; dim_t my_dims[] = {d0, d1, d2, d3}; switch (src) { case afHost: AF_THROW(af_create_array(arr, (const void * const)ptr, AF_MAX_DIMS, my_dims, ty)); break; case afDevice: AF_THROW(af_device_array(arr, (const void * )ptr, AF_MAX_DIMS, my_dims, ty)); break; default: AF_THROW_ERR("Can not create array from the requested source pointer", AF_ERR_ARG); } } array::array() : arr(0) { initEmptyArray(&arr, f32, 0, 1, 1, 1); } array::array(const dim4 &dims, af::dtype ty) : arr(0) { initEmptyArray(&arr, ty, dims[0], dims[1], dims[2], dims[3]); } array::array(dim_t d0, af::dtype ty) : arr(0) { initEmptyArray(&arr, ty, d0); } array::array(dim_t d0, dim_t d1, af::dtype ty) : arr(0) { initEmptyArray(&arr, ty, d0, d1); } array::array(dim_t d0, dim_t d1, dim_t d2, af::dtype ty) : arr(0) { initEmptyArray(&arr, ty, d0, d1, d2); } array::array(dim_t d0, dim_t d1, dim_t d2, dim_t d3, af::dtype ty) : arr(0) { initEmptyArray(&arr, ty, d0, d1, d2, d3); } #define INSTANTIATE(T) \ template<> AFAPI \ array::array(const dim4 &dims, const T *ptr, af::source src) \ : arr(0) \ { \ initDataArray(&arr, ptr, src, dims[0], dims[1], dims[2], \ dims[3]); \ } \ template<> AFAPI \ array::array(dim_t d0, const T *ptr, af::source src) \ : arr(0) \ { \ initDataArray(&arr, ptr, src, d0); \ } \ template<> AFAPI \ array::array(dim_t d0, dim_t d1, const T *ptr, af::source src) \ : arr(0) \ { \ initDataArray(&arr, ptr, src, d0, d1); \ } \ template<> AFAPI \ array::array(dim_t d0, dim_t d1, dim_t d2, const T *ptr, \ af::source src) \ : arr(0) \ { \ initDataArray(&arr, ptr, src, d0, d1, d2); \ } \ template<> AFAPI \ array::array(dim_t d0, dim_t d1, dim_t d2, dim_t d3, const T *ptr, \ af::source src) : \ arr(0) \ \ { \ initDataArray(&arr, ptr, src, d0, d1, d2, d3); \ } \ INSTANTIATE(cdouble) INSTANTIATE(cfloat) INSTANTIATE(double) INSTANTIATE(float) INSTANTIATE(unsigned) INSTANTIATE(int) INSTANTIATE(unsigned char) INSTANTIATE(char) INSTANTIATE(intl) INSTANTIATE(uintl) INSTANTIATE(short) INSTANTIATE(unsigned short) #undef INSTANTIATE array::~array() { af_array tmp = get(); // THOU SHALL NOT THROW IN DESTRUCTORS af_release_array(tmp); } af::dtype array::type() const { af::dtype my_type; AF_THROW(af_get_type(&my_type, arr)); return my_type; } dim_t array::elements() const { dim_t elems; AF_THROW(af_get_elements(&elems, get())); return elems; } void array::host(void *data) const { AF_THROW(af_get_data_ptr(data, get())); } af_array array::get() { return arr; } af_array array::get() const { return ((array *)(this))->get(); } // Helper functions dim4 array::dims() const { return getDims(get()); } dim_t array::dims(unsigned dim) const { return dims()[dim]; } unsigned array::numdims() const { return numDims(get()); } size_t array::bytes() const { dim_t nElements; AF_THROW(af_get_elements(&nElements, get())); return nElements * getSizeOf(type()); } size_t array::allocated() const { size_t result = 0; AF_THROW(af_get_allocated_bytes(&result, get())); return result; } array array::copy() const { af_array other = 0; AF_THROW(af_copy_array(&other, get())); return array(other); } #undef INSTANTIATE #define INSTANTIATE(fn) \ bool array::is##fn() const \ { \ bool ret = false; \ AF_THROW(af_is_##fn(&ret, get())); \ return ret; \ } INSTANTIATE(empty) INSTANTIATE(scalar) INSTANTIATE(vector) INSTANTIATE(row) INSTANTIATE(column) INSTANTIATE(complex) INSTANTIATE(double) INSTANTIATE(single) INSTANTIATE(realfloating) INSTANTIATE(floating) INSTANTIATE(integer) INSTANTIATE(bool) INSTANTIATE(sparse) #undef INSTANTIATE static array::array_proxy gen_indexing(const array &ref, const index &s0, const index &s1, const index &s2, const index &s3, bool linear = false) { ref.eval(); af_index_t inds[AF_MAX_DIMS]; inds[0] = s0.get(); inds[1] = s1.get(); inds[2] = s2.get(); inds[3] = s3.get(); return array::array_proxy(const_cast(ref), inds, linear); } array::array_proxy array::operator()(const index &s0) { return const_cast(this)->operator()(s0); } array::array_proxy array::operator()(const index &s0, const index &s1, const index &s2, const index &s3) { return const_cast(this)->operator()(s0, s1, s2, s3); } const array::array_proxy array::operator()(const index &s0) const { index z = index(0); if(isvector()){ switch(numDims(this->arr)) { case 1: return gen_indexing(*this, s0, z, z, z); case 2: return gen_indexing(*this, z, s0, z, z); case 3: return gen_indexing(*this, z, z, s0, z); case 4: return gen_indexing(*this, z, z, z, s0); default: AF_THROW_ERR("ndims for Array is invalid", AF_ERR_SIZE); } } else { return gen_indexing(*this, s0, z, z, z, true); } } const array::array_proxy array::operator()(const index &s0, const index &s1, const index &s2, const index &s3) const { return gen_indexing(*this, s0, s1, s2, s3); } const array::array_proxy array::row(int index) const { return this->operator()(index, span, span, span); } array::array_proxy array::row(int index) { return const_cast(this)->row(index); } const array::array_proxy array::col(int index) const { return this->operator()(span, index, span, span); } array::array_proxy array::col(int index) { return const_cast(this)->col(index); } const array::array_proxy array::slice(int index) const { return this->operator()(span, span, index, span); } array::array_proxy array::slice(int index) { return const_cast(this)->slice(index); } const array::array_proxy array::rows(int first, int last) const { seq idx(first, last, 1); return this->operator()(idx, span, span, span); } array::array_proxy array::rows(int first, int last) { return const_cast(this)->rows(first, last); } const array::array_proxy array::cols(int first, int last) const { seq idx(first, last, 1); return this->operator()(span, idx, span, span); } array::array_proxy array::cols(int first, int last) { return const_cast(this)->cols(first, last); } const array::array_proxy array::slices(int first, int last) const { seq idx(first, last, 1); return this->operator()(span, span, idx, span); } array::array_proxy array::slices(int first, int last) { return const_cast(this)->slices(first, last); } const array array::as(af::dtype type) const { af_array out; AF_THROW(af_cast(&out, this->get(), type)); return array(out); } array::array(const array& in) : arr(0) { AF_THROW(af_retain_array(&arr, in.get())); } array::array(const array& input, const dim4& dims) : arr(0) { AF_THROW(af_moddims(&arr, input.get(), AF_MAX_DIMS, dims.get())); } array::array(const array& input, const dim_t dim0, const dim_t dim1, const dim_t dim2, const dim_t dim3) : arr(0) { dim_t dims[] = {dim0, dim1, dim2, dim3}; AF_THROW(af_moddims(&arr, input.get(), AF_MAX_DIMS, dims)); } // Transpose and Conjugate Transpose array array::T() const { return transpose(*this); } array array::H() const { return transpose(*this, true); } void array::set(af_array tmp) { if (arr) AF_THROW(af_release_array(arr)); arr = tmp; } // Assign values to an array array::array_proxy& af::array::array_proxy::operator=(const array &other) { unsigned nd = numDims(impl->parent_->get()); const dim4 this_dims = getDims(impl->parent_->get()); const dim4 other_dims = other.dims(); int dim = gforDim(impl->indices_); af_array other_arr = other.get(); bool batch_assign = false; bool is_reordered = false; if (dim >= 0) { //FIXME: Figure out a faster, cleaner way to do this dim4 out_dims = seqToDims(impl->indices_, this_dims, false); batch_assign = true; for (int i = 0; i < AF_MAX_DIMS; i++) { if (this->impl->indices_[i].isBatch) batch_assign &= (other_dims[i] == 1); else batch_assign &= (other_dims[i] == out_dims[i]); } if (batch_assign) { af_array out; AF_THROW(af_tile(&out, other_arr, out_dims[0] / other_dims[0], out_dims[1] / other_dims[1], out_dims[2] / other_dims[2], out_dims[3] / other_dims[3])); other_arr = out; } else if (out_dims != other_dims) { // HACK: This is a quick check to see if other has been reordered inside gfor // TODO: Figure out if this breaks and implement a cleaner method other_arr = gforReorder(other_arr, dim); is_reordered = true; } } af_array par_arr = 0; if (impl->is_linear_) { AF_THROW(af_flat(&par_arr, impl->parent_->get())); nd = 1; } else { par_arr = impl->parent_->get(); } af_array tmp = 0; AF_THROW(af_assign_gen(&tmp, par_arr, nd, impl->indices_, other_arr)); af_array res = 0; if (impl->is_linear_) { AF_THROW(af_moddims(&res, tmp, this_dims.ndims(), this_dims.get())); AF_THROW(af_release_array(par_arr)); AF_THROW(af_release_array(tmp)); } else { res = tmp; } impl->parent_->set(res); if (dim >= 0 && (is_reordered || batch_assign)) { if (other_arr) AF_THROW(af_release_array(other_arr)); } return *this; } array::array_proxy& af::array::array_proxy::operator=(const array::array_proxy &other) { array out = other; return *this = out; } af::array::array_proxy::array_proxy(array& par, af_index_t *ssss, bool linear) : impl(new array_proxy_impl(par, ssss, linear)) { } af::array::array_proxy::array_proxy(const array_proxy &other) : impl(new array_proxy_impl(*other.impl->parent_, other.impl->indices_, other.impl->is_linear_)) { } #if __cplusplus > 199711L af::array::array_proxy::array_proxy(array_proxy &&other) { impl = other.impl; other.impl = nullptr; } array::array_proxy& af::array::array_proxy::operator=(array_proxy &&other) { array out = other; other.impl = nullptr; return *this = out; } #endif af::array::array_proxy::~array_proxy() { if(impl) delete impl; } array array::array_proxy::as(dtype type) const { array out = *this; return out.as(type); } dim_t array::array_proxy::dims(unsigned dim) const { array out = *this; return out.dims(dim); } void array::array_proxy::host(void *ptr) const { array out = *this; return out.host(ptr); } #define MEM_FUNC(PREFIX, FUNC) \ PREFIX array::array_proxy::FUNC() const \ { \ array out = *this; \ return out.FUNC(); \ } MEM_FUNC(dim_t , elements) MEM_FUNC(array , T) MEM_FUNC(array , H) MEM_FUNC(dtype , type) MEM_FUNC(dim4 , dims) MEM_FUNC(unsigned , numdims) MEM_FUNC(size_t , bytes) MEM_FUNC(size_t , allocated) MEM_FUNC(array , copy) MEM_FUNC(bool , isempty) MEM_FUNC(bool , isscalar) MEM_FUNC(bool , isvector) MEM_FUNC(bool , isrow) MEM_FUNC(bool , iscolumn) MEM_FUNC(bool , iscomplex) MEM_FUNC(bool , isdouble) MEM_FUNC(bool , issingle) MEM_FUNC(bool , isrealfloating) MEM_FUNC(bool , isfloating) MEM_FUNC(bool , isinteger) MEM_FUNC(bool , isbool) MEM_FUNC(bool , issparse) MEM_FUNC(void , eval) MEM_FUNC(af_array , get) //MEM_FUNC(void , unlock) #undef MEM_FUNC #define ASSIGN_TYPE(TY, OP) \ array::array_proxy& \ array::array_proxy::operator OP(const TY &value) \ { \ dim4 pdims = getDims(impl->parent_->get()); \ if (impl->is_linear_) pdims = dim4(pdims.elements()); \ dim4 dims = seqToDims(impl->indices_, pdims ); \ af::dtype ty = impl->parent_->type(); \ array cst = constant(value, dims, ty); \ return this->operator OP(cst); \ } \ #define ASSIGN_OP(OP, op1) \ ASSIGN_TYPE(double , OP) \ ASSIGN_TYPE(float , OP) \ ASSIGN_TYPE(cdouble , OP) \ ASSIGN_TYPE(cfloat , OP) \ ASSIGN_TYPE(int , OP) \ ASSIGN_TYPE(unsigned , OP) \ ASSIGN_TYPE(long , OP) \ ASSIGN_TYPE(unsigned long , OP) \ ASSIGN_TYPE(long long , OP) \ ASSIGN_TYPE(unsigned long long , OP) \ ASSIGN_TYPE(char , OP) \ ASSIGN_TYPE(unsigned char , OP) \ ASSIGN_TYPE(bool , OP) \ ASSIGN_TYPE(short , OP) \ ASSIGN_TYPE(unsigned short , OP) \ ASSIGN_OP(= , =) ASSIGN_OP(+=, +) ASSIGN_OP(-=, -) ASSIGN_OP(*=, *) ASSIGN_OP(/=, /) #undef ASSIGN_OP #undef ASSIGN_TYPE #define SELF_OP(OP, op1) \ array::array_proxy& array::array_proxy::operator OP(const array_proxy &other) \ { \ *this = *this op1 other; \ return *this; \ } \ array::array_proxy& array::array_proxy::operator OP(const array &other) \ { \ *this = *this op1 other; \ return *this; \ } \ SELF_OP(+=, +) SELF_OP(-=, -) SELF_OP(*=, *) SELF_OP(/=, /) #undef SELF_OP array::array_proxy::operator array() const { af_array tmp = 0; af_array arr = 0; if(impl->is_linear_) { AF_THROW(af_flat(&arr, impl->parent_->get())); } else { arr = impl->parent_->get(); } AF_THROW(af_index_gen(&tmp, arr, AF_MAX_DIMS, impl->indices_)); if (impl->is_linear_) { AF_THROW(af_release_array(arr)); } return array(tmp); } array::array_proxy::operator array() { af_array tmp = 0; af_array arr = 0; if(impl->is_linear_) { AF_THROW(af_flat(&arr, impl->parent_->get())); } else { arr = impl->parent_->get(); } AF_THROW(af_index_gen(&tmp, arr, AF_MAX_DIMS, impl->indices_)); if(impl->is_linear_) { AF_THROW(af_release_array(arr)); } int dim = gforDim(impl->indices_); if (tmp && dim >= 0) { arr = gforReorder(tmp, dim); if (tmp) AF_THROW(af_release_array(tmp)); } else { arr = tmp; } return array(arr); } #define MEM_INDEX(FUNC_SIG, USAGE) \ array::array_proxy \ array::array_proxy::FUNC_SIG \ { \ array* out = new array(*this); \ array::array_proxy proxy = out->USAGE; \ proxy.impl->delete_on_destruction(true); \ return proxy; \ } \ \ const array::array_proxy \ array::array_proxy::FUNC_SIG const \ { \ const array* out = new array(*this); \ array::array_proxy proxy = out->USAGE; \ proxy.impl->delete_on_destruction(true); \ return proxy; \ } MEM_INDEX(row(int index) , row(index)); MEM_INDEX(rows(int first, int last) , rows(first, last)); MEM_INDEX(col(int index) , col(index)); MEM_INDEX(cols(int first, int last) , cols(first, last)); MEM_INDEX(slice(int index) , slice(index)); MEM_INDEX(slices(int first, int last) , slices(first, last)); #undef MEM_INDEX /////////////////////////////////////////////////////////////////////////// // Operator = /////////////////////////////////////////////////////////////////////////// array& array::operator=(const array &other) { if (this->get() == other.get()) { return *this; } //TODO: Unsafe. loses data if af_weak_copy fails if(this->arr != 0) { AF_THROW(af_release_array(this->arr)); } af_array temp = 0; AF_THROW(af_retain_array(&temp, other.get())); this->arr = temp; return *this; } #define ASSIGN_TYPE(TY, OP) \ array& array::operator OP(const TY &value) \ { \ af::dim4 dims = this->dims(); \ af::dtype ty = this->type(); \ array cst = constant(value, dims, ty); \ return operator OP(cst); \ } \ #define ASSIGN_OP(OP, op1) \ array& array::operator OP(const array &other) \ { \ af_array out = 0; \ AF_THROW(op1(&out, this->get(), other.get(), gforGet())); \ this->set(out); \ return *this; \ } \ ASSIGN_TYPE(double , OP) \ ASSIGN_TYPE(float , OP) \ ASSIGN_TYPE(cdouble , OP) \ ASSIGN_TYPE(cfloat , OP) \ ASSIGN_TYPE(int , OP) \ ASSIGN_TYPE(unsigned , OP) \ ASSIGN_TYPE(long , OP) \ ASSIGN_TYPE(unsigned long , OP) \ ASSIGN_TYPE(long long , OP) \ ASSIGN_TYPE(unsigned long long , OP) \ ASSIGN_TYPE(char , OP) \ ASSIGN_TYPE(unsigned char , OP) \ ASSIGN_TYPE(bool , OP) \ ASSIGN_TYPE(short , OP) \ ASSIGN_TYPE(unsigned short , OP) \ ASSIGN_OP(+=, af_add) ASSIGN_OP(-=, af_sub) ASSIGN_OP(*=, af_mul) ASSIGN_OP(/=, af_div) #undef ASSIGN_OP #undef ASSIGN_TYPE #define ASSIGN_TYPE(TY, OP) \ array& array::operator OP(const TY &value) \ { \ af::dim4 dims = this->dims(); \ af::dtype ty = this->type(); \ array cst = constant(value, dims, ty); \ return operator OP(cst); \ } \ #define ASSIGN_OP(OP) \ ASSIGN_TYPE(double , OP) \ ASSIGN_TYPE(float , OP) \ ASSIGN_TYPE(cdouble , OP) \ ASSIGN_TYPE(cfloat , OP) \ ASSIGN_TYPE(int , OP) \ ASSIGN_TYPE(unsigned , OP) \ ASSIGN_TYPE(long , OP) \ ASSIGN_TYPE(unsigned long , OP) \ ASSIGN_TYPE(long long , OP) \ ASSIGN_TYPE(unsigned long long , OP) \ ASSIGN_TYPE(char , OP) \ ASSIGN_TYPE(unsigned char , OP) \ ASSIGN_TYPE(bool , OP) \ ASSIGN_TYPE(short , OP) \ ASSIGN_TYPE(unsigned short , OP) \ ASSIGN_OP(= ) #undef ASSIGN_OP #undef ASSIGN_TYPE af::dtype implicit_dtype(af::dtype scalar_type, af::dtype array_type) { // If same, do not do anything if (scalar_type == array_type) return scalar_type; // If complex, return appropriate complex type if (scalar_type == c32 || scalar_type == c64) { if (array_type == f64 || array_type == c64) return c64; return c32; } // If 64 bit precision, do not lose precision if (array_type == f64 || array_type == c64 || array_type == f32 || array_type == c32 ) return array_type; // Default to single precision by default when multiplying with scalar if ((scalar_type == f64 || scalar_type == c64) && (array_type != f64 && array_type != c64)) { return f32; } // Punt to C api for everything else return scalar_type; } #define BINARY_TYPE(TY, OP, func, dty) \ array operator OP(const array& plhs, const TY &value) \ { \ af_array out; \ af::dtype cty = implicit_dtype(dty, plhs.type()); \ array cst = constant(value, plhs.dims(), cty); \ AF_THROW(func(&out, plhs.get(), cst.get(), gforGet())); \ return array(out); \ } \ array operator OP(const TY &value, const array &other) \ { \ const af_array rhs = other.get(); \ af_array out; \ af::dtype cty = implicit_dtype(dty, other.type()); \ array cst = constant(value, other.dims(), cty); \ AF_THROW(func(&out, cst.get(), rhs, gforGet())); \ return array(out); \ } \ #define BINARY_OP(OP, func) \ array operator OP(const array &lhs, const array &rhs) \ { \ af_array out; \ AF_THROW(func(&out, lhs.get(), rhs.get(), gforGet())); \ return array(out); \ } \ BINARY_TYPE(double , OP, func, f64) \ BINARY_TYPE(float , OP, func, f32) \ BINARY_TYPE(cdouble , OP, func, c64) \ BINARY_TYPE(cfloat , OP, func, c32) \ BINARY_TYPE(int , OP, func, s32) \ BINARY_TYPE(unsigned , OP, func, u32) \ BINARY_TYPE(long , OP, func, s64) \ BINARY_TYPE(unsigned long , OP, func, u64) \ BINARY_TYPE(long long , OP, func, s64) \ BINARY_TYPE(unsigned long long , OP, func, u64) \ BINARY_TYPE(char , OP, func, b8) \ BINARY_TYPE(unsigned char , OP, func, u8) \ BINARY_TYPE(bool , OP, func, b8) \ BINARY_TYPE(short , OP, func, s16) \ BINARY_TYPE(unsigned short , OP, func, u16) \ BINARY_OP(+, af_add) BINARY_OP(-, af_sub) BINARY_OP(*, af_mul) BINARY_OP(/, af_div) BINARY_OP(==, af_eq) BINARY_OP(!=, af_neq) BINARY_OP(< , af_lt) BINARY_OP(<=, af_le) BINARY_OP(> , af_gt) BINARY_OP(>=, af_ge) BINARY_OP(&&, af_and) BINARY_OP(||, af_or) BINARY_OP(%, af_mod) BINARY_OP(&, af_bitand) BINARY_OP(|, af_bitor) BINARY_OP(^, af_bitxor) BINARY_OP(<<, af_bitshiftl) BINARY_OP(>>, af_bitshiftr) #undef BINARY_OP #undef BINARY_TYPE array array::operator-() const { af_array lhs = this->get(); af_array out; array cst = constant(0, this->dims(), this->type()); AF_THROW(af_sub(&out, cst.get(), lhs, gforGet())); return array(out); } array array::operator!() const { af_array lhs = this->get(); af_array out; AF_THROW(af_not(&out, lhs)); return array(out); } void array::eval() const { AF_THROW(af_eval(get())); } // array instanciations #define INSTANTIATE(T) \ template<> AFAPI T *array::host() const \ { \ if (type() != (af::dtype)dtype_traits::af_type) { \ AF_THROW_ERR("Requested type doesn't match with array", \ AF_ERR_TYPE); \ } \ void *res; \ AF_THROW(af_alloc_host(&res, bytes())); \ AF_THROW(af_get_data_ptr(res, get())); \ \ return (T*)res; \ } \ template<> AFAPI T array::scalar() const \ { \ af_dtype type = (af_dtype)af::dtype_traits::af_type; \ if (type != this->type()) \ AF_THROW_ERR("Requested type doesn't match array type", \ AF_ERR_TYPE); \ T val; \ AF_THROW(af_get_scalar(&val, get())); \ return val; \ } \ template<> AFAPI T* array::device() const \ { \ void *ptr = NULL; \ AF_THROW(af_get_device_ptr(&ptr, get())); \ return (T *)ptr; \ } \ template<> AFAPI void array::write(const T *ptr, \ const size_t bytes, \ af::source src) \ { \ if(src == afHost) { \ AF_THROW(af_write_array(get(), ptr, bytes, \ (af::source)afHost)); \ } \ if(src == afDevice) { \ AF_THROW(af_write_array(get(), ptr, bytes, \ (af::source)afDevice)); \ } \ } \ INSTANTIATE(cdouble) INSTANTIATE(cfloat) INSTANTIATE(double) INSTANTIATE(float) INSTANTIATE(unsigned) INSTANTIATE(int) INSTANTIATE(unsigned char) INSTANTIATE(char) INSTANTIATE(intl) INSTANTIATE(uintl) INSTANTIATE(short) INSTANTIATE(unsigned short) #undef INSTANTIATE template<> AFAPI void* array::device() const { void *ptr = NULL; AF_THROW(af_get_device_ptr(&ptr, get())); return (void *)ptr; } // array_proxy instanciations #define TEMPLATE_MEM_FUNC(TYPE, RETURN_TYPE, FUNC) \ template <> AFAPI \ RETURN_TYPE array::array_proxy::FUNC() const \ { \ array out = *this; \ return out.FUNC(); \ } #define INSTANTIATE(T) \ TEMPLATE_MEM_FUNC(T, T*, host) \ TEMPLATE_MEM_FUNC(T, T , scalar) \ TEMPLATE_MEM_FUNC(T, T*, device) \ INSTANTIATE(cdouble) INSTANTIATE(cfloat) INSTANTIATE(double) INSTANTIATE(float) INSTANTIATE(unsigned) INSTANTIATE(int) INSTANTIATE(unsigned char) INSTANTIATE(char) INSTANTIATE(intl) INSTANTIATE(uintl) INSTANTIATE(short) INSTANTIATE(unsigned short) #undef INSTANTIATE #undef TEMPLATE_MEM_FUNC //FIXME: These functions need to be implemented properly at a later point void array::array_proxy::unlock() const {} void array::array_proxy::lock() const {} bool array::array_proxy::isLocked() const { return false; } int array::nonzeros() const { return count(*this); } void array::lock() const { AF_THROW(af_lock_array(get())); } bool array::isLocked() const { bool res; AF_THROW(af_is_locked_array(&res, get())); return res; } void array::unlock() const { AF_THROW(af_unlock_array(get())); } void eval(int num, array **arrays) { std::vector outputs(num); for (int i = 0; i < num; i++) { outputs[i] = arrays[i]->get(); } AF_THROW(af_eval_multiple(num, &outputs[0])); } void setManualEvalFlag(bool flag) { AF_THROW(af_set_manual_eval_flag(flag)); } bool getManualEvalFlag() { bool flag; AF_THROW(af_get_manual_eval_flag(&flag)); return flag; } }