Converting a tensor
to a 2D numpy array and vice versa
Copyright 2022 National Technology & Engineering Solutions of Sandia,
LLC (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the
U.S. Government retains certain rights in this software.
We show how to convert a tensor
to a 2D numpy array stored with extra information so that it can be converted back to a tensor
. Converting to a 2D numpy array requires an ordered mapping of the tensor
indices to the rows and the columns of the 2D numpy array.
import pyttb as ttb
import numpy as np
Creating a tenmat
(tensor
as 2D numpy array) object
X = ttb.tensor(np.arange(1, 25), shape=(3, 2, 2, 2)) # Create a tensor.
X
tensor of shape (3, 2, 2, 2) with order F
data[:, :, 0, 0] =
[[1 4]
[2 5]
[3 6]]
data[:, :, 1, 0] =
[[ 7 10]
[ 8 11]
[ 9 12]]
data[:, :, 0, 1] =
[[13 16]
[14 17]
[15 18]]
data[:, :, 1, 1] =
[[19 22]
[20 23]
[21 24]]
# Dims [0,1] map to rows, [2,3] to columns.
A = X.to_tenmat(np.array([0, 1]), np.array([2, 3]))
A
matrix corresponding to a tensor of shape (3, 2, 2, 2) with order F
rindices = [ 0, 1 ] (modes of tensor corresponding to rows)
cindices = [ 2, 3 ] (modes of tensor corresponding to columns)
data[:, :] =
[[ 1 7 13 19]
[ 2 8 14 20]
[ 3 9 15 21]
[ 4 10 16 22]
[ 5 11 17 23]
[ 6 12 18 24]]
B = X.to_tenmat(np.array([1, 0]), np.array([2, 3])) # Order matters!
B
matrix corresponding to a tensor of shape (3, 2, 2, 2) with order F
rindices = [ 1, 0 ] (modes of tensor corresponding to rows)
cindices = [ 2, 3 ] (modes of tensor corresponding to columns)
data[:, :] =
[[ 1 7 13 19]
[ 4 10 16 22]
[ 2 8 14 20]
[ 5 11 17 23]
[ 3 9 15 21]
[ 6 12 18 24]]
C = X.to_tenmat(np.array([0, 1]), np.array([3, 2]))
C
matrix corresponding to a tensor of shape (3, 2, 2, 2) with order F
rindices = [ 0, 1 ] (modes of tensor corresponding to rows)
cindices = [ 3, 2 ] (modes of tensor corresponding to columns)
data[:, :] =
[[ 1 13 7 19]
[ 2 14 8 20]
[ 3 15 9 21]
[ 4 16 10 22]
[ 5 17 11 23]
[ 6 18 12 24]]
Creating a tenmat
by specifying the dimensions mapped to the rows
If just the row indices are specified, then the columns are arranged in increasing order.
X = ttb.tensor(np.arange(1, 25), shape=(3, 2, 2, 2)) # Create a tensor.
A = X.to_tenmat(np.array([1])) # np.array([1]) passed to the `rdims` parameter
A
matrix corresponding to a tensor of shape (3, 2, 2, 2) with order F
rindices = [ 1 ] (modes of tensor corresponding to rows)
cindices = [ 0, 2, 3 ] (modes of tensor corresponding to columns)
data[:, :] =
[[ 1 2 3 7 8 9 13 14 15 19 20 21]
[ 4 5 6 10 11 12 16 17 18 22 23 24]]
Creating a tenmat
by specifying the dimensions mapped to the columns
Likewise, just the columns can be specified if the cdims
argument is given. The columns are arranged in increasing order.
X = ttb.tensor(np.arange(1, 25), shape=(3, 2, 2, 2)) # Create a tensor.
# Same as A = ttb.tenmat.from_tensor_type(X, np.array([0,3]), np.array([1,2]))
A = X.to_tenmat(cdims=np.array([1, 2]))
A
matrix corresponding to a tensor of shape (3, 2, 2, 2) with order F
rindices = [ 0, 3 ] (modes of tensor corresponding to rows)
cindices = [ 1, 2 ] (modes of tensor corresponding to columns)
data[:, :] =
[[ 1 4 7 10]
[ 2 5 8 11]
[ 3 6 9 12]
[13 16 19 22]
[14 17 20 23]
[15 18 21 24]]
Vectorize via tenmat
X = ttb.tensor(np.arange(1, 25), shape=(3, 2, 2, 2)) # Create a tensor.
A = X.to_tenmat(cdims=np.arange(0, 4)) # Map all the dimensions to the columns
A
matrix corresponding to a tensor of shape (3, 2, 2, 2) with order F
rindices = [ ] (modes of tensor corresponding to rows)
cindices = [ 0, 1, 2, 3 ] (modes of tensor corresponding to columns)
data[:, :] =
[[ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24]]
Alternative ordering for the columns for mode-\(n\) matricization
Mode-\(n\) matricization means that only mode \(n\) is mapped to the rows. Different column orderings are available.
X = ttb.tensor(np.arange(1, 25), shape=(3, 2, 2, 2)) # Create a tensor.
A = X.to_tenmat(np.array([2])) # By default, columns are ordered as [0, 1, 3].
A
matrix corresponding to a tensor of shape (3, 2, 2, 2) with order F
rindices = [ 2 ] (modes of tensor corresponding to rows)
cindices = [ 0, 1, 3 ] (modes of tensor corresponding to columns)
data[:, :] =
[[ 1 2 3 4 5 6 13 14 15 16 17 18]
[ 7 8 9 10 11 12 19 20 21 22 23 24]]
X = ttb.tensor(np.arange(1, 25), shape=(3, 2, 2, 2)) # Create a tensor.
A = X.to_tenmat(np.array([1]), np.array([2, 0, 3])) # Explicit specification.
A
matrix corresponding to a tensor of shape (3, 2, 2, 2) with order F
rindices = [ 1 ] (modes of tensor corresponding to rows)
cindices = [ 2, 0, 3 ] (modes of tensor corresponding to columns)
data[:, :] =
[[ 1 7 2 8 3 9 13 19 14 20 15 21]
[ 4 10 5 11 6 12 16 22 17 23 18 24]]
A = X.to_tenmat(np.array([1]), cdims_cyclic="fc") # Forward cyclic, [2,3,0].
A
matrix corresponding to a tensor of shape (3, 2, 2, 2) with order F
rindices = [ 1 ] (modes of tensor corresponding to rows)
cindices = [ 2, 3, 0 ] (modes of tensor corresponding to columns)
data[:, :] =
[[ 1 7 13 19 2 8 14 20 3 9 15 21]
[ 4 10 16 22 5 11 17 23 6 12 18 24]]
A = X.to_tenmat(np.array([1]), cdims_cyclic="bc") # Backward cyclic, [0,3,2].
A
matrix corresponding to a tensor of shape (3, 2, 2, 2) with order F
rindices = [ 1 ] (modes of tensor corresponding to rows)
cindices = [ 0, 3, 2 ] (modes of tensor corresponding to columns)
data[:, :] =
[[ 1 2 3 13 14 15 7 8 9 19 20 21]
[ 4 5 6 16 17 18 10 11 12 22 23 24]]
Constituent parts of a tenmat
X = ttb.tensor(np.arange(1, 25), shape=(3, 2, 2, 2)) # Create a tensor.
A = X.to_tenmat(np.array([1]), cdims_cyclic="bc") # Backward cyclic, [0,3,2].
A.data # The 2D numpy array itself.
array([[ 1, 2, 3, 13, 14, 15, 7, 8, 9, 19, 20, 21],
[ 4, 5, 6, 16, 17, 18, 10, 11, 12, 22, 23, 24]])
A.tshape # Shape of the original tensor.
(3, 2, 2, 2)
A.rindices # Dimensions that were mapped to the rows.
array([1])
A.cindices # Dimensions that were mapped to the columns.
array([0, 3, 2])
Creating a tenmat
from its constituent parts
X = ttb.tensor(np.arange(1, 25), shape=(3, 2, 2, 2)) # Create a tensor.
A = X.to_tenmat(np.array([1]), cdims_cyclic="bc") # Backward cyclic, [0,3,2].
B = ttb.tenmat(A.data, A.rindices, A.cindices, A.tshape)
B # Recreates A.
matrix corresponding to a tensor of shape (3, 2, 2, 2) with order F
rindices = [ 1 ] (modes of tensor corresponding to rows)
cindices = [ 0, 3, 2 ] (modes of tensor corresponding to columns)
data[:, :] =
[[ 1 2 3 13 14 15 7 8 9 19 20 21]
[ 4 5 6 16 17 18 10 11 12 22 23 24]]
Creating an empty tenmat
B = ttb.tenmat() # Empty tenmat.
B
matrix corresponding to a tensor of shape () with order F
rindices = [ ] (modes of tensor corresponding to rows)
cindices = [ ] (modes of tensor corresponding to columns)
data = []
Use double
to convert a tenmat
to a 2D numpy array
X = ttb.tensor(np.arange(1, 25), shape=(3, 2, 2, 2)) # Create a tensor.
A = X.to_tenmat(np.array([1]), cdims_cyclic="bc") # Backward cyclic, [0,3,2].
A.double() # Converts A to a standard 2D numpy array.
array([[ 1., 2., 3., 13., 14., 15., 7., 8., 9., 19., 20., 21.],
[ 4., 5., 6., 16., 17., 18., 10., 11., 12., 22., 23., 24.]])
Use to_tensor
to convert a tenmat
to a tensor
X = ttb.tensor(np.arange(1, 25), shape=(3, 2, 2, 2)) # Create a tensor.
A = X.to_tenmat(np.array([1]), cdims_cyclic="bc") # Backward cyclic, [0,3,2].
Y = A.to_tensor()
Y
tensor of shape (3, 2, 2, 2) with order F
data[:, :, 0, 0] =
[[1 4]
[2 5]
[3 6]]
data[:, :, 1, 0] =
[[ 7 10]
[ 8 11]
[ 9 12]]
data[:, :, 0, 1] =
[[13 16]
[14 17]
[15 18]]
data[:, :, 1, 1] =
[[19 22]
[20 23]
[21 24]]
Use shape
and tshape
for the dimensions of a tenmat
X = ttb.tensor(np.arange(1, 25), shape=(3, 2, 2, 2)) # Create a tensor.
A = X.to_tenmat(np.array([1]), cdims_cyclic="bc") # Backward cyclic, [0,3,2].
A.shape # 2D numpy array shape.
(2, 12)
A.tshape # Corresponding tensor shape.
(3, 2, 2, 2)
Subscripted reference for a tenmat
X = ttb.tensor(np.arange(1, 25), shape=(3, 2, 2, 2)) # Create a tensor.
A = X.to_tenmat(np.array([1]), cdims_cyclic="bc") # Backward cyclic, [0,3,2].
A[1, 0] # Returns the (1,0) element of the 2D numpy array
np.int64(4)
Subscripted assignment for a tenmat
X = ttb.tensor(np.arange(1, 25), shape=(3, 2, 2, 2)) # Create a tensor.
A = X.to_tenmat(np.array([1]), cdims_cyclic="bc") # Backward cyclic, [0,3,2].
A[0:2, 0:2] = np.ones((2, 2))
A
matrix corresponding to a tensor of shape (3, 2, 2, 2) with order F
rindices = [ 1 ] (modes of tensor corresponding to rows)
cindices = [ 0, 3, 2 ] (modes of tensor corresponding to columns)
data[:, :] =
[[ 1 1 3 13 14 15 7 8 9 19 20 21]
[ 1 1 6 16 17 18 10 11 12 22 23 24]]
Using negative indexing for the last array index
A[-1][-1] # Same as A[1, 11].
np.int64(24)
Basic operations for tenmat
X = ttb.tensor(np.arange(1, 25), shape=(3, 2, 2, 2)) # Create a tensor.
A = X.to_tenmat(np.array([1]), cdims_cyclic="bc") # Backward cyclic, [0,3,2].
A.norm() # Norm of the 2D numpy array.
70.0
A.ctranspose() # Also swaps mapped dimensions.
matrix corresponding to a tensor of shape (3, 2, 2, 2) with order F
rindices = [ 0, 3, 2 ] (modes of tensor corresponding to rows)
cindices = [ 1 ] (modes of tensor corresponding to columns)
data[:, :] =
[[ 1 4]
[ 2 5]
[ 3 6]
[13 16]
[14 17]
[15 18]
[ 7 10]
[ 8 11]
[ 9 12]
[19 22]
[20 23]
[21 24]]
+A # Calls uplus.
matrix corresponding to a tensor of shape (3, 2, 2, 2) with order F
rindices = [ 1 ] (modes of tensor corresponding to rows)
cindices = [ 0, 3, 2 ] (modes of tensor corresponding to columns)
data[:, :] =
[[ 1 2 3 13 14 15 7 8 9 19 20 21]
[ 4 5 6 16 17 18 10 11 12 22 23 24]]
-A # Calls uminus.
matrix corresponding to a tensor of shape (3, 2, 2, 2) with order F
rindices = [ 1 ] (modes of tensor corresponding to rows)
cindices = [ 0, 3, 2 ] (modes of tensor corresponding to columns)
data[:, :] =
[[ -1 -2 -3 -13 -14 -15 -7 -8 -9 -19 -20 -21]
[ -4 -5 -6 -16 -17 -18 -10 -11 -12 -22 -23 -24]]
A + A # Calls plus
matrix corresponding to a tensor of shape (3, 2, 2, 2) with order F
rindices = [ 1 ] (modes of tensor corresponding to rows)
cindices = [ 0, 3, 2 ] (modes of tensor corresponding to columns)
data[:, :] =
[[ 2 4 6 26 28 30 14 16 18 38 40 42]
[ 8 10 12 32 34 36 20 22 24 44 46 48]]
A - A # Calls minus.
matrix corresponding to a tensor of shape (3, 2, 2, 2) with order F
rindices = [ 1 ] (modes of tensor corresponding to rows)
cindices = [ 0, 3, 2 ] (modes of tensor corresponding to columns)
data[:, :] =
[[0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0]]
Multiplying two tenmat
s
It is possible to compute the product of two tenmat
s and have a result that can be converted into a tensor
.
X = ttb.tensor(np.arange(1, 25), shape=(3, 2, 2, 2)) # Create a tensor.
A = X.to_tenmat(np.array([1]), cdims_cyclic="bc") # Backward cyclic, [0,3,2].
B = A * A.ctranspose() # Tenmat that is the product of two tenmats.
B
WARNING:root:Selected no copy, but input data isn't F ordered so must copy.
matrix corresponding to a tensor of shape (2, 2) with order F
rindices = [ 0 ] (modes of tensor corresponding to rows)
cindices = [ 1 ] (modes of tensor corresponding to columns)
data[:, :] =
[[2000 2396]
[2396 2900]]
B.to_tensor() # Corresponding tensor.
tensor of shape (2, 2) with order F
data[:, :] =
[[2000 2396]
[2396 2900]]
Displaying a tenmat
Shows the original tensor dimensions, the modes mapped to rows, the modes mapped to columns, and the 2D numpy array.
A
matrix corresponding to a tensor of shape (3, 2, 2, 2) with order F
rindices = [ 1 ] (modes of tensor corresponding to rows)
cindices = [ 0, 3, 2 ] (modes of tensor corresponding to columns)
data[:, :] =
[[ 1 2 3 13 14 15 7 8 9 19 20 21]
[ 4 5 6 16 17 18 10 11 12 22 23 24]]