Converting Sparse Tensors to Matrices 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 an sptensor
to a matrix stored in coordinate format with extra information so that is can be convertered back to an sptensor
.
import pyttb as ttb
import numpy as np
Creating an sptenmat
(sparse tensor as sparse matrix) object
A sparse tensor can be converted to a sparse matrix, with row and column indices stored explicitly.
First, we crease a sparse tensor to be converted.
np.random.seed(0) # Random seed for reproducibility
X = ttb.sptenrand((10, 10, 10, 10), nonzeros=10)
X
sparse tensor of shape (10, 10, 10, 10) with 10 nonzeros and order F
[0, 8, 7, 8] = 0.359507900573786
[1, 6, 1, 9] = 0.43703195379934145
[4, 5, 0, 6] = 0.6976311959272649
[4, 6, 4, 8] = 0.06022547162926983
[5, 4, 2, 7] = 0.6667667154456677
[5, 7, 6, 5] = 0.6706378696181594
[5, 9, 0, 0] = 0.2103825610738409
[6, 6, 9, 6] = 0.1289262976548533
[9, 3, 7, 5] = 0.31542835092418386
[9, 7, 4, 7] = 0.3637107709426226
Similar options as tenmat
are available for sptenmat
.
A = X.to_sptenmat(np.array([0])) # Mode-0 matricization
A
sptenmat corresponding to a sptensor of shape (10, 10, 10, 10) with 10 nonzeros and order F
rdims = [ 0 ] (modes of sptensor corresponding to rows)
cdims = [ 1, 2, 3 ] (modes of sptensor corresponding to columns)
[0, 878] = 0.359507900573786
[1, 916] = 0.43703195379934145
[4, 605] = 0.6976311959272649
[4, 846] = 0.06022547162926983
[5, 9] = 0.2103825610738409
[5, 567] = 0.6706378696181594
[5, 724] = 0.6667667154456677
[6, 696] = 0.1289262976548533
[9, 573] = 0.31542835092418386
[9, 747] = 0.3637107709426226
A = X.to_sptenmat(np.array([1, 2])) # Multiple modes mapped to rows.
A
sptenmat corresponding to a sptensor of shape (10, 10, 10, 10) with 10 nonzeros and order F
rdims = [ 1, 2 ] (modes of sptensor corresponding to rows)
cdims = [ 0, 3 ] (modes of sptensor corresponding to columns)
[5, 64] = 0.6976311959272649
[9, 5] = 0.2103825610738409
[16, 91] = 0.43703195379934145
[24, 75] = 0.6667667154456677
[46, 84] = 0.06022547162926983
[47, 79] = 0.3637107709426226
[67, 55] = 0.6706378696181594
[73, 59] = 0.31542835092418386
[78, 80] = 0.359507900573786
[96, 66] = 0.1289262976548533
A = X.to_sptenmat(cdims=np.array([1, 2])) # Specify column dimensions.
A
sptenmat corresponding to a sptensor of shape (10, 10, 10, 10) with 10 nonzeros and order F
rdims = [ 0, 3 ] (modes of sptensor corresponding to rows)
cdims = [ 1, 2 ] (modes of sptensor corresponding to columns)
[5, 9] = 0.2103825610738409
[55, 67] = 0.6706378696181594
[59, 73] = 0.31542835092418386
[64, 5] = 0.6976311959272649
[66, 96] = 0.1289262976548533
[75, 24] = 0.6667667154456677
[79, 47] = 0.3637107709426226
[80, 78] = 0.359507900573786
[84, 46] = 0.06022547162926983
[91, 16] = 0.43703195379934145
A = X.to_sptenmat(np.arange(4)) # All modes mapped to rows, i.e., vectorize.
A
sptenmat corresponding to a sptensor of shape (10, 10, 10, 10) with 10 nonzeros and order F
rdims = [ 0, 1, 2, 3 ] (modes of sptensor corresponding to rows)
cdims = [ ] (modes of sptensor corresponding to columns)
[95, 0] = 0.2103825610738409
[5675, 0] = 0.6706378696181594
[5739, 0] = 0.31542835092418386
[6054, 0] = 0.6976311959272649
[6966, 0] = 0.1289262976548533
[7245, 0] = 0.6667667154456677
[7479, 0] = 0.3637107709426226
[8464, 0] = 0.06022547162926983
[8780, 0] = 0.359507900573786
[9161, 0] = 0.43703195379934145
A = X.to_sptenmat(np.array([1])) # By default, columns are ordered as [0, 2, 3]
A
sptenmat corresponding to a sptensor of shape (10, 10, 10, 10) with 10 nonzeros and order F
rdims = [ 1 ] (modes of sptensor corresponding to rows)
cdims = [ 0, 2, 3 ] (modes of sptensor corresponding to columns)
[3, 579] = 0.31542835092418386
[4, 725] = 0.6667667154456677
[5, 604] = 0.6976311959272649
[6, 696] = 0.1289262976548533
[6, 844] = 0.06022547162926983
[6, 911] = 0.43703195379934145
[7, 565] = 0.6706378696181594
[7, 749] = 0.3637107709426226
[8, 870] = 0.359507900573786
[9, 5] = 0.2103825610738409
A = X.to_sptenmat(np.array([1]), np.array([3, 0, 2])) # Specify explicit ordering
A
sptenmat corresponding to a sptensor of shape (10, 10, 10, 10) with 10 nonzeros and order F
rdims = [ 1 ] (modes of sptensor corresponding to rows)
cdims = [ 3, 0, 2 ] (modes of sptensor corresponding to columns)
[3, 795] = 0.31542835092418386
[4, 257] = 0.6667667154456677
[5, 46] = 0.6976311959272649
[6, 119] = 0.43703195379934145
[6, 448] = 0.06022547162926983
[6, 966] = 0.1289262976548533
[7, 497] = 0.3637107709426226
[7, 655] = 0.6706378696181594
[8, 708] = 0.359507900573786
[9, 50] = 0.2103825610738409
A = X.to_sptenmat(np.array([1]), cdims_cyclic="fc") # Forward cyclic column ordering
A
sptenmat corresponding to a sptensor of shape (10, 10, 10, 10) with 10 nonzeros and order F
rdims = [ 1 ] (modes of sptensor corresponding to rows)
cdims = [ 2, 3, 0 ] (modes of sptensor corresponding to columns)
[3, 957] = 0.31542835092418386
[4, 572] = 0.6667667154456677
[5, 460] = 0.6976311959272649
[6, 191] = 0.43703195379934145
[6, 484] = 0.06022547162926983
[6, 669] = 0.1289262976548533
[7, 556] = 0.6706378696181594
[7, 974] = 0.3637107709426226
[8, 87] = 0.359507900573786
[9, 500] = 0.2103825610738409
A = X.to_sptenmat(np.array([1]), cdims_cyclic="bc") # Backward cyclic column ordering
A
sptenmat corresponding to a sptensor of shape (10, 10, 10, 10) with 10 nonzeros and order F
rdims = [ 1 ] (modes of sptensor corresponding to rows)
cdims = [ 0, 3, 2 ] (modes of sptensor corresponding to columns)
[3, 759] = 0.31542835092418386
[4, 275] = 0.6667667154456677
[5, 64] = 0.6976311959272649
[6, 191] = 0.43703195379934145
[6, 484] = 0.06022547162926983
[6, 966] = 0.1289262976548533
[7, 479] = 0.3637107709426226
[7, 655] = 0.6706378696181594
[8, 780] = 0.359507900573786
[9, 5] = 0.2103825610738409
Constituent parts of an sptenmat
A.subs # Subscripts of the nonzeros.
array([[ 3, 759],
[ 4, 275],
[ 5, 64],
[ 6, 191],
[ 6, 484],
[ 6, 966],
[ 7, 479],
[ 7, 655],
[ 8, 780],
[ 9, 5]])
A.vals # Corresponding nonzero values.
array([[0.31542835],
[0.66676672],
[0.6976312 ],
[0.43703195],
[0.06022547],
[0.1289263 ],
[0.36371077],
[0.67063787],
[0.3595079 ],
[0.21038256]])
A.tshape # Shape of the original tensor.
(10, 10, 10, 10)
A.rdims # Dimensions that were mapped to the rows.
array([1])
A.cdims # Dimensions that were mapped to the columns.
array([0, 3, 2])
Creating an sptenmat
from its constituent parts
B = ttb.sptenmat(A.subs, A.vals, A.rdims, A.cdims, A.tshape) # Effectively copies A
B
sptenmat corresponding to a sptensor of shape (10, 10, 10, 10) with 10 nonzeros and order F
rdims = [ 1 ] (modes of sptensor corresponding to rows)
cdims = [ 0, 3, 2 ] (modes of sptensor corresponding to columns)
[3, 759] = 0.31542835092418386
[4, 275] = 0.6667667154456677
[5, 64] = 0.6976311959272649
[6, 191] = 0.43703195379934145
[6, 484] = 0.06022547162926983
[6, 966] = 0.1289262976548533
[7, 479] = 0.3637107709426226
[7, 655] = 0.6706378696181594
[8, 780] = 0.359507900573786
[9, 5] = 0.2103825610738409
Creating an sptenmat
with no nonzeros
A = ttb.sptenmat(rdims=A.rdims, cdims=A.cdims, tshape=A.tshape) # An empty sptenmat
A
sptenmat corresponding to a sptensor of shape (10, 1000) with 0 nonzeros and order F
rdims = [ 1 ] (modes of sptensor corresponding to rows)
cdims = [ 0, 3, 2 ] (modes of sptensor corresponding to columns)
Creating an empty sptenmat
A = ttb.sptenmat() # A really empty sptenmat
A
sptenmat corresponding to a sptensor of shape () with 0 nonzeros and order F
rdims = [ ] (modes of sptensor corresponding to rows)
cdims = [ ] (modes of sptensor corresponding to columns)
Use double
to convert an sptenmat
to a SciPy COO Matrix
X = ttb.sptenrand((10, 10, 10, 10), nonzeros=10) # Create sptensor
A = X.to_sptenmat(np.array([0])) # Convert to an sptenmat
A
sptenmat corresponding to a sptensor of shape (10, 10, 10, 10) with 10 nonzeros and order F
rdims = [ 0 ] (modes of sptensor corresponding to rows)
cdims = [ 1, 2, 3 ] (modes of sptensor corresponding to columns)
[0, 256] = 0.31856895245132366
[2, 261] = 0.13179786240439217
[2, 431] = 0.6674103799636817
[4, 112] = 0.7163272041185655
[5, 194] = 0.18319136200711683
[5, 950] = 0.2894060929472011
[6, 311] = 0.5865129348100832
[7, 120] = 0.020107546187493552
[8, 80] = 0.8289400292173631
[9, 694] = 0.004695476192547066
B = A.double() # Convert to scipy
B
<10x1000 sparse matrix of type '<class 'numpy.float64'>'
with 10 stored elements in COOrdinate format>
Use full
to convert an sptenmat
to a tenmat
B = ttb.sptenrand((3, 3, 3), nonzeros=3).to_sptenmat(np.array([0]))
B
sptenmat corresponding to a sptensor of shape (3, 3, 3) with 3 nonzeros and order F
rdims = [ 0 ] (modes of sptensor corresponding to rows)
cdims = [ 1, 2 ] (modes of sptensor corresponding to columns)
[1, 1] = 0.952749011516985
[2, 3] = 0.44712537861762736
[2, 6] = 0.8464086724711278
C = B.full()
C
matrix corresponding to a tensor of shape (3, 3, 3) with order F
rindices = [ 0 ] (modes of tensor corresponding to rows)
cindices = [ 1, 2 ] (modes of tensor corresponding to columns)
data[:, :] =
[[0. 0. 0. 0. 0. 0.
0. 0. 0. ]
[0. 0.95274901 0. 0. 0. 0.
0. 0. 0. ]
[0. 0. 0. 0.44712538 0. 0.
0.84640867 0. 0. ]]
Use to_sptensor
to convert an sptenmat
to an sptensor
.
Y = B.to_sptensor()
Y
sparse tensor of shape (3, 3, 3) with 3 nonzeros and order F
[1, 1, 0] = 0.952749011516985
[2, 0, 1] = 0.44712537861762736
[2, 0, 2] = 0.8464086724711278
## Access `shape` and `tshape` for dimensions of an `sptenmat`
print(f"Matrix shape: {A.shape}\n" f"Original tensor shape: {A.tshape}")
Matrix shape: (10, 1000)
Original tensor shape: (10, 10, 10, 10)
## Subscripted assignment for an `sptenmat`
A[0:2, 0:2] = 1
A
sptenmat corresponding to a sptensor of shape (10, 10, 10, 10) with 14 nonzeros and order F
rdims = [ 0 ] (modes of sptensor corresponding to rows)
cdims = [ 1, 2, 3 ] (modes of sptensor corresponding to columns)
[0, 0] = 1.0
[0, 1] = 1.0
[0, 256] = 0.31856895245132366
[1, 0] = 1.0
[1, 1] = 1.0
[2, 261] = 0.13179786240439217
[2, 431] = 0.6674103799636817
[4, 112] = 0.7163272041185655
[5, 194] = 0.18319136200711683
[5, 950] = 0.2894060929472011
[6, 311] = 0.5865129348100832
[7, 120] = 0.020107546187493552
[8, 80] = 0.8289400292173631
[9, 694] = 0.004695476192547066
## Basic operations for `sptenmat`
A.norm() # Norm of the matrix.
2.4952551873589304
+A # Positive version of matrix (no change)
sptenmat corresponding to a sptensor of shape (10, 10, 10, 10) with 14 nonzeros and order F
rdims = [ 0 ] (modes of sptensor corresponding to rows)
cdims = [ 1, 2, 3 ] (modes of sptensor corresponding to columns)
[0, 0] = 1.0
[0, 1] = 1.0
[0, 256] = 0.31856895245132366
[1, 0] = 1.0
[1, 1] = 1.0
[2, 261] = 0.13179786240439217
[2, 431] = 0.6674103799636817
[4, 112] = 0.7163272041185655
[5, 194] = 0.18319136200711683
[5, 950] = 0.2894060929472011
[6, 311] = 0.5865129348100832
[7, 120] = 0.020107546187493552
[8, 80] = 0.8289400292173631
[9, 694] = 0.004695476192547066
-A # Negative version of matrix
sptenmat corresponding to a sptensor of shape (10, 10, 10, 10) with 14 nonzeros and order F
rdims = [ 0 ] (modes of sptensor corresponding to rows)
cdims = [ 1, 2, 3 ] (modes of sptensor corresponding to columns)
[0, 0] = -1.0
[0, 1] = -1.0
[0, 256] = -0.31856895245132366
[1, 0] = -1.0
[1, 1] = -1.0
[2, 261] = -0.13179786240439217
[2, 431] = -0.6674103799636817
[4, 112] = -0.7163272041185655
[5, 194] = -0.18319136200711683
[5, 950] = -0.2894060929472011
[6, 311] = -0.5865129348100832
[7, 120] = -0.020107546187493552
[8, 80] = -0.8289400292173631
[9, 694] = -0.004695476192547066