Decomposition class

Classes:

CoupledMatrixFactorization(cmf_matrices)

Class wrapper for coupled matrix factorizations.

Functions:

cmf_to_matrices(cmf[, validate])

Generate a list of all matrices represented by the coupled matrix factorisation.

cmf_to_matrix(cmf, matrix_idx[, validate])

Generate a single matrix from the coupled matrix factorisation.

cmf_to_slice(cmf, slice_idx[, validate])

Alias for cmf_to_matrix.

cmf_to_slices(cmf[, validate])

Alias for cmf_to_matrices.

cmf_to_tensor(cmf[, validate])

Generate the tensor represented by the coupled matrix factorization.

cmf_to_unfolded(cmf, mode[, pad, validate])

Generate the unfolded tensor represented by the coupled matrix factorization.

cmf_to_vec(cmf[, pad, validate])

Generate the vectorized tensor represented by the coupled matrix factorization.

class matcouply.coupled_matrices.CoupledMatrixFactorization(cmf_matrices)[source]

Class wrapper for coupled matrix factorizations.

Coupled matrix factorizations decompositions represent stacks of matrices and are on the form \((\mathbf{A} [\mathbf{B}^{(0)}, \mathbf{B}^{(1)}, ..., \mathbf{B}^{(I-1)}] \mathbf{C})\), such that the i-th matrix, \(\mathbf{X}^{(i)}\) is given by

\[\mathbf{X}^{(i)} = \mathbf{B}^{(i)} \text{diag}(\mathbf{a}_i) \mathbf{C}^T,\]

where \(\text{diag}(\mathbf{a}_i)\) is the diagonal matrix whose nonzero entries are equal to the \(i\)-th row of the \(I \times R\) factor matrix \(\mathbf{A}\), \(\mathbf{B}^{(i)}\) is a \(J_i \times R\) factor matrix, and \(\mathbf{C}\) is a \(K \times R\) factor matrix. For more information about coupled matrix decompositions, see What are coupled matrix factorizations?.

This class validates the decomposition and provides conversion to dense formats via methods.

Parameters:

cmf (CoupledMatrixFactorization - (weights, factors)) –

Coupled matrix factorization represented by weights and factors as described in What are coupled matrix factorizations?.

  • weights1D array of shape (rank,) or None

    weights of the factors

  • factorsList of factors of the coupled matrix decomposition

    List on the form [A, [B_0, B_1, ..., B_i], C], where A represents \(\mathbf{A}\), [B_0, B_1, ..., B_i] represents a list of all \(\mathbf{B}^{(i)}\)-matrices and C represents \(\mathbf{C}\)

Examples

>>> from tensorly.random import random_tensor
>>> from matcouply.coupled_matrices import CoupledMatrixFactorization
>>> A = random_tensor((5, 3))
>>> B_is = [random_tensor((10, 3)) for i in range(5)]
>>> C = random_tensor((15, 3))
>>> cmf = CoupledMatrixFactorization((None, (A, B_is, C)))

We can then convert the factorization to a dense format easily

>>> matrices = cmf.to_matrices()
>>> len(matrices)
5
>>> tl.shape(matrices[0])
(10, 15)

We see that we can get the shape of the decomposition without converting it to a dense format first also.

>>> len(cmf.shape)
5
>>> cmf.shape[0]
(10, 15)

We can also extract the weights and factor matrices from the decomposition object as if it was a tuple.

>>> weights, (A, B_is, C) = cmf

And if the decomposition is invalid, then a helpful error message will be printed.

>>> A = random_tensor((5, 3))
>>> B_is = [random_tensor((10, 3)) for i in range(5)]
>>> C = random_tensor((15, 15, 3))
>>> cmf = CoupledMatrixFactorization((None, (A, B_is, C)))
Traceback (most recent call last):
  ...
ValueError: The last factor matrix, C, should be a second order tensor. However C has shape (15, 15, 3)
>>> A = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> B_is = [random_tensor((10, 3)) for i in range(5)]
>>> C = random_tensor((15, 3))
>>> cmf = CoupledMatrixFactorization((None, (A, B_is, C)))
Traceback (most recent call last):
  ...
TypeError: The first factor matrix, A, should be a second order tensor of size (I, rank)), not <class 'list'>

Methods:

from_CPTensor(cp_tensor[, shapes])

Convert a CP tensor into a coupled matrix factorization.

from_Parafac2Tensor(parafac2_tensor)

Convert a PARAFAC2 tensor into a coupled matrix factorization.

to_matrices()

Convert to a list of matrices.

to_matrix(matrix_idx)

Construct a single dense matrix from the decomposition.

to_tensor()

Convert to a dense tensor (pad uneven slices by zeros).

to_unfolded(mode[, pad])

Convert to a matrix by first converting to a dense tensor and unfolding.

to_vec([pad])

Convert to a vector by first converting to a dense tensor and unraveling.

classmethod from_CPTensor(cp_tensor, shapes=None)[source]

Convert a CP tensor into a coupled matrix factorization.

Parameters:

cp_tensor (tl.cp_tensor.CPTensor) – CP tensor to convert into a coupled matrix factorization

Returns:

A coupled matrix factorization that represents the same tensor as cp_tensor.

Return type:

CoupledMatrixFactorization

Raises:

ValueError – If the CP tensor has more than tree modes.

classmethod from_Parafac2Tensor(parafac2_tensor)[source]

Convert a PARAFAC2 tensor into a coupled matrix factorization.

Parameters:

parafac2_tensor (tl.parafac2_tensor.Parafac2Tensor) – PARAFAC2 tensor to convert into a coupled matrix factorization

Returns:

A coupled matrix factorization that represents the same tensor as parafac2_tensor.

Return type:

CoupledMatrixFactorization

to_matrices()[source]

Convert to a list of matrices.

See also

cmf_to_matrices

to_matrix(matrix_idx)[source]

Construct a single dense matrix from the decomposition.

See also

cmf_to_matrix

to_tensor()[source]

Convert to a dense tensor (pad uneven slices by zeros).

See also

cmf_to_tensor

to_unfolded(mode, pad=True)[source]

Convert to a matrix by first converting to a dense tensor and unfolding.

See also

cmf_to_unfolded

to_vec(pad=True)[source]

Convert to a vector by first converting to a dense tensor and unraveling.

See also

cmf_to_vec

matcouply.coupled_matrices.cmf_to_matrices(cmf, validate=True)[source]

Generate a list of all matrices represented by the coupled matrix factorisation.

The decomposition is on the form \((\mathbf{A} [\mathbf{B}^{(0)}, \mathbf{B}^{(1)}, ..., \mathbf{B}^{(I-1)}] \mathbf{C})\) such that the i-th matrix, \(\mathbf{X}^{(i)}\) is given by

\[\mathbf{X}^{(i)} = \mathbf{B}^{(i)} \text{diag}(\mathbf{a}_i) \mathbf{C}^T,\]

where \(\text{diag}(\mathbf{a}_i)\) is the diagonal matrix whose nonzero entries are equal to the \(i\)-th row of the \(I \times R\) factor matrix \(\mathbf{A}\), \(\mathbf{B}^{(i)}\) is a \(J_i \times R\) factor matrix, and \(\mathbf{C}\) is a \(K \times R\) factor matrix.

Parameters:
  • cmf (CoupledMatrixFactorization - (weights, factors)) –

    Coupled matrix factorization represented by weights and factors as described in What are coupled matrix factorizations?.

    • weights1D array of shape (rank,) or None

      weights of the factors

    • factorsList of factors of the coupled matrix decomposition

      List on the form [A, [B_0, B_1, ..., B_i], C], where A represents \(\mathbf{A}\), [B_0, B_1, ..., B_i] represents a list of all \(\mathbf{B}^{(i)}\)-matrices and C represents \(\mathbf{C}\)

  • validate (bool) – If true, then the decomposition is validated before the matrix is constructed (see CoupledMatrixFactorization).

Returns:

List of all \(\mathbf{X}^{(i)}\)-matrices, where the i-th element of the list has shape [B_is[i].shape[0], C.shape[0]], where B_is is a list containing all the \(\mathbf{B}^{(i)}\)-factor matrices.

Return type:

List of ndarray

Examples

We can convert a coupled matrix factorization to a list of matrices

>>> from matcouply.random import random_coupled_matrices
>>> from matcouply.coupled_matrices import cmf_to_matrix
>>> shapes = ((5, 10), (6, 10), (7, 10))
>>> cmf = random_coupled_matrices(shapes, rank=3)
>>> matrices = cmf_to_matrices(cmf)
>>> for matrix in matrices:
...    print(tl.shape(matrix))
(5, 10)
(6, 10)
(7, 10)
matcouply.coupled_matrices.cmf_to_matrix(cmf, matrix_idx, validate=True)[source]

Generate a single matrix from the coupled matrix factorisation.

The decomposition is on the form \((\mathbf{A} [\mathbf{B}^{(0)}, \mathbf{B}^{(1)}, ..., \mathbf{B}^{(I-1)}] \mathbf{C})\) such that the i-th matrix, \(\mathbf{X}^{(i)}\) is given by

\[\mathbf{X}^{(i)} = \mathbf{B}^{(i)} \text{diag}(\mathbf{a}_i) \mathbf{C}^T,\]

where \(\text{diag}(\mathbf{a}_i)\) is the diagonal matrix whose nonzero entries are equal to the \(i\)-th row of the \(I \times R\) factor matrix \(\mathbf{A}\), \(\mathbf{B}^{(i)}\) is a \(J_i \times R\) factor matrix, and \(\mathbf{C}\) is a \(K \times R\) factor matrix.

Parameters:
  • cmf (CoupledMatrixFactorization - (weights, factors)) –

    Coupled matrix factorization represented by weights and factors as described in What are coupled matrix factorizations?.

    • weights1D array of shape (rank,) or None

      weights of the factors

    • factorsList of factors of the coupled matrix decomposition

      List on the form [A, [B_0, B_1, ..., B_i], C], where A represents \(\mathbf{A}\), [B_0, B_1, ..., B_i] represents a list of all \(\mathbf{B}^{(i)}\)-matrices and C represents \(\mathbf{C}\)

  • matrix_idx (int) – Index of the matrix we want to construct, \(i\) in the equations above.

  • validate (bool) – If true, then the decomposition is validated before the matrix is constructed (see CoupledMatrixFactorization).

Returns:

Dense tensor of shape [B_is[matrix_idx].shape[0], C.shape[0]], where B is a list containing all the \(\mathbf{B}^{(i)}\)-factor matrices.

Return type:

ndarray

Examples

An example where we calculate one of the matrices described by a coupled matrix factorization

>>> from matcouply.random import random_coupled_matrices
>>> from matcouply.coupled_matrices import cmf_to_matrix
>>> shapes = ((5, 10), (6, 10), (7, 10))
>>> cmf = random_coupled_matrices(shapes, rank=3)
>>> matrix = cmf_to_matrix(cmf, matrix_idx=1)
>>> tl.shape(matrix)
(6, 10)
matcouply.coupled_matrices.cmf_to_slice(cmf, slice_idx, validate=True)[source]

Alias for cmf_to_matrix.

See also

cmf_to_matrix

matcouply.coupled_matrices.cmf_to_slices(cmf, validate=True)[source]

Alias for cmf_to_matrices.

See also

cmf_to_matrices

matcouply.coupled_matrices.cmf_to_tensor(cmf, validate=True)[source]

Generate the tensor represented by the coupled matrix factorization.

If all \(\mathbf{B}^{(i)}\)-factor matrices have the same number of rows, then this function returnes a tensorized version of cmf_to_matrices. Otherwise, each matrix is padded by zeros to have the same number of rows before forming the tensor.

The decomposition is on the form \((\mathbf{A} [\mathbf{B}^{(0)}, \mathbf{B}^{(1)}, ..., \mathbf{B}^{(I-1)}] \mathbf{C})\) such that the i-th matrix, \(\mathbf{X}^{(i)}\) is given by

\[\mathbf{X}^{(i)} = \mathbf{B}^{(i)} \text{diag}(\mathbf{a}_i) \mathbf{C}^T,\]

where \(\text{diag}(\mathbf{a}_i)\) is the diagonal matrix whose nonzero entries are equal to the \(i\)-th row of the \(I \times R\) factor matrix \(\mathbf{A}\), \(\mathbf{B}^{(i)}\) is a \(J_i \times R\) factor matrix, and \(\mathbf{C}\) is a \(K \times R\) factor matrix.

Parameters:
  • cmf (CoupledMatrixFactorization - (weights, factors)) –

    Coupled matrix factorization represented by weights and factors as described in What are coupled matrix factorizations?.

    • weights1D array of shape (rank,) or None

      weights of the factors

    • factorsList of factors of the coupled matrix decomposition

      List on the form [A, [B_0, B_1, ..., B_i], C], where A represents \(\mathbf{A}\), [B_0, B_1, ..., B_i] represents a list of all \(\mathbf{B}^{(i)}\)-matrices and C represents \(\mathbf{C}\)

  • validate (bool) – If true, then the decomposition is validated before the matrix is constructed (see CoupledMatrixFactorization).

Returns:

Full tensor of shape [A.shape[0], J, C.shape[0]], where J is the maximum number of rows in all the \(\mathbf{B}^{(i)}\)-factor matrices.

Return type:

ndarray

Examples

We can convert a coupled matrix factorization to a tensor. This will be equivalent to stacking the matrices using axis=0.

>>> from matcouply.random import random_coupled_matrices
>>> from matcouply.coupled_matrices import cmf_to_tensor
>>> shapes = ((5, 10), (5, 10), (5, 10), (5, 10))
>>> cmf = random_coupled_matrices(shapes, rank=3)
>>> tensor = cmf_to_tensor(cmf)
>>> tl.shape(tensor)
(4, 5, 10)

We can also convert a coupled matrix factorization that represent matrices with different numbers of columns. Then, the smaller matrices will be padded by zeros so all have the same shape.

>>> shapes = ((5, 10), (5, 10), (5, 10), (3, 10))
>>> cmf = random_coupled_matrices(shapes, rank=3)
>>> tensor = cmf_to_tensor(cmf)
>>> tl.shape(tensor)
(4, 5, 10)

It is only the last matrix which has a different shape. It has two fewer rows than the rest, which means that it has 20 zero-valued elements (\(20 = 2 \times 10\)).

>>> num_zeros = (tensor == 0).sum()
>>> num_zeros
20
matcouply.coupled_matrices.cmf_to_unfolded(cmf, mode, pad=True, validate=True)[source]

Generate the unfolded tensor represented by the coupled matrix factorization.

By default the function is an alias for first constructing a tensor (cmf_to_tensor) and then vectorizing that tensor. Note that if the matrices have a different number of rows, then they will be padded when the tensor is constructed, and thus, there will be zeros in the unfolded tensor too.

If the zero-padding is unwanted, then setting the pad parameter to False (only available for mode=2) will instead construct each matrix and concatenate them.

Parameters:
  • cmf (CoupledMatrixFactorization - (weights, factors)) –

    Coupled matrix factorization represented by weights and factors as described in What are coupled matrix factorizations?.

    • weights1D array of shape (rank,) or None

      weights of the factors

    • factorsList of factors of the coupled matrix decomposition

      List on the form [A, [B_0, B_1, ..., B_i], C], where A represents \(\mathbf{A}\), [B_0, B_1, ..., B_i] represents a list of all \(\mathbf{B}^{(i)}\)-matrices and C represents \(\mathbf{C}\)

  • pad (bool (default=True)) – If true, then the coupled matrix factorization will be converted into a dense tensor, padding the matrices with zeros so all have the same size, and then unfolded. Can only be False if mode=2.

  • validate (bool (default=True)) – If true, then the decomposition is validated before the matrix is constructed (see CoupledMatrixFactorization).

Returns:

Matrix of an appropriate shape (see cmf_to_tensor and tensorly.unfold).

Return type:

ndarray

Raises:

ValueError – If pad=False and mode!=2.

Examples

Here, we show how to unfold a coupled matrix factorization along a given mode.

>>> from matcouply.random import random_coupled_matrices
>>> from matcouply.coupled_matrices import cmf_to_tensor
>>> shapes = ((5, 10), (5, 10), (5, 10), (5, 10))
>>> cmf = random_coupled_matrices(shapes, rank=3)
>>> matrix_0 = cmf_to_unfolded(cmf, mode=0)
>>> tl.shape(matrix_0)
(4, 50)

We can also unfold the tensor using mode=1

>>> matrix_1 = cmf_to_unfolded(cmf, mode=1)
>>> tl.shape(matrix_1)
(5, 40)

And using mode=2

>>> matrix_2 = cmf_to_unfolded(cmf, mode=2)
>>> tl.shape(matrix_2)
(10, 20)

We can also unfold a coupled matrix factorization where the matrices have a varying number of rows

>>> shapes = ((5, 10), (3, 10), (2, 10), (4, 10))
>>> cmf = random_coupled_matrices(shapes, rank=3)
>>> matrix_0 = cmf_to_unfolded(cmf, mode=0)
>>> tl.shape(matrix_0)
(4, 50)

However, as we see, the shape of the unfolded tensor is still (4, 50) despite some matrices being shorter. This is because the matrices are padded by zeros to construct the tensor, which is subsequently unfolded.

This padding happens independently of the unfolding mode.

>>> matrix_1 = cmf_to_unfolded(cmf, mode=1)
>>> tl.shape(matrix_1)
(5, 40)
>>> matrix_2 = cmf_to_unfolded(cmf, mode=2)
>>> tl.shape(matrix_2)
(10, 20)

We can see the padding by counting the number of zeros. The number of zeros should be \(((5 - 5) + (5 - 3) + (5 - 2) + (5 - 4)) \times 10 = 60\).

>>> nonzeros_0 = tl.sum(matrix_0 == 0)
>>> nonzeros_1 = tl.sum(matrix_1 == 0)
>>> nonzeros_2 = tl.sum(matrix_2 == 0)
>>> nonzeros_0, nonzeros_1, nonzeros_2
(60, 60, 60)

If we want to unfold with mode=2 without padding with zeros, then we can use the pad argument

>>> shapes = ((5, 10), (3, 10), (2, 10), (4, 10))
>>> cmf = random_coupled_matrices(shapes, rank=3)
>>> matrix_3 = cmf_to_unfolded(cmf, pad=False, mode=2)
>>> tl.shape(matrix_3)
(10, 14)
matcouply.coupled_matrices.cmf_to_vec(cmf, pad=True, validate=True)[source]

Generate the vectorized tensor represented by the coupled matrix factorization.

By default the function is an alias for first constructing a tensor (cmf_to_tensor) and then vectorizing that tensor. Note that if the matrices have a different number of rows, then they will be padded when the tensor is constructed, and thus, there will be zeros in the vectorized tensor too.

If the zero-padding is unwanted, then setting the pad parameter to False will instead construct and vectorize each matrix described by the decomposition and then concatenate these vectors forming one vector with no padded zero values.

Parameters:
  • cmf (CoupledMatrixFactorization - (weights, factors)) –

    Coupled matrix factorization represented by weights and factors as described in What are coupled matrix factorizations?.

    • weights1D array of shape (rank,) or None

      weights of the factors

    • factorsList of factors of the coupled matrix decomposition

      List on the form [A, [B_0, B_1, ..., B_i], C], where A represents \(\mathbf{A}\), [B_0, B_1, ..., B_i] represents a list of all \(\mathbf{B}^{(i)}\)-matrices and C represents \(\mathbf{C}\)

  • pad (bool (default=True)) – If true then if the matrices described by the decomposition have a different number of rows, then they will be padded by zeros to construct a tensor which are vectorized, and there will be zeros in the vectorized tensor too. If false, the matrices will not be padded.

  • validate (bool (default=True)) – If true, then the decomposition is validated before the matrix is constructed (see CoupledMatrixFactorization).

Returns:

Vector of length A.shape[0] * J * C.shape[0], where J is the maximum number of rows in all the \(\mathbf{B}^{(i)}\)-factor matrices.

Return type:

ndarray

Examples

Here, we show how to vectorize a coupled matrix factorization

>>> from matcouply.random import random_coupled_matrices
>>> from matcouply.coupled_matrices import cmf_to_tensor
>>> shapes = ((5, 10), (5, 10), (5, 10), (5, 10))
>>> cmf = random_coupled_matrices(shapes, rank=3)
>>> vector = cmf_to_vec(cmf)
>>> tl.shape(vector)
(200,)

We can also vectorize a coupled matrix factorization where the matrices have a varying number of rows

>>> shapes = ((5, 10), (3, 10), (2, 10), (4, 10))
>>> cmf = random_coupled_matrices(shapes, rank=3)
>>> vector = cmf_to_vec(cmf)
>>> tl.shape(vector)
(200,)

However, as we see, the length of the vectorized coupled matrix factorization is still 200, despite some matrices being shorter. This is because the matrices are padded by zeros to construct a tensor, which is subsequently unfolded.

We can see the padding by counting the number of zeros. The number of zeros should be \(((5 - 5) + (5 - 3) + (5 - 2) + (5 - 4)) \times 10 = 60\).

>>> tl.sum(vector == 0)
60

If we want to vectorize without padding with zeros, we can use the pad argument

>>> shapes = ((5, 10), (3, 10), (2, 10), (4, 10))
>>> cmf = random_coupled_matrices(shapes, rank=3)
>>> vector = cmf_to_vec(cmf, pad=False)
>>> tl.shape(vector)
(140,)