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

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.9473
	    0.8133
	    0.9238
	    0.1990
	    0.6743

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.9271
	    0.3438
	    0.5945
	    0.6155
	    0.0034

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.9820    0.7010    0.1121
	    0.8995    0.6097    0.2916
	    0.6928    0.2999    0.0974
	    0.4397    0.8560    0.3974
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.3333    0.0429    0.8068
	    0.9442    0.0059    0.6376
	    0.8386    0.5744    0.2513
	    0.2584    0.7439    0.1443

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

  X         1x1                              296  tensor object
  Y         4x3x1                            368  tensor object

Grand total is 25 elements using 664 bytes

The constituent parts of a tensor

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

    0.6516    0.3099    0.2110
    0.9461    0.2688    0.2168
    0.8159    0.5365    0.6518
    0.9302    0.1633    0.0528


ans(:,:,2) =

    0.2293    0.7207    0.1252
    0.6674    0.9544    0.1662
    0.3109    0.1311    0.9114
    0.3066    0.0683    0.1363

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.6516    0.3099    0.2110
	    0.9461    0.2688    0.2168
	    0.8159    0.5365    0.6518
	    0.9302    0.1633    0.0528
	Y(:,:,2) = 
	    0.2293    0.7207    0.1252
	    0.6674    0.9544    0.1662
	    0.3109    0.1311    0.9114
	    0.3066    0.0683    0.1363

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 = []

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.6170    0.9413    0.8802    0.9562
	    0.2690    0.3299    0.7496    0.1962
	    0.2207    0.7045    0.3796    0.7762
	    0.7129    0.9434    0.7256    0.6133
	    0.5490    0.5816    0.1628    0.1623
	X(:,:,2) = 
	    0.0311    0.9585    0.2154    0.0178
	    0.2886    0.6799    0.1824    0.8779
	    0.9711    0.0550    0.0768    0.3525
	    0.9505    0.5998    0.0074    0.7221
	    0.2280    0.3931    0.7888    0.9685

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.6516    0.3099    0.2110
	    0.9461    0.2688    0.2168
	    0.8159    0.5365    0.6518
	    0.9302    0.1633    0.0528
	ans(:,:,2) = 
	    0.2293    0.7207    0.1252
	    0.6674    0.9544    0.1662
	    0.3109    0.1311    0.9114
	    0.3066    0.0683    0.1363

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

double(Y) %<-- Converts Y to a standard MATLAB array.
ans(:,:,1) =

    0.6516    0.3099    0.2110
    0.9461    0.2688    0.2168
    0.8159    0.5365    0.6518
    0.9302    0.1633    0.0528


ans(:,:,2) =

    0.2293    0.7207    0.1252
    0.6674    0.9544    0.1662
    0.3109    0.1311    0.9114
    0.3066    0.0683    0.1363

Y.data %<-- Same thing.
ans(:,:,1) =

    0.6516    0.3099    0.2110
    0.9461    0.2688    0.2168
    0.8159    0.5365    0.6518
    0.9302    0.1633    0.0528


ans(:,:,2) =

    0.2293    0.7207    0.1252
    0.6674    0.9544    0.1662
    0.3109    0.1311    0.9114
    0.3066    0.0683    0.1363

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.1557

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.1557

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.1557
	    0.1630
	    0.3134

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.0294    0.6226
	    0.3576    0.1326

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.1557
    0.8445

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

    0.1557
    0.8445

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.8792
	    0.1870
	    0.9913
	    0.7120
	    0.8714
	    0.4796
X([1:6]','extract') %<-- Same thing *but* result is a vector.
ans =

    0.8792
    0.1870
    0.9913
    0.7120
    0.8714
    0.4796

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.0134    0.8893    0.3167
	    0.9171    0.3697    0.5938    0.2334
	    0.1233    0.6986    0.1567    0.0084
	X(:,:,2) = 
	    0.3969    0.7688    0.7820    0.2632
	    0.6499    0.9697    0.2376    0.7138
	    0.0850    0.7148    0.1957    0.9776
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.8893    0.3167
	    1.0000    1.0000    0.5938    0.2334
	    0.1233    0.6986    0.1567    0.0084
	X(:,:,2) = 
	    0.3969    0.7688    0.7820    0.2632
	    0.6499    0.9697    0.2376    0.7138
	    0.0850    0.7148    0.1957    0.9776
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.8893    0.3167
	    1.0000    1.0000    0.5938    0.2334
	    0.1233    0.6986    0.1567    0.0084
	X(:,:,2) = 
	    7.0000    0.7688    0.7820    0.2632
	    0.6499    0.9697    0.2376    0.7138
	    0.0850    0.7148    0.1957    0.9776
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.8893    0.3167
	    1.0000    1.0000    0.5938    0.2334
	    0.1233    0.6986    0.1567    0.0084
	X(:,:,2) = 
	    7.0000    0.7688    0.7820    0.2632
	    0.6499    0.9697    0.2376    0.7138
	    0.0850    0.7148    0.1957    0.9776

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.8893    0.3167
	    1.0000    1.0000    0.5938    0.2334
	    0.1233    0.6986    0.1567    0.0084
	X(:,:,2) = 
	    7.0000    0.7688    0.7820    0.2632
	    0.6499    0.9697    0.2376    0.7138
	    0.0850    0.7148    0.1957    0.9776
	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.8893    0.3167
	    1.0000    1.0000    0.5938    0.2334
	    0.1233    0.6986    0.1567    0.0084
	X(:,:,2) = 
	    7.0000    0.7688    0.7820    0.2632
	    0.6499    0.9697    0.2376    0.7138
	    0.0850    0.7148    0.1957    0.9776
	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     2
	     1     2
	X(:,:,2) = 
	     2     2
	     2     2
[S,V] = find(X) %<-- Find all the nonzero subscripts and values.
S =

     1     1     1
     2     1     1
     1     2     1
     2     2     1
     1     1     2
     2     1     2
     1     2     2
     2     2     2


V =

     1
     1
     2
     2
     2
     2
     2
     2

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

     1     2     1
     2     2     1
     1     1     2
     2     1     2
     1     2     2
     2     2     2

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

     2
     2
     2
     2
     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) = 
	     1     1     1
	     2     0     1
	A(:,:,2) = 
	     2     0     1
	     1     2     0
B is a tensor of size 2 x 3 x 2
	B(:,:,1) = 
	     2     2     0
	     0     0     1
	B(:,:,2) = 
	     0     0     0
	     1     0     1
A & B %<-- Calls and.
ans is a tensor of size 2 x 3 x 2
	ans(:,:,1) = 
	     1     1     0
	     0     0     1
	ans(:,:,2) = 
	     0     0     0
	     1     0     0
A | B %<-- Calls or.
ans is a tensor of size 2 x 3 x 2
	ans(:,:,1) = 
	     1     1     1
	     1     0     1
	ans(:,:,2) = 
	     1     0     1
	     1     1     1
xor(A,B) %<-- Calls xor.
ans is a tensor of size 2 x 3 x 2
	ans(:,:,1) = 
	     0     0     1
	     1     0     0
	ans(:,:,2) = 
	     1     0     1
	     0     1     1
A==B %<-- Calls eq.
ans is a tensor of size 2 x 3 x 2
	ans(:,:,1) = 
	     0     0     0
	     0     1     1
	ans(:,:,2) = 
	     0     1     0
	     1     0     0
A~=B %<-- Calls neq.
ans is a tensor of size 2 x 3 x 2
	ans(:,:,1) = 
	     1     1     1
	     1     0     0
	ans(:,:,2) = 
	     1     0     1
	     0     1     1
A>B %<-- Calls gt.
ans is a tensor of size 2 x 3 x 2
	ans(:,:,1) = 
	     0     0     1
	     1     0     0
	ans(:,:,2) = 
	     1     0     1
	     0     1     0
A>=B %<-- Calls ge.
ans is a tensor of size 2 x 3 x 2
	ans(:,:,1) = 
	     0     0     1
	     1     1     1
	ans(:,:,2) = 
	     1     1     1
	     1     1     0
A<B %<-- Calls lt.
ans is a tensor of size 2 x 3 x 2
	ans(:,:,1) = 
	     1     1     0
	     0     0     0
	ans(:,:,2) = 
	     0     0     0
	     0     0     1
A<=B %<-- Calls le.
ans is a tensor of size 2 x 3 x 2
	ans(:,:,1) = 
	     1     1     0
	     0     1     1
	ans(:,:,2) = 
	     0     1     0
	     1     0     1
~A %<-- Calls not.
ans is a tensor of size 2 x 3 x 2
	ans(:,:,1) = 
	     0     0     0
	     0     1     0
	ans(:,:,2) = 
	     0     1     0
	     0     0     1
+A %<-- Calls uplus.
ans is a tensor of size 2 x 3 x 2
	ans(:,:,1) = 
	     1     1     1
	     2     0     1
	ans(:,:,2) = 
	     2     0     1
	     1     2     0
-A %<-- Calls uminus.
ans is a tensor of size 2 x 3 x 2
	ans(:,:,1) = 
	    -1    -1    -1
	    -2     0    -1
	ans(:,:,2) = 
	    -2     0    -1
	    -1    -2     0
A+B %<-- Calls plus.
ans is a tensor of size 2 x 3 x 2
	ans(:,:,1) = 
	     3     3     1
	     2     0     2
	ans(:,:,2) = 
	     2     0     1
	     2     2     1
A-B %<-- Calls minus.
ans is a tensor of size 2 x 3 x 2
	ans(:,:,1) = 
	    -1    -1     1
	     2     0     0
	ans(:,:,2) = 
	     2     0     1
	     0     2    -1
A.*B %<-- Calls times.
ans is a tensor of size 2 x 3 x 2
	ans(:,:,1) = 
	     2     2     0
	     0     0     1
	ans(:,:,2) = 
	     0     0     0
	     1     0     0
5*A %<-- Calls mtimes.
ans is a tensor of size 2 x 3 x 2
	ans(:,:,1) = 
	     5     5     5
	    10     0     5
	ans(:,:,2) = 
	    10     0     5
	     5    10     0
A.^B %<-- Calls power.
ans is a tensor of size 2 x 3 x 2
	ans(:,:,1) = 
	     1     1     1
	     1     1     1
	ans(:,:,2) = 
	     1     1     1
	     1     1     0
A.^2 %<-- Calls power.
ans is a tensor of size 2 x 3 x 2
	ans(:,:,1) = 
	     1     1     1
	     4     0     1
	ans(:,:,2) = 
	     4     0     1
	     1     4     0
A.\B %<-- Calls ldivide.
Warning: Divide by zero.
ans is a tensor of size 2 x 3 x 2
	ans(:,:,1) = 
	     2     2     0
	     0   NaN     1
	ans(:,:,2) = 
	     0   NaN     0
	     1     0   Inf
A./2 %<-- Calls rdivide.
ans is a tensor of size 2 x 3 x 2
	ans(:,:,1) = 
	    0.5000    0.5000    0.5000
	    1.0000         0    0.5000
	ans(:,:,2) = 
	    1.0000         0    0.5000
	    0.5000    1.0000         0
A./B %<-- Calls rdivide (but beware divides by zero!)
Warning: Divide by zero.
ans is a tensor of size 2 x 3 x 2
	ans(:,:,1) = 
	    0.5000    0.5000       Inf
	       Inf       NaN    1.0000
	ans(:,:,2) = 
	   Inf   NaN   Inf
	     1   Inf     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)+1,A) %<-- Increment every element of A by one.
ans is a tensor of size 2 x 3 x 2
	ans(:,:,1) = 
	     1     1     1
	     1     1     1
	ans(:,:,2) = 
	     1     1     1
	     1     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) = 
	     2     2     1
	     2     0     1
	ans(:,:,2) = 
	     2     0     1
	     1     2     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) = 
	     4     0     4
	     4     3     3
	C(:,:,2) = 
	     4     3     4
	     3     1     1
ans is a tensor of size 2 x 3 x 2
	ans(:,:,1) = 
	     2     1     1
	     2     0     1
	ans(:,:,2) = 
	     2     0     1
	     1     1     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+016 *
	    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-013 *
	    0.1300    0.1600    0.1900    0.2200
	    0.1400    0.1700    0.2000    0.2300
	    0.1500    0.1800    0.2100    0.2400