Converting Sparse Tensors to Matrices and Vice Versa

We show how to convert a sptensor to a matrix stored in coordinate format and with extra information so that it can be converted back to a sptensor.

Contents

Creating a sptenmat (sparse tensor as sparse matrix) object

A sparse tensor can be converted to a sparse matrix. The matrix, however, is not stored as a MATLAB sparse matrix because that format is sometimes inefficient for converted sparse tensors. Instead, the row and column indices are stored explicitly.

First, we create a sparse tensor to be converted.

rng('default'); %<- Setting random seed for reproducibility of this script
X = sptenrand([10 10 10 10],10) %<-- Generate some data.
X is a sparse tensor of size 10 x 10 x 10 x 10 with 10 nonzeros
	( 1, 2, 8, 9)    0.4387
	( 2,10, 9, 3)    0.3816
	( 3, 5, 8, 7)    0.7655
	( 6,10, 4, 4)    0.7952
	( 7, 9, 7, 1)    0.1869
	( 9, 2, 7, 8)    0.4898
	(10, 5,10, 1)    0.4456
	(10, 8, 7,10)    0.6463
	(10,10, 1, 1)    0.7094
	(10,10, 2, 1)    0.7547

All the same options for tenmat are available as for tenmat.

A = sptenmat(X,1) %<-- Mode-1 matricization.
A is a sptenmat from an sptensor of size 10 x 10 x 10 x 10 with 10 nonzeros
	A.rindices = [ 1 ] (modes of tensor corresponding to rows)
	A.cindices = [ 2  3  4 ] (modes of tensor corresponding to columns)
	( 1,872)	0.438744
	( 2,290)	0.381558
	( 3,675)	0.765517
	( 6,340)	0.7952
	( 7, 69)	0.186873
	( 9,762)	0.489764
	(10, 95)	0.445586
	(10,968)	0.646313
	(10, 10)	0.709365
	(10, 20)	0.754687
A = sptenmat(X,[2 3]) %<-- More than one mode is mapped to the columns.
A is a sptenmat from an sptensor of size 10 x 10 x 10 x 10 with 10 nonzeros
	A.rindices = [ 2  3 ] (modes of tensor corresponding to rows)
	A.cindices = [ 1  4 ] (modes of tensor corresponding to columns)
	(72, 81)	0.438744
	(90, 22)	0.381558
	(75, 63)	0.765517
	(40, 36)	0.7952
	(69,  7)	0.186873
	(62, 79)	0.489764
	(95, 10)	0.445586
	(68,100)	0.646313
	(10, 10)	0.709365
	(20, 10)	0.754687
A = sptenmat(X,[2 3],'t') %<-- Specify column dimensions (transpose).
A is a sptenmat from an sptensor of size 10 x 10 x 10 x 10 with 10 nonzeros
	A.rindices = [ 1  4 ] (modes of tensor corresponding to rows)
	A.cindices = [ 2  3 ] (modes of tensor corresponding to columns)
	( 81,72)	0.438744
	( 22,90)	0.381558
	( 63,75)	0.765517
	( 36,40)	0.7952
	(  7,69)	0.186873
	( 79,62)	0.489764
	( 10,95)	0.445586
	(100,68)	0.646313
	( 10,10)	0.709365
	( 10,20)	0.754687
A = sptenmat(X,1:4) %<-- All modes mapped to rows, i.e., vectorize.
A is a sptenmat from an sptensor of size 10 x 10 x 10 x 10 with 10 nonzeros
	A.rindices = [ 1  2  3  4 ] (modes of tensor corresponding to rows)
	A.cindices = [  ] (modes of tensor corresponding to columns)
	(8711,1)	0.438744
	(2892,1)	0.381558
	(6743,1)	0.765517
	(3396,1)	0.7952
	( 687,1)	0.186873
	(7619,1)	0.489764
	( 950,1)	0.445586
	(9680,1)	0.646313
	( 100,1)	0.709365
	( 200,1)	0.754687
A = sptenmat(X,2) %<-- By default, columns are ordered as [1 3 4].
A is a sptenmat from an sptensor of size 10 x 10 x 10 x 10 with 10 nonzeros
	A.rindices = [ 2 ] (modes of tensor corresponding to rows)
	A.cindices = [ 1  3  4 ] (modes of tensor corresponding to columns)
	( 2,871)	0.438744
	(10,282)	0.381558
	( 5,673)	0.765517
	(10,336)	0.7952
	( 9, 67)	0.186873
	( 2,769)	0.489764
	( 5,100)	0.445586
	( 8,970)	0.646313
	(10, 10)	0.709365
	(10, 20)	0.754687
A = sptenmat(X,2,[3 1 4]) %<-- Explicit column ordering.
A is a sptenmat from an sptensor of size 10 x 10 x 10 x 10 with 10 nonzeros
	A.rindices = [ 2 ] (modes of tensor corresponding to rows)
	A.cindices = [ 3  1  4 ] (modes of tensor corresponding to columns)
	( 2,808)	0.438744
	(10,219)	0.381558
	( 5,628)	0.765517
	(10,354)	0.7952
	( 9, 67)	0.186873
	( 2,787)	0.489764
	( 5,100)	0.445586
	( 8,997)	0.646313
	(10, 91)	0.709365
	(10, 92)	0.754687
A = sptenmat(X,2,'fc') %<-- Foward cyclic.
A is a sptenmat from an sptensor of size 10 x 10 x 10 x 10 with 10 nonzeros
	A.rindices = [ 2 ] (modes of tensor corresponding to rows)
	A.cindices = [ 3  4  1 ] (modes of tensor corresponding to columns)
	( 2, 88)	0.438744
	(10,129)	0.381558
	( 5,268)	0.765517
	(10,534)	0.7952
	( 9,607)	0.186873
	( 2,877)	0.489764
	( 5,910)	0.445586
	( 8,997)	0.646313
	(10,901)	0.709365
	(10,902)	0.754687
A = sptenmat(X,2,'bc') %<-- Backward cyclic.
A is a sptenmat from an sptensor of size 10 x 10 x 10 x 10 with 10 nonzeros
	A.rindices = [ 2 ] (modes of tensor corresponding to rows)
	A.cindices = [ 1  4  3 ] (modes of tensor corresponding to columns)
	( 2,781)	0.438744
	(10,822)	0.381558
	( 5,763)	0.765517
	(10,336)	0.7952
	( 9,607)	0.186873
	( 2,679)	0.489764
	( 5,910)	0.445586
	( 8,700)	0.646313
	(10, 10)	0.709365
	(10,110)	0.754687

Constituent parts of a sptenmat

A.subs %<-- Subscripts of the nonzeros.
ans =

     2   781
    10   822
     5   763
    10   336
     9   607
     2   679
     5   910
     8   700
    10    10
    10   110

A.vals %<-- The corresponding nonzero values.
ans =

    0.4387
    0.3816
    0.7655
    0.7952
    0.1869
    0.4898
    0.4456
    0.6463
    0.7094
    0.7547

A.tsize %<-- Size of the original tensor.
ans =

    10    10    10    10

A.rdims %<-- Dimensions that were mapped to the rows.
ans =

     2

A.cdims %<-- Dimensions that were mapped to the columns.
ans =

     1     4     3

Creating a sptenmat from its constituent parts

B = sptenmat(A.subs,A.vals,A.rdims,A.cdims,A.tsize) %<-- Copies A
B is a sptenmat from an sptensor of size 10 x 10 x 10 x 10 with 10 nonzeros
	B.rindices = [ 2 ] (modes of tensor corresponding to rows)
	B.cindices = [ 1  4  3 ] (modes of tensor corresponding to columns)
	( 2,679)	0.489764
	( 2,781)	0.438744
	( 5,763)	0.765517
	( 5,910)	0.445586
	( 8,700)	0.646313
	( 9,607)	0.186873
	(10, 10)	0.709365
	(10,110)	0.754687
	(10,336)	0.7952
	(10,822)	0.381558
B = sptenmat(double(A),A.rdims,A.cdims,A.tsize) %<-- More efficient to pass a matrix.
B is a sptenmat from an sptensor of size 10 x 10 x 10 x 10 with 10 nonzeros
	B.rindices = [ 2 ] (modes of tensor corresponding to rows)
	B.cindices = [ 1  4  3 ] (modes of tensor corresponding to columns)
	(10, 10)	0.709365
	(10,110)	0.754687
	(10,336)	0.7952
	( 9,607)	0.186873
	( 2,679)	0.489764
	( 8,700)	0.646313
	( 5,763)	0.765517
	( 2,781)	0.438744
	(10,822)	0.381558
	( 5,910)	0.445586

Creating a sptenmat with no nonzeros

A = sptenmat([],[],A.rdims,A.cdims,A.tsize) %<-- An empty sptenmat.
A is an all-zero sptenmat from an sptensor of size 10 x 10 x 10 x 10
	A.rindices = [ 2 ] (modes of tensor corresponding to rows)
	A.cindices = [ 1  4  3 ] (modes of tensor corresponding to columns)

Creating an emtpy sptenmat

A = sptenmat %<-- A really empty sptenmat.
A is an all-zero sptenmat from an sptensor of size [empty tensor]
	A.rindices = [  ] (modes of tensor corresponding to rows)
	A.cindices = [  ] (modes of tensor corresponding to columns)

Use double to convert a sptenmat to a MATLAB sparse matrix

X = sptenrand([10 10 10 10],10); %<-- Create a tensor.
A = sptenmat(X,1) %<-- Convert it to a sptenmat
A is a sptenmat from an sptensor of size 10 x 10 x 10 x 10 with 10 nonzeros
	A.rindices = [ 1 ] (modes of tensor corresponding to rows)
	A.cindices = [ 2  3  4 ] (modes of tensor corresponding to columns)
	( 2,527)	0.0758543
	( 2,999)	0.0539501
	( 3,543)	0.530798
	( 3,388)	0.779167
	( 4,722)	0.934011
	( 5,240)	0.129906
	( 6,362)	0.568824
	( 7,823)	0.469391
	( 7,586)	0.0119021
	(10,716)	0.337123
B = double(A) %<-- Convert it to a MATLAB sparse matrix
B =

   (5,240)     0.1299
   (6,362)     0.5688
   (3,388)     0.7792
   (2,527)     0.0759
   (3,543)     0.5308
   (7,586)     0.0119
  (10,716)     0.3371
   (4,722)     0.9340
   (7,823)     0.4694
   (2,999)     0.0540

whos A B %<-- The storage for B (the sparse matrix) is larger than for A.
  Name       Size              Bytes  Class       Attributes

  A         10x1000             1144  sptenmat              
  B         10x1000             8168  double      sparse    

C = B'; %<-- Transposing the result fixes the problem.
whos C
  Name         Size            Bytes  Class     Attributes

  C         1000x10              248  double    sparse    

Use full to convert a sptenmat to a tenmat

B = sptenmat(sptenrand([3 3 3], 3), 1) %<-- Create a sptenmat
B is a sptenmat from an sptensor of size 3 x 3 x 3 with 3 nonzeros
	B.rindices = [ 1 ] (modes of tensor corresponding to rows)
	B.cindices = [ 2  3 ] (modes of tensor corresponding to columns)
	(1,2)	0.748152
	(1,8)	0.450542
	(3,4)	0.0838214
C = full(B) %<-- Convert to a tenmat
C is a matrix corresponding to a tensor of size 3 x 3 x 3
	C.rindices = [ 1 ] (modes of tensor corresponding to rows)
	C.cindices = [ 2  3 ] (modes of tensor corresponding to columns)
	C.data = 
		  Columns 1 through 7
		         0    0.7482         0         0         0         0         0
		         0         0         0         0         0         0         0
		         0         0         0    0.0838         0         0         0
		  Columns 8 through 9
		    0.4505         0
		         0         0
		         0         0

Use sptensor to convert a sptenmat to a sptensor

Y = sptensor(A) %<-- Convert a sptenmat to a sptensor
Y is a sparse tensor of size 10 x 10 x 10 x 10 with 10 nonzeros
	( 2, 7, 3, 6)    0.0759
	( 2, 9,10,10)    0.0540
	( 3, 3, 5, 6)    0.5308
	( 3, 8, 9, 4)    0.7792
	( 4, 2, 3, 8)    0.9340
	( 5,10, 4, 3)    0.1299
	( 6, 2, 7, 4)    0.5688
	( 7, 3, 3, 9)    0.4694
	( 7, 6, 9, 6)    0.0119
	(10, 6, 2, 8)    0.3371

Use size and tsize for the dimensions of a sptenmat

size(A) %<-- Matrix size
tsize(A) %<-- Corresponding tensor size
ans =

          10        1000


ans =

    10    10    10    10

Subscripted reference for a sptenmat

This is not supported beyond getting the constituent parts.

Subscripted assignment for a sptenmat

A(1:2,1:2) = ones(2) %<-- Replace part of the matrix.
A is a sptenmat from an sptensor of size 10 x 10 x 10 x 10 with 14 nonzeros
	A.rindices = [ 1 ] (modes of tensor corresponding to rows)
	A.cindices = [ 2  3  4 ] (modes of tensor corresponding to columns)
	( 1,  1)	1
	( 1,  2)	1
	( 2,  1)	1
	( 2,  2)	1
	( 2,527)	0.0758543
	( 2,999)	0.0539501
	( 3,388)	0.779167
	( 3,543)	0.530798
	( 4,722)	0.934011
	( 5,240)	0.129906
	( 6,362)	0.568824
	( 7,586)	0.0119021
	( 7,823)	0.469391
	(10,716)	0.337123

Use end for the last index

End is not supported.

Basic operations for sptenmat

norm(A) %<-- Norm of the matrix.
ans =

    2.5386

+A %<-- Calls uplus.
ans is a sptenmat from an sptensor of size 10 x 10 x 10 x 10 with 14 nonzeros
	ans.rindices = [ 1 ] (modes of tensor corresponding to rows)
	ans.cindices = [ 2  3  4 ] (modes of tensor corresponding to columns)
	( 1,  1)	1
	( 1,  2)	1
	( 2,  1)	1
	( 2,  2)	1
	( 2,527)	0.0758543
	( 2,999)	0.0539501
	( 3,388)	0.779167
	( 3,543)	0.530798
	( 4,722)	0.934011
	( 5,240)	0.129906
	( 6,362)	0.568824
	( 7,586)	0.0119021
	( 7,823)	0.469391
	(10,716)	0.337123
-A %<-- Calls uminus.
ans is a sptenmat from an sptensor of size 10 x 10 x 10 x 10 with 14 nonzeros
	ans.rindices = [ 1 ] (modes of tensor corresponding to rows)
	ans.cindices = [ 2  3  4 ] (modes of tensor corresponding to columns)
	( 1,  1)	-1
	( 1,  2)	-1
	( 2,  1)	-1
	( 2,  2)	-1
	( 2,527)	-0.0758543
	( 2,999)	-0.0539501
	( 3,388)	-0.779167
	( 3,543)	-0.530798
	( 4,722)	-0.934011
	( 5,240)	-0.129906
	( 6,362)	-0.568824
	( 7,586)	-0.0119021
	( 7,823)	-0.469391
	(10,716)	-0.337123

Use aatx to efficiently compute A * A' * x for a sptenmat

x = ones(10,1); %<-- Create vector
aatx(A,x) %<-- Compute A * A' * x
ans =

    4.0000
    4.0087
    0.8888
    0.8724
    0.0169
    0.3236
    0.2205
         0
         0
    0.1137

double(A) * double(A)' * x %<-- Same as above but less efficient
ans =

    4.0000
    4.0087
    0.8888
    0.8724
    0.0169
    0.3236
    0.2205
         0
         0
    0.1137

Displaying a tenmat

Shows the original tensor dimensions, the modes mapped to rows, the modes mapped to columns, and the matrix.

disp(A)
ans is a sptenmat from an sptensor of size 10 x 10 x 10 x 10 with 14 nonzeros
	ans.rindices = [ 1 ] (modes of tensor corresponding to rows)
	ans.cindices = [ 2  3  4 ] (modes of tensor corresponding to columns)
	( 1,  1)	1
	( 1,  2)	1
	( 2,  1)	1
	( 2,  2)	1
	( 2,527)	0.0758543
	( 2,999)	0.0539501
	( 3,388)	0.779167
	( 3,543)	0.530798
	( 4,722)	0.934011
	( 5,240)	0.129906
	( 6,362)	0.568824
	( 7,586)	0.0119021
	( 7,823)	0.469391
	(10,716)	0.337123