There are three types of operations used in ND4J: scalars, transforms and accumulations. We’ll use the word op synonymously with operation.
Scalars, transforms and accumulations each have their own patterns. Transforms are the simplest, since the take a single argument and perform an operation on it. Absolute value is a transform that takes the argument
abs(IComplexNDArray ndarray)and produces the result which is the absolute value of x. Similarly, you would apply to the sigmoid transform
sigmoid()to produce the “sigmoid of x”.
Scalars just take two arguments: the input and the scalar to be applied to that input. For example,
ScalarAdd()takes two arguments: the input
INDArray xand the scalar
Number num; i.e.
ScalarAdd(INDArray x, Number num). The same format applies to every Scalar op.
Finally, we have accumulations, which are also known as reductions in GPU-land. Accumulations add arrays and vectors to one another and can reduce the dimensions of those arrays in the result by adding their elements in a rowwise op. For example, we might run an accumulation on the array
Which would give us the vector
Reducing the columns (i.e. dimensions) from two to one.
Accumulations can be either pairwise or scalar. In a pairwise reduction, we might be dealing with two arrays, x and y, which have the same shape. In that case, we could calculate the cosine similarity of x and y by taking their elements two by two.
EuclideanDistance(arr, arr2), a reduction between one array
Many ND4J ops are overloaded, meaning methods sharing a common name have different argument lists. Below we will explain only the simplest configurations.
As you can see, there are three possible argument types with ND4J ops: inputs, optional arguments and outputs. The outputs are specified in the ops’ constructor. The inputs are specified in the parentheses following the method name, always in the first position, and the optional arguments are used to transform the inputs; e.g. the scalar to add; the coefficient to multiply by, always in the second position.
Here are two examples of performing
z = tanh(x), in which the original array
INDArray x = Nd4j.rand(3,2); //input
INDArray z = Nd4j.create(3,2); //output
The latter two examples above use ND4J’s basic convention for all ops, in which we have 3 NDArrays, x, y and z.
x is input, always required
y is (optional) input, only used in some ops (like CosineSimilarity, AddOp etc)
z is output
z = x(this is the default if you use a constructor with only one argument). But there are exceptions for situations like
x = x + y. Another possibility is
z = x + y, etc.
Most accumulations are accessable directly via the INDArray interface.
For example, to add up all elements of an NDArray:
double sum = myArray.sumNumber().doubleValue();
Accum along dimension example - i.e., sum values in each row:
INDArray tenBy3 = Nd4j.ones(10,3); //10 rows, 3 columns
INDArray sumRows = tenBy3.sum(0);
System.out.println(sumRows); //Output: [ 10.00, 10.00, 10.00]
Accumulations along dimensions generalize, so you can sum along two dimensions of any array with two or more dimensions.
A simple example:
INDArray random = Nd4j.rand(3, 3);
INDArray lastTwoRows = random.get(NDArrayIndex.interval(1,3),NDArrayIndex.all());
Interval is fromInclusive, toExclusive; note that can equivalently use inclusive version: NDArrayIndex.interval(1,2,true);
INDArray twoValues = random.get(NDArrayIndex.point(1),NDArrayIndex.interval(0, 2));
[ 0.20, 0.57]
These are views of the underlying array, not copy operations (which provides greater flexibility and doesn’t have cost of copying).
[ 5.20, 5.57]
To avoid in-place behaviour, random.get(…).dup() to make a copy.