# Tensors

Tensors are extensions of multidimensional arrays with additional operations defined on them. Here we explain the basics of creating and working with tensors.

## Contents

- Creating a tensor from an array
- Creating a one-dimensional tensor
- Specifying trailing singleton dimensions in a tensor
- The constituent parts of a tensor
- Creating a tensor from its constituent parts
- Creating an empty tensor
- Use tenone to create a tensor of all ones
- Use tenzeros to create a tensor of all zeros
- Use tenrand to create a random tensor
- Use squeeze to remove singleton dimensions from a tensor
- Use double to convert a tensor to a (multidimensional) array
- Use ndims and size to get the size of a tensor
- Subscripted reference for a tensor
- Subscripted assignment for a tensor
- Using end for the last array index.
- Use find for subscripts of nonzero elements of a tensor
- Basic operations (plus, minus, and, or, etc.) on a tensor
- Using tenfun for elementwise operations on one or more tensors
- Use permute to reorder the modes of a tensor
- Displaying a tensor

## Creating a tensor from an array

The `tensor` command converts a (multidimensional) array to a tensor object.

    M = ones(4,3,2); %<-- A 4 x 3 x 2 array.
    X = tensor(M) %<-- Convert to a tensor object.

    X is a tensor of size 4 x 3 x 2
    	X(:,:,1) =
    	     1     1     1
    	     1     1     1
    	     1     1     1
    	     1     1     1
    	X(:,:,2) =
    	     1     1     1
    	     1     1     1
    	     1     1     1
    	     1     1     1

Optionally, you can specify a different shape for the tensor, so long as the input array has the right number of elements.

    X = tensor(M,[2 3 4]) %<-- M has 24 elements.

    X is a tensor of size 2 x 3 x 4
    	X(:,:,1) =
    	     1     1     1
    	     1     1     1
    	X(:,:,2) =
    	     1     1     1
    	     1     1     1
    	X(:,:,3) =
    	     1     1     1
    	     1     1     1
    	X(:,:,4) =
    	     1     1     1
    	     1     1     1

## Creating a one-dimensional tensor

The tensor class explicitly supports order-one tensors as well as trailing singleton dimensions, but the size must be explicit in the constructor. By default, a column array produces a 2-way tensor.

    X = tensor(rand(5,1)) %<-- Creates a 2-way tensor.

    X is a tensor of size 5 x 1
    	X(:,:) =
    	    0.8147
    	    0.9058
    	    0.1270
    	    0.9134
    	    0.6324

This is fixed by specifying the size explicitly.

    X = tensor(rand(5,1),5) %<-- Creates a 1-way tensor.

    X is a tensor of size 5
    	X(:) =
    	    0.0975
    	    0.2785
    	    0.5469
    	    0.9575
    	    0.9649

## Specifying trailing singleton dimensions in a tensor

Likewise, trailing singleton dimensions must be explictly specified.

    Y = tensor(rand(4,3,1)) %<-- Creates a 2-way tensor.

    Y is a tensor of size 4 x 3
    	Y(:,:) =
    	    0.1576    0.8003    0.7922
    	    0.9706    0.1419    0.9595
    	    0.9572    0.4218    0.6557
    	    0.4854    0.9157    0.0357

    Y = tensor(rand(4,3,1),[4 3 1]) %<-- Creates a 3-way tensor.

    Y is a tensor of size 4 x 3 x 1
    	Y(:,:,1) =
    	    0.8491    0.7431    0.7060
    	    0.9340    0.3922    0.0318
    	    0.6787    0.6555    0.2769
    	    0.7577    0.1712    0.0462

Unfortunately, the `whos` command does not report the size of 1D objects correctly (last checked for MATLAB 2006a).

    whos X Y %<-- Doesn't report the right size for X!

      Name      Size             Bytes  Class     Attributes

      X         1x1                400  tensor
      Y         4x3x1              472  tensor

## The constituent parts of a tensor

    X = tenrand([4 3 2]); %<-- Create data.
    X.data %<-- The array.

    ans(:,:,1) =
        0.0971    0.9502    0.7655
        0.8235    0.0344    0.7952
        0.6948    0.4387    0.1869
        0.3171    0.3816    0.4898
    ans(:,:,2) =
        0.4456    0.2760    0.1190
        0.6463    0.6797    0.4984
        0.7094    0.6551    0.9597
        0.7547    0.1626    0.3404

    X.size %<-- The size.

    ans =
         4     3     2

## Creating a tensor from its constituent parts

    Y = tensor(X.data,X.size) %<-- Copies X.

    Y is a tensor of size 4 x 3 x 2
    	Y(:,:,1) =
    	    0.0971    0.9502    0.7655
    	    0.8235    0.0344    0.7952
    	    0.6948    0.4387    0.1869
    	    0.3171    0.3816    0.4898
    	Y(:,:,2) =
    	    0.4456    0.2760    0.1190
    	    0.6463    0.6797    0.4984
    	    0.7094    0.6551    0.9597
    	    0.7547    0.1626    0.3404

## Creating an empty tensor

An empty constructor exists, primarily to support loading previously saved data in MAT-files.

    X = tensor %<-- Creates an empty tensor.

    X is a tensor of size [empty tensor]
    	X = []
    	X =

## Use tenone to create a tensor of all ones

    X = tenones([3 4 2]) %<-- Creates a 3 x 4 x 2 tensor of ones.

    X is a tensor of size 3 x 4 x 2
    	X(:,:,1) =
    	     1     1     1     1
    	     1     1     1     1
    	     1     1     1     1
    	X(:,:,2) =
    	     1     1     1     1
    	     1     1     1     1
    	     1     1     1     1

## Use tenzeros to create a tensor of all zeros

    X = tenzeros([1 4 2]) %<-- Creates a 1 x 4 x 2 tensor of zeros.

    X is a tensor of size 1 x 4 x 2
    	X(:,:,1) =
    	     0     0     0     0
    	X(:,:,2) =
    	     0     0     0     0

## Use tenrand to create a random tensor

    X = tenrand([5 4 2]) %<-- Creates a random 5 x 4 x 2 tensor.

    X is a tensor of size 5 x 4 x 2
    	X(:,:,1) =
    	    0.5853    0.6991    0.1493    0.2435
    	    0.2238    0.8909    0.2575    0.9293
    	    0.7513    0.9593    0.8407    0.3500
    	    0.2551    0.5472    0.2543    0.1966
    	    0.5060    0.1386    0.8143    0.2511
    	X(:,:,2) =
    	    0.6160    0.5497    0.3804    0.7792
    	    0.4733    0.9172    0.5678    0.9340
    	    0.3517    0.2858    0.0759    0.1299
    	    0.8308    0.7572    0.0540    0.5688
    	    0.5853    0.7537    0.5308    0.4694

## Use squeeze to remove singleton dimensions from a tensor

    squeeze(Y) %<-- Removes singleton dimensions.

    ans is a tensor of size 4 x 3 x 2
    	ans(:,:,1) =
    	    0.0971    0.9502    0.7655
    	    0.8235    0.0344    0.7952
    	    0.6948    0.4387    0.1869
    	    0.3171    0.3816    0.4898
    	ans(:,:,2) =
    	    0.4456    0.2760    0.1190
    	    0.6463    0.6797    0.4984
    	    0.7094    0.6551    0.9597
    	    0.7547    0.1626    0.3404

## Use double to convert a tensor to a (multidimensional) array

    double(Y) %<-- Converts Y to a standard MATLAB array.

    ans(:,:,1) =
        0.0971    0.9502    0.7655
        0.8235    0.0344    0.7952
        0.6948    0.4387    0.1869
        0.3171    0.3816    0.4898
    ans(:,:,2) =
        0.4456    0.2760    0.1190
        0.6463    0.6797    0.4984
        0.7094    0.6551    0.9597
        0.7547    0.1626    0.3404

    Y.data %<-- Same thing.

    ans(:,:,1) =
        0.0971    0.9502    0.7655
        0.8235    0.0344    0.7952
        0.6948    0.4387    0.1869
        0.3171    0.3816    0.4898
    ans(:,:,2) =
        0.4456    0.2760    0.1190
        0.6463    0.6797    0.4984
        0.7094    0.6551    0.9597
        0.7547    0.1626    0.3404

## Use ndims and size to get the size of a tensor

    ndims(Y) %<-- Number of dimensions (or ways).

    ans =
         3

    size(Y) %<-- Row vector with the sizes of all dimension.

    ans =
         4     3     2

    size(Y,3) %<-- Size of a single dimension.

    ans =
         2

## Subscripted reference for a tensor

    X = tenrand([3 4 2 1]); %<-- Create a 3 x 4 x 2 x 1 random tensor.
    X(1,1,1,1) %<-- Extract a single element.

    ans =
        0.0119

It is possible to extract a subtensor that contains a single element. Observe that singleton dimensions are not dropped unless they are specifically specified, e.g., as above.

    X(1,1,1,:) %<-- Produces a tensor of order 1 and size 1.

    ans is a tensor of size 1
    	ans(:) =
    	    0.0119

In general, specified dimensions are dropped from the result. Here we specify the second and third dimension.

    X(:,1,1,:) %<-- Produces a tensor of size 3 x 1.

    ans is a tensor of size 3 x 1
    	ans(:,:) =
    	    0.0119
    	    0.3371
    	    0.1622

Moreover, the subtensor is automatically renumbered/resized in the same way that MATLAB works for arrays except that singleton dimensions are handled explicitly.

    X(1:2,[2 4],1,:) %<-- Produces a tensor of size 2 x 2 x 1.

    ans is a tensor of size 2 x 2 x 1
    	ans(:,:,1) =
    	    0.7943    0.6541
    	    0.3112    0.6892

It's also possible to extract a list of elements by passing in an array of subscripts or a column array of linear indices.

    subs = [1,1,1,1; 3,4,2,1]; X(subs) %<-- Extract 2 values by subscript.

    ans =
        0.0119
        0.9619

    inds = [1; 24]; X(inds) %<-- Same thing with linear indices.

    ans =
        0.0119
        0.9619

The difference between extracting a subtensor and a list of linear indices is ambiguous for 1-dimensional tensors. We can specify 'extract' as a second argument whenever we are using a list of subscripts.

    X = tenrand(10); %<-- Create a random tensor.
    X([1:6]') %<-- Extract a subtensor.

    ans is a tensor of size 6
    	ans(:) =
    	    0.0046
    	    0.7749
    	    0.8173
    	    0.8687
    	    0.0844
    	    0.3998

    X([1:6]','extract') %<-- Same thing *but* result is a vector.

    ans =
        0.0046
        0.7749
        0.8173
        0.8687
        0.0844
        0.3998

## Subscripted assignment for a tensor

We can assign a single element, an entire subtensor, or a list of values for a tensor.

    X = tenrand([3,4,2]); %<-- Create some data.
    X(1,1,1) = 0 %<-- Replaces the (1,1,1) element.

    X is a tensor of size 3 x 4 x 2
    	X(:,:,1) =
    	         0    0.1361    0.5499    0.6221
    	    0.2638    0.8693    0.1450    0.3510
    	    0.1455    0.5797    0.8530    0.5132
    	X(:,:,2) =
    	    0.4018    0.1233    0.4173    0.9448
    	    0.0760    0.1839    0.0497    0.4909
    	    0.2399    0.2400    0.9027    0.4893

    X(1:2,1:2,1) = ones(2,2) %<-- Replaces a 2 x 2 subtensor.

    X is a tensor of size 3 x 4 x 2
    	X(:,:,1) =
    	    1.0000    1.0000    0.5499    0.6221
    	    1.0000    1.0000    0.1450    0.3510
    	    0.1455    0.5797    0.8530    0.5132
    	X(:,:,2) =
    	    0.4018    0.1233    0.4173    0.9448
    	    0.0760    0.1839    0.0497    0.4909
    	    0.2399    0.2400    0.9027    0.4893

    X([1 1 1;1 1 2]) = [5;7] %<-- Replaces the (1,1,1) and (1,1,2) elements.

    X is a tensor of size 3 x 4 x 2
    	X(:,:,1) =
    	    5.0000    1.0000    0.5499    0.6221
    	    1.0000    1.0000    0.1450    0.3510
    	    0.1455    0.5797    0.8530    0.5132
    	X(:,:,2) =
    	    7.0000    0.1233    0.4173    0.9448
    	    0.0760    0.1839    0.0497    0.4909
    	    0.2399    0.2400    0.9027    0.4893

    X([1;13]) = [5;7] %<-- Same as above using linear indices.

    X is a tensor of size 3 x 4 x 2
    	X(:,:,1) =
    	    5.0000    1.0000    0.5499    0.6221
    	    1.0000    1.0000    0.1450    0.3510
    	    0.1455    0.5797    0.8530    0.5132
    	X(:,:,2) =
    	    7.0000    0.1233    0.4173    0.9448
    	    0.0760    0.1839    0.0497    0.4909
    	    0.2399    0.2400    0.9027    0.4893

It is possible to grow the tensor automatically by assigning elements outside the original range of the tensor.

    X(1,1,3) = 1 %<-- Grows the size of the tensor.

    X is a tensor of size 3 x 4 x 3
    	X(:,:,1) =
    	    5.0000    1.0000    0.5499    0.6221
    	    1.0000    1.0000    0.1450    0.3510
    	    0.1455    0.5797    0.8530    0.5132
    	X(:,:,2) =
    	    7.0000    0.1233    0.4173    0.9448
    	    0.0760    0.1839    0.0497    0.4909
    	    0.2399    0.2400    0.9027    0.4893
    	X(:,:,3) =
    	     1     0     0     0
    	     0     0     0     0
    	     0     0     0     0

## Using end for the last array index.

    X(end,end,end)  %<-- Same as X(3,4,3).

    ans =
         0

    X(1,1,1:end-1)  %<-- Same as X(1,1,1:2).

    ans is a tensor of size 2
    	ans(:) =
    	     5
    	     7

It is also possible to use `end` to index past the end of an array.

    X(1,1,end+1) = 5 %<-- Same as X(1,1,4).

    X is a tensor of size 3 x 4 x 4
    	X(:,:,1) =
    	    5.0000    1.0000    0.5499    0.6221
    	    1.0000    1.0000    0.1450    0.3510
    	    0.1455    0.5797    0.8530    0.5132
    	X(:,:,2) =
    	    7.0000    0.1233    0.4173    0.9448
    	    0.0760    0.1839    0.0497    0.4909
    	    0.2399    0.2400    0.9027    0.4893
    	X(:,:,3) =
    	     1     0     0     0
    	     0     0     0     0
    	     0     0     0     0
    	X(:,:,4) =
    	     5     0     0     0
    	     0     0     0     0
    	     0     0     0     0

## Use find for subscripts of nonzero elements of a tensor

The `find` function returns a list of nonzero subscripts for a tensor. Note that differs from the standard version, which returns linear indices.

    X = tensor(floor(3*rand(2,2,2))) %<-- Generate some data.

    X is a tensor of size 2 x 2 x 2
    	X(:,:,1) =
    	     1     1
    	     2     0
    	X(:,:,2) =
    	     2     0
    	     1     1

    [S,V] = find(X) %<-- Find all the nonzero subscripts and values.

    S =
         1     1     1
         2     1     1
         1     2     1
         1     1     2
         2     1     2
         2     2     2
    V =
         1
         2
         1
         2
         1
         1

    S = find(X >= 2) %<-- Find subscripts of values >= 2.

    S =
         2     1     1
         1     1     2

    V = X(S) %<-- Extract the corresponding values from X.

    V =
         2
         2

## Basic operations (plus, minus, and, or, etc.) on a tensor

The tensor object supports many basic operations, illustrated here.

    A = tensor(floor(3*rand(2,3,2)))
    B = tensor(floor(3*rand(2,3,2)))

    A is a tensor of size 2 x 3 x 2
    	A(:,:,1) =
    	     0     2     1
    	     0     2     0
    	A(:,:,2) =
    	     0     2     0
    	     1     0     0
    B is a tensor of size 2 x 3 x 2
    	B(:,:,1) =
    	     1     1     1
    	     2     1     0
    	B(:,:,2) =
    	     2     2     1
    	     0     0     1

    A & B %<-- Calls and.

    ans is a tensor of size 2 x 3 x 2
    	ans(:,:,1) =
    	     0     1     1
    	     0     1     0
    	ans(:,:,2) =
    	     0     1     0
    	     0     0     0

    A | B %<-- Calls or.

    ans is a tensor of size 2 x 3 x 2
    	ans(:,:,1) =
    	     1     1     1
    	     1     1     0
    	ans(:,:,2) =
    	     1     1     1
    	     1     0     1

    xor(A,B) %<-- Calls xor.

    ans is a tensor of size 2 x 3 x 2
    	ans(:,:,1) =
    	     1     0     0
    	     1     0     0
    	ans(:,:,2) =
    	     1     0     1
    	     1     0     1

    A==B %<-- Calls eq.

    ans is a tensor of size 2 x 3 x 2
    	ans(:,:,1) =
    	     0     0     1
    	     0     0     1
    	ans(:,:,2) =
    	     0     1     0
    	     0     1     0

    A~=B %<-- Calls neq.

    ans is a tensor of size 2 x 3 x 2
    	ans(:,:,1) =
    	     1     1     0
    	     1     1     0
    	ans(:,:,2) =
    	     1     0     1
    	     1     0     1

    A>B %<-- Calls gt.

    ans is a tensor of size 2 x 3 x 2
    	ans(:,:,1) =
    	     0     1     0
    	     0     1     0
    	ans(:,:,2) =
    	     0     0     0
    	     1     0     0

    A>=B %<-- Calls ge.

    ans is a tensor of size 2 x 3 x 2
    	ans(:,:,1) =
    	     0     1     1
    	     0     1     1
    	ans(:,:,2) =
    	     0     1     0
    	     1     1     0

    A<B %<-- Calls lt.

    ans is a tensor of size 2 x 3 x 2
    	ans(:,:,1) =
    	     1     0     0
    	     1     0     0
    	ans(:,:,2) =
    	     1     0     1
    	     0     0     1

    A<=B %<-- Calls le.

    ans is a tensor of size 2 x 3 x 2
    	ans(:,:,1) =
    	     1     0     1
    	     1     0     1
    	ans(:,:,2) =
    	     1     1     1
    	     0     1     1

    ~A %<-- Calls not.

    ans is a tensor of size 2 x 3 x 2
    	ans(:,:,1) =
    	     1     0     0
    	     1     0     1
    	ans(:,:,2) =
    	     1     0     1
    	     0     1     1

    +A %<-- Calls uplus.

    ans is a tensor of size 2 x 3 x 2
    	ans(:,:,1) =
    	     0     2     1
    	     0     2     0
    	ans(:,:,2) =
    	     0     2     0
    	     1     0     0

    -A %<-- Calls uminus.

    ans is a tensor of size 2 x 3 x 2
    	ans(:,:,1) =
    	     0    -2    -1
    	     0    -2     0
    	ans(:,:,2) =
    	     0    -2     0
    	    -1     0     0

    A+B %<-- Calls plus.

    ans is a tensor of size 2 x 3 x 2
    	ans(:,:,1) =
    	     1     3     2
    	     2     3     0
    	ans(:,:,2) =
    	     2     4     1
    	     1     0     1

    A-B %<-- Calls minus.

    ans is a tensor of size 2 x 3 x 2
    	ans(:,:,1) =
    	    -1     1     0
    	    -2     1     0
    	ans(:,:,2) =
    	    -2     0    -1
    	     1     0    -1

    A.*B %<-- Calls times.

    ans is a tensor of size 2 x 3 x 2
    	ans(:,:,1) =
    	     0     2     1
    	     0     2     0
    	ans(:,:,2) =
    	     0     4     0
    	     0     0     0

    5*A %<-- Calls mtimes.

    ans is a tensor of size 2 x 3 x 2
    	ans(:,:,1) =
    	     0    10     5
    	     0    10     0
    	ans(:,:,2) =
    	     0    10     0
    	     5     0     0

    A.^B %<-- Calls power.

    ans is a tensor of size 2 x 3 x 2
    	ans(:,:,1) =
    	     0     2     1
    	     0     2     1
    	ans(:,:,2) =
    	     0     4     0
    	     1     1     0

    A.^2 %<-- Calls power.

    ans is a tensor of size 2 x 3 x 2
    	ans(:,:,1) =
    	     0     4     1
    	     0     4     0
    	ans(:,:,2) =
    	     0     4     0
    	     1     0     0

    A.\B %<-- Calls ldivide.

    ans is a tensor of size 2 x 3 x 2
    	ans(:,:,1) =
    	       Inf    0.5000    1.0000
    	       Inf    0.5000       NaN
    	ans(:,:,2) =
    	   Inf     1   Inf
    	     0   NaN   Inf

    A./2 %<-- Calls rdivide.

    ans is a tensor of size 2 x 3 x 2
    	ans(:,:,1) =
    	         0    1.0000    0.5000
    	         0    1.0000         0
    	ans(:,:,2) =
    	         0    1.0000         0
    	    0.5000         0         0

    A./B %<-- Calls rdivide (but beware divides by zero!)

    ans is a tensor of size 2 x 3 x 2
    	ans(:,:,1) =
    	     0     2     1
    	     0     2   NaN
    	ans(:,:,2) =
    	     0     1     0
    	   Inf   NaN     0

## Using tenfun for elementwise operations on one or more tensors

The function `tenfun` applies a specified function to a number of tensors. This can be used for any function that is not predefined for tensors.

    tenfun(@(x)(x+1),A) %<-- Increment every element of A by one.

    ans is a tensor of size 2 x 3 x 2
    	ans(:,:,1) =
    	     1     3     2
    	     1     3     1
    	ans(:,:,2) =
    	     1     3     1
    	     2     1     1

    tenfun(@max,A,B) %<-- Max of A and B, elementwise.

    ans is a tensor of size 2 x 3 x 2
    	ans(:,:,1) =
    	     1     2     1
    	     2     2     0
    	ans(:,:,2) =
    	     2     2     1
    	     1     0     1

    C = tensor(floor(5*rand(2,3,2))) %<-- Create another tensor.
    tenfun(@median,A,B,C) %<-- Elementwise means for A, B, and C.

    C is a tensor of size 2 x 3 x 2
    	C(:,:,1) =
    	     3     4     2
    	     0     3     2
    	C(:,:,2) =
    	     2     2     4
    	     1     2     3
    ans is a tensor of size 2 x 3 x 2
    	ans(:,:,1) =
    	     1     2     1
    	     0     2     0
    	ans(:,:,2) =
    	     2     2     1
    	     1     0     1

## Use permute to reorder the modes of a tensor

    X = tensor(1:24,[3 4 2]) %<-- Create a tensor.

    X is a tensor of size 3 x 4 x 2
    	X(:,:,1) =
    	     1     4     7    10
    	     2     5     8    11
    	     3     6     9    12
    	X(:,:,2) =
    	    13    16    19    22
    	    14    17    20    23
    	    15    18    21    24

    permute(X,[3 2 1]) %<-- Reverse the modes.

    ans is a tensor of size 2 x 4 x 3
    	ans(:,:,1) =
    	     1     4     7    10
    	    13    16    19    22
    	ans(:,:,2) =
    	     2     5     8    11
    	    14    17    20    23
    	ans(:,:,3) =
    	     3     6     9    12
    	    15    18    21    24

Permuting a 1-dimensional tensor works correctly.

    X = tensor(1:4,4); %<-- Create a 1-way tensor.
    permute(X,1) %<-- Call permute with *only* one dimension.

    ans is a tensor of size 4
    	ans(:) =
    	     1
    	     2
    	     3
    	     4

## Displaying a tensor

The function `disp` can be used to display a tensor and correctly displays very small and large elements.

    X = tensor(1:24,[3 4 2]); %<-- Create a 3 x 4 x 2 tensor.
    X(:,:,1) = X(:,:,1) * 1e15; %<-- Make the first slice very large.
    X(:,:,2) = X(:,:,2) * 1e-15; %<-- Make the second slice very small.
    disp(X)

    ans is a tensor of size 3 x 4 x 2
    	ans(:,:,1) =
    	   1.0e+16 *
    	    0.1000    0.4000    0.7000    1.0000
    	    0.2000    0.5000    0.8000    1.1000
    	    0.3000    0.6000    0.9000    1.2000
    	ans(:,:,2) =
    	   1.0e-13 *
    	    0.1300    0.1600    0.1900    0.2200
    	    0.1400    0.1700    0.2000    0.2300
    	    0.1500    0.1800    0.2100    0.2400