From d55874800fdcc10065bbe00e293cffff6d69c532 Mon Sep 17 00:00:00 2001 From: pradeep Date: Thu, 27 Apr 2017 12:13:26 +0530 Subject: [PATCH 01/30] Remove rustc_serialize in favor of Serde crate --- Cargo.toml | 4 +++- build.rs | 11 +++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2b7205976..a086f0397 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,9 @@ num = "0.1.32" lazy_static = "0.2.1" [build-dependencies] -rustc-serialize = "0.3.19" +serde_json = "1.0.0" +serde_derive = "1.0.1" +serde = "1.0.1" rustc_version = "0.1.7" [lib] diff --git a/build.rs b/build.rs index b4cdd132c..c93619ba4 100644 --- a/build.rs +++ b/build.rs @@ -1,10 +1,13 @@ /* -- Lots of reuse from: https://github.com/alexcrichton/git2-rs/blob/master/libgit2-sys/build.rs */ -extern crate rustc_serialize; +#[macro_use] +extern crate serde_derive; + +extern crate serde; +extern crate serde_json; extern crate rustc_version; use std::env; use std::fs; -use rustc_serialize::json; use std::fs::OpenOptions; use std::io::{ErrorKind, Read}; use std::path::PathBuf; @@ -21,7 +24,7 @@ static UNIX_OCL_LIB_NAME: &'static str = "libafopencl"; static UNIX_UNI_LIB_NAME: &'static str = "libaf"; #[allow(dead_code)] -#[derive(RustcDecodable)] +#[derive(Deserialize, Debug)] struct Config { // Use the existing lib if it exists use_lib: bool, @@ -116,7 +119,7 @@ fn read_file(file_name: &std::path::PathBuf) -> String { fn read_conf(conf_file: &std::path::PathBuf) -> Config { let raw_conf = read_file(conf_file); - let decoded: Config = json::decode(&raw_conf).unwrap(); + let decoded: Config = serde_json::from_str(&raw_conf).unwrap(); decoded } From 95c9ec574047ea1ac61da534d622ecdd205ce977 Mon Sep 17 00:00:00 2001 From: pradeep Date: Fri, 28 Apr 2017 19:20:13 +0530 Subject: [PATCH 02/30] lint for warning missing documentation This change also includes the fixes for currently missing documentation and crate level documentation. --- src/defines.rs | 21 +++++++++++++++++++++ src/dim4.rs | 2 +- src/error.rs | 4 ++++ src/index.rs | 1 + src/lib.rs | 20 ++++++++++++++++++++ src/util.rs | 1 + 6 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/defines.rs b/src/defines.rs index d777f310a..40163bd3e 100644 --- a/src/defines.rs +++ b/src/defines.rs @@ -339,13 +339,21 @@ pub enum HomographyType { #[repr(C)] #[derive(Clone, Copy, Debug, PartialEq)] pub enum MarkerType { + /// No marker NONE = 0, + /// Pointer marker POINT = 1, + /// Hollow circle marker CIRCLE = 2, + /// Hollow Square marker SQUARE = 3, + /// Hollow Triangle marker TRIANGLE = 4, + /// Cross-hair marker CROSS = 5, + /// Plus symbol marker PLUS = 6, + /// Start symbol marker STAR = 7 } @@ -353,10 +361,15 @@ pub enum MarkerType { #[repr(C)] #[derive(Clone, Copy, Debug, PartialEq)] pub enum MomentType { + /// Central moment of order (0 + 0) M00 = 1, // 1<<0 + /// Central moment of order (0 + 1) M01 = 2, // 1<<1 + /// Central moment of order (1 + 0) M10 = 4, // 1<<2 + /// Central moment of order (1 + 1) M11 = 8, // 1<<3 + /// All central moments of order (0,0), (0,1), (1,0) and (1,1) FIRST_ORDER = 1<<0 | 1<<1 | 1<<2 | 1<<3 } @@ -378,9 +391,13 @@ pub enum SparseFormat { #[repr(C)] #[derive(Clone, Copy, Debug, PartialEq)] pub enum BinaryOp { + /// Addition operation ADD = 0, + /// Multiplication operation MUL = 1, + /// Minimum operation MIN = 2, + /// Maximum operation MAX = 3 } @@ -396,9 +413,13 @@ pub enum RandomEngineType { MERSENNE_GP11213 = 300 } +/// Default Philon RandomEngine that points to [PHILOX_4X32_10](./enum.RandomEngineType.html) pub const PHILOX : RandomEngineType = RandomEngineType::PHILOX_4X32_10; +/// Default Threefry RandomEngine that points to [THREEFRY_2X32_16](./enum.RandomEngineType.html) pub const THREEFRY : RandomEngineType = RandomEngineType::THREEFRY_2X32_16; +/// Default Mersenne RandomEngine that points to [MERSENNE_GP11213](./enum.RandomEngineType.html) pub const MERSENNE : RandomEngineType = RandomEngineType::MERSENNE_GP11213; +/// Default RandomEngine that defaults to [PHILOX](./constant.PHILOX.html) pub const DEFAULT_RANDOM_ENGINE : RandomEngineType = PHILOX; /// Scalar value types diff --git a/src/dim4.rs b/src/dim4.rs index 0ddcac59f..cc31b3bba 100644 --- a/src/dim4.rs +++ b/src/dim4.rs @@ -84,7 +84,7 @@ impl Dim4 { } } - // Get the dimensions as a slice of 4 values + /// Get the dimensions as a slice of 4 values pub fn get(&self) -> &[u64; 4] { &self.dims } diff --git a/src/error.rs b/src/error.rs index ddb765605..b20b7673c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,11 +4,14 @@ use std::error::Error; use std::marker::{Send, Sync}; use std::sync::RwLock; +/// Signature of callback function to be called to handle errors pub type ErrorCallback = Fn(AfError); /// Wrap ErrorCallback function pointer inside a structure /// to enable implementing Send, Sync traits on it. pub struct Callback<'cblifetime> { + ///Reference to a valid error callback function + ///Make sure this callback stays relevant throughout the lifetime of application. pub cb: &'cblifetime ErrorCallback, } @@ -60,6 +63,7 @@ pub fn register_error_handler(cb_value: Callback<'static>) { *gaurd.deref_mut() = cb_value; } +/// Default error handling callback provided by ArrayFire crate pub fn handle_error_general(error_code: AfError) { match error_code { AfError::SUCCESS => {}, /* No-op */ diff --git a/src/index.rs b/src/index.rs index e478b1b9d..67d2b3857 100644 --- a/src/index.rs +++ b/src/index.rs @@ -68,6 +68,7 @@ impl Indexable for Seq where c_double: From { impl Indexer { #[allow(unused_mut)] + /// Create a new Indexer object and set the dimension specific index objects later pub fn new() -> Indexer { unsafe { let mut temp: i64 = 0; diff --git a/src/lib.rs b/src/lib.rs index 2ead5233d..b3e099fa6 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,26 @@ +//! ArrayFire is a high performance software library for parallel computing with +//! an easy-to-use API. ArrayFire abstracts away much of the details of +//! programming parallel architectures by providing a high-level container object, +//! the [Array](./struct.Array.html), that represents data stored on a CPU, GPU, FPGA, +//! or other type of accelerator. This abstraction permits developers to write +//! massively parallel applications in a high-level language where they need +//! not be concerned about low-level optimizations that are frequently required to +//! achieve high throughput on most parallel architectures. + +//! This crate provides Rust bindings for the ArrayFire library. Given below table shows the rust bindings compatability with ArrayFire upstream. If you find any bugs, please report them on [github](https://github.com/arrayfire/arrayfire-rust/issues). +//! +//! +//! | ArrayFire Upstream | Rust Crate | +//! |:------------------:|:---------------:| +//! | 3.3.x | 3.3.x | +//! | 3.4.x | 3.4.x | +//! +//! Only, Major & Minor version numbers need to match. + #![doc(html_logo_url = "http://www.arrayfire.com/logos/arrayfire_logo_symbol.png", html_favicon_url = "http://www.rust-lang.org/favicon.ico", html_root_url = "http://arrayfire.com/docs/rust")] +#![warn(missing_docs)] #[macro_use] extern crate lazy_static; diff --git a/src/util.rs b/src/util.rs index 648b05bbe..181f735ba 100644 --- a/src/util.rs +++ b/src/util.rs @@ -153,6 +153,7 @@ impl From for ColorMap { /// - u16 /// pub trait HasAfEnum { + /// Return trait implmentors corresponding [DType](./enum.DType.html) fn get_af_dtype() -> DType; } From f66736df1d3816787e0cf8159170cd1db8470f40 Mon Sep 17 00:00:00 2001 From: pradeep Date: Mon, 1 May 2017 09:11:44 +0530 Subject: [PATCH 03/30] Associated method for empty Array creation --- src/array.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/array.rs b/src/array.rs index a2e515403..60770a1c3 100644 --- a/src/array.rs +++ b/src/array.rs @@ -18,6 +18,8 @@ extern { fn af_create_array(out: MutAfArray, data: *const c_void, ndims: c_uint, dims: *const DimT, aftype: uint8_t) -> c_int; + fn af_create_handle(out: MutAfArray, ndims: c_uint, dims: *const DimT, aftype: uint8_t) -> c_int; + fn af_get_elements(out: MutAfArray, arr: AfArray) -> c_int; fn af_get_type(out: *mut c_int, arr: AfArray) -> c_int; @@ -175,6 +177,27 @@ impl Array { } } + /// Constructs a new Array object of specified dimensions and type + /// + /// # Examples + /// + /// ```rust + /// use arrayfire::{Array, Dim4, DType}; + /// let garbageVals = Array::new_empty(Dim4::new(&[3, 1, 1, 1]), DType::F32); + /// ``` + #[allow(unused_mut)] + pub fn new_empty(dims: Dim4, aftype: DType) -> Array { + unsafe { + let mut temp: i64 = 0; + let err_val = af_create_handle(&mut temp as MutAfArray, + dims.ndims() as c_uint, + dims.get().as_ptr() as * const c_longlong, + aftype as uint8_t); + HANDLE_ERROR(AfError::from(err_val)); + Array::from(temp) + } + } + /// Returns the backend of the Array /// /// # Return Values From 4e0e6dad82488a0ebe3b3e2c0162094f38da643b Mon Sep 17 00:00:00 2001 From: pradeep Date: Mon, 1 May 2017 11:48:01 +0530 Subject: [PATCH 04/30] fix documentation in Seq::end method --- src/seq.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/seq.rs b/src/seq.rs index cd625dd19..643fde357 100644 --- a/src/seq.rs +++ b/src/seq.rs @@ -38,7 +38,7 @@ impl Seq { self.begin } - /// Get begin index of Seq + /// Get end index of Seq pub fn end(&self) -> T { self.end } From 2673c9f6d24e7b9df2810e14ef7807b5313230f6 Mon Sep 17 00:00:00 2001 From: pradeep Date: Mon, 1 May 2017 17:10:35 +0530 Subject: [PATCH 05/30] Change documentation generation workflow Earlier to this change, documentation was generated using the command `cargo doc`. However, this has one drawback - when external markdown files were used, additional set of commands had to be run using `rustdoc` tool to generate the html files corresponding to the md files. With this change, `generate_docs.sh` bash script will handle those intricacies of running `cargo doc` followed by execution of `rustdoc` for external md files. This change adds the following tutorials to the documetation. * Getting Started with ArrayFire * Introduction to Vectorization * Array and Matrix Manipulation * Indexing * Configure ArrayFire Environment --- doc/array_and_matrix_manipulation.md | 271 +++++++++++++++++++++++ doc/configuring_arrayfire_environment.md | 161 ++++++++++++++ doc/external_docs.css | 19 ++ doc/getting_started.md | 232 +++++++++++++++++++ doc/indexing.md | 135 +++++++++++ doc/vectorization.md | 171 ++++++++++++++ generate_docs.sh | 14 ++ src/lib.rs | 8 + 8 files changed, 1011 insertions(+) create mode 100644 doc/array_and_matrix_manipulation.md create mode 100644 doc/configuring_arrayfire_environment.md create mode 100644 doc/external_docs.css create mode 100644 doc/getting_started.md create mode 100644 doc/indexing.md create mode 100644 doc/vectorization.md create mode 100755 generate_docs.sh diff --git a/doc/array_and_matrix_manipulation.md b/doc/array_and_matrix_manipulation.md new file mode 100644 index 000000000..7abcc3296 --- /dev/null +++ b/doc/array_and_matrix_manipulation.md @@ -0,0 +1,271 @@ +% Array and Matrix Manipulation + +ArrayFire provides several different methods for manipulating arrays and matrices. +The functionality includes: + +* [moddims()](#moddims) - change the dimensions of an array without changing the data +* [flat()](#flat) - flatten an array to one dimension +* [flip()](#flip) - flip an array along a dimension +* [join()](#join) - join up to 4 arrays +* [reorder()](#reorder) - changes the dimension order within the array +* [shift()](#shift) - shifts data along a dimension +* [tile()](#tile) - repeats an array along a dimension +* [transpose()](#transpose) - performs a matrix transpose + +Below we provide several examples of these functions and their use. + +### moddims() + +The [moddims](./fn.moddims.html) function changes the dimensions of an array without +changing its data or order. Note that this function modifies only the _metadata_ +associated with the array. It does not modify the content of the array. +Here is an example of moddims() converting an 8x1 array into a 2x4 and then +back to a 8x1: + +```rust +a [8 1 1 1] + 1.0000 + 2.0000 + 1.0000 + 2.0000 + 1.0000 + 2.0000 + 1.0000 + 2.0000 + +let new_dims = Dim4::new(&[2, 4, 1, 1]); +moddims(&a, new_dims) +[2 4 1 1] + 1.0000 1.0000 1.0000 1.0000 + 2.0000 2.0000 2.0000 2.0000 + +let out = moddims(&a, a.elements(), 1, 1, 1); +[8 1 1 1] + 1.0000 + 2.0000 + 1.0000 + 2.0000 + 1.0000 + 2.0000 + 1.0000 + 2.0000 +``` + +### flat() + +The [flat](./fn.flat.html) function flattens an array to one dimension: + +``` +a [3 3 1 1] + 1.0000 4.0000 7.0000 + 2.0000 5.0000 8.0000 + 3.0000 6.0000 9.0000 + +flat(&a) +[9 1 1 1] + 1.0000 + 2.0000 + 3.0000 + 4.0000 + 5.0000 + 6.0000 + 7.0000 + 8.0000 + 9.0000 +``` + +### flip() + +The [flip](./fn.flip.html) function flips the contents of an array along a +chosen dimension. In the example below, we show the 5x2 array flipped +along the zeroth (i.e. within a column) and first (e.g. across rows) axes: + +```rust +a [5 2 1 1] + 1.0000 6.0000 + 2.0000 7.0000 + 3.0000 8.0000 + 4.0000 9.0000 + 5.0000 10.0000 + +flip(a, 0) [5 2 1 1] + 5.0000 10.0000 + 4.0000 9.0000 + 3.0000 8.0000 + 2.0000 7.0000 + 1.0000 6.0000 + +flip(a, 1) [5 2 1 1] + 6.0000 1.0000 + 7.0000 2.0000 + 8.0000 3.0000 + 9.0000 4.0000 + 10.0000 5.0000 +``` + +### join() + +The [join](./fn.join.html), [join_many](./fn.join_many.html) functions can be +used to join arrays along a specific dimension. + +Here is an example of how to use join an array to itself: + +```rust +a [5 1 1 1] + 1.0000 + 2.0000 + 3.0000 + 4.0000 + 5.0000 + +join(0, a, a) [10 1 1 1] + 1.0000 + 2.0000 + 3.0000 + 4.0000 + 5.0000 + 1.0000 + 2.0000 + 3.0000 + 4.0000 + 5.0000 + +join(1, a, a) [5 2 1 1] + 1.0000 1.0000 + 2.0000 2.0000 + 3.0000 3.0000 + 4.0000 4.0000 + 5.0000 5.0000 +``` + +### reorder() + +The [reorder](./fn.reorder.html) function modifies the order of data within an array by +exchanging data according to the change in dimensionality. The linear ordering +of data within the array is preserved. + +```rust +a [2 2 3 1] + 1.0000 3.0000 + 2.0000 4.0000 + + 1.0000 3.0000 + 2.0000 4.0000 + + 1.0000 3.0000 + 2.0000 4.0000 + + +reorder(&a, 1, 0, 2) +[2 2 3 1] //equivalent to a transpose + 1.0000 2.0000 + 3.0000 4.0000 + + 1.0000 2.0000 + 3.0000 4.0000 + + 1.0000 2.0000 + 3.0000 4.0000 + + +reorder(&a, 2, 0, 1) +[3 2 2 1] + 1.0000 2.0000 + 1.0000 2.0000 + 1.0000 2.0000 + + 3.0000 4.0000 + 3.0000 4.0000 + 3.0000 4.0000 +``` + +### shift() + +The [shift](./fn.shift.html) function shifts data in a circular buffer fashion along a +chosen dimension. Consider the following example: + +```rust +a [3 5 1 1] + 0.0000 0.0000 0.0000 0.0000 0.0000 + 3.0000 4.0000 5.0000 1.0000 2.0000 + 3.0000 4.0000 5.0000 1.0000 2.0000 + +shift(&a, 0, 2 ) +[3 5 1 1] + 0.0000 0.0000 0.0000 0.0000 0.0000 + 1.0000 2.0000 3.0000 4.0000 5.0000 + 1.0000 2.0000 3.0000 4.0000 5.0000 + +shift(&a, -1, 2 ) +[3 5 1 1] + 1.0000 2.0000 3.0000 4.0000 5.0000 + 1.0000 2.0000 3.0000 4.0000 5.0000 + 0.0000 0.0000 0.0000 0.0000 0.0000 +``` + +### tile() + +The [tile](./fn.tile.html) function repeats an array along the specified dimension. +For example below we show how to tile an array along the zeroth and first +dimensions of an array: + +```rust +a [3 1 1 1] + 1.0000 + 2.0000 + 3.0000 + +// Repeat array a twice in the zeroth dimension +tile(&a, 2) +[6 1 1 1] + 1.0000 + 2.0000 + 3.0000 + 1.0000 + 2.0000 + 3.0000 + +// Repeat array a twice along both the zeroth and first dimensions +tile(&a, 2, 2) +[6 2 1 1] + 1.0000 1.0000 + 2.0000 2.0000 + 3.0000 3.0000 + 1.0000 1.0000 + 2.0000 2.0000 + 3.0000 3.0000 + +// Repeat array a twice along the first and three times along the second +// dimension. +let tile_dims = Dim4::new(&[1, 2, 3, 1]); +tile(a, tile_dims) [3 2 3 1] + 1.0000 1.0000 + 2.0000 2.0000 + 3.0000 3.0000 + + 1.0000 1.0000 + 2.0000 2.0000 + 3.0000 3.0000 + + 1.0000 1.0000 + 2.0000 2.0000 + 3.0000 3.0000 +``` + +### transpose() + +The [transpose](./fn.transpose.html) function performs a standard matrix transpose. The input +array must have the dimensions of a 2D-matrix. + +```rust +a [3 3 1 1] + 1.0000 3.0000 3.0000 + 2.0000 1.0000 3.0000 + 2.0000 2.0000 1.0000 + +transpose(&a, False) //Second parameter to be used for conjugate transpose +[3 3 1 1] + 1.0000 2.0000 2.0000 + 3.0000 1.0000 2.0000 + 3.0000 3.0000 1.0000 +``` diff --git a/doc/configuring_arrayfire_environment.md b/doc/configuring_arrayfire_environment.md new file mode 100644 index 000000000..7310144b5 --- /dev/null +++ b/doc/configuring_arrayfire_environment.md @@ -0,0 +1,161 @@ +% Configuring Arrayfire Environment + +Following are the list of environment and runtime configurations that will help enhance +your experience with ArrayFire. + +# AF_PATH + +This is the path with ArrayFire gets installed, ie. the includes and libs are +present in this directory. You can use this variable to add include paths and +libraries to your projects. + +# AF_PRINT_ERRORS + +When AF_PRINT_ERRORS is set to 1, the exceptions thrown are more verbose and +detailed. This helps in locating the exact failure. + +``` +AF_PRINT_ERRORS=1 ./myprogram +``` + +# AF_CUDA_DEFAULT_DEVICE + +Use this variable to set the default CUDA device. Valid values for this +variable are the device identifiers shown when [af::info](./fn.info.html) is run. + +``` +AF_CUDA_DEFAULT_DEVICE=1 ./myprogram +``` + +Note: af::setDevice call in the source code will take precedence over this +variable. + +# AF_OPENCL_DEFAULT_DEVICE + +Use this variable to set the default OpenCL device. Valid values for this +variable are the device identifiers shown when [af::info](./fn.info.html) is run. + +``` +AF_OPENCL_DEFAULT_DEVICE=1 ./myprogram +``` + +Note: [af::set_device](./fn.set_device.html) call in the source code will take precedence over this +variable. + +# AF_OPENCL_DEFAULT_DEVICE_TYPE + +Use this variable to set the default OpenCL device type. Valid values for this +variable are: CPU, GPU, ACC (Accelerators). + +When set, the first device of the specified type is chosen as default device. + +``` +AF_OPENCL_DEFAULT_DEVICE_TYPE=CPU ./myprogram +``` + +Note: `AF_OPENCL_DEFAULT_DEVICE` and [af::set_device](./fn.set_device.html) takes precedence over this variable. + +# AF_OPENCL_DEVICE_TYPE + +Use this variable to only choose OpenCL devices of specified type. Valid values for this +variable are: + +- ALL: All OpenCL devices. (Default behavior). +- CPU: CPU devices only. +- GPU: GPU devices only. +- ACC: Accelerator devices only. + +When set, the remaining OpenCL device types are ignored by the OpenCL backend. + +``` +AF_OPENCL_DEVICE_TYPE=CPU ./myprogram +``` + +# AF_OPENCL_CPU_OFFLOAD + +When ArrayFire runs on devices with unified memory with the host (ie. +`CL_DEVICE_HOST_UNIFIED_MENORY` is true for the device) then certain functions +are offloaded to run on the CPU using mapped buffers. + +ArrayFire takes advantage of fast libraries such as MKL while spending no time +copying memory from device to host. The device memory is mapped to a host +pointer which can be used in the offloaded functions. + +This functionality can be disabled by using the environment variable +`AF_OPENCL_CPU_OFFLOAD=0`. + +The default bevaior of this has changed in version 3.4. + +Prior to v3.4, CPU Offload functionality was used only when the user set +`AF_OPENCL_CPU_OFFLOAD=1` and disabled otherwise. + +From v3.4 onwards, CPU Offload is enabled by default and is disabled only when +`AF_OPENCL_CPU_OFFLOAD=0` is set. + +# AF_OPENCL_SHOW_BUILD_INFO + +This variable is useful when debuggin OpenCL kernel compilation failures. When +this variable is set to 1, and an error occurs during a OpenCL kernel +compilation, then the log and kernel are printed to screen. + +# AF_DISABLE_GRAPHICS + +Setting this variable to 1 will disable window creation when graphics +functions are being called. Disabling window creation will disable all other +graphics calls at runtime as well. + +This is a useful enviornment variable when running code on servers and systems +without displays. When graphics calls are run on such machines, they will +print warning about window creation failing. To suppress those calls, set this +variable. + +# AF_SYNCHRONOUS_CALLS + +When this environment variable is set to 1, ArrayFire will execute all +functions synchronously. + +# AF_SHOW_LOAD_PATH + +When using the Unified backend, if this variable is set to 1, it will show the +path where the ArrayFire backend libraries are loaded from. + +If the libraries are loaded from system paths, such as PATH or LD_LIBRARY_PATH +etc, then it will print "system path". If the libraries are loaded from other +paths, then those paths are shown in full. + +# AF_MEM_DEBUG + +When AF_MEM_DEBUG is set to 1 (or anything not equal to 0), the caching mechanism in the memory manager is disabled. +The device buffers are allocated using native functions as needed and freed when going out of scope. + +When the environment variable is not set, it is treated to be non zero. + +``` +AF_MEM_DEBUG=1 ./myprogram +``` + +# AF_MAX_BUFFERS + +When AF_MAX_BUFFERS is set, this environment variable specifies the maximum number of buffers allocated before garbage collection kicks in. + +Please note that the total number of buffers that can exist simultaneously can be higher than this number. This variable tells the garbage collector that it should free any available buffers immediately if the treshold is reached. + +When not set, the default value is 1000. + +# AF_OPENCL_MAX_JIT_LEN + +When set, this environment variable specifies the maximum height of the OpenCL JIT tree after which evaluation is forced. + +The default value, as of v3.4, is 50 on OSX, 100 everywhere else. This value was 20 for older versions. + +# AF_CUDA_MAX_JIT_LEN + +When set, this environment variable specifies the maximum height of the CUDA JIT tree after which evaluation is forced. + +The default value, as of v3.4, 100. This value was 20 for older versions. + +# AF_CPU_MAX_JIT_LEN + +When set, this environment variable specifies the maximum length of the CPU JIT tree after which evaluation is forced. + +The default value, as of v3.4, 100. This value was 20 for older versions. diff --git a/doc/external_docs.css b/doc/external_docs.css new file mode 100644 index 000000000..7e26a173d --- /dev/null +++ b/doc/external_docs.css @@ -0,0 +1,19 @@ +/* unvisited link */ +a:link { + color: #4d76ae; +} + +/* visited link */ +a:visited { + color: #4d76ae; +} + +/* mouse over link */ +a:hover { + color: #4d76ae; +} + +/* selected link */ +a:active { + color: #4d76ae; +} diff --git a/doc/getting_started.md b/doc/getting_started.md new file mode 100644 index 000000000..f42c16f09 --- /dev/null +++ b/doc/getting_started.md @@ -0,0 +1,232 @@ +% Getting Started + +# Introduction + +ArrayFire is a high performance software library for parallel computing with +an easy-to-use API. ArrayFire abstracts away much of the details of +programming parallel architectures by providing a high-level container object, +the [Array](./struct.Array.html), that represents data stored on a CPU, GPU, FPGA, +or other type of accelerator. This abstraction permits developers to write +massively parallel applications in a high-level language where they need +not be concerned about low-level optimizations that are frequently required to +achieve high throughput on most parallel architectures. + +# Supported data types + +ArrayFire provides one generic container object, the [Array](./struct.Array.html) +on which functions and mathematical operations are performed. The `array` +can represent one of many different [basic data types](./enum.DType.html): + +* [F32](./enum.DType.html) real single-precision (`float`) +* [C32](./enum.DType.html) complex single-precision (`cfloat`) +* [F64](./enum.DType.html) real double-precision (`double`) +* [C64](./enum.DType.html) complex double-precision (`cdouble`) +* [B8 ](./enum.DType.html) 8-bit boolean values (`bool`) +* [S32](./enum.DType.html) 32-bit signed integer (`int`) +* [U32](./enum.DType.html) 32-bit unsigned integer (`unsigned`) +* [U8 ](./enum.DType.html) 8-bit unsigned values (`unsigned char`) +* [S64](./enum.DType.html) 64-bit signed integer (`intl`) +* [U64](./enum.DType.html) 64-bit unsigned integer (`uintl`) +* [S16](./enum.DType.html) 16-bit signed integer (`short`) +* [U16](./enum.DType.html) 16-bit unsigned integer (`unsigned short`) + +Most of these data types are supported on all modern GPUs; however, some +older devices may lack support for double precision arrays. In this case, +a runtime error will be generated when the array is constructed. + +If not specified otherwise, `Array`s are created as single precision floating +point numbers ([F32](./enum.DType.html)). + +# Creating and populating an ArrayFire array + +ArrayFire [Array](./struct.Array.html)s represent memory stored on the device. +As such, creation and population of an array will consume memory on the device +which cannot freed until the `array` object goes out of scope. As device memory +allocation can be expensive, ArrayFire also includes a memory manager which +will re-use device memory whenever possible. + +Arrays can be created using one of the [array constructors](./struct.Array.html#method.new_empty). +Below we show how to create 1D, 2D, and 3D arrays with uninitialized values: + +```rust +let garbageVals = Array::new_empty(Dim4::new(&[3, 1, 1, 1]), DType::F32); +``` + +However, uninitialized memory is likely not useful in your application. +ArrayFire provides several convenient functions for creating arrays that contain +pre-populated values including constants, uniform random numbers, uniform +normally distributed numbers, and the identity matrix: + +```rust +// Create an array filled with constant value of 2.0 of type floating point +// The type of Array is infered from the type of the constant argument +let cnst = constant(2.0f32, Dim4::new(&[5, 5, 1, 1])); +print(&cnst); +``` +```rust +println!("Create a 5-by-3 matrix of random floats on the GPU"); +let dims = Dim4::new(&[5, 3, 1, 1]); +let a = randu::(dims); +print(&a); +``` + +As stated above, the default data type for arrays is [F32](./enum.DType.html) (a +32-bit floating point number) unless specified otherwise. + +ArrayFire `Array`s may also be populated from data found on the host. +For example: + +```rust +let values: [u32; 3] = [1u32, 2, 3]; +let indices = Array::new(&values, Dim4::new(&[3, 1, 1, 1])); +print(&indices); +``` + + + +# ArrayFire array contents, dimensions, and properties + +ArrayFire provides several functions to determine various aspects of arrays. +This includes functions to print the contents, query the dimensions, and +determine various other aspects of arrays. + +The [print](./fn.print.html) function can be used to print arrays that +have already been generated or any expression involving arrays: + +```rust +let values: [f32; 3] = [1.0, 2.0, 3.0]; +let indices = Array::new(&values, Dim4::new(&[3, 1, 1, 1])); +print(&indices); +``` + +The dimensions of an array may be determined using either a [Dim4](./struct.Dim4.html) object or by accessing the dimensions directly using the [Dim4::get](./struct.Dim4.html#method.get) and [Dim4::numdims](./struct.Dim4.html#method.ndims) functions: + +```rust +let values: [f32; 3] = [1.0, 2.0, 3.0]; +let dims: Dim4 = Dim4::new(&[3, 1, 1, 1]); +let indices = Array::new(&values, dims); +println!("Dims {:?} with dimensions {}", dims.get(), dims.ndims()); +``` + +In addition to dimensions, arrays also carry several properties including +methods to determine the underlying type and size (in bytes). You can even +determine whether the array is empty, real/complex, a row/column, or a scalar +or a vector. For further information on these capabilities, we suggest you consult the +full documentation on the [Array](./struct.Array.html). + +# Writing mathematical expressions using ArrayFire + +ArrayFire features an intelligent Just-In-Time (JIT) compilation engine that +converts expressions using arrays into the smallest number of CUDA/OpenCL +kernels. For most operations on Arrays, ArrayFire functions like a vector library. +That means that an element-wise operation, like `c[i] = a[i] + b[i]` in C, +would be written more concisely without indexing, like `c = a + b`. +When there are multiple expressions involving arrays, ArrayFire's JIT engine +will merge them together. This "kernel fusion" technology not only decreases +the number of kernel calls, but, more importantly, avoids extraneous global +memory operations. + +Our JIT functionality extends across C API boundary and only ends +when a non-JIT function is encountered or a synchronization operation is +explicitly called by the code. + +ArrayFire provides hundreds of functions for element-wise +operations. All of the standard operators (e.g. +,-,\*,/) are supported +as are most transcendental functions (sin, cos, log, sqrt, etc.). +Here are a few examples: + +```rust +let num_rows: u64 = 5; +let num_cols: u64 = 3; +let dims = Dim4::new(&[num_rows, num_cols, 1, 1]); +let a = randu::(dims); +let b = randu::(dims); +print(&a); +print(&b); +let c = a + b; +print(&c); + +//Example of *Assign traits +let mut d = randu::(dims); +let e = constant(1f32, dims); +d += e; +print(&d); +``` + + + +# Indexing + +Like all functions in ArrayFire, indexing is also executed in parallel on +the OpenCL/CUDA device. To index `af::array`s you may use one or a combination of the following functions: + +* [Seq](./struct.Seq.html) representing a linear sequence +* [Seq::Default()](./struct.Seq.html) representing the entire dimension +* [row(&Array, i)](./fn.row.html) or [col(&Array, i)](./fn.col.html) specifying a single row/column +* [rows(&Array, first,last)](./fn.rows.html) or [cols(&Array, first,last)](./fn.cols.html) + specifying a span of rows or columns + +Please see the [indexing page](./indexing.html) for several examples of how to +use these functions. + +# Getting access to ArrayFire array memory on the host + +Memory in `af::Array`s may be accessed using the [Array::host()](./struct.Array.html#method.host) method. +The `host` function *copies* the data from the device and makes it available +in a standard slice or similar container on the host. As such, it is up to the developer to manage +any memory returned by `host`. + + + + +# Bitwise operators + +In addition to supporting standard mathematical functions, Arrays +that contain integer data types also support bitwise operators including +and, or, and shift etc. + +# Where to go for help? + +* [Google Groups](https://groups.google.com/forum/#!forum/arrayfire-users) +* ArrayFire Services: [Consulting](http://arrayfire.com/consulting/) | [Support](http://arrayfire.com/support/) | [Training](http://arrayfire.com/training/) +* [ArrayFire Blogs](http://arrayfire.com/blog/) +* Email: diff --git a/doc/indexing.md b/doc/indexing.md new file mode 100644 index 000000000..b5505d774 --- /dev/null +++ b/doc/indexing.md @@ -0,0 +1,135 @@ +% Indexing + +[Indexer](./struct.Indexer.html) structure is the key element used in Rust +wrapper for ArrayFire for creating references to existing Arrays. Given +below are few of such functions and their corresponding example use cases. +Use [Indexer::new](./struct.Indexer.html#method.new) to create an Indexer +object and set either a `Seq` object or `Array` as indexing object for a +given dimension. + +# Using Seq for all dimensions + +Create a view of an existing Array using Sequences and [index](./fn.index.html). + +```rust +let dims = Dim4::new(&[5, 5, 1, 1]); +let a = randu::(dims); +af_print!("a", a); +//a +//[5 5 1 1] +// 0.3990 0.5160 0.8831 0.9107 0.6688 +// 0.6720 0.3932 0.0621 0.9159 0.8434 +// 0.5339 0.2706 0.7089 0.0231 0.1328 +// 0.1386 0.9455 0.9434 0.2330 0.2657 +// 0.7353 0.1587 0.1227 0.2220 0.2299 + +// Index array using sequences +let seqs = &[Seq::new(1u32, 3, 1), Seq::default()]; +let sub = index(&a, seqs); +af_print!("a(seq(1,3,1), span)", sub); +// [3 5 1 1] +// 0.6720 0.3932 0.0621 0.9159 0.8434 +// 0.5339 0.2706 0.7089 0.0231 0.1328 +// 0.1386 0.9455 0.9434 0.2330 0.2657 +``` + +Set a sub-portion of an existing Array with a constant value using [assign_seq](./fn.assign_seq.html). + +```rust +let a = constant(2.0 as f32, Dim4::new(&[5, 3, 1, 1])); +let b = constant(1.0 as f32, Dim4::new(&[3, 3, 1, 1])); +let seqs = &[Seq::new(1.0, 3.0, 1.0), Seq::default()]; +let sub = assign_seq(&a, seqs, &b); +print(&a); +// 2.0 2.0 2.0 +// 2.0 2.0 2.0 +// 2.0 2.0 2.0 +// 2.0 2.0 2.0 +// 2.0 2.0 2.0 + +print(&sub); +// 2.0 2.0 2.0 +// 1.0 1.0 1.0 +// 1.0 1.0 1.0 +// 1.0 1.0 1.0 +// 2.0 2.0 2.0 +``` + +# Using Array and Seq combination + +Create a view of an existing Array using another Array and Sequence. + +```rust +use arrayfire::{Array, Dim4, Seq, print, randu, index_gen, Indexer}; +let values: [f32; 3] = [1.0, 2.0, 3.0]; +let indices = Array::new(&values, Dim4::new(&[3, 1, 1, 1])); +let seq4gen = Seq::new(0.0, 2.0, 1.0); +let a = randu::(Dim4::new(&[5, 3, 1, 1])); +// [5 3 1 1] +// 0.0000 0.2190 0.3835 +// 0.1315 0.0470 0.5194 +// 0.7556 0.6789 0.8310 +// 0.4587 0.6793 0.0346 +// 0.5328 0.9347 0.0535 + +let mut idxrs = Indexer::new(); +idxrs.set_index(&indices, 0, None); // 2nd parameter is indexing dimension +idxrs.set_index(&seq4gen, 1, Some(false)); // 3rd parameter indicates batch operation + +let sub2 = index_gen(&a, idxrs); +println!("a(indices, seq(0, 2, 1))"); print(&sub2); +// [3 3 1 1] +// 0.1315 0.0470 0.5194 +// 0.7556 0.6789 0.8310 +// 0.4587 0.6793 0.0346 +``` + +Set a sub-portion of an existing Array with another Array using a combination +of `Seq` and `Array`. + + ```rust + use arrayfire::{Array, Dim4, Seq, print, randu, constant, Indexer, assign_gen}; + let values: [f32; 3] = [1.0, 2.0, 3.0]; + let indices = Array::new(&values, Dim4::new(&[3, 1, 1, 1])); + let seq4gen = Seq::new(0.0, 2.0, 1.0); + let a = randu::(Dim4::new(&[5, 3, 1, 1])); + // [5 3 1 1] + // 0.0000 0.2190 0.3835 + // 0.1315 0.0470 0.5194 + // 0.7556 0.6789 0.8310 + // 0.4587 0.6793 0.0346 + // 0.5328 0.9347 0.0535 + + let b = constant(2.0 as f32, Dim4::new(&[3, 3, 1, 1])); + + let mut idxrs = Indexer::new(); + idxrs.set_index(&indices, 0, None); // 2nd parameter is indexing dimension + idxrs.set_index(&seq4gen, 1, Some(false)); // 3rd parameter indicates batch operation + + let sub2 = assign_gen(&a, &idxrs, &b); + println!("a(indices, seq(0, 2, 1))"); print(&sub2); + // [5 3 1 1] + // 0.0000 0.2190 0.3835 + // 2.0000 2.0000 2.0000 + // 2.0000 2.0000 2.0000 + // 2.0000 2.0000 2.0000 + // 0.5328 0.9347 0.0535 + ``` + +# Extract or Set rows/coloumns + +Extract a specific set of rows/coloumns from an existing Array. + +```rust +let dims = Dim4::new(&[5, 5, 1, 1]); +let a = randu::(dims); +println!("Grab last row of the random matrix"); +print(&a); +print(&row(&a, 4)); +print(&col(&a, 4)); +``` + +You can also use [rows](./fn.rows.html) & [cols](./fn.cols.html) to retrieve a +subset of rows or coloumns respectively. + +Similarly, [set_row](./fn.set_row.html) & [set_rows](./fn.set_rows.html) can be used to change the values in a particular set of rows using another Array. [set_col](./fn.set_col.html) & [set_cols](./fn.set_cols.html) has same functionality, except that it is for coloumns. diff --git a/doc/vectorization.md b/doc/vectorization.md new file mode 100644 index 000000000..2888bc2cd --- /dev/null +++ b/doc/vectorization.md @@ -0,0 +1,171 @@ +% Vectorization + + +By its very nature, ArrayFire is a vectorized library. Most functions operate on +Arrays as a whole -- on all elements in parallel. For example consider the following code: + +```rust +let mut a = af::range(Dim::new(&[10, 1, 1, 1])); // [0, 9] +a = a + 1; // [1, 10] +``` + +This code will result in a single backend kernel that operates on all 10 elements +of `a` in parallel. + +A small subset of such vectorized ArrayFire functions are given below for quick reference: + +| Operator Category | Functions | +|--------------------------------------------------------------|----------------------------| +| Arithmetic operations | +, -, *, /, %, >>, << | +| Logical operations | &&, \|\|, <, >, ==, != etc. | +| Numeric functions | [abs](./fn.abs.html), [floor](./fn.floor.html), [round](./fn.round.html), [min](./fn.min.html), [max](./fn.max.html), etc. | +| Complex operations | [real](./fn.real.html), [imag](./fn.imag.html), [conj](./fn.conj.html), etc. | +| Exponential and logarithmic functions | [exp](./fn.exp.html), [log](./fn.log.html), [expm1](./fn.expm1.html), [log1p](./fn.log1p.html), etc. | +| Trigonometric functions | [sin](./fn.sin.html), [cos](./fn.cos.html), [tan](./fn.tan.html), etc. | +| Hyperbolic functions | [sinh](./fn.sinh.html), [cosh](./fn.cosh.html), [tanh](./fn.tanh.html), etc. | + +In addition to element-wise operations, many other functions are also +vectorized in ArrayFire. + +Notice that even functions that perform some form of aggregation (e.g. +[sum](./fn.sum.html) or [min](./fn.min.html)), signal processing (like +[convolve](./fn.convolve.html)), and image processing functions +(i.e. [rotate](./fn.rotate.html) etc.) - all support vectorization on + different columns or images. + +For example, if we have `NUM` images of size `WIDTH`x`HEIGHT`, one could +convolve each image in a vector fashion as follows: + +```rust +let g_coef: [f32, 9] = { 1, 2, 1, 2, 4, 2, 1, 2, 1 }; + +let f = Array::new(g_coef, Dim4::new(&[3, 3, 1, 1])); +let filter = f * 1.0f32/16; + +let signal = randu(WIDTH, HEIGHT, NUM); +let conv = convolve2(signal, filter, ConvMode::DEFAULT, ConvDomain::AUTO); +``` + +Similarly, one can rotate 100 images by 45 degrees in a single call using +code like the following: + +```rust +// Construct an array of 100 WIDTH x HEIGHT images of random numbers +let imgs = randu(WIDTH, HEIGHT, 100); + +// Rotate all of the images in a single command +let rot_imgs = rotate(imgs, 45.0, False, InterpType::LINEAR); +``` + +Although *most* functions in ArrayFire do support vectorization, some do not. +Most notably, all linear algebra functions. Even though they are not vectorized +linear algebra operations still execute in parallel on your hardware. + +Using the built in vectorized operations should be the first +and preferred method of vectorizing any code written with ArrayFire. + + diff --git a/generate_docs.sh b/generate_docs.sh new file mode 100755 index 000000000..5a60b6a14 --- /dev/null +++ b/generate_docs.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +cargo doc + +if [ -d "./target/doc/arrayfire" ]; then + # If cargo doc(generates ./target/doc directory) has been run + cp ./doc/external_docs.css ./target/doc/ + + rustdoc "./doc/getting_started.md" --markdown-css "../rustdoc.css" --markdown-css "../main.css" --markdown-css "../normalize.css" --markdown-css "../external_docs.css" -o "./target/doc/arrayfire/" + rustdoc "./doc/array_and_matrix_manipulation.md" --markdown-css "../rustdoc.css" --markdown-css "../main.css" --markdown-css "../normalize.css" --markdown-css "../external_docs.css" --markdown-no-toc -o "./target/doc/arrayfire/" + rustdoc "./doc/vectorization.md" --markdown-css "../rustdoc.css" --markdown-css "../main.css" --markdown-css "../normalize.css" --markdown-css "../external_docs.css" --markdown-no-toc -o "./target/doc/arrayfire/" + rustdoc "./doc/indexing.md" --markdown-css "../rustdoc.css" --markdown-css "../main.css" --markdown-css "../normalize.css" --markdown-css "../external_docs.css" -o "./target/doc/arrayfire/" + rustdoc "./doc/configuring_arrayfire_environment.md" --markdown-css "../rustdoc.css" --markdown-css "../main.css" --markdown-css "../normalize.css" --markdown-css "../external_docs.css" -o "./target/doc/arrayfire/" +fi diff --git a/src/lib.rs b/src/lib.rs index b3e099fa6..c59d22869 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,6 +16,14 @@ //! | 3.4.x | 3.4.x | //! //! Only, Major & Minor version numbers need to match. +//! +//! ## Tutorials +//! +//! - [Getting Started with ArrayFire](./getting_started.html) +//! - [Introduction to Vectorization](./vectorization.html) +//! - [Array and Matrix Manipulation](./array_and_matrix_manipulation.html) +//! - [Indexing](./indexing.html) +//! - [Configure ArrayFire Environment](./configuring_arrayfire_environment.html) #![doc(html_logo_url = "http://www.arrayfire.com/logos/arrayfire_logo_symbol.png", html_favicon_url = "http://www.rust-lang.org/favicon.ico", From aa68391ece5db8e5ed1d3c17c286b4a0528ca00b Mon Sep 17 00:00:00 2001 From: pradeep Date: Thu, 8 Jun 2017 13:50:05 +0530 Subject: [PATCH 06/30] add lint to allow non camel case naming for enums --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index c59d22869..678f28432 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,6 +29,7 @@ html_favicon_url = "http://www.rust-lang.org/favicon.ico", html_root_url = "http://arrayfire.com/docs/rust")] #![warn(missing_docs)] +#![allow(non_camel_case_types)] #[macro_use] extern crate lazy_static; From 8481527b28653bd7ba30faa7835e70f8951bb5c5 Mon Sep 17 00:00:00 2001 From: pradeep Date: Thu, 8 Jun 2017 13:55:20 +0530 Subject: [PATCH 07/30] change Array::elements return type to usize --- src/array.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/array.rs b/src/array.rs index 60770a1c3..ba6b711dc 100644 --- a/src/array.rs +++ b/src/array.rs @@ -233,12 +233,12 @@ impl Array { } /// Returns the number of elements in the Array - pub fn elements(&self) -> i64 { + pub fn elements(&self) -> usize { unsafe { let mut ret_val: i64 = 0; let err_val = af_get_elements(&mut ret_val as MutAfArray, self.handle as AfArray); HANDLE_ERROR(AfError::from(err_val)); - ret_val + ret_val as usize } } From de0278ad6ee71dda08839ec477d6f96d09a02dae Mon Sep 17 00:00:00 2001 From: pradeep Date: Thu, 8 Jun 2017 13:55:46 +0530 Subject: [PATCH 08/30] add HasAfEnum trait bound and size check to Array::host method --- src/array.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/array.rs b/src/array.rs index ba6b711dc..1b44b4937 100644 --- a/src/array.rs +++ b/src/array.rs @@ -308,7 +308,10 @@ impl Array { } /// Copies the data from the Array to the mutable slice `data` - pub fn host(&self, data: &mut [T]) { + pub fn host(&self, data: &mut [T]) { + if data.len() != self.elements() { + HANDLE_ERROR(AfError::ERR_SIZE); + } unsafe { let err_val = af_get_data_ptr(data.as_mut_ptr() as *mut c_void, self.handle as AfArray); HANDLE_ERROR(AfError::from(err_val)); From b646fbbb14155c87c244d7ecef2e3318d533d880 Mon Sep 17 00:00:00 2001 From: pradeep Date: Thu, 8 Jun 2017 16:27:51 +0530 Subject: [PATCH 09/30] fix ref count for arrays used in indexing The Array objects used by Indexer objects were not properly incrementing the reference count. This led to memory corruption and undefined behaviour which is fixed now. --- src/index.rs | 42 ++++++++++++++++-------------------------- src/util.rs | 2 +- 2 files changed, 17 insertions(+), 27 deletions(-) diff --git a/src/index.rs b/src/index.rs index 67d2b3857..ae85344b2 100644 --- a/src/index.rs +++ b/src/index.rs @@ -5,26 +5,25 @@ use defines::AfError; use error::HANDLE_ERROR; use seq::Seq; use self::libc::{c_double, c_int, c_uint}; -use util::{AfArray, DimT, IndexT, MutAfArray, MutAfIndex}; +use util::{AfArray, AfIndex, DimT, MutAfArray, MutAfIndex}; #[allow(dead_code)] extern { fn af_create_indexers(indexers: MutAfIndex) -> c_int; - fn af_set_array_indexer(indexer: MutAfIndex, idx: AfArray, dim: DimT) -> c_int; - fn af_set_seq_indexer(indexer: MutAfIndex, idx: *const SeqInternal, dim: DimT, is_batch: c_int) -> c_int; - fn af_release_indexers(indexers: MutAfIndex) -> c_int; + fn af_set_array_indexer(indexer: AfIndex, idx: AfArray, dim: DimT) -> c_int; + fn af_set_seq_indexer(indexer: AfIndex, idx: *const SeqInternal, dim: DimT, is_batch: c_int) -> c_int; + fn af_release_indexers(indexers: AfIndex) -> c_int; fn af_index(out: MutAfArray, input: AfArray, ndims: c_uint, index: *const SeqInternal) -> c_int; fn af_lookup(out: MutAfArray, arr: AfArray, indices: AfArray, dim: c_uint) -> c_int; fn af_assign_seq(out: MutAfArray, lhs: AfArray, ndims: c_uint, indices: *const SeqInternal, rhs: AfArray) -> c_int; - fn af_index_gen(out: MutAfArray, input: AfArray, ndims: DimT, indices: *const IndexT) -> c_int; - fn af_assign_gen(out: MutAfArray, lhs: AfArray, ndims: DimT, indices: *const IndexT, rhs: AfArray) -> c_int; + fn af_index_gen(out: MutAfArray, input: AfArray, ndims: DimT, indices: AfIndex) -> c_int; + fn af_assign_gen(out: MutAfArray, lhs: AfArray, ndims: DimT, indices: AfIndex, rhs: AfArray) -> c_int; } /// Struct to manage an array of resources of type `af_indexer_t`(ArrayFire C struct) pub struct Indexer { handle: i64, - count: u32, } // Trait that indicates that object can be used for indexing @@ -32,7 +31,7 @@ pub struct Indexer { // Any object to be able to be passed on to [./struct.Indexer.html#method.set_index] method // should implement this trait with appropriate implementation pub trait Indexable { - fn set(&self, idxr: &Indexer, dim: u32, is_batch: Option); + fn set(&self, idxr: &mut Indexer, dim: u32, is_batch: Option); } /// Enables [Array](./struct.Array.html) to be used to index another Array @@ -41,11 +40,10 @@ pub trait Indexable { /// [assign_gen](./fn.assign_gen.html) impl Indexable for Array { #[allow(unused_variables)] - fn set(&self, idxr: &Indexer, dim: u32, is_batch: Option) { + fn set(&self, idxr: &mut Indexer, dim: u32, is_batch: Option) { unsafe { - let err_val = af_set_array_indexer(idxr.clone().get() as MutAfIndex, - self.get() as AfArray, - dim as DimT); + let err_val = af_set_array_indexer(idxr.get() as AfIndex, self.clone().get() as AfArray, + dim as DimT); HANDLE_ERROR(AfError::from(err_val)); } } @@ -56,9 +54,9 @@ impl Indexable for Array { /// This is used in functions [index_gen](./fn.index_gen.html) and /// [assign_gen](./fn.assign_gen.html) impl Indexable for Seq where c_double: From { - fn set(&self, idxr: &Indexer, dim: u32, is_batch: Option) { + fn set(&self, idxr: &mut Indexer, dim: u32, is_batch: Option) { unsafe { - let err_val = af_set_seq_indexer(idxr.clone().get() as MutAfIndex, + let err_val = af_set_seq_indexer(idxr.get() as AfIndex, &SeqInternal::from_seq(self) as *const SeqInternal, dim as DimT, is_batch.unwrap() as c_int); HANDLE_ERROR(AfError::from(err_val)); @@ -74,13 +72,12 @@ impl Indexer { let mut temp: i64 = 0; let err_val = af_create_indexers(&mut temp as MutAfIndex); HANDLE_ERROR(AfError::from(err_val)); - Indexer{handle: temp, count: 0} + Indexer{handle: temp} } } /// Set either [Array](./struct.Array.html) or [Seq](./struct.Seq.html) to index an Array along `idx` dimension pub fn set_index(&mut self, idx: &T, dim: u32, is_batch: Option) { - self.count = self.count + 1; idx.set(self, dim, is_batch) } @@ -88,19 +85,12 @@ impl Indexer { pub fn get(&self) -> i64 { self.handle } - - /// Get number of indexers - /// - /// This can be a maximum of four since currently ArrayFire supports maximum of four dimensions - pub fn len(&self) -> u32 { - self.count - } } impl Drop for Indexer { fn drop(&mut self) { unsafe { - let ret_val = af_release_indexers(self.handle as MutAfIndex); + let ret_val = af_release_indexers(self.handle as AfIndex); match ret_val { 0 => (), _ => panic!("Failed to release indexers resource: {}", ret_val), @@ -338,7 +328,7 @@ pub fn index_gen(input: &Array, indices: Indexer) -> Array { unsafe{ let mut temp: i64 = 0; let err_val = af_index_gen(&mut temp as MutAfArray, input.get() as AfArray, - indices.len() as DimT, indices.get() as *const IndexT); + 4, indices.get() as AfIndex); HANDLE_ERROR(AfError::from(err_val)); Array::from(temp) } @@ -380,7 +370,7 @@ pub fn assign_gen(lhs: &Array, indices: &Indexer, rhs: &Array) -> Array { unsafe{ let mut temp: i64 = 0; let err_val = af_assign_gen(&mut temp as MutAfArray, lhs.get() as AfArray, - indices.len() as DimT, indices.get() as *const IndexT, + 4, indices.get() as AfIndex, rhs.get() as AfArray); HANDLE_ERROR(AfError::from(err_val)); Array::from(temp) diff --git a/src/util.rs b/src/util.rs index 181f735ba..36dbc8335 100644 --- a/src/util.rs +++ b/src/util.rs @@ -9,12 +9,12 @@ use self::num::Complex; use self::libc::{uint8_t, c_int, size_t, c_void}; pub type AfArray = self::libc::c_longlong; +pub type AfIndex = self::libc::c_longlong; pub type CellPtr = *const self::libc::c_void; pub type Complex32 = Complex; pub type Complex64 = Complex; pub type DimT = self::libc::c_longlong; pub type Feat = *const self::libc::c_void; -pub type IndexT = self::libc::c_longlong; pub type Intl = self::libc::c_longlong; pub type MutAfArray = *mut self::libc::c_longlong; pub type MutAfIndex = *mut self::libc::c_longlong; From c53c7f225da01fcfd53262c9be8ffbb4f853980d Mon Sep 17 00:00:00 2001 From: pradeep Date: Thu, 8 Jun 2017 16:59:15 +0530 Subject: [PATCH 10/30] Update docs about type of Array returned by comparison ops --- src/arith/mod.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/arith/mod.rs b/src/arith/mod.rs index 7e77df60d..623a69e3c 100644 --- a/src/arith/mod.rs +++ b/src/arith/mod.rs @@ -255,6 +255,17 @@ macro_rules! overloaded_binary_func { /// /// An Array with results of the binary operation. /// + /// In the case of comparison operations such as the following, the type of output + /// Array is [DType::B8](./enum.DType.html). To retrieve the results of such boolean output + /// to host, an array of 8-bit wide types(eg. u8, i8) should be used since ArrayFire's internal + /// implementation uses char for boolean. + /// + /// * [gt](./fn.gt.html) + /// * [lt](./fn.lt.html) + /// * [ge](./fn.ge.html) + /// * [le](./fn.le.html) + /// * [eq](./fn.eq.html) + /// ///# Note /// /// The trait `Convertable` essentially translates to a scalar native type on rust or Array. From ff4e1be866bacc4f3358d7da8950cffaa7b21b9f Mon Sep 17 00:00:00 2001 From: pradeep Date: Thu, 8 Jun 2017 17:44:58 +0530 Subject: [PATCH 11/30] Update load_image docs --- src/image/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/image/mod.rs b/src/image/mod.rs index fec01bca5..0579f3c22 100644 --- a/src/image/mod.rs +++ b/src/image/mod.rs @@ -126,6 +126,8 @@ pub fn gradient(input: &Array) -> (Array, Array) { /// Load Image into Array /// +/// Only, Images with 8/16/32 bits per channel can be loaded using this function. +/// /// # Parameters /// /// - `filename` is aboslute path of the image to be loaded. From 33f9175fb7c7509df9bed63d608603ae804e9301 Mon Sep 17 00:00:00 2001 From: pradeep Date: Thu, 8 Jun 2017 23:25:33 +0530 Subject: [PATCH 12/30] Update README with 1.15.1 as minimum Rust version required starting commit d55874800fdcc10065bbe00e293cffff6d69c532 rustc-serialize has been replaced in favor of serde. Serde requires rust 1.15.1. Thus, README has been updated reflecting the same. --- README.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/README.md b/README.md index 573442f83..dd55af841 100644 --- a/README.md +++ b/README.md @@ -18,13 +18,7 @@ You can find the most recent updated documentation [here](http://arrayfire.githu ## Supported platforms -- Linux and OSX: The bindings have been tested with Rust 1.x. -- Windows: Rust 1.5 (MSVC ABI) is the first version that works with our bindings and ArrayFire library(built using MSVC compiler). - -We recommend using Rust 1.5 and higher. - -Rust 1.8 stabilized the traits for compound assignment operations. These are automatically enabled -based on the rust version you are using. +Linux, Windows and OSX. We recommend using Rust 1.15.1 or higher. ## Use from Crates.io [![](http://meritbadge.herokuapp.com/arrayfire)](https://crates.io/crates/arrayfire) From 1c19542f4306c0f9b757493e5fdaadd568fe3e11 Mon Sep 17 00:00:00 2001 From: pradeep Date: Sun, 11 Jun 2017 16:15:23 +0530 Subject: [PATCH 13/30] Enforce Indexable objects to use lifetime of Indexer object Currently, only Seq and Array implement the Indexable trait. Earlier to this change, there was no proper lifetime specification for Indexer struct and Indexer::set_index method to enfore borrow checking. This should be fixed now. --- src/arith/mod.rs | 19 +++++++----- src/index.rs | 81 ++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 83 insertions(+), 17 deletions(-) diff --git a/src/arith/mod.rs b/src/arith/mod.rs index 623a69e3c..22083e243 100644 --- a/src/arith/mod.rs +++ b/src/arith/mod.rs @@ -460,13 +460,15 @@ macro_rules! arith_assign_func { #[allow(unused_variables)] fn $fn_name(&mut self, rhs: Array) { + let tmp_seq = Seq::::default(); let mut idxrs = Indexer::new(); - for n in 0..self.numdims() { - idxrs.set_index(&Seq::::default(), n, Some(false)); - } + idxrs.set_index(&tmp_seq, 0, Some(false)); + idxrs.set_index(&tmp_seq, 1, Some(false)); + idxrs.set_index(&tmp_seq, 2, Some(false)); + idxrs.set_index(&tmp_seq, 3, Some(false)); let tmp = assign_gen(self as &Array, &idxrs, & $func(self as &Array, &rhs, false)); - mem::replace(self, tmp); + let old = mem::replace(self, tmp); } } ) @@ -486,11 +488,14 @@ macro_rules! bit_assign_func { #[allow(unused_variables)] fn $fn_name(&mut self, rhs: Array) { + let tmp_seq = Seq::::default(); let mut idxrs = Indexer::new(); - idxrs.set_index(&Seq::::default(), 0, Some(false)); - idxrs.set_index(&Seq::::default(), 1, Some(false)); + idxrs.set_index(&tmp_seq, 0, Some(false)); + idxrs.set_index(&tmp_seq, 1, Some(false)); + idxrs.set_index(&tmp_seq, 2, Some(false)); + idxrs.set_index(&tmp_seq, 3, Some(false)); let tmp = assign_gen(self as &Array, &idxrs, & $func(self as &Array, &rhs, false)); - mem::replace(self, tmp); + let old = mem::replace(self, tmp); } } ) diff --git a/src/index.rs b/src/index.rs index ae85344b2..d3d9fd3ff 100644 --- a/src/index.rs +++ b/src/index.rs @@ -7,6 +7,8 @@ use seq::Seq; use self::libc::{c_double, c_int, c_uint}; use util::{AfArray, AfIndex, DimT, MutAfArray, MutAfIndex}; +use std::marker::PhantomData; + #[allow(dead_code)] extern { fn af_create_indexers(indexers: MutAfIndex) -> c_int; @@ -22,8 +24,60 @@ extern { } /// Struct to manage an array of resources of type `af_indexer_t`(ArrayFire C struct) -pub struct Indexer { +/// +/// # Examples +/// +/// Given below are examples illustrating correct and incorrect usage of Indexer struct. +/// +///

Correct Usage

+/// +/// ```rust +/// use arrayfire::{Array, Dim4, randu, index_gen, Indexer}; +/// +/// // Always be aware of the fact that, the `Seq` or `Array` objects +/// // that we intend to use for indexing via `Indexer` have to outlive +/// // the `Indexer` object created in this context. +/// +/// let dims = Dim4::new(&[1, 3, 1, 1]); +/// let bools = Array::new(&[1, 0, 1], dims); +/// let values = Array::new(&[2, 5, 6], dims); +/// +/// let mut idxr = Indexer::new(); +/// +/// // `bools` is created much before idxr, thus will +/// // stay in scope at least as long as idxr +/// idxr.set_index(&bools, 0, None); +/// +/// index_gen(&values, idxr); +/// ``` +/// +///

Incorrect Usage

+/// +/// ```rust,ignore +/// // Say, you create an Array on the fly and try +/// // to call set_index, it will throw the given below +/// // error or something similar to that +/// idxr.set_index(&Array::new(&[1, 0, 1], dims), 0, None); +/// ``` +/// +/// ```text +/// error: borrowed value does not live long enough +/// --> :16:55 +/// | +///16 | idxr.set_index(&Array::new(&[1, 0, 1], dims), 0, None); +/// | ---------------------------- ^ temporary value dropped here while still borrowed +/// | | +/// | temporary value created here +///... +///19 | } +/// | - temporary value needs to live until here +/// | +/// = note: consider using a `let` binding to increase its lifetime +/// ``` +pub struct Indexer<'object> { handle: i64, + count: usize, + marker: PhantomData<&'object ()>, } // Trait that indicates that object can be used for indexing @@ -42,7 +96,7 @@ impl Indexable for Array { #[allow(unused_variables)] fn set(&self, idxr: &mut Indexer, dim: u32, is_batch: Option) { unsafe { - let err_val = af_set_array_indexer(idxr.get() as AfIndex, self.clone().get() as AfArray, + let err_val = af_set_array_indexer(idxr.get() as AfIndex, self.get() as AfArray, dim as DimT); HANDLE_ERROR(AfError::from(err_val)); } @@ -64,21 +118,28 @@ impl Indexable for Seq where c_double: From { } } -impl Indexer { +impl<'object> Indexer<'object> { #[allow(unused_mut)] /// Create a new Indexer object and set the dimension specific index objects later - pub fn new() -> Indexer { + pub fn new() -> Indexer<'object> { unsafe { let mut temp: i64 = 0; let err_val = af_create_indexers(&mut temp as MutAfIndex); HANDLE_ERROR(AfError::from(err_val)); - Indexer{handle: temp} + Indexer{handle: temp, count: 0, marker: PhantomData} } } /// Set either [Array](./struct.Array.html) or [Seq](./struct.Seq.html) to index an Array along `idx` dimension - pub fn set_index(&mut self, idx: &T, dim: u32, is_batch: Option) { - idx.set(self, dim, is_batch) + pub fn set_index<'s, T>(&'s mut self, idx: &'object T, dim: u32, is_batch: Option) + where T : Indexable + 'object { + idx.set(self, dim, is_batch); + self.count = self.count+1; + } + + /// Get number of indexing objects set + pub fn len(&self) -> usize { + self.count } /// Get native(ArrayFire) resource handle @@ -87,7 +148,7 @@ impl Indexer { } } -impl Drop for Indexer { +impl<'object> Drop for Indexer<'object> { fn drop(&mut self) { unsafe { let ret_val = af_release_indexers(self.handle as AfIndex); @@ -328,7 +389,7 @@ pub fn index_gen(input: &Array, indices: Indexer) -> Array { unsafe{ let mut temp: i64 = 0; let err_val = af_index_gen(&mut temp as MutAfArray, input.get() as AfArray, - 4, indices.get() as AfIndex); + indices.len() as DimT, indices.get() as AfIndex); HANDLE_ERROR(AfError::from(err_val)); Array::from(temp) } @@ -370,7 +431,7 @@ pub fn assign_gen(lhs: &Array, indices: &Indexer, rhs: &Array) -> Array { unsafe{ let mut temp: i64 = 0; let err_val = af_assign_gen(&mut temp as MutAfArray, lhs.get() as AfArray, - 4, indices.get() as AfIndex, + indices.len() as DimT, indices.get() as AfIndex, rhs.get() as AfArray); HANDLE_ERROR(AfError::from(err_val)); Array::from(temp) From 8893683fb875673798ca941edd498f24fc17eba2 Mon Sep 17 00:00:00 2001 From: pradeep Date: Mon, 12 Jun 2017 20:27:10 +0530 Subject: [PATCH 14/30] fix indexer calls in Assign trait impls --- src/arith/mod.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/arith/mod.rs b/src/arith/mod.rs index 22083e243..d5cf69573 100644 --- a/src/arith/mod.rs +++ b/src/arith/mod.rs @@ -462,10 +462,9 @@ macro_rules! arith_assign_func { fn $fn_name(&mut self, rhs: Array) { let tmp_seq = Seq::::default(); let mut idxrs = Indexer::new(); - idxrs.set_index(&tmp_seq, 0, Some(false)); - idxrs.set_index(&tmp_seq, 1, Some(false)); - idxrs.set_index(&tmp_seq, 2, Some(false)); - idxrs.set_index(&tmp_seq, 3, Some(false)); + for n in 0..self.numdims() { + idxrs.set_index(&tmp_seq, n, Some(false)); + } let tmp = assign_gen(self as &Array, &idxrs, & $func(self as &Array, &rhs, false)); let old = mem::replace(self, tmp); @@ -490,10 +489,9 @@ macro_rules! bit_assign_func { fn $fn_name(&mut self, rhs: Array) { let tmp_seq = Seq::::default(); let mut idxrs = Indexer::new(); - idxrs.set_index(&tmp_seq, 0, Some(false)); - idxrs.set_index(&tmp_seq, 1, Some(false)); - idxrs.set_index(&tmp_seq, 2, Some(false)); - idxrs.set_index(&tmp_seq, 3, Some(false)); + for n in 0..self.numdims() { + idxrs.set_index(&tmp_seq, n, Some(false)); + } let tmp = assign_gen(self as &Array, &idxrs, & $func(self as &Array, &rhs, false)); let old = mem::replace(self, tmp); } From bba7ef4dfe288fecd0fde2c0f1945221a9f228ab Mon Sep 17 00:00:00 2001 From: pradeep Date: Fri, 16 Jun 2017 08:56:08 +0530 Subject: [PATCH 15/30] Typo fixes in README --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index dd55af841..89eb3cd68 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ You can find the most recent updated documentation [here](http://arrayfire.githu ## Supported platforms -Linux, Windows and OSX. We recommend using Rust 1.15.1 or higher. +Linux, Windows and OSX. Rust 1.15.1 or higher is required. ## Use from Crates.io [![](http://meritbadge.herokuapp.com/arrayfire)](https://crates.io/crates/arrayfire) @@ -69,7 +69,6 @@ af_print!("Create a 5-by-3 matrix of random floats on the GPU", a); ```bash ~/p/arrayfire_rust> cargo run --example helloworld ... - running 1 test Create a 5-by-3 matrix of random floats on the GPU [5 3 1 1] 0.7402 0.4464 0.7762 From d81faf84cb9f099a1406577c931a18199ba04fef Mon Sep 17 00:00:00 2001 From: pradeep Date: Fri, 16 Jun 2017 11:26:44 +0530 Subject: [PATCH 16/30] Update arrayfire submodule to v3.4.2 tag --- arrayfire | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arrayfire b/arrayfire index 10d9716a4..2da99675a 160000 --- a/arrayfire +++ b/arrayfire @@ -1 +1 @@ -Subproject commit 10d9716a4aca5bc413d642be6295ce08614d5c36 +Subproject commit 2da99675a12fd10f38cd5633a77c3d09e8070bbf From 4c685d830ac5bd321958c5a9341439b42cc3dd09 Mon Sep 17 00:00:00 2001 From: pradeep Date: Sat, 17 Jun 2017 21:56:30 +0530 Subject: [PATCH 17/30] Add conditional compiliation attributes for modules --- Cargo.toml | 18 +++++++++ src/array.rs | 1 - src/lib.rs | 110 +++++++++++++++++++++------------------------------ 3 files changed, 63 insertions(+), 66 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a086f0397..3d89a3617 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,24 @@ exclude = [ "arrayfire/*", ] +[features] +algorithm = [] +arithmetic = [] +blas = [] +data = [] +indexing = [] +graphics = [] +image = [] +lapack = [] +macros = [] +random = [] +signal = [] +sparse = [] +statistics = [] +vision = [] +default = ["algorithm", "arithmetic", "blas", "data", "indexing", "graphics", "image", "lapack", +"macros", "random", "signal", "sparse", "statistics", "vision"] + [dependencies] libc = "0.2.11" num = "0.1.32" diff --git a/src/array.rs b/src/array.rs index 1b44b4937..e159ec359 100644 --- a/src/array.rs +++ b/src/array.rs @@ -8,7 +8,6 @@ use self::libc::{uint8_t, c_void, c_int, c_uint, c_longlong, c_char}; use std::ffi::CString; // Some unused functions from array.h in C-API of ArrayFire -// af_create_handle // af_copy_array // af_write_array // af_get_data_ref_count diff --git a/src/lib.rs b/src/lib.rs index 678f28432..106e7411c 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,50 +34,36 @@ #[macro_use] extern crate lazy_static; -pub use array::Array; -pub use array::{print, print_gen, eval_multiple, is_eval_manual, set_manual_eval}; +pub use array::*; mod array; -//pub use algorithm::{sum_nan, product_nan, sum_nan_all, product_nan_all}; -pub use algorithm::{sum, product, min, max, all_true, any_true, count, sum_nan, product_nan}; -pub use algorithm::{sum_all, product_all, min_all, max_all, sum_nan_all, product_nan_all}; -pub use algorithm::{all_true_all, any_true_all, count_all, imin, imax, imin_all, imax_all}; -pub use algorithm::{accum, locate, diff1, diff2, sort, sort_index, sort_by_key}; -pub use algorithm::{set_unique, set_union, set_intersect, scan, scan_by_key}; +#[cfg(feature="algorithm")] +pub use algorithm::*; +#[cfg(feature="algorithm")] mod algorithm; -pub use arith::{add, sub, div, mul, lt, gt, le, ge, eq, neq, and, or, minof, maxof, rem}; -pub use arith::{bitand, bitor, bitxor, shiftl, shiftr}; -pub use arith::{abs, sign, round, trunc, floor, ceil, modulo, sigmoid, clamp}; -pub use arith::{sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, asinh, acosh, atanh}; -pub use arith::{atan2, cplx2, arg, cplx, real, imag, conjg, hypot}; -pub use arith::{sqrt, log, log1p, log10, log2, pow2, exp, expm1, erf, erfc, root, pow}; -pub use arith::{cbrt, factorial, tgamma, lgamma, iszero, isinf, isnan}; +#[cfg(feature="arithmetic")] +pub use arith::*; +#[cfg(feature="arithmetic")] mod arith; -pub use backend::{set_backend, get_backend_count, get_available_backends, get_active_backend}; +pub use backend::*; mod backend; -pub use blas::{matmul, dot, transpose, transpose_inplace}; +#[cfg(feature="blas")] +pub use blas::*; +#[cfg(feature="blas")] mod blas; -pub use data::{constant, range, iota, identity}; -pub use data::{diag_create, diag_extract, lower, upper}; -pub use data::{join, join_many, tile}; -pub use data::{reorder, shift, moddims, flat, flip}; -pub use data::{select, selectl, selectr, replace, replace_scalar}; -pub use data::{range_t, iota_t, identity_t, constant_t}; +#[cfg(feature="data")] +pub use data::*; +#[cfg(feature="data")] mod data; -pub use device::{get_version, get_revision, info, info_string, device_info, init, device_count, is_double_available, set_device, get_device}; -pub use device::{device_mem_info, print_mem_info, set_mem_step_size, get_mem_step_size, device_gc, sync}; +pub use device::*; mod device; -pub use defines::{DType, AfError, Backend, ColorMap, YCCStd, HomographyType}; -pub use defines::{InterpType, BorderType, MatchType, NormType}; -pub use defines::{Connectivity, ConvMode, ConvDomain, ColorSpace, MatProp}; -pub use defines::{MarkerType, MomentType, SparseFormat, BinaryOp, RandomEngineType}; -pub use defines::{PHILOX, THREEFRY, MERSENNE, DEFAULT_RANDOM_ENGINE, Scalar}; +pub use defines::*; mod defines; pub use dim4::Dim4; @@ -86,68 +72,62 @@ mod dim4; pub use error::{Callback, ErrorCallback, register_error_handler, handle_error_general}; mod error; -pub use index::{Indexer, index, row, rows, col, cols, slice, slices, - set_row, set_rows, set_col, set_cols, set_slice, set_slices, - lookup, assign_seq, index_gen, assign_gen}; +#[cfg(feature="indexing")] +pub use index::*; +#[cfg(feature="indexing")] mod index; pub use seq::Seq; mod seq; +#[cfg(feature="graphics")] pub use graphics::Window; +#[cfg(feature="graphics")] mod graphics; -pub use image::{gaussian_kernel, load_image, load_image_native, save_image, save_image_native}; -pub use image::{resize, transform, rotate, translate, scale, skew}; -pub use image::{dilate, dilate3, erode, erode3, minfilt, maxfilt}; -pub use image::{gradient, histogram, hist_equal, regions}; -pub use image::{gray2rgb, rgb2gray, hsv2rgb, rgb2hsv, color_space}; -pub use image::{bilateral, mean_shift, medfilt, sobel, medfilt1}; -pub use image::{unwrap, wrap, sat, rgb2ycbcr, ycbcr2rgb, is_imageio_available, transform_coords}; -pub use image::{moments, moments_all}; +#[cfg(feature="image")] +pub use image::*; +#[cfg(feature="image")] mod image; -pub use lapack::{svd, lu, qr, cholesky, solve, solve_lu, inverse, det, rank, norm}; -pub use lapack::{svd_inplace, lu_inplace, qr_inplace, cholesky_inplace, is_lapack_available}; +#[cfg(feature="lapack")] +pub use lapack::*; +#[cfg(feature="lapack")] mod lapack; + +#[cfg(feature="macros")] mod macros; mod num; -pub use random::RandomEngine; -pub use random::{set_seed, get_seed, randu, randn, random_uniform, random_normal}; -pub use random::{get_default_random_engine, set_default_random_engine_type}; +#[cfg(feature="random")] +pub use random::*; +#[cfg(feature="random")] mod random; -pub use signal::{approx1, approx2, set_fft_plan_cache_size}; -pub use signal::{fft, fft2, fft3, ifft, ifft2, ifft3}; -pub use signal::{fft_r2c, fft2_r2c, fft3_r2c, fft_c2r, fft2_c2r, fft3_c2r}; -pub use signal::{fft_inplace, fft2_inplace, fft3_inplace}; -pub use signal::{ifft_inplace, ifft2_inplace, ifft3_inplace}; -pub use signal::{convolve1, convolve2, convolve3, convolve2_sep}; -pub use signal::{fft_convolve1, fft_convolve2, fft_convolve3}; -pub use signal::{fir, iir}; +#[cfg(feature="signal")] +pub use signal::*; +#[cfg(feature="signal")] mod signal; -pub use sparse::{sparse, sparse_from_host, sparse_from_dense, sparse_convert_to}; -pub use sparse::{sparse_to_dense, sparse_get_info, sparse_get_values, sparse_get_nnz}; -pub use sparse::{sparse_get_row_indices, sparse_get_col_indices, sparse_get_format}; +#[cfg(feature="sparse")] +pub use sparse::*; +#[cfg(feature="sparse")] mod sparse; -pub use statistics::{mean, stdev, median, var, cov, corrcoef}; -pub use statistics::{mean_weighted, var_weighted}; -pub use statistics::{var_all, mean_all, stdev_all, median_all}; -pub use statistics::{mean_all_weighted, var_all_weighted}; +#[cfg(feature="statistics")] +pub use statistics::*; +#[cfg(feature="statistics")] mod statistics; pub use util::{HasAfEnum, get_size}; mod util; -pub use vision::Features; -pub use vision::{fast, harris, orb, hamming_matcher, nearest_neighbour, match_template, susan, dog}; -pub use vision::{homography}; +#[cfg(feature="vision")] +pub use vision::*; +#[cfg(feature="vision")] mod vision; -// headers that are not exposed through rust wrapper are as follows: +// headers that are not exposed through rust wrapper are given follows: // compatible.h // constants.h // complex.h From 3625eaf0b605feed274115c88cb9c0080f28c4aa Mon Sep 17 00:00:00 2001 From: pradeep Date: Mon, 19 Jun 2017 12:40:20 +0530 Subject: [PATCH 18/30] Add docs for Convertable trait --- src/arith/mod.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/arith/mod.rs b/src/arith/mod.rs index d5cf69573..cadd40cfd 100644 --- a/src/arith/mod.rs +++ b/src/arith/mod.rs @@ -195,7 +195,29 @@ binary_func!("Elementwise minimum operation of two Arrays", minof, af_minof); binary_func!("Elementwise maximum operation of two Arrays", maxof, af_maxof); binary_func!("Compute length of hypotenuse of two Arrays", hypot, af_hypot); +/// Type Trait to convert to an [Array](./struct.Array.html) +/// +/// Generic functions that overload the binary operations such as add, div, mul, rem, ge etc. are +/// bound by this trait to allow combinations of scalar values and Array objects as parameters +/// to those functions. +/// +/// Internally, Convertable trait is implemented by following types. +/// +/// - f32 +/// - f64 +/// - num::Complex\ +/// - num::Complex\ +/// - bool +/// - i32 +/// - u32 +/// - u8 +/// - i64 +/// - u64 +/// - i16 +/// - u16 +/// pub trait Convertable { + /// Get an Array from given type fn convert(&self) -> Array; } From 8d93e6f1d16ac7fbaaf98fe39b45db8482d5a549 Mon Sep 17 00:00:00 2001 From: pradeep Date: Mon, 19 Jun 2017 12:40:34 +0530 Subject: [PATCH 19/30] Add docs for ConsGenerator trait --- src/data/mod.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/data/mod.rs b/src/data/mod.rs index 30326bbb7..c97cf636a 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -54,7 +54,29 @@ extern { fn af_replace_scalar(a: AfArray, cond: AfArray, b: c_double) -> c_int; } +/// Type Trait to generate a constant [Array](./struct.Array.html) of given size +/// +/// Internally, ConstGenerator trait is implemented by following types. +/// +/// - f32 +/// - f64 +/// - num::Complex\ +/// - num::Complex\ +/// - bool +/// - i32 +/// - u32 +/// - u8 +/// - i64 +/// - u64 +/// - i16 +/// - u16 +/// pub trait ConstGenerator { + /// Create an Array of `dims` size from scalar value `self`. + /// + /// # Parameters + /// + /// - `dims` are the dimensions of the output constant [Array](./struct.Array.html) fn generate(&self, dims: Dim4) -> Array; } From 3b9c9ec045783b46c43b7d4b9ef56bda8fcf3f8a Mon Sep 17 00:00:00 2001 From: pradeep Date: Mon, 19 Jun 2017 12:40:58 +0530 Subject: [PATCH 20/30] Add docs for Indexable trait --- src/index.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/index.rs b/src/index.rs index d3d9fd3ff..9d505f647 100644 --- a/src/index.rs +++ b/src/index.rs @@ -80,11 +80,19 @@ pub struct Indexer<'object> { marker: PhantomData<&'object ()>, } -// Trait that indicates that object can be used for indexing -// -// Any object to be able to be passed on to [./struct.Indexer.html#method.set_index] method -// should implement this trait with appropriate implementation +/// Trait bound indicating indexability +/// +/// Any object to be able to be passed on to [Indexer::set_index()](./struct.Indexer.html#method.set_index) method should implement this trait with appropriate implementation of `set` method. pub trait Indexable { + /// Set indexing object for a given dimension + /// + /// # Parameters + /// + /// - `idxr` is mutable reference to [Indexer](./struct.Indexer.html) object which will + /// be modified to set `self` indexable along `dim` dimension. + /// - `dim` is the dimension along which `self` indexable will be used for indexing. + /// - `is_batch` is only used if `self` is [Seq](./struct.Seq.html) to indicate if indexing + /// along `dim` is a batched operation. fn set(&self, idxr: &mut Indexer, dim: u32, is_batch: Option); } From 2666346ab32f96a270a06bc45454bad83bea396a Mon Sep 17 00:00:00 2001 From: pradeep Date: Mon, 19 Jun 2017 12:42:33 +0530 Subject: [PATCH 21/30] Remove unused declaration from tests --- tests/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/lib.rs b/tests/lib.rs index fa0ce65f2..11990e92a 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1,4 +1,3 @@ -#[macro_use] extern crate arrayfire as af; use std::error::Error; From 6b0dec7ea15f3f2ce5db47d7bfa1628f46413647 Mon Sep 17 00:00:00 2001 From: pradeep Date: Mon, 19 Jun 2017 22:04:05 +0530 Subject: [PATCH 22/30] Fix slice data type in Indexer example code snippet --- src/index.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.rs b/src/index.rs index 9d505f647..3fb3e0418 100644 --- a/src/index.rs +++ b/src/index.rs @@ -39,8 +39,8 @@ extern { /// // the `Indexer` object created in this context. /// /// let dims = Dim4::new(&[1, 3, 1, 1]); -/// let bools = Array::new(&[1, 0, 1], dims); -/// let values = Array::new(&[2, 5, 6], dims); +/// let bools = Array::new(&[1.0f32, 0.0, 1.0], dims); +/// let values = Array::new(&[2.0f32, 5.0, 6.0], dims); /// /// let mut idxr = Indexer::new(); /// From bea015b8424801a97b0b5714e09d7e46756c4e62 Mon Sep 17 00:00:00 2001 From: pradeep Date: Tue, 20 Jun 2017 02:01:43 +0530 Subject: [PATCH 23/30] Temporary work around in Indexer example snippet for OSX This issue doesn't happen on Windows and Linux. Creating an Array from `&[]` directly instead of creating a vector first and then borrowing a slice is causing crash on OSX platform. --- src/index.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/index.rs b/src/index.rs index 3fb3e0418..f3efb98e5 100644 --- a/src/index.rs +++ b/src/index.rs @@ -38,17 +38,19 @@ extern { /// // that we intend to use for indexing via `Indexer` have to outlive /// // the `Indexer` object created in this context. /// -/// let dims = Dim4::new(&[1, 3, 1, 1]); -/// let bools = Array::new(&[1.0f32, 0.0, 1.0], dims); -/// let values = Array::new(&[2.0f32, 5.0, 6.0], dims); +/// let dims = Dim4::new(&[1, 3, 1, 1]); +/// let indices = [1u8, 0, 1]; +/// let idx = Array::new(&indices, dims); +/// let values = [2.0f32, 5.0, 6.0]; +/// let arr = Array::new(&values, dims); /// /// let mut idxr = Indexer::new(); /// -/// // `bools` is created much before idxr, thus will +/// // `idx` is created much before idxr, thus will /// // stay in scope at least as long as idxr -/// idxr.set_index(&bools, 0, None); +/// idxr.set_index(&idx, 0, None); /// -/// index_gen(&values, idxr); +/// index_gen(&arr, idxr); /// ``` /// ///

Incorrect Usage

From e316fe940253e6146bcc55df7c418fdf45dac4a5 Mon Sep 17 00:00:00 2001 From: pradeep Date: Tue, 20 Jun 2017 15:53:33 +0530 Subject: [PATCH 24/30] Bump version to 3.5.0 for release --- Cargo.toml | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3d89a3617..eaf3df84f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "arrayfire" description = "ArrayFire is a high performance software library for parallel computing with an easy-to-use API. Its array based function set makes parallel programming simple. ArrayFire's multiple backends (CUDA, OpenCL and native CPU) make it platform independent and highly portable. A few lines of code in ArrayFire can replace dozens of lines of parallel computing code, saving you valuable time and lowering development costs. This crate provides Rust bindings for ArrayFire library." -version = "3.4.3" +version = "3.5.0" documentation = "http://arrayfire.github.io/arrayfire-rust/arrayfire/index.html" homepage = "https://github.com/arrayfire/arrayfire" repository = "https://github.com/arrayfire/arrayfire-rust" diff --git a/README.md b/README.md index 89eb3cd68..a23168ba8 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ first. 3. Make sure you add the path to library files to your path environment variables. - On Linux & OSX: do `export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$AF_PATH/lib` - On Windows: Add `%AF_PATH%\lib` to your PATH environment variable. -4. Add `arrayfire = "3.4.3"` to the dependencies section of your project's Cargo.toml file - 3.4.3 +4. Add `arrayfire = "3.5.0"` to the dependencies section of your project's Cargo.toml file - 3.5.0 is the lastest version of crate. Once step (4) is over, you should be able to use ArrayFire in your Rust project. If you find any bugs, please report them [here](https://github.com/arrayfire/arrayfire-rust/issues). From d6c1aff1d416b72388eb9a35eab3e634cb4ac6cb Mon Sep 17 00:00:00 2001 From: pradeep Date: Mon, 26 Jun 2017 13:17:21 +0530 Subject: [PATCH 25/30] Removed dead code from build script --- build.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/build.rs b/build.rs index c93619ba4..8e6ea8a65 100644 --- a/build.rs +++ b/build.rs @@ -58,13 +58,6 @@ struct Config { opencl_sdk: String, } -macro_rules! t { - ($e:expr) => (match $e { - Ok(n) => n, - Err(e) => fail(&format!("\n{} failed with {}\n", stringify!($e), e)), - }) -} - fn fail(s: &str) -> ! { panic!("\n{}\n\nbuild script failed, must exit now", s) } From 9b21917d033d7e4d771943c93647f292dda74f84 Mon Sep 17 00:00:00 2001 From: pradeep Date: Mon, 26 Jun 2017 13:20:53 +0530 Subject: [PATCH 26/30] Increment arrayfire submodule to v3.5.0 --- arrayfire | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arrayfire b/arrayfire index 2da99675a..05999f3c4 160000 --- a/arrayfire +++ b/arrayfire @@ -1 +1 @@ -Subproject commit 2da99675a12fd10f38cd5633a77c3d09e8070bbf +Subproject commit 05999f3c473da5b56acd8ac8816a062f7fb9dc9f From b4de986fb377bd03f52d120045ab1eaccd017847 Mon Sep 17 00:00:00 2001 From: pradeep Date: Mon, 26 Jun 2017 13:29:35 +0530 Subject: [PATCH 27/30] new Array method: get_allocated_bytes --- src/array.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/array.rs b/src/array.rs index e159ec359..cf9efbf3b 100644 --- a/src/array.rs +++ b/src/array.rs @@ -4,7 +4,7 @@ use dim4::Dim4; use defines::{AfError, DType, Backend}; use error::HANDLE_ERROR; use util::{AfArray, DimT, HasAfEnum, MutAfArray, MutVoidPtr}; -use self::libc::{uint8_t, c_void, c_int, c_uint, c_longlong, c_char}; +use self::libc::{uint8_t, c_void, c_int, c_uint, c_longlong, c_ulong, c_char}; use std::ffi::CString; // Some unused functions from array.h in C-API of ArrayFire @@ -100,6 +100,8 @@ extern { fn af_unlock_array(arr: AfArray) -> c_int; fn af_get_device_ptr(ptr: MutVoidPtr, arr: AfArray) -> c_int; + + fn af_get_allocated_bytes(result: *mut c_ulong, arr: AfArray) -> c_int; } /// A multidimensional data container @@ -405,6 +407,19 @@ impl Array { temp } } + + /// Get the size of physical allocated bytes. + /// + /// This function will return the size of the parent/owner if the current Array object is an + /// indexed Array. + pub fn get_allocated_bytes(&self) -> u64 { + unsafe { + let mut temp: u64 = 0; + let err_val = af_get_allocated_bytes(&mut temp as *mut c_ulong, self.handle as AfArray); + HANDLE_ERROR(AfError::from(err_val)); + temp + } + } } /// Used for creating Array object from native resource id From d0c75c8c16ea35020e27cd1999fdc356f1b98a25 Mon Sep 17 00:00:00 2001 From: pradeep Date: Mon, 26 Jun 2017 13:53:36 +0530 Subject: [PATCH 28/30] canny edge detection function --- src/defines.rs | 10 ++++++++++ src/image/mod.rs | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/defines.rs b/src/defines.rs index 40163bd3e..2b7586b1b 100644 --- a/src/defines.rs +++ b/src/defines.rs @@ -450,3 +450,13 @@ pub enum Scalar { /// 16 bit unsigned integer U16(u16), } + +/// Canny edge detector threshold operations types +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum CannyThresholdType { + /// User has to define canny thresholds manually + MANUAL = 0, + /// Determine canny algorithm high threshold using Otsu algorithm automatically + OTSU = 1, +} diff --git a/src/image/mod.rs b/src/image/mod.rs index 0579f3c22..ddfd75113 100644 --- a/src/image/mod.rs +++ b/src/image/mod.rs @@ -1,7 +1,7 @@ extern crate libc; use array::Array; -use defines::{AfError, BorderType, ColorSpace, Connectivity, InterpType, YCCStd, MomentType}; +use defines::{AfError, BorderType, CannyThresholdType, ColorSpace, Connectivity, InterpType, YCCStd, MomentType}; use error::HANDLE_ERROR; use util::{AfArray, DimT, HasAfEnum, MutAfArray}; use self::libc::{uint8_t, c_uint, c_int, c_float, c_double, c_char}; @@ -95,6 +95,9 @@ extern { fn af_moments(out: MutAfArray, input: AfArray, moment: c_int) ->c_int; fn af_moments_all(out: *mut c_double, input: AfArray, moment: c_int) ->c_int; + + fn af_canny(out: MutAfArray, input: AfArray, thres_type: c_int, low: c_float, high: c_float, + swindow: c_uint, is_fast: c_int) -> c_int; } /// Calculate the gradients @@ -1216,3 +1219,32 @@ pub fn medfilt1(input: &Array, wlen: u64, etype: BorderType) -> Array { Array::from(temp) } } + +/// Canny edge detection operator +/// +/// The Canny edge detector is an edge detection operator that uses a multi-stage algorithm to detect a wide range of edges in images. A more in depth discussion on it can be found [here](https://en.wikipedia.org/wiki/Canny_edge_detector). +/// +/// # Parameters +/// +/// - `input` is the input image +/// - `threshold_type` helps determine if user set high threshold is to be used or not. It can take values defined by the enum [CannyThresholdType](./enum.CannyThresholdType.html) +/// - `low` is the lower threshold % of the maximum or auto-derived high +/// - `hight` is the higher threshold % of maximum value in gradient image used in hysteresis procedure. This value is ignored if [CannyThresholdType::OTSU](./enum.CannyThresholdType.html) is chosen. +/// - `sobel_window` is the window size of sobel kernel for computing gradient direction and magnitude. +/// - `is_fast` indicates if L1 norm(faster but less accurate) is used to compute image gradient magnitude instead of L2 norm. +/// +/// # Return Values +/// +/// An Array of binary type [DType::B8](./enum.DType.html) indicating edges(All pixels with +/// non-zero values are edges). +pub fn canny(input: &Array, threshold_type: CannyThresholdType, low: f32, hight: f32, + sobel_window: u32, is_fast: bool) -> Array { + unsafe { + let mut temp: i64 = 0; + let err_val = af_canny(&mut temp as MutAfArray, input.get() as AfArray, + threshold_type as c_int, low as c_float, hight as c_float, + sobel_window as c_uint, is_fast as c_int); + HANDLE_ERROR(AfError::from(err_val)); + Array::from(temp) + } +} From e99ea54bb81dd47799f4ad2055ea075adfc71b17 Mon Sep 17 00:00:00 2001 From: pradeep Date: Mon, 26 Jun 2017 14:16:19 +0530 Subject: [PATCH 29/30] Change get_allocated_bytes return to be usize --- src/array.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/array.rs b/src/array.rs index cf9efbf3b..3ca196494 100644 --- a/src/array.rs +++ b/src/array.rs @@ -4,7 +4,7 @@ use dim4::Dim4; use defines::{AfError, DType, Backend}; use error::HANDLE_ERROR; use util::{AfArray, DimT, HasAfEnum, MutAfArray, MutVoidPtr}; -use self::libc::{uint8_t, c_void, c_int, c_uint, c_longlong, c_ulong, c_char}; +use self::libc::{uint8_t, c_void, c_int, c_uint, c_longlong, c_char}; use std::ffi::CString; // Some unused functions from array.h in C-API of ArrayFire @@ -101,7 +101,7 @@ extern { fn af_get_device_ptr(ptr: MutVoidPtr, arr: AfArray) -> c_int; - fn af_get_allocated_bytes(result: *mut c_ulong, arr: AfArray) -> c_int; + fn af_get_allocated_bytes(result: *mut usize, arr: AfArray) -> c_int; } /// A multidimensional data container @@ -412,10 +412,10 @@ impl Array { /// /// This function will return the size of the parent/owner if the current Array object is an /// indexed Array. - pub fn get_allocated_bytes(&self) -> u64 { + pub fn get_allocated_bytes(&self) -> usize { unsafe { - let mut temp: u64 = 0; - let err_val = af_get_allocated_bytes(&mut temp as *mut c_ulong, self.handle as AfArray); + let mut temp: usize = 0; + let err_val = af_get_allocated_bytes(&mut temp as *mut usize, self.handle as AfArray); HANDLE_ERROR(AfError::from(err_val)); temp } From 8db332ea3553ecf1fd3f5f9ec95b6791bb418fc0 Mon Sep 17 00:00:00 2001 From: pradeep Date: Wed, 28 Jun 2017 18:24:04 +0530 Subject: [PATCH 30/30] Change Callback struct to hold fn() instead of &Fn() This removed the need to impl unsafe Send & Sync traits for Callback type. Also updated the error handler test to be more rusty. --- src/error.rs | 63 ++++++++++++++++++++++++++-------------------------- tests/lib.rs | 41 ++++++++++++++-------------------- 2 files changed, 48 insertions(+), 56 deletions(-) diff --git a/src/error.rs b/src/error.rs index b20b7673c..0a42ec793 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,31 +1,39 @@ use std::ops::{Deref, DerefMut}; use defines::AfError; use std::error::Error; -use std::marker::{Send, Sync}; use std::sync::RwLock; -/// Signature of callback function to be called to handle errors -pub type ErrorCallback = Fn(AfError); +/// Signature of error handling callback function +pub type ErrorCallback = fn(AfError); -/// Wrap ErrorCallback function pointer inside a structure -/// to enable implementing Send, Sync traits on it. -pub struct Callback<'cblifetime> { - ///Reference to a valid error callback function - ///Make sure this callback stays relevant throughout the lifetime of application. - pub cb: &'cblifetime ErrorCallback, +/// Structure holding handle to callback function +pub struct Callback { + cb: ErrorCallback, } -// Implement Send, Sync traits for Callback structure to -// enable the user of Callback function pointer in conjunction -// with threads using a mutex. -unsafe impl<'cblifetime> Send for Callback<'cblifetime> {} -unsafe impl<'cblifetime> Sync for Callback<'cblifetime> {} +impl Callback { + /// Associated function to create a new Callback object + pub fn new(callback: ErrorCallback) -> Self { + Callback {cb: callback} + } + + /// call invokes the error callback with `error_code`. + pub fn call(&self, error_code: AfError) { + (self.cb)(error_code) + } +} -pub const DEFAULT_HANDLE_ERROR: Callback<'static> = Callback{cb: &handle_error_general}; +/// Default error handling callback provided by ArrayFire crate +pub fn handle_error_general(error_code: AfError) { + match error_code { + AfError::SUCCESS => {}, /* No-op */ + _ => panic!("Error message: {}", error_code.description()), + } +} lazy_static! { - static ref ERROR_HANDLER_LOCK: RwLock< Callback<'static> > = - RwLock::new(DEFAULT_HANDLE_ERROR); + static ref ERROR_HANDLER_LOCK: RwLock< Callback > = + RwLock::new(Callback::new(handle_error_general)); } /// Register user provided error handler @@ -45,16 +53,17 @@ lazy_static! { /// } /// } /// -/// pub const ERR_HANDLE: Callback<'static> = Callback{ cb: &handleError}; -/// /// fn main() { -/// register_error_handler(ERR_HANDLE); +/// //Registering the error handler should be the first call +/// //before any other functions are called if your version +/// //of error is to be used for subsequent function calls +/// register_error_handler(Callback::new(handleError)); /// /// info(); /// } /// ``` #[allow(unused_must_use)] -pub fn register_error_handler(cb_value: Callback<'static>) { +pub fn register_error_handler(cb_value: Callback) { let mut gaurd = match ERROR_HANDLER_LOCK.write() { Ok(g) => g, Err(_)=> panic!("Failed to acquire lock to register error handler"), @@ -63,14 +72,6 @@ pub fn register_error_handler(cb_value: Callback<'static>) { *gaurd.deref_mut() = cb_value; } -/// Default error handling callback provided by ArrayFire crate -pub fn handle_error_general(error_code: AfError) { - match error_code { - AfError::SUCCESS => {}, /* No-op */ - _ => panic!("Error message: {}", error_code.description()), - } -} - #[allow(non_snake_case)] pub fn HANDLE_ERROR(error_code: AfError) { let gaurd = match ERROR_HANDLER_LOCK.read() { @@ -78,7 +79,5 @@ pub fn HANDLE_ERROR(error_code: AfError) { Err(_)=> panic!("Failed to acquire lock while handling FFI return value"), }; - let func = gaurd.deref().cb; - - func(error_code); + (*gaurd.deref()).call(error_code); } diff --git a/tests/lib.rs b/tests/lib.rs index 11990e92a..ba7365b21 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -2,51 +2,44 @@ extern crate arrayfire as af; use std::error::Error; use std::thread; -use std::time::Duration; use af::*; macro_rules! implement_handler { - ($fn_name:ident, $msg: expr) => ( - + ($fn_name:ident) => ( pub fn $fn_name(error_code: AfError) { - println!("{:?}", $msg); match error_code { AfError::SUCCESS => {}, /* No-op */ _ => panic!("Error message: {}", error_code.description()), } } - ) } -implement_handler!(handler_sample1, "Error Handler Sample1"); -implement_handler!(handler_sample2, "Error Handler Sample2"); -implement_handler!(handler_sample3, "Error Handler Sample3"); -implement_handler!(handler_sample4, "Error Handler Sample4"); - -pub const HANDLE1: Callback<'static> = Callback{ cb: &handler_sample1}; -pub const HANDLE2: Callback<'static> = Callback{ cb: &handler_sample2}; -pub const HANDLE3: Callback<'static> = Callback{ cb: &handler_sample3}; -pub const HANDLE4: Callback<'static> = Callback{ cb: &handler_sample4}; +implement_handler!(handler_sample1); +implement_handler!(handler_sample2); +implement_handler!(handler_sample3); +implement_handler!(handler_sample4); #[allow(unused_must_use)] #[test] fn check_error_handler_mutation() { - for i in 0..4 { + let children = (0..4).map(|i| { thread::Builder::new().name(format!("child {}",i+1).to_string()).spawn(move || { - println!("{:?}", thread::current()); + let target_device = i%af::device_count(); + println!("Thread {:?} 's target device is {}", thread::current(), target_device); match i { - 0 => register_error_handler(HANDLE1), - 1 => register_error_handler(HANDLE2), - 2 => register_error_handler(HANDLE3), - 3 => register_error_handler(HANDLE4), + 0 => register_error_handler(Callback::new(handler_sample1)), + 1 => register_error_handler(Callback::new(handler_sample2)), + 2 => register_error_handler(Callback::new(handler_sample3)), + 3 => register_error_handler(Callback::new(handler_sample4)), _ => panic!("Impossible scenario"), } - }); - } + }).ok().expect("Failed to launch a thread") + }).collect::< Vec<_> >(); - af::info(); - thread::sleep(Duration::from_millis(50)); + for c in children { + c.join(); + } }