Skip to content

Tutorial: N Dimensional Array

Liang Wang edited this page Dec 9, 2016 · 11 revisions

N-dimensional Array

Multi-dimensional array (i.e., n-dimensional array) is extremely useful in scientific computing, e.g., computer vision, image processing, and etc. Therefore, n-dimensional array support is necessary in modern numerical libraries.

Owl has two very powerful modules to manipulate dense n-dimensional arrays. One is Ndarray, and the other is Ndview. Ndarray is very similar to the corresponding modules in Numpy and Julia, whereas Ndview is specifically optimised for pipelining the operations on the ndarray.

In this tutorial, I will only focus on the Ndarray module, and I will present a series of examples to walk you through the functionality in the module.

Create N-dimensional Arrays

First, we can create empty ndarrays of shape [|3;4;5|]. Owl supports four types of ndarrays: Float32, Float64, Complex32, and Complex64.

let x0 = Dense.Ndarray.empty Bigarray.Float32 [|3;4;5|];;
let x1 = Dense.Ndarray.empty Bigarray.Float64 [|3;4;5|];;
let x2 = Dense.Ndarray.empty Bigarray.Complex32 [|3;4;5|];;
let x3 = Dense.Ndarray.empty Bigarray.Complex64 [|3;4;5|];;

You can also assign the initial values to the elements, generate a zero/one ndarray, or even a random ndarray.

Dense.Ndarray.zeros Bigarray.Complex32 [|3;4;5|];;
Dense.Ndarray.ones Bigarray.Float64 [|3;4;5|];;
Dense.Ndarray.create Bigarray.Float32 [|3;4;5|] 1.5;;
Dense.Ndarray.create Bigarray.Complex32 [|3;4;5|] Complex.({im=1.5; re=2.5});;
Dense.Ndarray.uniform Bigarray.Float64 [|3;4;5|];;

With these created ndarray, you can do some math operation as below.

let x = Dense.Ndarray.uniform Bigarray.Float64 [|3;4;5|];;
let y = Dense.Ndarray.uniform Bigarray.Float64 [|3;4;5|];;
let z = Dense.Ndarray.add x y;;
Dense.Ndarray.print z;;

Obtain Ndarray Properties

Basic Maths Functions

Owl supports many math operations and these operations have been well vectorised so they are very fast.

Dense.Ndarray.sin x;;
Dense.Ndarray.tan x;;
Dense.Ndarray.exp x;;
Dense.Ndarray.log x;;
Dense.Ndarray.min x;;
Dense.Ndarray.add_scalar x 2.;;
Dense.Ndarray.mul_scalar x 2.;;
...

Examine Elements and Compare Ndarrays

Examining elements and comparing two ndarrays are also very easy.

Dense.Ndarray.is_zero x;;
Dense.Ndarray.is_positive x;;
Dense.Ndarray.is_nonnegative x;;
...
Dense.Ndarray.is_equal x y;;
Dense.Ndarray.is_greater x y;;
Dense.Ndarray.equal_or_smaller x y;;
...

You can certainly plugin your own functions to check each elements.

Dense.Ndarray.exists ((>) 2.) x;;
Dense.Ndarray.not_exists ((<) 2.) x;;
Dense.Ndarray.for_all ((=) 2.) x;;

Define a Slice in Ndarray

Most importantly, you can use Owl to iterate a ndarray in various ways. Owl provides a simple but flexible and powerful way to define a "slice" in ndarray. Comparing to the "Bigarray.slice_left" function, the slice in Owl does not have to start from the left-most axis. E.g., for the previously defined [|3;4;5|] ndarray, you can define a slice in the following ways:

let s0 = [|None; None; None|]      (* (*,*,*), essentially the whole ndarray as one slice *)
let s1 = [|Some 0; None; None|]    (* (0,*,*) *)
let s2 = [|None; Some 2; None|]    (* (*,2,*) *)
let s3 = [|None; None; Some 1|]    (* (*,*,1) *)
let s4 = [|Some 1; None; Some 2|]  (* (1,*,2) *)
...

Iterate Elements in a Slice

With the slice definition above, we can iterate and map the elements in a slice. E.g., we add one to all the elements in slice (0,*,*).

Dense.Ndarray.map ~axis:[|Some 0; None; None|] (fun a -> a +. 1.) x;;

There are more functions to help you to iterate elements and slices in a ndarray: iteri, iter, mapi, map, filteri, filter, foldi, fold, iteri_slice, iter_slice, iter2i, iter2. Please refer to the documentation for their details.