From a796a89e7aeb3d654296e1d6882df197353843f1 Mon Sep 17 00:00:00 2001 From: syurkevi Date: Tue, 5 Jul 2016 18:05:22 -0400 Subject: [PATCH 01/14] initial v34 api additions to python wrapper --- arrayfire/algorithm.py | 57 ++++++++++++++++++++++++++++++++++++++++++ arrayfire/image.py | 22 ++++++++++++++++ arrayfire/library.py | 19 ++++++++++++++ arrayfire/signal.py | 12 +++++++++ 4 files changed, 110 insertions(+) diff --git a/arrayfire/algorithm.py b/arrayfire/algorithm.py index 6701d96a8..50c9b618d 100644 --- a/arrayfire/algorithm.py +++ b/arrayfire/algorithm.py @@ -299,6 +299,63 @@ def accum(a, dim=0): """ return _parallel_dim(a, dim, backend.get().af_accum) +def scan(a, dim=0, op=BINARYOP.BINARY_ADD, inclusive_scan=True): + """ + Generalized scan of an array. + + Parameters + ---------- + a : af.Array + Multi dimensional arrayfire array. + + dim : optional: int. default: 0 + Dimension along which the scan is performed. + + op : optional: af.BINARYOP. default: af.BINARYOP.BINARY_ADD. + - Interpolation method used for resizing. + + inclusive_scan: optional: bool. default: True + Specifies if the scan is inclusive + + Returns + --------- + out : af.Array + - will contain scan of input. + """ + out = Array() + safe_call(backend.get().af_scan(ct.pointer(out.arr), a.arr, dim, op, inclusive_scan)) + return out + +def scan_by_key(key, a, dim=0, op=BINARYOP.BINARY_ADD, inclusive_scan=True): + """ + Generalized scan by key of an array. + + Parameters + ---------- + key : af.Array + key array. + + a : af.Array + Multi dimensional arrayfire array. + + dim : optional: int. default: 0 + Dimension along which the scan is performed. + + op : optional: af.BINARYOP. default: af.BINARYOP.BINARY_ADD. + - Interpolation method used for resizing. + + inclusive_scan: optional: bool. default: True + Specifies if the scan is inclusive + + Returns + --------- + out : af.Array + - will contain scan of input. + """ + out = Array() + safe_call(backend.get().af_scan_by_key(ct.pointer(out.arr), key.arr, a.arr, dim, op, inclusive_scan)) + return out + def where(a): """ Find the indices of non zero elements diff --git a/arrayfire/image.py b/arrayfire/image.py index ac4b8ed19..94a424a21 100644 --- a/arrayfire/image.py +++ b/arrayfire/image.py @@ -1198,6 +1198,28 @@ def rgb2ycbcr(image, standard=YCC_STD.BT_601): safe_call(backend.get().af_rgb2ycbcr(ct.pointer(out.arr), image.arr, standard.value)) return out +def moments(image, moment = MOMENT.MOMENT_FIRST_ORDER): + """ + Calculate image moments. + + Parameters + ---------- + image : af.Array + - A 2 D arrayfire array representing an image, or + - A multi dimensional array representing batch of images. + + moment : optional: af.MOMENT. default: af.MOMENT.MOMENT_FIRST_ORDER. + - Moment(s) to calculate + + Returns + --------- + out : af.Array + - array containing requested moment(s) of each image + """ + output = Array() + safe_call(backend.get().af_moments(ct.pointer(output.arr), image.arr, moment.value)) + return output + def is_image_io_available(): """ Function to check if the arrayfire library was built with Image IO support. diff --git a/arrayfire/library.py b/arrayfire/library.py index ff09f051d..79aa82343 100644 --- a/arrayfire/library.py +++ b/arrayfire/library.py @@ -349,6 +349,25 @@ class MARKER(_Enum): PLUS = _Enum_Type(6) STAR = _Enum_Type(7) +class MOMENT(_Enum): + """ + Image Moments + """ + MOMENT_M00 = _Enum_Type(1) + MOMENT_M01 = _Enum_Type(2) + MOMENT_M10 = _Enum_Type(4) + MOMENT_M11 = _Enum_Type(8) + MOMENT_FIRST_ORDER = _Enum_Type(15) + +class BINARYOP(_Enum): + """ + Binary Operators + """ + BINARY_ADD = _Enum_Type(0) + BINARY_MUL = _Enum_Type(1) + BINARY_MIN = _Enum_Type(2) + BINARY_MAX = _Enum_Type(3) + def _setup(): import platform import os diff --git a/arrayfire/signal.py b/arrayfire/signal.py index fe377ed1c..78f2c6f2a 100644 --- a/arrayfire/signal.py +++ b/arrayfire/signal.py @@ -1264,3 +1264,15 @@ def iir(B, A, X): Y = Array() safe_call(backend.get().af_iir(ct.pointer(Y.arr), B.arr, A.arr, X.arr)) return Y + +def set_fft_plan_cache_size(cache_size): + """ + Sets plan cache size. + + Parameters + ---------- + + cache_size : scalar + the number of plans that shall be cached + """ + safe_call(backend.get().af_set_fft_plan_cache_size(ct.c_size_t(cache_size))) From 204d9000ea2080afeb5f1591a5b7bfcfab33739d Mon Sep 17 00:00:00 2001 From: Pavan Yalamanchili Date: Thu, 22 Sep 2016 14:12:46 -0700 Subject: [PATCH 02/14] Cleaning up scan, scan_by_key and moments - Use consistent naming scheme for enums - Fixed minor bugs when passing enum to the C function --- arrayfire/algorithm.py | 24 ++++++++++++++++-------- arrayfire/image.py | 11 ++++++++--- arrayfire/library.py | 36 +++++++++++++++++++++--------------- 3 files changed, 45 insertions(+), 26 deletions(-) diff --git a/arrayfire/algorithm.py b/arrayfire/algorithm.py index 50c9b618d..a219ea663 100644 --- a/arrayfire/algorithm.py +++ b/arrayfire/algorithm.py @@ -299,7 +299,7 @@ def accum(a, dim=0): """ return _parallel_dim(a, dim, backend.get().af_accum) -def scan(a, dim=0, op=BINARYOP.BINARY_ADD, inclusive_scan=True): +def scan(a, dim=0, op=BINARYOP.ADD, inclusive_scan=True): """ Generalized scan of an array. @@ -311,8 +311,12 @@ def scan(a, dim=0, op=BINARYOP.BINARY_ADD, inclusive_scan=True): dim : optional: int. default: 0 Dimension along which the scan is performed. - op : optional: af.BINARYOP. default: af.BINARYOP.BINARY_ADD. - - Interpolation method used for resizing. + op : optional: af.BINARYOP. default: af.BINARYOP.ADD. + Binary option the scan algorithm uses. Can be one of: + - af.BINARYOP.ADD + - af.BINARYOP.MUL + - af.BINARYOP.MIN + - af.BINARYOP.MAX inclusive_scan: optional: bool. default: True Specifies if the scan is inclusive @@ -323,10 +327,10 @@ def scan(a, dim=0, op=BINARYOP.BINARY_ADD, inclusive_scan=True): - will contain scan of input. """ out = Array() - safe_call(backend.get().af_scan(ct.pointer(out.arr), a.arr, dim, op, inclusive_scan)) + safe_call(backend.get().af_scan(ct.pointer(out.arr), a.arr, dim, op.value, inclusive_scan)) return out -def scan_by_key(key, a, dim=0, op=BINARYOP.BINARY_ADD, inclusive_scan=True): +def scan_by_key(key, a, dim=0, op=BINARYOP.ADD, inclusive_scan=True): """ Generalized scan by key of an array. @@ -341,8 +345,12 @@ def scan_by_key(key, a, dim=0, op=BINARYOP.BINARY_ADD, inclusive_scan=True): dim : optional: int. default: 0 Dimension along which the scan is performed. - op : optional: af.BINARYOP. default: af.BINARYOP.BINARY_ADD. - - Interpolation method used for resizing. + op : optional: af.BINARYOP. default: af.BINARYOP.ADD. + Binary option the scan algorithm uses. Can be one of: + - af.BINARYOP.ADD + - af.BINARYOP.MUL + - af.BINARYOP.MIN + - af.BINARYOP.MAX inclusive_scan: optional: bool. default: True Specifies if the scan is inclusive @@ -353,7 +361,7 @@ def scan_by_key(key, a, dim=0, op=BINARYOP.BINARY_ADD, inclusive_scan=True): - will contain scan of input. """ out = Array() - safe_call(backend.get().af_scan_by_key(ct.pointer(out.arr), key.arr, a.arr, dim, op, inclusive_scan)) + safe_call(backend.get().af_scan_by_key(ct.pointer(out.arr), key.arr, a.arr, dim, op.value, inclusive_scan)) return out def where(a): diff --git a/arrayfire/image.py b/arrayfire/image.py index 94a424a21..842ce22fd 100644 --- a/arrayfire/image.py +++ b/arrayfire/image.py @@ -1198,7 +1198,7 @@ def rgb2ycbcr(image, standard=YCC_STD.BT_601): safe_call(backend.get().af_rgb2ycbcr(ct.pointer(out.arr), image.arr, standard.value)) return out -def moments(image, moment = MOMENT.MOMENT_FIRST_ORDER): +def moments(image, moment = MOMENT.FIRST_ORDER): """ Calculate image moments. @@ -1208,8 +1208,13 @@ def moments(image, moment = MOMENT.MOMENT_FIRST_ORDER): - A 2 D arrayfire array representing an image, or - A multi dimensional array representing batch of images. - moment : optional: af.MOMENT. default: af.MOMENT.MOMENT_FIRST_ORDER. - - Moment(s) to calculate + moment : optional: af.MOMENT. default: af.MOMENT.FIRST_ORDER. + Moment(s) to calculate. Can be one of: + - af.MOMENT.M00 + - af.MOMENT.M01 + - af.MOMENT.M10 + - af.MOMENT.M11 + - af.MOMENT.FIRST_ORDER Returns --------- diff --git a/arrayfire/library.py b/arrayfire/library.py index 79aa82343..ef108d4d6 100644 --- a/arrayfire/library.py +++ b/arrayfire/library.py @@ -113,11 +113,16 @@ class INTERP(_Enum): """ Interpolation method """ - NEAREST = _Enum_Type(0) - LINEAR = _Enum_Type(1) - BILINEAR = _Enum_Type(2) - CUBIC = _Enum_Type(3) - LOWER = _Enum_Type(4) + NEAREST = _Enum_Type(0) + LINEAR = _Enum_Type(1) + BILINEAR = _Enum_Type(2) + CUBIC = _Enum_Type(3) + LOWER = _Enum_Type(4) + LINEAR_COSINE = _Enum_Type(5) + BILINEAR_COSINE = _Enum_Type(6) + BICUBIC = _Enum_Type(7) + CUBIC_SPLINE = _Enum_Type(8) + BICUBIC_SPLINE = _Enum_Type(9) class PAD(_Enum): """ @@ -351,22 +356,23 @@ class MARKER(_Enum): class MOMENT(_Enum): """ - Image Moments + Image Moments types """ - MOMENT_M00 = _Enum_Type(1) - MOMENT_M01 = _Enum_Type(2) - MOMENT_M10 = _Enum_Type(4) - MOMENT_M11 = _Enum_Type(8) - MOMENT_FIRST_ORDER = _Enum_Type(15) + M00 = _Enum_Type(1) + M01 = _Enum_Type(2) + M10 = _Enum_Type(4) + M11 = _Enum_Type(8) + FIRST_ORDER = _Enum_Type(15) class BINARYOP(_Enum): """ Binary Operators """ - BINARY_ADD = _Enum_Type(0) - BINARY_MUL = _Enum_Type(1) - BINARY_MIN = _Enum_Type(2) - BINARY_MAX = _Enum_Type(3) + ADD = _Enum_Type(0) + MUL = _Enum_Type(1) + MIN = _Enum_Type(2) + MAX = _Enum_Type(3) + def _setup(): import platform From 48803553cb872d45f426b2299f161da26ce33a0f Mon Sep 17 00:00:00 2001 From: Pavan Yalamanchili Date: Thu, 22 Sep 2016 14:14:13 -0700 Subject: [PATCH 03/14] Adding tests for scan and scan_by_key --- arrayfire/tests/simple/algorithm.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arrayfire/tests/simple/algorithm.py b/arrayfire/tests/simple/algorithm.py index f68e354e4..75ab814d4 100644 --- a/arrayfire/tests/simple/algorithm.py +++ b/arrayfire/tests/simple/algorithm.py @@ -16,6 +16,7 @@ def simple_algorithm(verbose = False): print_func = _util.print_func(verbose) a = af.randu(3, 3) + k = af.constant(1, 3, 3, dtype=af.Dtype.u32) print_func(af.sum(a), af.product(a), af.min(a), af.max(a), af.count(a), af.any_true(a), af.all_true(a)) @@ -44,6 +45,12 @@ def simple_algorithm(verbose = False): display_func(af.accum(a, 0)) display_func(af.accum(a, 1)) + display_func(af.scan(a, 0, af.BINARYOP.ADD)) + display_func(af.scan(a, 1, af.BINARYOP.MAX)) + + display_func(af.scan_by_key(k, a, 0, af.BINARYOP.ADD)) + display_func(af.scan_by_key(k, a, 1, af.BINARYOP.MAX)) + display_func(af.sort(a, is_ascending=True)) display_func(af.sort(a, is_ascending=False)) From bdfed50cdffdf04f6bbaadb773cf3440cfca3d3a Mon Sep 17 00:00:00 2001 From: Pavan Yalamanchili Date: Thu, 22 Sep 2016 14:14:35 -0700 Subject: [PATCH 04/14] Add missing enums from arrayfire 3.4 release --- arrayfire/library.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/arrayfire/library.py b/arrayfire/library.py index ef108d4d6..cbf85f2e1 100644 --- a/arrayfire/library.py +++ b/arrayfire/library.py @@ -373,6 +373,25 @@ class BINARYOP(_Enum): MIN = _Enum_Type(2) MAX = _Enum_Type(3) +class RANDOM_ENGINE(_Enum): + """ + Random engine types + """ + PHILOX_4X32_10 = _Enum_Type(100) + THREEFRY_2X32_16 = _Enum_Type(200) + MERSENNE_GP11213 = _Enum_Type(300) + PHILOX = PHILOX_4X32_10 + THREEFRY = THREEFRY_2X32_16 + DEFAULT = PHILOX + +class STORAGE(_Enum): + """ + Matrix Storage types + """ + DENSE = _Enum_Type(0) + CSR = _Enum_Type(1) + CSC = _Enum_Type(2) + COO = _Enum_Type(3) def _setup(): import platform From 596951d9ecc16c74c446f1b21ebe3644f3c39067 Mon Sep 17 00:00:00 2001 From: Pavan Yalamanchili Date: Thu, 22 Sep 2016 14:28:34 -0700 Subject: [PATCH 05/14] FEAT: Adding clamp function and relevant tests --- arrayfire/arith.py | 38 +++++++++++++++++++++++++++++++++ arrayfire/tests/simple/arith.py | 5 +++++ 2 files changed, 43 insertions(+) diff --git a/arrayfire/arith.py b/arrayfire/arith.py index 4c73e59bf..3b397c4a5 100644 --- a/arrayfire/arith.py +++ b/arrayfire/arith.py @@ -126,6 +126,44 @@ def maxof(lhs, rhs): """ return _arith_binary_func(lhs, rhs, backend.get().af_maxof) +def clamp(val, low, high): + """ + Clamp the input value between low and high + + + Parameters + ---------- + val : af.Array + Multi dimensional arrayfire array to be clamped. + + low : af.Array or scalar + Multi dimensional arrayfire array or a scalar number denoting the lower value(s). + + high : af.Array or scalar + Multi dimensional arrayfire array or a scalar number denoting the higher value(s). + """ + out = Array() + + is_low_array = isinstance(low, Array) + is_high_array = isinstance(high, Array) + + vdims = dim4_to_tuple(val.dims()) + vty = val.type() + + if not is_low_array: + low_arr = constant_array(low, vdims[0], vdims[1], vdims[2], vdims[3], vty) + else: + low_arr = low.arr + + if not is_high_array: + high_arr = constant_array(high, vdims[0], vdims[1], vdims[2], vdims[3], vty) + else: + high_arr = high.arr + + safe_call(backend.get().af_clamp(ct.pointer(out.arr), val.arr, low_arr, high_arr, _bcast_var.get())) + + return out + def rem(lhs, rhs): """ Find the remainder. diff --git a/arrayfire/tests/simple/arith.py b/arrayfire/tests/simple/arith.py index f8407c17f..84c291aec 100644 --- a/arrayfire/tests/simple/arith.py +++ b/arrayfire/tests/simple/arith.py @@ -134,6 +134,11 @@ def simple_arith(verbose = False): display_func(af.cast(a, af.Dtype.c32)) display_func(af.maxof(a,b)) display_func(af.minof(a,b)) + + display_func(af.clamp(a, 0, 1)) + display_func(af.clamp(a, 0, b)) + display_func(af.clamp(a, b, 1)) + display_func(af.rem(a,b)) a = af.randu(3,3) - 0.5 From d8db269942e92a9eeab482d1dc5b3d6788e31122 Mon Sep 17 00:00:00 2001 From: pradeep Date: Sun, 18 Sep 2016 22:17:39 +0530 Subject: [PATCH 06/14] Check and load a functional backend Though libraries of a given backend can be present on the target machine, there can be scenarios where a compatible hardware device is not available. In such cases, backend loader checks if current selected backend has a device ready for use. If no appropriate device exists, the next backend in the priority list is checked for same criteria. This process is repeated until a working backend is found. In the event that no functional backend is found, the program throws a runtime error. --- arrayfire/library.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/arrayfire/library.py b/arrayfire/library.py index cbf85f2e1..93d3cd548 100644 --- a/arrayfire/library.py +++ b/arrayfire/library.py @@ -534,9 +534,23 @@ def __init__(self): except: pass + c_dim4 = c_dim_t*4 + + out = c_dim_t(0) + dims = c_dim4(10, 10, 10, 10) + + for key, value in self.__clibs: + err = value.af_randu(ct.pointer(out), 4, ct.pointer(dims), 0) + if (err == ERR.NONE.value): + if (self.__name != key): + self.__name = key + break + else: + self.__name = None + pass + if (self.__name is None): - raise RuntimeError("Could not load any ArrayFire libraries.\n" + - more_info_str) + raise RuntimeError("Could not load any ArrayFire libraries.\n" + more_info_str) def get_id(self, name): return self.__backend_name_map[name] From 2f8503d8a60c402f751a065334d7ce7065dbfdb0 Mon Sep 17 00:00:00 2001 From: Pavan Yalamanchili Date: Thu, 22 Sep 2016 14:45:48 -0700 Subject: [PATCH 07/14] Check if arrayfire can be run immediately after loading the library --- arrayfire/library.py | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/arrayfire/library.py b/arrayfire/library.py index 93d3cd548..fc76c9fa8 100644 --- a/arrayfire/library.py +++ b/arrayfire/library.py @@ -521,6 +521,10 @@ def __init__(self): except: pass + c_dim4 = c_dim_t*4 + out = ct.c_void_p(0) + dims = c_dim4(10, 10, 1, 1) + # Iterate in reverse order of preference for name in ('cpu', 'opencl', 'cuda', ''): libnames = self.__libname(name) @@ -528,27 +532,16 @@ def __init__(self): try: ct.cdll.LoadLibrary(libname) __name = 'unified' if name == '' else name - self.__clibs[__name] = ct.CDLL(libname) - self.__name = __name + clib = ct.CDLL(libname) + self.__clibs[__name] = clib + err = clib.af_randu(ct.pointer(out), 4, ct.pointer(dims), Dtype.f32.value) + if (err == ERR.NONE.value): + self.__name = __name + clib.af_release_array(out) break; except: pass - c_dim4 = c_dim_t*4 - - out = c_dim_t(0) - dims = c_dim4(10, 10, 10, 10) - - for key, value in self.__clibs: - err = value.af_randu(ct.pointer(out), 4, ct.pointer(dims), 0) - if (err == ERR.NONE.value): - if (self.__name != key): - self.__name = key - break - else: - self.__name = None - pass - if (self.__name is None): raise RuntimeError("Could not load any ArrayFire libraries.\n" + more_info_str) From 7594769fd905e1aff0a7f1518cf2283bc1c27710 Mon Sep 17 00:00:00 2001 From: Pavan Yalamanchili Date: Thu, 22 Sep 2016 15:26:09 -0700 Subject: [PATCH 08/14] Adding functions from array.h and device.h - is_sparse - is_locked_array - modified eval to use af_eval_multiple - set_manual_eval_flag - get_manual_eval_flag - Added necessary tests --- arrayfire/array.py | 8 +++ arrayfire/device.py | 100 ++++++++++++++++++++++++--- arrayfire/tests/simple/array_test.py | 2 + arrayfire/tests/simple/device.py | 19 +++++ 4 files changed, 119 insertions(+), 10 deletions(-) diff --git a/arrayfire/array.py b/arrayfire/array.py index 2c2eda464..4e27af691 100644 --- a/arrayfire/array.py +++ b/arrayfire/array.py @@ -667,6 +667,14 @@ def is_vector(self): safe_call(backend.get().af_is_vector(ct.pointer(res), self.arr)) return res.value + def is_sparse(self): + """ + Check if the array is a sparse matrix. + """ + res = ct.c_bool(False) + safe_call(backend.get().af_is_sparse(ct.pointer(res), self.arr)) + return res.value + def is_complex(self): """ Check if the array is of complex type. diff --git a/arrayfire/device.py b/arrayfire/device.py index 0a303851e..b13989b65 100644 --- a/arrayfire/device.py +++ b/arrayfire/device.py @@ -163,24 +163,87 @@ def sync(device=None): safe_call(backend.get().af_sync(dev)) def __eval(*args): - for A in args: - if isinstance(A, tuple): - __eval(*A) - if isinstance(A, list): - __eval(*A) - if isinstance(A, Array): - safe_call(backend.get().af_eval(A.arr)) + nargs = len(args) + if (nargs == 1): + safe_call(backend.get().af_eval(args[0].arr)) + else: + c_void_p_n = ct.c_void_p * nargs + arrs = c_void_p_n() + for n in range(nargs): + arrs[n] = args[n].arr + safe_call(backend.get().af_eval_multiple(ct.c_int(nargs), ct.pointer(arrs))) + return def eval(*args): """ - Evaluate the input + Evaluate one or more inputs together Parameters ----------- args : arguments to be evaluated + + Note + ----- + + All the input arrays to this function should be of the same size. + + Examples + -------- + + >>> a = af.constant(1, 3, 3) + >>> b = af.constant(2, 3, 3) + >>> c = a + b + >>> d = a - b + >>> af.eval(c, d) # A single kernel is launched here + >>> c + arrayfire.Array() + Type: float + [3 3 1 1] + 3.0000 3.0000 3.0000 + 3.0000 3.0000 3.0000 + 3.0000 3.0000 3.0000 + + >>> d + arrayfire.Array() + Type: float + [3 3 1 1] + -1.0000 -1.0000 -1.0000 + -1.0000 -1.0000 -1.0000 + -1.0000 -1.0000 -1.0000 + """ + for arg in args: + if not isinstance(arg, Array): + raise RuntimeError("All inputs to eval must be of type arrayfire.Array") + + __eval(*args) + +def set_manual_eval_flag(flag): + """ + Tells the backend JIT engine to disable heuristics for determining when to evaluate a JIT tree. + + Parameters + ---------- + + flag : optional: bool. + - Specifies if the heuristic evaluation of the JIT tree needs to be disabled. + + Note + ---- + This does not affect the evaluation that occurs when a non JIT function forces the evaluation. """ + safe_call(backend.get().af_set_manual_eval_flag(flag)) - __eval(args) +def get_manual_eval_flag(): + """ + Query the backend JIT engine to see if the user disabled heuristic evaluation of the JIT tree. + + Note + ---- + This does not affect the evaluation that occurs when a non JIT function forces the evaluation. + """ + res = ct.c_bool(False) + safe_call(backend.get().af_get_manual_eval_flag(ct.pointer(res))) + return res.value def device_mem_info(): """ @@ -258,10 +321,27 @@ def lock_array(a): Note ----- - - The device pointer of `a` is not freed by memory manager until `unlock_device_ptr()` is called. + - The device pointer of `a` is not freed by memory manager until `unlock_array()` is called. """ safe_call(backend.get().af_lock_array(a.arr)) +def is_locked_array(a): + """ + Check if the input array is locked by the user. + + Parameters + ---------- + a: af.Array + - A multi dimensional arrayfire array. + + Returns + ----------- + A bool specifying if the input array is locked. + """ + res = ct.c_bool(False) + safe_call(backend.get().af_is_locked_array(ct.pointer(res), a.arr)) + return res.value + def unlock_device_ptr(a): """ This functions is deprecated. Please use unlock_array instead. diff --git a/arrayfire/tests/simple/array_test.py b/arrayfire/tests/simple/array_test.py index 1aec93494..0c6ab5262 100644 --- a/arrayfire/tests/simple/array_test.py +++ b/arrayfire/tests/simple/array_test.py @@ -60,4 +60,6 @@ def simple_array(verbose=False): print_func(arr) print_func(lst) + print_func(a.is_sparse()) + _util.tests['array'] = simple_array diff --git a/arrayfire/tests/simple/device.py b/arrayfire/tests/simple/device.py index 925add294..279fa3168 100644 --- a/arrayfire/tests/simple/device.py +++ b/arrayfire/tests/simple/device.py @@ -51,4 +51,23 @@ def simple_device(verbose=False): af.lock_array(c) af.unlock_array(c) + a = af.constant(1, 3, 3) + b = af.constant(2, 3, 3) + af.eval(a) + af.eval(b) + print_func(a) + print_func(b) + c = a + b + d = a - b + af.eval(c, d) + print_func(c) + print_func(d) + + print_func(af.set_manual_eval_flag(True)) + assert(af.get_manual_eval_flag() == True) + print_func(af.set_manual_eval_flag(False)) + assert(af.get_manual_eval_flag() == False) + + display_func(af.is_locked_array(a)) + _util.tests['device'] = simple_device From fe18684550281f9fedfcaf7a76badba177a14aaa Mon Sep 17 00:00:00 2001 From: Pavan Yalamanchili Date: Fri, 23 Sep 2016 14:34:55 -0700 Subject: [PATCH 09/14] Adding random engine class and relavent methods - Also added necessary tests --- arrayfire/data.py | 100 +------------ arrayfire/features.py | 2 + arrayfire/random.py | 232 +++++++++++++++++++++++++++++++ arrayfire/tests/simple/data.py | 10 -- arrayfire/tests/simple/random.py | 38 +++++ 5 files changed, 273 insertions(+), 109 deletions(-) create mode 100644 arrayfire/random.py create mode 100644 arrayfire/tests/simple/random.py diff --git a/arrayfire/data.py b/arrayfire/data.py index 0df045fca..786d64902 100644 --- a/arrayfire/data.py +++ b/arrayfire/data.py @@ -16,6 +16,7 @@ from .array import * from .util import * from .util import _is_number +from .random import randu, randn, set_seed, get_seed def constant(val, d0, d1=None, d2=None, d3=None, dtype=Dtype.f32): """ @@ -186,105 +187,6 @@ def iota(d0, d1=None, d2=None, d3=None, dim=-1, tile_dims=None, dtype=Dtype.f32) 4, ct.pointer(tdims), dtype.value)) return out -def randu(d0, d1=None, d2=None, d3=None, dtype=Dtype.f32): - """ - Create a multi dimensional array containing values from a uniform distribution. - - Parameters - ---------- - d0 : int. - Length of first dimension. - - d1 : optional: int. default: None. - Length of second dimension. - - d2 : optional: int. default: None. - Length of third dimension. - - d3 : optional: int. default: None. - Length of fourth dimension. - - dtype : optional: af.Dtype. default: af.Dtype.f32. - Data type of the array. - - Returns - ------- - - out : af.Array - Multi dimensional array whose elements are sampled uniformly between [0, 1]. - - If d1 is None, `out` is 1D of size (d0,). - - If d1 is not None and d2 is None, `out` is 2D of size (d0, d1). - - If d1 and d2 are not None and d3 is None, `out` is 3D of size (d0, d1, d2). - - If d1, d2, d3 are all not None, `out` is 4D of size (d0, d1, d2, d3). - """ - out = Array() - dims = dim4(d0, d1, d2, d3) - - safe_call(backend.get().af_randu(ct.pointer(out.arr), 4, ct.pointer(dims), dtype.value)) - return out - -def randn(d0, d1=None, d2=None, d3=None, dtype=Dtype.f32): - """ - Create a multi dimensional array containing values from a normal distribution. - - Parameters - ---------- - d0 : int. - Length of first dimension. - - d1 : optional: int. default: None. - Length of second dimension. - - d2 : optional: int. default: None. - Length of third dimension. - - d3 : optional: int. default: None. - Length of fourth dimension. - - dtype : optional: af.Dtype. default: af.Dtype.f32. - Data type of the array. - - Returns - ------- - - out : af.Array - Multi dimensional array whose elements are sampled from a normal distribution with mean 0 and sigma of 1. - - If d1 is None, `out` is 1D of size (d0,). - - If d1 is not None and d2 is None, `out` is 2D of size (d0, d1). - - If d1 and d2 are not None and d3 is None, `out` is 3D of size (d0, d1, d2). - - If d1, d2, d3 are all not None, `out` is 4D of size (d0, d1, d2, d3). - """ - - out = Array() - dims = dim4(d0, d1, d2, d3) - - safe_call(backend.get().af_randn(ct.pointer(out.arr), 4, ct.pointer(dims), dtype.value)) - return out - -def set_seed(seed=0): - """ - Set the seed for the random number generator. - - Parameters - ---------- - seed: int. - Seed for the random number generator - """ - safe_call(backend.get().af_set_seed(ct.c_ulonglong(seed))) - -def get_seed(): - """ - Get the seed for the random number generator. - - Returns - ---------- - seed: int. - Seed for the random number generator - """ - seed = ct.c_ulonglong(0) - safe_call(backend.get().af_get_seed(ct.pointer(seed))) - return seed.value - def identity(d0, d1, d2=None, d3=None, dtype=Dtype.f32): """ Create an identity matrix or batch of identity matrices. diff --git a/arrayfire/features.py b/arrayfire/features.py index 21705e260..6e006afcc 100644 --- a/arrayfire/features.py +++ b/arrayfire/features.py @@ -6,9 +6,11 @@ # The complete license agreement can be obtained at: # http://arrayfire.com/licenses/BSD-3-Clause ######################################################## + """ Features class used for Computer Vision algorithms. """ + from .library import * from .array import * import numbers diff --git a/arrayfire/random.py b/arrayfire/random.py new file mode 100644 index 000000000..60148a8da --- /dev/null +++ b/arrayfire/random.py @@ -0,0 +1,232 @@ +####################################################### +# 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 +######################################################## + +""" +Random engine class and functions to generate random numbers. +""" + +from .library import * +from .array import * +import numbers + +class Random_Engine(object): + """ + Class to handle random number generator engines. + + Parameters + ---------- + + engine_type : optional: RANDOME_ENGINE. default: RANDOM_ENGINE.PHILOX + - Specifies the type of random engine to be created. Can be one of: + - RANDOM_ENGINE.PHILOX_4X32_10 + - RANDOM_ENGINE.THREEFRY_2X32_16 + - RANDOM_ENGINE.MERSENNE_GP11213 + - RANDOM_ENGINE.PHILOX (same as RANDOM_ENGINE.PHILOX_4X32_10) + - RANDOM_ENGINE.THREEFRY (same as RANDOM_ENGINE.THREEFRY_2X32_16) + - RANDOM_ENGINE.DEFAULT + - Not used if engine is not None + + seed : optional int. default: 0 + - Specifies the seed for the random engine + - Not used if engine is not None + + engine : optional ctypes.c_void_p. default: None. + - Used a handle created by the C api to create the Random_Engine. + """ + + def __init__(self, engine_type = RANDOM_ENGINE.PHILOX, seed = 0, engine = None): + if (engine is None): + self.engine = ct.c_void_p(0) + safe_call(backend.get().af_create_random_engine(ct.pointer(self.engine), engine_type.value, ct.c_longlong(seed))) + else: + self.engine = engine + + def __del__(self): + safe_call(backend.get().af_release_random_engine(self.engine)) + + def set_type(self, engine_type): + """ + Set the type of the random engine. + """ + safe_call(backend.get().af_random_engine_set_type(ct.pointer(self.engine), engine_type.value)) + + def get_type(self): + """ + Get the type of the random engine. + """ + __to_random_engine_type = [RANDOM_ENGINE.PHILOX_4X32_10, + RANDOM_ENGINE.THREEFRY_2X32_16, + RANDOM_ENGINE.MERSENNE_GP11213] + rty = ct.c_int(RANDOM_ENGINE.PHILOX.value) + safe_call(backend.get().af_random_engine_get_type(ct.pointer(rty), self.engine)) + return __to_random_engine_type[rty] + + def set_seed(self, seed): + """ + Set the seed for the random engine. + """ + safe_call(backend.get().af_random_engine_set_seed(ct.pointer(self.engine), ct.c_longlong(seed))) + + def get_seed(self): + """ + Get the seed for the random engine. + """ + seed = ct.c_longlong(0) + safe_call(backend.get().af_random_engine_get_seed(ct.pointer(seed), self.engine)) + return seed.value + +def randu(d0, d1=None, d2=None, d3=None, dtype=Dtype.f32, random_engine=None): + """ + Create a multi dimensional array containing values from a uniform distribution. + + Parameters + ---------- + d0 : int. + Length of first dimension. + + d1 : optional: int. default: None. + Length of second dimension. + + d2 : optional: int. default: None. + Length of third dimension. + + d3 : optional: int. default: None. + Length of fourth dimension. + + dtype : optional: af.Dtype. default: af.Dtype.f32. + Data type of the array. + + random_engine : optional: Random_Engine. default: None. + If random_engine is None, uses a default engine created by arrayfire. + + Returns + ------- + + out : af.Array + Multi dimensional array whose elements are sampled uniformly between [0, 1]. + - If d1 is None, `out` is 1D of size (d0,). + - If d1 is not None and d2 is None, `out` is 2D of size (d0, d1). + - If d1 and d2 are not None and d3 is None, `out` is 3D of size (d0, d1, d2). + - If d1, d2, d3 are all not None, `out` is 4D of size (d0, d1, d2, d3). + """ + out = Array() + dims = dim4(d0, d1, d2, d3) + + if random_engine is None: + safe_call(backend.get().af_randu(ct.pointer(out.arr), 4, ct.pointer(dims), dtype.value)) + else: + safe_call(backend.get().af_random_uniform(ct.pointer(out.arr), 4, ct.pointer(dims), random_engine.engine)) + + return out + +def randn(d0, d1=None, d2=None, d3=None, dtype=Dtype.f32, random_engine=None): + """ + Create a multi dimensional array containing values from a normal distribution. + + Parameters + ---------- + d0 : int. + Length of first dimension. + + d1 : optional: int. default: None. + Length of second dimension. + + d2 : optional: int. default: None. + Length of third dimension. + + d3 : optional: int. default: None. + Length of fourth dimension. + + dtype : optional: af.Dtype. default: af.Dtype.f32. + Data type of the array. + + random_engine : optional: Random_Engine. default: None. + If random_engine is None, uses a default engine created by arrayfire. + + Returns + ------- + + out : af.Array + Multi dimensional array whose elements are sampled from a normal distribution with mean 0 and sigma of 1. + - If d1 is None, `out` is 1D of size (d0,). + - If d1 is not None and d2 is None, `out` is 2D of size (d0, d1). + - If d1 and d2 are not None and d3 is None, `out` is 3D of size (d0, d1, d2). + - If d1, d2, d3 are all not None, `out` is 4D of size (d0, d1, d2, d3). + """ + + out = Array() + dims = dim4(d0, d1, d2, d3) + + if random_engine is None: + safe_call(backend.get().af_randn(ct.pointer(out.arr), 4, ct.pointer(dims), dtype.value)) + else: + safe_call(backend.get().af_random_normal(ct.pointer(out.arr), 4, ct.pointer(dims), random_engine.engine)) + + return out + +def set_seed(seed=0): + """ + Set the seed for the random number generator. + + Parameters + ---------- + seed: int. + Seed for the random number generator + """ + safe_call(backend.get().af_set_seed(ct.c_ulonglong(seed))) + +def get_seed(): + """ + Get the seed for the random number generator. + + Returns + ---------- + seed: int. + Seed for the random number generator + """ + seed = ct.c_ulonglong(0) + safe_call(backend.get().af_get_seed(ct.pointer(seed))) + return seed.value + +def set_default_random_engine_type(engine_type): + """ + Set random engine type for default random engine. + + Parameters + ---------- + engine_type : RANDOME_ENGINE. + - Specifies the type of random engine to be created. Can be one of: + - RANDOM_ENGINE.PHILOX_4X32_10 + - RANDOM_ENGINE.THREEFRY_2X32_16 + - RANDOM_ENGINE.MERSENNE_GP11213 + - RANDOM_ENGINE.PHILOX (same as RANDOM_ENGINE.PHILOX_4X32_10) + - RANDOM_ENGINE.THREEFRY (same as RANDOM_ENGINE.THREEFRY_2X32_16) + - RANDOM_ENGINE.DEFAULT + + Note + ---- + + This only affects randu and randn when a random engine is not specified. + """ + safe_call(backend.get().af_set_default_random_engine_type(ct.pointer(self.engine), engine_type.value)) + +def get_default_random_engine(): + """ + Get the default random engine + + Returns + ------ + + The default random engine used by randu and randn + """ + engine = ct.c_void_p(0) + default_engine = ct.c_void_p(0) + safe_call(backend.get().af_get_default_random_engine(ct.pointer(default_engine))) + safe_call(backend.get().af_retain_random_engine(ct.pointer(engine), default_engine)) + return Random_Engine(engine=engine) diff --git a/arrayfire/tests/simple/data.py b/arrayfire/tests/simple/data.py index 7c95a8194..86b900baf 100644 --- a/arrayfire/tests/simple/data.py +++ b/arrayfire/tests/simple/data.py @@ -24,16 +24,6 @@ def simple_data(verbose=False): display_func(af.range(3, 3)) display_func(af.iota(3, 3, tile_dims=(2,2))) - display_func(af.randu(3, 3, 1, 2)) - display_func(af.randu(3, 3, 1, 2, af.Dtype.b8)) - display_func(af.randu(3, 3, dtype=af.Dtype.c32)) - - display_func(af.randn(3, 3, 1, 2)) - display_func(af.randn(3, 3, dtype=af.Dtype.c32)) - - af.set_seed(1024) - assert(af.get_seed() == 1024) - display_func(af.identity(3, 3, 1, 2, af.Dtype.b8)) display_func(af.identity(3, 3, dtype=af.Dtype.c32)) diff --git a/arrayfire/tests/simple/random.py b/arrayfire/tests/simple/random.py new file mode 100644 index 000000000..6b72a910a --- /dev/null +++ b/arrayfire/tests/simple/random.py @@ -0,0 +1,38 @@ +#!/usr/bin/python +####################################################### +# 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 +######################################################## + +import arrayfire as af +from . import _util + +def simple_random(verbose=False): + display_func = _util.display_func(verbose) + print_func = _util.print_func(verbose) + + display_func(af.randu(3, 3, 1, 2)) + display_func(af.randu(3, 3, 1, 2, af.Dtype.b8)) + display_func(af.randu(3, 3, dtype=af.Dtype.c32)) + + display_func(af.randn(3, 3, 1, 2)) + display_func(af.randn(3, 3, dtype=af.Dtype.c32)) + + af.set_seed(1024) + assert(af.get_seed() == 1024) + + engine = Random_Engine(RANDOM_ENGINE.MERSENNE_GP11213, 100) + + display_func(af.randu(3, 3, 1, 2, engine=engine)) + display_func(af.randu(3, 3, 1, 2, af.Dtype.s32, engine=engine)) + display_func(af.randu(3, 3, dtype=af.Dtype.c32, engine=engine)) + + display_func(af.randn(3, 3, engine=engine)) + engine.set_seed(100) + assert(engine.get_seed() == 100) + +_util.tests['random'] = simple_random From 208465d1cd72542922876e6ed897765137871661 Mon Sep 17 00:00:00 2001 From: Pavan Yalamanchili Date: Fri, 23 Sep 2016 14:46:22 -0700 Subject: [PATCH 10/14] Adding medfilt1 and medfilt2 and necessary tests --- arrayfire/image.py | 33 +----------- arrayfire/signal.py | 91 ++++++++++++++++++++++++++++++++ arrayfire/tests/simple/signal.py | 4 ++ 3 files changed, 96 insertions(+), 32 deletions(-) diff --git a/arrayfire/image.py b/arrayfire/image.py index 842ce22fd..7ddb6de9a 100644 --- a/arrayfire/image.py +++ b/arrayfire/image.py @@ -14,6 +14,7 @@ from .library import * from .array import * from .data import constant +from .signal import medfilt import os def gradient(image): @@ -619,38 +620,6 @@ def mean_shift(image, s_sigma, c_sigma, n_iter, is_color = False): ct.c_uint(n_iter), is_color)) return output -def medfilt(image, w0 = 3, w1 = 3, edge_pad = PAD.ZERO): - """ - Apply median filter for the image. - - Parameters - ---------- - image : af.Array - - A 2 D arrayfire array representing an image, or - - A multi dimensional array representing batch of images. - - w0 : optional: int. default: 3. - - The length of the filter along the first dimension. - - w1 : optional: int. default: 3. - - The length of the filter along the second dimension. - - edge_pad : optional: af.PAD. default: af.PAD.ZERO - - Flag specifying how the median at the edge should be treated. - - Returns - --------- - - output : af.Array - - The image after median filter is applied. - - """ - output = Array() - safe_call(backend.get().af_medfilt(ct.pointer(output.arr), - image.arr, c_dim_t(w0), - c_dim_t(w1), edge_pad.value)) - return output - def minfilt(image, w_len = 3, w_wid = 3, edge_pad = PAD.ZERO): """ Apply min filter for the image. diff --git a/arrayfire/signal.py b/arrayfire/signal.py index 78f2c6f2a..363ed248c 100644 --- a/arrayfire/signal.py +++ b/arrayfire/signal.py @@ -1265,6 +1265,97 @@ def iir(B, A, X): safe_call(backend.get().af_iir(ct.pointer(Y.arr), B.arr, A.arr, X.arr)) return Y +def medfilt(signal, w0 = 3, w1 = 3, edge_pad = PAD.ZERO): + """ + Apply median filter for the signal. + + Parameters + ---------- + signal : af.Array + - A 2 D arrayfire array representing a signal, or + - A multi dimensional array representing batch of signals. + + w0 : optional: int. default: 3. + - The length of the filter along the first dimension. + + w1 : optional: int. default: 3. + - The length of the filter along the second dimension. + + edge_pad : optional: af.PAD. default: af.PAD.ZERO + - Flag specifying how the median at the edge should be treated. + + Returns + --------- + + output : af.Array + - The signal after median filter is applied. + + """ + output = Array() + safe_call(backend.get().af_medfilt(ct.pointer(output.arr), + signal.arr, c_dim_t(w0), + c_dim_t(w1), edge_pad.value)) + return output + +def medfilt1(signal, length = 3, edge_pad = PAD.ZERO): + """ + Apply median filter for the signal. + + Parameters + ---------- + signal : af.Array + - A 1 D arrayfire array representing a signal, or + - A multi dimensional array representing batch of signals. + + length : optional: int. default: 3. + - The length of the filter. + + edge_pad : optional: af.PAD. default: af.PAD.ZERO + - Flag specifying how the median at the edge should be treated. + + Returns + --------- + + output : af.Array + - The signal after median filter is applied. + + """ + output = Array() + safe_call(backend.get().af_medfilt1(ct.pointer(output.arr), signal.arr, c_dim_t(length), edge_pad.value)) + return output + +def medfilt2(signal, w0 = 3, w1 = 3, edge_pad = PAD.ZERO): + """ + Apply median filter for the signal. + + Parameters + ---------- + signal : af.Array + - A 2 D arrayfire array representing a signal, or + - A multi dimensional array representing batch of signals. + + w0 : optional: int. default: 3. + - The length of the filter along the first dimension. + + w1 : optional: int. default: 3. + - The length of the filter along the second dimension. + + edge_pad : optional: af.PAD. default: af.PAD.ZERO + - Flag specifying how the median at the edge should be treated. + + Returns + --------- + + output : af.Array + - The signal after median filter is applied. + + """ + output = Array() + safe_call(backend.get().af_medfilt2(ct.pointer(output.arr), + signal.arr, c_dim_t(w0), + c_dim_t(w1), edge_pad.value)) + return output + def set_fft_plan_cache_size(cache_size): """ Sets plan cache size. diff --git a/arrayfire/tests/simple/signal.py b/arrayfire/tests/simple/signal.py index 03d89b4a8..817685c5a 100644 --- a/arrayfire/tests/simple/signal.py +++ b/arrayfire/tests/simple/signal.py @@ -110,4 +110,8 @@ def simple_signal(verbose=False): display_func(af.fir(b, x)) display_func(af.iir(b, a, x)) + display_func(af.medfilt1(a)) + display_func(af.medfilt2(a)) + display_func(af.medfilt(a)) + _util.tests['signal'] = simple_signal From ef44a7a21f7118616c23c377d4257ca039b858a6 Mon Sep 17 00:00:00 2001 From: Pavan Yalamanchili Date: Fri, 23 Sep 2016 16:09:21 -0700 Subject: [PATCH 11/14] Adding new graphics functions from v3.4 --- arrayfire/graphics.py | 225 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 208 insertions(+), 17 deletions(-) diff --git a/arrayfire/graphics.py b/arrayfire/graphics.py index 3d0b3f213..59da8dbf7 100644 --- a/arrayfire/graphics.py +++ b/arrayfire/graphics.py @@ -140,9 +140,9 @@ def image(self, img, title=None): _cell = _Cell(self._r, self._c, title, self._cmap) safe_call(backend.get().af_draw_image(self._wnd, img.arr, ct.pointer(_cell))) - def scatter(self, X, Y, marker=MARKER.POINT, title=None): + def scatter(self, X, Y, Z=None, points=None, marker=MARKER.POINT, title=None): """ - Renders input arrays as 2D scatter plot. + Renders input arrays as 2D or 3D scatter plot. Paramters --------- @@ -153,67 +153,212 @@ def scatter(self, X, Y, marker=MARKER.POINT, title=None): Y: af.Array. A 1 dimensional array containing Y co-ordinates. + Z: optional: af.Array. default: None. + - A 1 dimensional array containing Z co-ordinates. + - Not used if line is not None + + points: optional: af.Array. default: None. + - A 2 dimensional array of size [n 2]. Each column denotes X and Y co-ordinates for 2D scatter plot. + - A 3 dimensional array of size [n 3]. Each column denotes X, Y, and Z co-ordinates for 3D scatter plot. + + marker: af.MARKER + Specifies how the points look + + title: str. + Title used for the plot. + """ + _cell = _Cell(self._r, self._c, title, self._cmap) + + if points is None: + if Z is None: + safe_call(backend.get().af_draw_scatter_2d(self._wnd, X.arr, Y.arr, + marker.value, ct.pointer(_cell))) + else: + safe_call(backend.get().af_draw_scatter_3d(self._wnd, X.arr, Y.arr, Z.arr, + marker.value, ct.pointer(_cell))) + else: + safe_call(backend.get().af_draw_scatter_nd(self._wnd, points.arr, marker.value, ct.pointer(_cell))) + + def scatter2(self, points, marker=MARKER.POINT, title=None): + """ + Renders the input array as a 2D Scatter plot. + + Paramters + --------- + + points: af.Array. + A 2 dimensional array containing (X,Y) co-ordinates. + marker: af.MARKER Specifies how the points look title: str. Title used for the plot. """ + assert(points.numdims() == 2) _cell = _Cell(self._r, self._c, title, self._cmap) - safe_call(backend.get().af_draw_scatter(self._wnd, X.arr, Y.arr, - marker.value, ct.pointer(_cell))) + safe_call(backend.get().af_draw_scatter2(self._wnd, points.arr, + marker.value, ct.pointer(_cell))) - def scatter3(self, P, marker=MARKER.POINT, title=None): + def scatter3(self, points, marker=MARKER.POINT, title=None): """ Renders the input array as a 3D Scatter plot. Paramters --------- - P: af.Array. + points: af.Array. A 2 dimensional array containing (X,Y,Z) co-ordinates. + marker: af.MARKER + Specifies how the points look + title: str. Title used for the plot. """ + assert(points.numdims() == 3) _cell = _Cell(self._r, self._c, title, self._cmap) - safe_call(backend.get().af_draw_scatter3(self._wnd, P.arr, + safe_call(backend.get().af_draw_scatter3(self._wnd, points.arr, marker.value, ct.pointer(_cell))) - - def plot(self, X, Y, title=None): + def plot(self, X, Y, Z=None, line = None, title=None): """ - Display a 2D Plot. + Display a 2D or 3D Plot. Paramters --------- X: af.Array. - A 1 dimensional array containing X co-ordinates. + - A 1 dimensional array containing X co-ordinates. + - Not used if line is not None Y: af.Array. - A 1 dimensional array containing Y co-ordinates. + - A 1 dimensional array containing Y co-ordinates. + - Not used if line is not None + + Z: optional: af.Array. default: None. + - A 1 dimensional array containing Z co-ordinates. + - Not used if line is not None + + line: optional: af.Array. default: None. + - A 2 dimensional array of size [n 2]. Each column denotes X and Y co-ordinates for plotting 2D lines. + - A 3 dimensional array of size [n 3]. Each column denotes X, Y, and Z co-ordinates for plotting 3D lines. title: str. Title used for the plot. + + Note + ---- + + The line parameter takes precedence. """ _cell = _Cell(self._r, self._c, title, self._cmap) - safe_call(backend.get().af_draw_plot(self._wnd, X.arr, Y.arr, ct.pointer(_cell))) + if line is None: + if Z is None: + safe_call(backend.get().af_draw_plot_2d(self._wnd, X.arr, Y.arr, ct.pointer(_cell))) + else: + safe_call(backend.get().af_draw_plot_3d(self._wnd, X.arr, Y.arr, Z.arr, ct.pointer(_cell))) + else: + safe_call(backend.get().af_draw_plot_nd(self._wnd, line.arr, ct.pointer(_cell))) - def plot3(self, line, title=None): + def plot2(self, line, title=None): """ - Renders the input array as a 3D line plot. + Display a 2D Plot. Paramters --------- line: af.Array. - A 2 dimensional array containing (X,Y,Z) co-ordinates. + - A 2 dimensional array of size [n 2]. Each column denotes X, and Y co-ordinates for plotting 2D lines. title: str. Title used for the plot. + """ + + assert(line.numdims() == 2) _cell = _Cell(self._r, self._c, title, self._cmap) - safe_call(backend.get().af_draw_plot3(self._wnd, line.arr, ct.pointer(_cell))) + safe_call(backend.get().af_draw_plot_nd(self._wnd, line.arr, ct.pointer(_cell))) + + def plot3(self, X=None, Y=None, Z=None, line=None, title=None): + """ + Display a 3D Plot. + + Paramters + --------- + + line: af.Array. + - A 3 dimensional array of size [n 3]. Each column denotes X, Y, and Z co-ordinates for plotting 3D lines. + + title: str. + Title used for the plot. + """ + + assert(line.numdims() == 3) + _cell = _Cell(self._r, self._c, title, self._cmap) + safe_call(backend.get().af_draw_plot_nd(self._wnd, line.arr, ct.pointer(_cell))) + + def vector_field(self, xpoints, xdirs, ypoints, ydirs, zpoints=None, zdirs=None, + points = None, dirs = None, title=None): + """ + Display a 2D or 3D Vector_Field. + + Paramters + --------- + + xpoints : af.Array. + - A 1 dimensional array containing X co-ordinates. + - Not used if points is not None + + xdirs : af.Array. + - A 1 dimensional array specifying direction at current location. + - Not used if dirs is not None + + ypoints : af.Array. + - A 1 dimensional array containing Y co-ordinates. + - Not used if points is not None + + ydirs : af.Array. + - A 1 dimensional array specifying direction at current location. + - Not used if dirs is not None + + zpoints : optional: af.Array. default: None. + - A 1 dimensional array containing Z co-ordinates. + - Not used if points is not None + + zdirs : optional: af.Array. default: none. + - A 1 dimensional array specifying direction at current location. + - Not used if dirs is not None + + points : optional: af.Array. default: None. + - A 2 dimensional array of size [n 2]. Each column denotes X and Y co-ordinates for plotting 2D lines. + - A 3 dimensional array of size [n 3]. Each column denotes X, Y, and Z co-ordinates for plotting 3D lines. + + dirs : optional: af.Array. default: None. + - A 2 dimensional array of size [n 2]. Each column denotes X and Y directions for plotting 2D lines. + - A 3 dimensional array of size [n 3]. Each column denotes X, Y, and Z directions for plotting 3D lines. + + title : str. + Title used for the plot. + + Note + ---- + + The line parameter takes precedence. + """ + _cell = _Cell(self._r, self._c, title, self._cmap) + if line is None: + if Z is None: + safe_call(backend.get().af_draw_vector_field_2d(self._wnd, + xpoints.arr, ypoints.arr, + xdirs.arr, ydirs.arr, + ct.pointer(_cell))) + else: + safe_call(backend.get().af_draw_vector_field_2d(self._wnd, + xpoints.arr, ypoints.arr, zpoints.arr, + xdirs.arr, ydirs.arr, zdirs.arr, + ct.pointer(_cell))) + else: + safe_call(backend.get().af_draw_plot_nd(self._wnd, points.arr, dirs.arr, ct.pointer(_cell))) def surface(self, x_vals, y_vals, z_vals, title=None): """ @@ -305,6 +450,52 @@ def set_visibility(is_visible): """ safe_call(backend.get().af_set_visibility(self._wnd, is_visible)) + def set_axes_limits(self, xmin, xmax, ymin, ymax, zmin=None, zmax=None, exact=False): + """ + Set axis limits. + + Paramters + --------- + + xmin : af.Array. + - lower limit of the x axis. + + xmax : af.Array. + - upper limit of the x axis. + + ymin : af.Array. + - lower limit of the y axis. + + ymax : af.Array. + - upper limit of the y axis. + + zmin : optional: af.Array. default: None. + - lower limit of the z axis. + + zmax : optional: af.Array. default: None. + - upper limit of the z axis. + + title : str. + Title used for the plot. + + Note + ---- + + The line parameter takes precedence. + """ + _cell = _Cell(self._r, self._c, "", self._cmap) + if (zmin is None or zmax is None): + safe_call(backend.get().af_set_axes_limits_2d(self._wnd, + ct.c_float(xmin), ct.c_float(xmax), + ct.c_float(ymin), ct.c_float(ymax), + exact, ct.pointer(_cell))) + else: + safe_call(backend.get().af_set_axes_limits_2d(self._wnd, + ct.c_float(xmin), ct.c_float(xmax), + ct.c_float(ymin), ct.c_float(ymax), + ct.c_float(zmin), ct.c_float(zmax), + exact, ct.pointer(_cell))) + def __getitem__(self, keys): """ Get access to a specific grid location within the window. From 4b3369f664453c27a79a223dac3432b22bcc35f8 Mon Sep 17 00:00:00 2001 From: Pavan Yalamanchili Date: Fri, 23 Sep 2016 16:15:03 -0700 Subject: [PATCH 12/14] Adding get_size_of --- arrayfire/library.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arrayfire/library.py b/arrayfire/library.py index fc76c9fa8..c0541620d 100644 --- a/arrayfire/library.py +++ b/arrayfire/library.py @@ -671,4 +671,12 @@ def get_device_id(A): safe_call(backend.get().af_get_device_id(ct.pointer(device_id), A.arr)) return device_id +def get_size_of(dtype): + """ + Get the size of the type represented by arrayfire.Dtype + """ + size = ct.c_size_t(0) + safe_call(backend.get().af_get_size_of(ct.pointer(size), dtype.value)) + return size.value + from .util import safe_call From 85465c2fae89da37f4d6f0452383b18ab08f6390 Mon Sep 17 00:00:00 2001 From: Pavan Yalamanchili Date: Fri, 23 Sep 2016 17:36:35 -0700 Subject: [PATCH 13/14] Fixing bugs in random and random tests --- arrayfire/__init__.py | 1 + arrayfire/random.py | 20 ++++++++++---------- arrayfire/tests/simple/__init__.py | 1 + arrayfire/tests/simple/random.py | 2 +- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/arrayfire/__init__.py b/arrayfire/__init__.py index 74d1dca43..c392d24be 100644 --- a/arrayfire/__init__.py +++ b/arrayfire/__init__.py @@ -72,6 +72,7 @@ from .index import * from .interop import * from .timer import * +from .random import * # do not export default modules as part of arrayfire del ct diff --git a/arrayfire/random.py b/arrayfire/random.py index 60148a8da..d95fa9081 100644 --- a/arrayfire/random.py +++ b/arrayfire/random.py @@ -81,7 +81,7 @@ def get_seed(self): safe_call(backend.get().af_random_engine_get_seed(ct.pointer(seed), self.engine)) return seed.value -def randu(d0, d1=None, d2=None, d3=None, dtype=Dtype.f32, random_engine=None): +def randu(d0, d1=None, d2=None, d3=None, dtype=Dtype.f32, engine=None): """ Create a multi dimensional array containing values from a uniform distribution. @@ -102,8 +102,8 @@ def randu(d0, d1=None, d2=None, d3=None, dtype=Dtype.f32, random_engine=None): dtype : optional: af.Dtype. default: af.Dtype.f32. Data type of the array. - random_engine : optional: Random_Engine. default: None. - If random_engine is None, uses a default engine created by arrayfire. + engine : optional: Random_Engine. default: None. + If engine is None, uses a default engine created by arrayfire. Returns ------- @@ -118,14 +118,14 @@ def randu(d0, d1=None, d2=None, d3=None, dtype=Dtype.f32, random_engine=None): out = Array() dims = dim4(d0, d1, d2, d3) - if random_engine is None: + if engine is None: safe_call(backend.get().af_randu(ct.pointer(out.arr), 4, ct.pointer(dims), dtype.value)) else: - safe_call(backend.get().af_random_uniform(ct.pointer(out.arr), 4, ct.pointer(dims), random_engine.engine)) + safe_call(backend.get().af_random_uniform(ct.pointer(out.arr), 4, ct.pointer(dims), dtype.value, engine.engine)) return out -def randn(d0, d1=None, d2=None, d3=None, dtype=Dtype.f32, random_engine=None): +def randn(d0, d1=None, d2=None, d3=None, dtype=Dtype.f32, engine=None): """ Create a multi dimensional array containing values from a normal distribution. @@ -146,8 +146,8 @@ def randn(d0, d1=None, d2=None, d3=None, dtype=Dtype.f32, random_engine=None): dtype : optional: af.Dtype. default: af.Dtype.f32. Data type of the array. - random_engine : optional: Random_Engine. default: None. - If random_engine is None, uses a default engine created by arrayfire. + engine : optional: Random_Engine. default: None. + If engine is None, uses a default engine created by arrayfire. Returns ------- @@ -163,10 +163,10 @@ def randn(d0, d1=None, d2=None, d3=None, dtype=Dtype.f32, random_engine=None): out = Array() dims = dim4(d0, d1, d2, d3) - if random_engine is None: + if engine is None: safe_call(backend.get().af_randn(ct.pointer(out.arr), 4, ct.pointer(dims), dtype.value)) else: - safe_call(backend.get().af_random_normal(ct.pointer(out.arr), 4, ct.pointer(dims), random_engine.engine)) + safe_call(backend.get().af_random_normal(ct.pointer(out.arr), 4, ct.pointer(dims), dtype.value, engine.engine)) return out diff --git a/arrayfire/tests/simple/__init__.py b/arrayfire/tests/simple/__init__.py index 465197202..0a1588ed8 100644 --- a/arrayfire/tests/simple/__init__.py +++ b/arrayfire/tests/simple/__init__.py @@ -18,4 +18,5 @@ from .lapack import * from .signal import * from .statistics import * +from .random import * from ._util import tests diff --git a/arrayfire/tests/simple/random.py b/arrayfire/tests/simple/random.py index 6b72a910a..544389836 100644 --- a/arrayfire/tests/simple/random.py +++ b/arrayfire/tests/simple/random.py @@ -25,7 +25,7 @@ def simple_random(verbose=False): af.set_seed(1024) assert(af.get_seed() == 1024) - engine = Random_Engine(RANDOM_ENGINE.MERSENNE_GP11213, 100) + engine = af.Random_Engine(af.RANDOM_ENGINE.MERSENNE_GP11213, 100) display_func(af.randu(3, 3, 1, 2, engine=engine)) display_func(af.randu(3, 3, 1, 2, af.Dtype.s32, engine=engine)) From 3637f589ba20297f06f47d7150a7ea6e6d3f3e91 Mon Sep 17 00:00:00 2001 From: Pavan Yalamanchili Date: Fri, 23 Sep 2016 17:37:21 -0700 Subject: [PATCH 14/14] Adding functions to create and manipulate sparse matrices --- arrayfire/__init__.py | 1 + arrayfire/sparse.py | 275 +++++++++++++++++++++++++++++ arrayfire/tests/simple/__init__.py | 1 + arrayfire/tests/simple/sparse.py | 28 +++ 4 files changed, 305 insertions(+) create mode 100644 arrayfire/sparse.py create mode 100644 arrayfire/tests/simple/sparse.py diff --git a/arrayfire/__init__.py b/arrayfire/__init__.py index c392d24be..255fbbeeb 100644 --- a/arrayfire/__init__.py +++ b/arrayfire/__init__.py @@ -73,6 +73,7 @@ from .interop import * from .timer import * from .random import * +from .sparse import * # do not export default modules as part of arrayfire del ct diff --git a/arrayfire/sparse.py b/arrayfire/sparse.py new file mode 100644 index 000000000..9a4c30460 --- /dev/null +++ b/arrayfire/sparse.py @@ -0,0 +1,275 @@ +####################################################### +# 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 +######################################################## + +""" +Functions to create and manipulate sparse matrices. +""" + +from .library import * +from .array import * +import numbers +from .interop import to_array + +__to_sparse_enum = [STORAGE.DENSE, + STORAGE.CSR, + STORAGE.CSC, + STORAGE.COO] + + +def sparse(values, row_idx, col_idx, nrows, ncols, storage = STORAGE.CSR): + """ + Create a sparse matrix from it's constituent parts. + + Parameters + ---------- + + values : af.Array. + - Contains the non zero elements of the sparse array. + + row_idx : af.Array. + - Contains row indices of the sparse array. + + col_idx : af.Array. + - Contains column indices of the sparse array. + + nrows : int. + - specifies the number of rows in sparse matrix. + + ncols : int. + - specifies the number of columns in sparse matrix. + + storage : optional: arrayfire.STORAGE. default: arrayfire.STORAGE.CSR. + - Can be one of arrayfire.STORAGE.CSR, arrayfire.STORAGE.COO. + + Returns + ------- + + A sparse matrix. + """ + assert(isinstance(values, Array)) + assert(isinstance(row_idx, Array)) + assert(isinstance(col_idx, Array)) + out = Array() + safe_call(backend.get().af_create_sparse_array(ct.pointer(out.arr), c_dim_t(nrows), c_dim_t(ncols), + values.arr, row_idx.arr, col_idx.arr, storage.value)) + return out + +def sparse_from_host(values, row_idx, col_idx, nrows, ncols, storage = STORAGE.CSR): + """ + Create a sparse matrix from it's constituent parts. + + Parameters + ---------- + + values : Any datatype that can be converted to array. + - Contains the non zero elements of the sparse array. + + row_idx : Any datatype that can be converted to array. + - Contains row indices of the sparse array. + + col_idx : Any datatype that can be converted to array. + - Contains column indices of the sparse array. + + nrows : int. + - specifies the number of rows in sparse matrix. + + ncols : int. + - specifies the number of columns in sparse matrix. + + storage : optional: arrayfire.STORAGE. default: arrayfire.STORAGE.CSR. + - Can be one of arrayfire.STORAGE.CSR, arrayfire.STORAGE.COO. + + Returns + ------- + + A sparse matrix. + """ + return sparse(to_array(values), to_array(row_idx), to_array(col_idx), nrows, ncols, storage) + +def sparse_from_dense(dense, storage = STORAGE.CSR): + """ + Create a sparse matrix from a dense matrix. + + Parameters + ---------- + + dense : af.Array. + - A dense matrix. + + storage : optional: arrayfire.STORAGE. default: arrayfire.STORAGE.CSR. + - Can be one of arrayfire.STORAGE.CSR, arrayfire.STORAGE.COO. + + Returns + ------- + + A sparse matrix. + """ + assert(isinstance(dense, Array)) + out = Array() + safe_call(backend.get().af_create_sparse_array_from_dense(ct.pointer(out.arr), dense.arr, storage.value)) + return out + +def sparse_to_dense(sparse): + """ + Create a dense matrix from a sparse matrix. + + Parameters + ---------- + + sparse : af.Array. + - A sparse matrix. + + Returns + ------- + + A dense matrix. + """ + out = Array() + safe_call(backend.get().af_sparse_to_dense(ct.pointer(out.arr), sparse.arr)) + return out + +def sparse_get_info(sparse): + """ + Get the constituent arrays and storage info from a sparse matrix. + + Parameters + ---------- + + sparse : af.Array. + - A sparse matrix. + + Returns + -------- + (values, row_idx, col_idx, storage) where + values : arrayfire.Array containing non zero elements from sparse matrix + row_idx : arrayfire.Array containing the row indices + col_idx : arrayfire.Array containing the column indices + storage : sparse storage + """ + values = Array() + row_idx = Array() + col_idx = Array() + stype = ct.c_int(0) + safe_call(backend.get().af_sparse_get_info(ct.pointer(values.arr), ct.pointer(row_idx.arr), + ct.pointer(col_idx.arr), ct.pointer(stype), + sparse.arr)) + return (values, row_idx, col_idx, __to_sparse_enum[stype.value]) + +def sparse_get_values(sparse): + """ + Get the non zero values from sparse matrix. + + Parameters + ---------- + + sparse : af.Array. + - A sparse matrix. + + Returns + -------- + arrayfire array containing the non zero elements. + + """ + values = Array() + safe_call(backend.get().af_sparse_get_values(ct.pointer(values.arr), sparse.arr)) + return values + +def sparse_get_row_idx(sparse): + """ + Get the row indices from sparse matrix. + + Parameters + ---------- + + sparse : af.Array. + - A sparse matrix. + + Returns + -------- + arrayfire array containing the non zero elements. + + """ + row_idx = Array() + safe_call(backend.get().af_sparse_get_row_idx(ct.pointer(row_idx.arr), sparse.arr)) + return row_idx + +def sparse_get_col_idx(sparse): + """ + Get the column indices from sparse matrix. + + Parameters + ---------- + + sparse : af.Array. + - A sparse matrix. + + Returns + -------- + arrayfire array containing the non zero elements. + + """ + col_idx = Array() + safe_call(backend.get().af_sparse_get_col_idx(ct.pointer(col_idx.arr), sparse.arr)) + return col_idx + +def sparse_get_nnz(sparse): + """ + Get the column indices from sparse matrix. + + Parameters + ---------- + + sparse : af.Array. + - A sparse matrix. + + Returns + -------- + Number of non zero elements in the sparse matrix. + + """ + nnz = c_dim_t(0) + safe_call(backend.get().af_sparse_get_nnz(ct.pointer(nnz), sparse.arr)) + return nnz.value + +def sparse_get_storage(sparse): + """ + Get the column indices from sparse matrix. + + Parameters + ---------- + + sparse : af.Array. + - A sparse matrix. + + Returns + -------- + Number of non zero elements in the sparse matrix. + + """ + storage = ct.c_int(0) + safe_call(backend.get().af_sparse_get_storage(ct.pointer(storage), sparse.arr)) + return __to_sparse_enum[storage.value] + +def sparse_convert_to(sparse, storage): + """ + Convert sparse matrix from one format to another. + + Parameters + ---------- + + storage : arrayfire.STORAGE. + + Returns + ------- + + Sparse matrix converted to the appropriate type. + """ + out = Array() + safe_call(backend.get().af_sparse_convert_to(ct.pointer(out.arr), sparse.arr, storage.value)) + return out diff --git a/arrayfire/tests/simple/__init__.py b/arrayfire/tests/simple/__init__.py index 0a1588ed8..528215ffc 100644 --- a/arrayfire/tests/simple/__init__.py +++ b/arrayfire/tests/simple/__init__.py @@ -19,4 +19,5 @@ from .signal import * from .statistics import * from .random import * +from .sparse import * from ._util import tests diff --git a/arrayfire/tests/simple/sparse.py b/arrayfire/tests/simple/sparse.py new file mode 100644 index 000000000..b756cd5a5 --- /dev/null +++ b/arrayfire/tests/simple/sparse.py @@ -0,0 +1,28 @@ +#!/usr/bin/python +####################################################### +# 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 +######################################################## + +import arrayfire as af +from . import _util + +def simple_sparse(verbose=False): + display_func = _util.display_func(verbose) + print_func = _util.print_func(verbose) + + dd = af.randu(5, 5) + ds = dd * (dd > 0.5) + sp = af.sparse_from_dense(ds) + display_func(af.sparse_get_info(sp)) + display_func(af.sparse_get_values(sp)) + display_func(af.sparse_get_row_idx(sp)) + display_func(af.sparse_get_col_idx(sp)) + print_func(af.sparse_get_nnz(sp)) + print_func(af.sparse_get_storage(sp)) + +_util.tests['sparse'] = simple_sparse