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.

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, 5, 2, 3)    0.4712
	( 1,10, 2,10)    0.1493
	( 3, 5, 1, 4)    0.1359
	( 3, 9,10, 8)    0.5325
	( 4, 6,10, 6)    0.7258
	( 5, 2, 1, 9)    0.3987
	( 5, 7, 7, 2)    0.3584
	( 9, 4, 6, 2)    0.2853
	( 9, 7, 6, 5)    0.8686
	(10, 6, 9,10)    0.6264

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,215)	0.471156
	( 1,920)	0.14931
	( 3,305)	0.135864
	( 3,799)	0.532498
	( 4,596)	0.725789
	( 5,802)	0.398703
	( 5,167)	0.358419
	( 9,154)	0.285279
	( 9,457)	0.868635
	(10,986)	0.626413
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)
	(15, 21)	0.471156
	(20, 91)	0.14931
	( 5, 33)	0.135864
	(99, 73)	0.532498
	(96, 54)	0.725789
	( 2, 85)	0.398703
	(67, 15)	0.358419
	(54, 19)	0.285279
	(57, 49)	0.868635
	(86,100)	0.626413
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)
	( 21,15)	0.471156
	( 91,20)	0.14931
	( 33, 5)	0.135864
	( 73,99)	0.532498
	( 54,96)	0.725789
	( 85, 2)	0.398703
	( 15,67)	0.358419
	( 19,54)	0.285279
	( 49,57)	0.868635
	(100,86)	0.626413
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)
	(2141,1)	0.471156
	(9191,1)	0.14931
	(3043,1)	0.135864
	(7983,1)	0.532498
	(5954,1)	0.725789
	(8015,1)	0.398703
	(1665,1)	0.358419
	(1539,1)	0.285279
	(4569,1)	0.868635
	(9860,1)	0.626413
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)
	( 5,211)	0.471156
	(10,911)	0.14931
	( 5,303)	0.135864
	( 9,793)	0.532498
	( 6,594)	0.725789
	( 2,805)	0.398703
	( 7,165)	0.358419
	( 4,159)	0.285279
	( 7,459)	0.868635
	( 6,990)	0.626413
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)
	( 5,202)	0.471156
	(10,902)	0.14931
	( 5,321)	0.135864
	( 9,730)	0.532498
	( 6,540)	0.725789
	( 2,841)	0.398703
	( 7,147)	0.358419
	( 4,186)	0.285279
	( 7,486)	0.868635
	( 6,999)	0.626413
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)
	( 5, 22)	0.471156
	(10, 92)	0.14931
	( 5,231)	0.135864
	( 9,280)	0.532498
	( 6,360)	0.725789
	( 2,481)	0.398703
	( 7,417)	0.358419
	( 4,816)	0.285279
	( 7,846)	0.868635
	( 6,999)	0.626413
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)
	( 5,121)	0.471156
	(10,191)	0.14931
	( 5, 33)	0.135864
	( 9,973)	0.532498
	( 6,954)	0.725789
	( 2, 85)	0.398703
	( 7,615)	0.358419
	( 4,519)	0.285279
	( 7,549)	0.868635
	( 6,900)	0.626413

Constituent parts of a sptenmat

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

     5   121
    10   191
     5    33
     9   973
     6   954
     2    85
     7   615
     4   519
     7   549
     6   900

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

    0.4712
    0.1493
    0.1359
    0.5325
    0.7258
    0.3987
    0.3584
    0.2853
    0.8686
    0.6264

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, 85)	0.398703
	( 4,519)	0.285279
	( 5, 33)	0.135864
	( 5,121)	0.471156
	( 6,900)	0.626413
	( 6,954)	0.725789
	( 7,549)	0.868635
	( 7,615)	0.358419
	( 9,973)	0.532498
	(10,191)	0.14931
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)
	( 5, 33)	0.135864
	( 2, 85)	0.398703
	( 5,121)	0.471156
	(10,191)	0.14931
	( 4,519)	0.285279
	( 7,549)	0.868635
	( 7,615)	0.358419
	( 6,900)	0.626413
	( 6,954)	0.725789
	( 9,973)	0.532498

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)
	( 1,384)	0.608106
	( 2,763)	0.175996
	( 2,499)	0.00202556
	( 3,111)	0.790224
	( 3,451)	0.513609
	( 3,781)	0.213229
	( 7,852)	0.10345
	( 7,636)	0.157337
	( 7,658)	0.407515
	(10,129)	0.407757
B = double(A) %<-- Convert it to a MATLAB sparse matrix
B =

   (3,111)     0.7902
  (10,129)     0.4078
   (1,384)     0.6081
   (3,451)     0.5136
   (2,499)     0.0020
   (7,636)     0.1573
   (7,658)     0.4075
   (2,763)     0.1760
   (3,781)     0.2132
   (7,852)     0.1034

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

  A         10x1000             1184  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,7)	0.410904
	(1,8)	0.399794
	(3,1)	0.505522
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         0         0         0         0    0.4109
		         0         0         0         0         0         0         0
		    0.5055         0         0         0         0         0         0
		  Columns 8 through 9
		    0.3998         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
	( 1,4, 9,4)    0.6081
	( 2,3, 7,8)    0.1760
	( 2,9,10,5)    0.0020
	( 3,1, 2,2)    0.7902
	( 3,1, 6,5)    0.5136
	( 3,1, 9,8)    0.2132
	( 7,2, 6,9)    0.1034
	( 7,6, 4,7)    0.1573
	( 7,8, 6,7)    0.4075
	(10,9, 3,2)    0.4078

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
	( 1,384)	0.608106
	( 2,  1)	1
	( 2,  2)	1
	( 2,499)	0.00202556
	( 2,763)	0.175996
	( 3,111)	0.790224
	( 3,451)	0.513609
	( 3,781)	0.213229
	( 7,636)	0.157337
	( 7,658)	0.407515
	( 7,852)	0.10345
	(10,129)	0.407757

Use end for the last index

End is not supported.

Basic operations for sptenmat

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

    2.3879

+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
	( 1,384)	0.608106
	( 2,  1)	1
	( 2,  2)	1
	( 2,499)	0.00202556
	( 2,763)	0.175996
	( 3,111)	0.790224
	( 3,451)	0.513609
	( 3,781)	0.213229
	( 7,636)	0.157337
	( 7,658)	0.407515
	( 7,852)	0.10345
	(10,129)	0.407757
-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
	( 1,384)	-0.608106
	( 2,  1)	-1
	( 2,  2)	-1
	( 2,499)	-0.00202556
	( 2,763)	-0.175996
	( 3,111)	-0.790224
	( 3,451)	-0.513609
	( 3,781)	-0.213229
	( 7,636)	-0.157337
	( 7,658)	-0.407515
	( 7,852)	-0.10345
	(10,129)	-0.407757

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.3698
    4.0310
    0.9337
         0
         0
         0
    0.2015
         0
         0
    0.1663

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

    4.3698
    4.0310
    0.9337
         0
         0
         0
    0.2015
         0
         0
    0.1663

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
	( 1,384)	0.608106
	( 2,  1)	1
	( 2,  2)	1
	( 2,499)	0.00202556
	( 2,763)	0.175996
	( 3,111)	0.790224
	( 3,451)	0.513609
	( 3,781)	0.213229
	( 7,636)	0.157337
	( 7,658)	0.407515
	( 7,852)	0.10345
	(10,129)	0.407757