diff --git a/Cargo.toml b/Cargo.toml index 2b7205976..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" @@ -14,13 +14,33 @@ 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" 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/README.md b/README.md index 573442f83..a23168ba8 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. Rust 1.15.1 or higher is required. ## Use from Crates.io [![](http://meritbadge.herokuapp.com/arrayfire)](https://crates.io/crates/arrayfire) @@ -37,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). @@ -75,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 diff --git a/arrayfire b/arrayfire index 10d9716a4..05999f3c4 160000 --- a/arrayfire +++ b/arrayfire @@ -1 +1 @@ -Subproject commit 10d9716a4aca5bc413d642be6295ce08614d5c36 +Subproject commit 05999f3c473da5b56acd8ac8816a062f7fb9dc9f diff --git a/build.rs b/build.rs index b4cdd132c..8e6ea8a65 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, @@ -55,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) } @@ -116,7 +112,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 } 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/arith/mod.rs b/src/arith/mod.rs index 7e77df60d..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; } @@ -255,6 +277,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. @@ -449,13 +482,14 @@ 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, n, 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); } } ) @@ -475,11 +509,13 @@ 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)); + 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)); - mem::replace(self, tmp); + let old = mem::replace(self, tmp); } } ) diff --git a/src/array.rs b/src/array.rs index a2e515403..3ca196494 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 @@ -18,6 +17,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; @@ -99,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 usize, arr: AfArray) -> c_int; } /// A multidimensional data container @@ -175,6 +178,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 @@ -210,12 +234,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 } } @@ -285,7 +309,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)); @@ -380,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) -> usize { + unsafe { + 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 + } + } } /// Used for creating Array object from native resource id 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; } diff --git a/src/defines.rs b/src/defines.rs index d777f310a..2b7586b1b 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 @@ -429,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/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..0a42ec793 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,28 +1,39 @@ use std::ops::{Deref, DerefMut}; use defines::AfError; use std::error::Error; -use std::marker::{Send, Sync}; use std::sync::RwLock; -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> { - 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} + } -pub const DEFAULT_HANDLE_ERROR: Callback<'static> = Callback{cb: &handle_error_general}; + /// call invokes the error callback with `error_code`. + pub fn call(&self, error_code: AfError) { + (self.cb)(error_code) + } +} + +/// 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 @@ -42,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"), @@ -60,13 +72,6 @@ pub fn register_error_handler(cb_value: Callback<'static>) { *gaurd.deref_mut() = cb_value; } -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() { @@ -74,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/src/image/mod.rs b/src/image/mod.rs index fec01bca5..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 @@ -126,6 +129,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. @@ -1214,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) + } +} diff --git a/src/index.rs b/src/index.rs index e478b1b9d..f3efb98e5 100644 --- a/src/index.rs +++ b/src/index.rs @@ -5,34 +5,97 @@ 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}; + +use std::marker::PhantomData; #[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 { +/// +/// # 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 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(); +/// +/// // `idx` is created much before idxr, thus will +/// // stay in scope at least as long as idxr +/// idxr.set_index(&idx, 0, None); +/// +/// index_gen(&arr, 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: u32, + count: usize, + 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 { - fn set(&self, idxr: &Indexer, dim: u32, is_batch: Option); + /// 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); } /// Enables [Array](./struct.Array.html) to be used to index another Array @@ -41,11 +104,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.get() as AfArray, + dim as DimT); HANDLE_ERROR(AfError::from(err_val)); } } @@ -56,9 +118,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)); @@ -66,40 +128,40 @@ impl Indexable for Seq where c_double: From { } } -impl Indexer { +impl<'object> Indexer<'object> { #[allow(unused_mut)] - pub fn new() -> Indexer { + /// Create a new Indexer object and set the dimension specific index objects later + 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, count: 0} + 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) { - self.count = self.count + 1; - 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 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 { +impl<'object> Drop for Indexer<'object> { 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), @@ -337,7 +399,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); + indices.len() as DimT, indices.get() as AfIndex); HANDLE_ERROR(AfError::from(err_val)); Array::from(temp) } @@ -379,7 +441,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, + indices.len() as DimT, indices.get() as AfIndex, rhs.get() as AfArray); HANDLE_ERROR(AfError::from(err_val)); Array::from(temp) diff --git a/src/lib.rs b/src/lib.rs index 2ead5233d..106e7411c 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,54 +1,69 @@ +//! 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. +//! +//! ## 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", html_root_url = "http://arrayfire.com/docs/rust")] +#![warn(missing_docs)] +#![allow(non_camel_case_types)] #[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; @@ -57,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 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 } diff --git a/src/util.rs b/src/util.rs index 648b05bbe..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; @@ -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; } diff --git a/tests/lib.rs b/tests/lib.rs index fa0ce65f2..ba7365b21 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1,53 +1,45 @@ -#[macro_use] 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(); + } }