Quickstart
The Basics
In Multik, as in many similar libraries, the NDArray object is of great importance. It represents homogeneous and single-typed numeric data with a multidimensional abstraction. For multidimensionality, the following concepts are employed:
Dimension — This mathematical concept refers to the number of coordinates required to determine a point in space. In our case, it refers to the necessary number of indexes for a data item. It is also often referred to as an
axis.Shape — This is a set of the actual sizes for each
axis.Strides — This is a set of the necessary number of steps to move to the next dimension in a given array. Steps are defined as indices; knowing what type of data the array stores, we can easily determine how many bytes are needed to move to the next axis.
These concepts help us operate on a simple array as if it were multidimensional. Let's take a closer look at the ndarray itself and the properties that describe this array.
Take a simple matrix, which is actually a two-dimensional array:
Let's assign this matrix to a variable arr. Then the following properties are available:
arr.dim— Returns a dimension object, which characterizes the number of axes in the array. In this array, it will be2D. There are several such objects:1D,2D,3D,4D, andND. Therefore, if we are working with arrays up to the fifth dimension, we can easily check the legitimacy of such operations at compile time. For larger dimensions, we don't have this capability.arr.dim.d— Returns the number of axes in the array.
arr.shape— This is an array containing the sizes of the array for each axis. In our array, it will return(2, 3), where2is the number of rows, and3is the number of columns in our matrix. The length of the shape isdim.d.arr.size— The number of elements in the array. You can obtain it by multiplying each number in shape. Specifically,arr.sizewill return6, and if you multiply each number inarr.shape, you will get the same result(2 * 3).arr.dtype— The type of elements in the array. The supported types are:Byte,Short,Int,Long,Float,Double,ComplexFloat, andComplexDouble. Because of the specific way arrays are stored, we can only operate with primitive types.
Array Creation
There are numerous ways to create an array. A straightforward way to do so is by using the mk[] structure, which should be passed to the mk.ndarray method. In this case, the array type will be inferred from the passed elements, and the dimension will be determined based on the nesting of these data. For instance:
You can create an array from Kotlin collections and standard arrays.
Moreover, you can manually specify the size of each dimension. For example, you can create a three-dimensional array from a regular array in this way.
There are also standard functions that return an array filled with either zeros or ones, namely the zeros and ones functions. For these functions, you need to specify the element type.
In line with the Kotlin standard library, there are functions with a lambda.
For numerical sequences, Multik provides two methods. arange returns an array within a given range, while linspace allows you to better control the number of numbers within a specified range.
Arithmetic Operations
Arithmetic operations are performed element-wise on the array, resulting in a new array filled with the outcome of the operation.
When operating on a scalar and an array, only the type must match; the shape of the array is retained.
When conducting operations between two arrays, it's necessary that both the type, dimensionality, and shape of the arrays match. Dimensionality is checked at compile-time. However, shape conformity can only be verified at runtime. The operation remains element-wise, maintaining the original array shape.
Please note that the multiplication operator * performs element-wise operations. For matrix product, use the dot method.
Operations such as +=, -=, /= and *= are designed to modify the current array directly, without creating a new one, i.e., in-place.
Basic Operations
Although Multik's NDArray does not implement the Collection or Iterable interfaces, it does offer a subset of these methods for array operations. Functions such as filter, map, reduce, among others, are available for use with NDArray objects.
Indexing, Slicing and Iterating
Multik provides intuitive ways to index, slice, and iterate over NDArrays, similar to traditional collections with additional features for multidimensional arrays.
Indexing
In Multik, each index corresponds to a specific dimension (axis) of the array. Here's how you can access elements in an NDArray:
Slicing
Multik introduces slicing, a feature that allows for creating sequences from arrays. Slices are created by specifying the start, end, and step values within the array.
If all indices are not provided, the unmentioned ones are considered to be full slices — i.e., slices from start to end with step 1 — retrieving all elements along the corresponding axis.
Iterating
Iterating over an NDArray in Multik is conducted element-wise, irrespective of the array's dimension:
To facilitate easy navigation through multidimensional arrays, Multik provides multidimensional indices: