pyttb.ktensor

Classes and functions for working with Kruskal tensors.

class pyttb.ktensor.ktensor(factor_matrices: Sequence[ndarray] | None = None, weights: ndarray | None = None, copy: bool = True)[source]

Bases: object

KTENSOR Class for Kruskal tensors (decomposed).

Contains the following data members:

weights: numpy.ndarray vector containing the weights of the rank-1 tensors defined by the outer products of the column vectors of the factor_matrices.

factor_matrices: list of numpy.ndarray. The length of the list is equal to the number of dimensions of the tensor. The shape of the ith element of the list is (n_i, r), where n_i is the length dimension i and r is the rank of the tensor (as well as the length of the weights vector).

Instances of pyttb.ktensor can be created using __init__() or one of the following methods:

Examples

For all examples listed below, the following module imports are assumed:

>>> import pyttb as ttb
>>> import numpy as np

Create a pyttb.ktensor.

Created in one of the following ways:
  • With no inputs (or weights and factor_matrices both None), return an empty pyttb.ktensor.

  • Otherwise, return a pyttb.ktensor with weights and factor_matrices as provided.

Parameters:
  • factor_matrices – Factors for ktensor.

  • weights – Tensor weights, defaults to all 1’s.

  • copy – Whether or not to copy the input data or just reference it.

Examples

Create an empty pyttb.ktensor:

>>> K = ttb.ktensor()
>>> print(K)
ktensor of shape () with order F
weights=[]
factor_matrices=[]

Create a pyttb.ktensor from weights and a list of factor matrices:

>>> weights = np.array([1.0, 2.0])
>>> fm0 = np.array([[1.0, 2.0], [3.0, 4.0]])
>>> fm1 = np.array([[5.0, 6.0], [7.0, 8.0]])
>>> K = ttb.ktensor([fm0, fm1], weights)
>>> print(K)
ktensor of shape (2, 2) with order F
weights=[1. 2.]
factor_matrices[0] =
[[1. 2.]
 [3. 4.]]
factor_matrices[1] =
[[5. 6.]
 [7. 8.]]

Create a pyttb.ktensor from a list of factor matrices (without providing weights):

>>> fm0 = np.array([[1.0, 2.0], [3.0, 4.0]])
>>> fm1 = np.array([[5.0, 6.0], [7.0, 8.0]])
>>> factor_matrices = [fm0, fm1]
>>> K = ttb.ktensor([fm0, fm1])
>>> print(K)
ktensor of shape (2, 2) with order F
weights=[1. 1.]
factor_matrices[0] =
[[1. 2.]
 [3. 4.]]
factor_matrices[1] =
[[5. 6.]
 [7. 8.]]
classmethod from_function(function_handle: Callable[[Tuple[int, ...]], ndarray], shape: int | Iterable[int], num_components: int)[source]

Construct a pyttb.ktensor.

Factor matrix entries are set using a function. The weights of the returned pyttb.ktensor will all be equal to 1.

Parameters:
  • function_handle – A function that can accept a shape (i.e., tuple of dimension sizes) and return a numpy.ndarray of that shape. Example functions include numpy.random.random_sample, numpy.zeros, numpy.ones.

  • shape – Shape of the resulting tensor.

  • num_components – Number of components/weights for resulting tensor.

Returns:

Constructed ktensor.

Examples

Create a pyttb.ktensor with entries of the factor matrices taken from a uniform random distribution:

>>> np.random.seed(1)
>>> K = ttb.ktensor.from_function(np.random.random_sample, (2, 3, 4), 2)
>>> print(K)  
ktensor of shape (2, 3, 4) with order F
weights=[1. 1.]
factor_matrices[0] =
[[4.1702...e-01 7.2032...e-01]
 [1.1437...e-04 3.0233...e-01]]
factor_matrices[1] =
[[0.1467... 0.0923...]
 [0.1862... 0.3455...]
 [0.3967... 0.5388...]]
factor_matrices[2] =
[[0.4191... 0.6852...]
 [0.2044... 0.8781...]
 [0.0273... 0.6704...]
 [0.4173...  0.5586...]]

Create a pyttb.ktensor with entries equal to 1:

>>> K = ttb.ktensor.from_function(np.ones, (2, 3, 4), 2)
>>> print(K)
ktensor of shape (2, 3, 4) with order F
weights=[1. 1.]
factor_matrices[0] =
[[1. 1.]
 [1. 1.]]
factor_matrices[1] =
[[1. 1.]
 [1. 1.]
 [1. 1.]]
factor_matrices[2] =
[[1. 1.]
 [1. 1.]
 [1. 1.]
 [1. 1.]]

Create a pyttb.ktensor with entries equal to 0:

>>> K = ttb.ktensor.from_function(np.zeros, (2, 3, 4), 2)
>>> print(K)
ktensor of shape (2, 3, 4) with order F
weights=[1. 1.]
factor_matrices[0] =
[[0. 0.]
 [0. 0.]]
factor_matrices[1] =
[[0. 0.]
 [0. 0.]
 [0. 0.]]
factor_matrices[2] =
[[0. 0.]
 [0. 0.]
 [0. 0.]
 [0. 0.]]
classmethod from_vector(data: ndarray, shape: int | Iterable[int], contains_weights: bool)[source]

Construct a pyttb.ktensor from a vector and shape.

The rank of the pyttb.ktensor is inferred from the shape and length of the vector.

Parameters:
  • data – Vector containing either elements of the factor matrices or elements of the weights and factor matrices. When both the elements of the weights and the factor_matrices are present, the weights come first and the columns of the factor matrices come next.

  • shape – Shape of the resulting ktensor.

  • contains_weights – Flag to specify if data contains weights. If False, all weights are set to 1.

Returns:

Constructed ktensor.

Examples

Create a pyttb.ktensor from a vector containing only elements of the factor matrices:

>>> rank = 2
>>> shape = (2, 3, 4)
>>> data = np.arange(1, rank * sum(shape) + 1).astype(float)
>>> K = ttb.ktensor.from_vector(data[:], shape, False)
>>> print(K)
ktensor of shape (2, 3, 4) with order F
weights=[1. 1.]
factor_matrices[0] =
[[1. 3.]
 [2. 4.]]
factor_matrices[1] =
[[ 5.  8.]
 [ 6.  9.]
 [ 7. 10.]]
factor_matrices[2] =
[[11. 15.]
 [12. 16.]
 [13. 17.]
 [14. 18.]]

Create a pyttb.ktensor from a vector containing elements of both the weights and the factor matrices:

>>> weights = 2 * np.ones(rank).astype(float)
>>> weights_and_data = np.concatenate((weights, data), axis=0)
>>> K = ttb.ktensor.from_vector(weights_and_data[:], shape, True)
>>> print(K)
ktensor of shape (2, 3, 4) with order F
weights=[2. 2.]
factor_matrices[0] =
[[1. 3.]
 [2. 4.]]
factor_matrices[1] =
[[ 5.  8.]
 [ 6.  9.]
 [ 7. 10.]]
factor_matrices[2] =
[[11. 15.]
 [12. 16.]
 [13. 17.]
 [14. 18.]]
property order: Literal['F']

Return the data layout of the underlying storage.

arrange(weight_factor: int | None = None, permutation: Tuple | List | ndarray | None = None)[source]

Arrange the rank-1 components of a pyttb.ktensor in place.

If permutation is passed, the columns of self.factor_matrices are arranged using the provided permutation, so you must make a copy before calling this method if you want to store the original pyttb.ktensor. If weight_factor is passed, then the values in self.weights are absorbed into self.factor_matrices[weight_factor]. If no parameters are passed, then the columns of self.factor_matrices are normalized and then permuted such that the resulting self.weights are sorted by magnitude, greatest to least. Passing both parameters leads to an error.

Parameters:
  • weight_factor – Index of the factor matrix the weights will be absorbed into.

  • permutation – The new order of the components of the pyttb.ktensor into which to permute. The permutation must be of length equal to the number of components of the pyttb.ktensor, self.ncomponents and must be a permutation of [0,…,`self.ncomponents`-1].

Examples

Create the initial pyttb.ktensor:

>>> weights = np.array([1.0, 2.0])
>>> fm0 = np.array([[1.0, 2.0], [3.0, 4.0]])
>>> fm1 = np.array([[5.0, 6.0], [7.0, 8.0]])
>>> K = ttb.ktensor([fm0, fm1], weights)
>>> print(K)
ktensor of shape (2, 2) with order F
weights=[1. 2.]
factor_matrices[0] =
[[1. 2.]
 [3. 4.]]
factor_matrices[1] =
[[5. 6.]
 [7. 8.]]

Arrange the columns of the factor matrices using a permutation:

>>> p = [1, 0]
>>> K.arrange(permutation=p)
>>> print(K)
ktensor of shape (2, 2) with order F
weights=[2. 1.]
factor_matrices[0] =
[[2. 1.]
 [4. 3.]]
factor_matrices[1] =
[[6. 5.]
 [8. 7.]]

Normalize and permute columns such that weights are sorted in decreasing order:

>>> K.arrange()
>>> print(K)  
ktensor of shape (2, 2) with order F
weights=[89.4427... 27.2029...]
factor_matrices[0] =
[[0.4472... 0.3162...]
 [0.8944... 0.9486...]]
factor_matrices[1] =
[[0.6... 0.5812...]
 [0.8... 0.8137...]]

Absorb the weights into the second factor:

>>> K.arrange(weight_factor=1)
>>> print(K)  
ktensor of shape (2, 2) with order F
weights=[1. 1.]
factor_matrices[0] =
[[0.4472... 0.3162...]
 [0.8944... 0.9486...]]
factor_matrices[1] =
[[53.6656... 15.8113...]
 [71.5541... 22.1359...]]
copy() ktensor[source]

Make a deep copy of a pyttb.ktensor.

Returns:

Copy of original ktensor.

Examples

Create a random pyttb.ktensor with weights of 1:

>>> np.random.seed(1)
>>> K = ttb.ktensor.from_function(np.random.random_sample, (2, 3, 4), 2)
>>> print(K)  
ktensor of shape (2, 3, 4) with order F
weights=[1. 1.]
factor_matrices[0] =
[[4.1702...e-01 7.2032...e-01]
 [1.1437...e-04 3.0233...e-01]]
factor_matrices[1] =
[[0.1467... 0.0923...]
 [0.1862... 0.3455...]
 [0.3967... 0.5388...]]
factor_matrices[2] =
[[0.4191... 0.6852...]
 [0.2044... 0.8781...]
 [0.0273... 0.6704...]
 [0.4173... 0.5586...]]

Create a copy of the pyttb.ktensor and change the weights:

>>> K2 = K.copy()
>>> K2.weights = np.array([2.0, 3.0])
>>> print(K2)  
ktensor of shape (2, 3, 4) with order F
weights=[2. 3.]
factor_matrices[0] =
[[4.1702...e-01 7.2032...e-01]
 [1.1437...e-04 3.023...e-01]]
factor_matrices[1] =
[[0.1467... 0.0923...]
 [0.1862... 0.3455...]
 [0.3967... 0.5388...]]
factor_matrices[2] =
[[0.4191... 0.6852...]
 [0.2044... 0.8781...]
 [0.0273... 0.6704...]
 [0.4173... 0.5586...]]

Show that the original pyttb.ktensor is unchanged:

>>> print(K)  
ktensor of shape (2, 3, 4) with order F
weights=[1. 1.]
factor_matrices[0] =
[[4.1702...e-01 7.2032...e-01]
 [1.1437...e-04 3.0233...e-01]]
factor_matrices[1] =
[[0.1467... 0.0923...]
 [0.1862... 0.3455...]
 [0.3967... 0.5388...]]
factor_matrices[2] =
[[0.4191... 0.6852...]
 [0.2044... 0.8781...]
 [0.0273... 0.6704...]
 [0.4173... 0.5586...]]
__deepcopy__(memo)[source]

Return deep copy of ktensor.

double() ndarray[source]

Convert pyttb.ktensor to numpy.ndarray.

Returns:

Array of re-assembled ktensor.

Examples

>>> weights = np.array([1.0, 2.0])
>>> fm0 = np.array([[1.0, 2.0], [3.0, 4.0]])
>>> fm1 = np.array([[5.0, 6.0], [7.0, 8.0]])
>>> factor_matrices = [fm0, fm1]
>>> K = ttb.ktensor(factor_matrices, weights)
>>> K.double()
array([[29., 39.],
       [63., 85.]])
>>> type(K.double())
<class 'numpy.ndarray'>
extract(idx: int | tuple | list | ndarray | None = None) ktensor[source]

Create a new pyttb.ktensor with only the specified components.

Parameters:

idx – Index set of components to extract. It should be the case that idx is a subset of [0,…,`self.ncomponents`]. If this parameter is None or is empty, a copy of the pyttb.ktensor is returned.

Returns:

Subset of original ktensor.

Examples

Create a pyttb.ktensor:

>>> weights = np.array([1.0, 2.0])
>>> fm0 = np.array([[1.0, 2.0], [3.0, 4.0]])
>>> fm1 = np.array([[5.0, 6.0], [7.0, 8.0]])
>>> K = ttb.ktensor([fm0, fm1], weights)
>>> print(K)
ktensor of shape (2, 2) with order F
weights=[1. 2.]
factor_matrices[0] =
[[1. 2.]
 [3. 4.]]
factor_matrices[1] =
[[5. 6.]
 [7. 8.]]

Create a new pyttb.ktensor, extracting only the second component from each factor of the original pyttb.ktensor:

>>> K.extract([1])
ktensor of shape (2, 2) with order F
weights=[2.]
factor_matrices[0] =
[[2.]
 [4.]]
factor_matrices[1] =
[[6.]
 [8.]]
fixsigns(other: ktensor | None = None) ktensor[source]

Change the elements of a pyttb.ktensor in place.

Update so that the largest magnitude entries for each column vector in each factor matrix are positive, provided that the sign on pairs of vectors in a rank-1 component can be flipped.

Parameters:

other – If not None, returns a version of the pyttb.ktensor where some of the signs of the columns of the factor matrices have been flipped to better align with other. In not None, both pyttb.ktensor objects are first normalized (using normalize()).

Returns:

Self for chained operations.

Examples

Create a pyttb.ktensor with negative large magnitude entries:

>>> weights = np.array([1.0, 2.0])
>>> fm0 = np.array([[1.0, 2.0], [3.0, 4.0]])
>>> fm1 = np.array([[5.0, 6.0], [7.0, 8.0]])
>>> K = ttb.ktensor([fm0, fm1], weights)
>>> K.factor_matrices[0][1, 1] = -K.factor_matrices[0][1, 1]
>>> K.factor_matrices[1][1, 1] = -K.factor_matrices[1][1, 1]
>>> print(K)
ktensor of shape (2, 2) with order F
weights=[1. 2.]
factor_matrices[0] =
[[ 1.  2.]
 [ 3. -4.]]
factor_matrices[1] =
[[ 5.  6.]
 [ 7. -8.]]

Fix the signs of the largest magnitude entries:

>>> print(K.fixsigns())
ktensor of shape (2, 2) with order F
weights=[1. 2.]
factor_matrices[0] =
[[ 1. -2.]
 [ 3.  4.]]
factor_matrices[1] =
[[ 5. -6.]
 [ 7.  8.]]

Fix the signs using another pyttb.ktensor:

>>> K = ttb.ktensor([fm0, fm1], weights)
>>> K2 = K.copy()
>>> K2.factor_matrices[0][1, 1] = -K2.factor_matrices[0][1, 1]
>>> K2.factor_matrices[1][1, 1] = -K2.factor_matrices[1][1, 1]
>>> K = K.fixsigns(K2)
>>> print(K)  
ktensor of shape (2, 2) with order F
weights=[27.2029... 89.4427...]
factor_matrices[0] =
[[ 0.3162... -0.4472...]
 [ 0.9486... -0.8944...]]
factor_matrices[1] =
[[ 0.5812... -0.6...]
 [ 0.8137... -0.8...]]
to_tensor() tensor[source]

Convert to tensor.

Same as pyttb.ktensor.full().

full() tensor[source]

Convert a pyttb.ktensor to a pyttb.tensor.

Returns:

Re-assembled dense tensor.

Examples

>>> weights = np.array([1.0, 2.0])
>>> fm0 = np.array([[1.0, 2.0], [3.0, 4.0]])
>>> fm1 = np.array([[5.0, 6.0], [7.0, 8.0]])
>>> K = ttb.ktensor([fm0, fm1], weights)
>>> print(K)
ktensor of shape (2, 2) with order F
weights=[1. 2.]
factor_matrices[0] =
[[1. 2.]
 [3. 4.]]
factor_matrices[1] =
[[5. 6.]
 [7. 8.]]
>>> print(K.full())  
tensor of shape (2, 2) with order F
data[:, :] =
[[29. 39.]
 [63. 85.]]
to_tenmat(rdims: ndarray | None = None, cdims: ndarray | None = None, cdims_cyclic: Literal['fc'] | Literal['bc'] | Literal['t'] | None = None, copy: bool = True) tenmat[source]

Construct a pyttb.tenmat from a pyttb.ktensor.

Parameters:
  • rdims – Mapping of row indices.

  • cdims – Mapping of column indices.

  • cdims_cyclic

    When only rdims is specified maps a single rdim to the rows and

    the remaining dimensions span the columns. _fc_ (forward cyclic) in the order range(rdims,self.ndims()) followed by range(0, rdims). _bc_ (backward cyclic) range(rdims-1, -1, -1) then range(self.ndims(), rdims, -1).

  • copy – Whether to make a copy of provided data or just reference it.

Notes

Forward cyclic is defined by Kiers [1] and backward cyclic is defined by

De Lathauwer, De Moor, and Vandewalle [2].

References

Examples

>>> weights = np.array([1.0, 2.0])
>>> fm0 = np.array([[1.0, 2.0], [3.0, 4.0]])
>>> fm1 = np.array([[5.0, 6.0], [7.0, 8.0]])
>>> K = ttb.ktensor([fm0, fm1], weights)
>>> print(K)
ktensor of shape (2, 2) with order F
weights=[1. 2.]
factor_matrices[0] =
[[1. 2.]
 [3. 4.]]
factor_matrices[1] =
[[5. 6.]
 [7. 8.]]
>>> K.full()  
tensor of shape (2, 2) with order F
data[:, :] =
[[29. 39.]
 [63. 85.]]
>>> K.to_tenmat(np.array([0]))  
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[:, :] =
[[29. 39.]
 [63. 85.]]
innerprod(other: tensor | sptensor | ktensor | ttensor) float[source]

Efficient inner product with a pyttb.ktensor.

Efficiently computes the inner product between two tensors, self

and other. If other is a pyttb.ktensor, the inner product is computed using inner products of the factor matrices. Otherwise, the inner product is computed using the ttv (tensor times vector) of other with all of the columns of self.factor_matrices.

Parameters:

other – Tensor with which to compute the inner product.

Returns:

Innerproduct value.

Examples

>>> K = ttb.ktensor.from_function(np.ones, (2, 3, 4), 2)
>>> print(K.innerprod(K))
96.0
isequal(other: ktensor) bool[source]

Equal comparator for pyttb.ktensor objects.

Parameters:

otherpyttb.ktensor with which to compare.

Examples

>>> K1 = ttb.ktensor.from_function(np.ones, (2, 3, 4), 2)
>>> weights = np.ones((2,))
>>> factor_matrices = [np.ones((2, 2)), np.ones((3, 2)), np.ones((4, 2))]
>>> K2 = ttb.ktensor(factor_matrices, weights)
>>> print(K1.isequal(K2))
True
issymmetric(return_diffs: Literal[False]) bool[source]
issymmetric(return_diffs: Literal[True]) Tuple[bool, ndarray]

Return True if pyttb.ktensor is symmetric for every permutation.

Parameters:

return_diffs – If True, returns the matrix of the norm of the differences between the factor matrices.

Returns:

Answer and optionally matrix of the norm of the differences between the factor matrices

Examples

Create a pyttb.ktensor that is symmetric and test if it is symmetric:

>>> K = ttb.ktensor.from_function(np.ones, (3, 3, 3), 2)
>>> print(K.issymmetric())
True

Create a pyttb.ktensor that is not symmetric and return the differences:

>>> weights = np.array([1., 2.])
>>> fm0 = np.array([[1., 2.], [3., 4.]])
>>> fm1 = np.array([[5., 6.], [7., 8.]])
>>> K2 = ttb.ktensor([fm0, fm1], weights)
>>> issym, diffs = K2.issymmetric(return_diffs=True)
>>> print(diffs)
[[0. 8.]
 [0. 0.]]
mask(W: tensor | sptensor) ndarray[source]

Extract pyttb.ktensor values as specified by W.

W is a

pyttb.tensor or pyttb.sptensor containing only values of zeros (0) and ones (1). The values in the pyttb.ktensor corresponding to the indices for the ones (1) in W will be returned as a column vector.

Parameters:

W – Mask tensor to apply to ktensor.

Returns:

Extracted values in a column vector (array).

Examples

Create a pyttb.ktensor:

>>> weights = np.array([1.0, 2.0])
>>> fm0 = np.array([[1.0, 2.0], [3.0, 4.0]])
>>> fm1 = np.array([[5.0, 6.0], [7.0, 8.0]])
>>> K = ttb.ktensor([fm0, fm1], weights)

Create a mask pyttb.tensor and extract the elements of the pyttb.ktensor using the mask:

>>> W = ttb.tensor(np.array([[0, 1], [1, 0]]))
>>> print(K.mask(W))
[[63.]
 [39.]]
mttkrp(U: ktensor | Sequence[ndarray], n: int | integer) ndarray[source]

Matricized tensor times Khatri-Rao product for pyttb.ktensor.

Efficiently calculates the matrix product of the n-mode matricization of the ktensor with the Khatri-Rao product of all entries in U, a list of factor matrices, except the nth.

Parameters:
  • U – Factor matrices.

  • n – Multiply by all modes except n.

Returns:

Computed result.

Examples

>>> K = ttb.ktensor.from_function(np.ones, (2, 3, 4), 2)
>>> U = [np.ones((2, 2)), np.ones((3, 2)), np.ones(((4, 2)))]
>>> print(K.mttkrp(U, 0))
[[24. 24.]
 [24. 24.]]
property ncomponents: int

Number of columns in each factor matrix for the pyttb.ktensor.

Examples

>>> K = ttb.ktensor.from_function(np.ones, (2, 3, 4), 2)
>>> print(K.ncomponents)
2
property ndims: int

Number of dimensions of the pyttb.ktensor.

Examples

>>> K = ttb.ktensor.from_function(np.ones, (2, 3, 4), 2)
>>> print(K.ndims)
3
norm() float[source]

Compute the norm of a pyttb.ktensor.

Frobenius norm, or square root of the sum of squares of entries.

Examples

>>> K = ttb.ktensor.from_function(np.ones, (2, 3, 4), 2)
>>> K.norm()  
9.79795897...
normalize(weight_factor: int | Literal['all'] | None = None, sort: bool | None = False, normtype: float = 2, mode: int | None = None) ktensor[source]

Normalize the columns of the factor matrices in place.

Optionally absorb the weights into desired normalized factors.

Parameters:
  • weight_factor – Absorb the weights into one or more factors. If “all”, absorb weight equally across all factors. If int, absorb weight into a single dimension (value must be in range(self.ndims)).

  • sort – Sort the columns in descending order of the weights.

  • normtype – Order of the norm (see numpy.linalg.norm() for possible values).

  • mode – Index of factor matrix to normalize. A value of None means normalize all factor matrices.

Returns:

Self for chained operations.

Examples

>>> K = ttb.ktensor.from_function(np.ones, (2, 3, 4), 2)
>>> print(K.normalize())  
ktensor of shape (2, 3, 4) with order F
weights=[4.898... 4.898...]
factor_matrices[0] =
[[0.7071... 0.7071...]
 [0.7071... 0.7071...]]
factor_matrices[1] =
[[0.5773... 0.5773...]
 [0.5773... 0.5773...]
 [0.5773... 0.5773...]]
factor_matrices[2] =
[[0.5 0.5]
 [0.5 0.5]
 [0.5 0.5]
 [0.5 0.5]]
nvecs(n: int, r: int, flipsign: bool = True) ndarray[source]

Compute the leading mode-n vectors of the ktensor.

Computes the r leading eigenvectors of Xn*Xn.T (where Xn is the mode-n matricization/unfolding of self), which provides information about the mode-n fibers. In two-dimensions, the r leading mode-1 vectors are the same as the r left singular vectors and the r leading mode-2 vectors are the same as the r right singular vectors. By default, this method computes the top r eigenvectors of Xn*Xn.T.

Parameters:
  • n – Mode for tensor matricization.

  • r – Number of eigenvectors to compute and use.

  • flipsign – If True, make each column’s largest element positive.

Returns:

Computed eigenvectors.

Examples

Compute single eigenvector for dimension 0:

>>> K = ttb.ktensor.from_function(np.ones, (2, 3, 4), 2)
>>> nvecs1 = K.nvecs(0, 1)
>>> print(nvecs1)  
[[0.70710678...]
 [0.70710678...]]

Compute first 2 leading eigenvectors for dimension 0:

>>> nvecs2 = K.nvecs(0, 2)
>>> print(nvecs2)  
[[ 0.70710678...  0.70710678...]
 [ 0.70710678... -0.70710678...]]
permute(order: int | float | Iterable[int] | Iterable[float] | ndarray) ktensor[source]

Permute pyttb.ktensor dimensions.

Rearranges the dimensions of a pyttb.ktensor so that they are in the order specified by order. The corresponding ktensor has the same components as self but the order of the subscripts needed to access any particular element is rearranged as specified by order.

Parameters:

order – Permutation of [0,…,self.ndimensions].

Returns:

Permuted pyttb.ktensor.

Examples

>>> weights = np.array([1.0, 2.0])
>>> fm0 = np.array([[1.0, 2.0], [3.0, 4.0]])
>>> fm1 = np.array([[5.0, 6.0], [7.0, 8.0]])
>>> factor_matrices = [fm0, fm1]
>>> K = ttb.ktensor(factor_matrices, weights)
>>> print(K)
ktensor of shape (2, 2) with order F
weights=[1. 2.]
factor_matrices[0] =
[[1. 2.]
 [3. 4.]]
factor_matrices[1] =
[[5. 6.]
 [7. 8.]]

Permute the order of the dimension so they are in reverse order:

>>> K1 = K.permute(np.array([1, 0]))
>>> print(K1)
ktensor of shape (2, 2) with order F
weights=[1. 2.]
factor_matrices[0] =
[[5. 6.]
 [7. 8.]]
factor_matrices[1] =
[[1. 2.]
 [3. 4.]]
redistribute(mode: int) ktensor[source]

Distribute weights of a pyttb.ktensor to the specified mode.

The redistribution is performed in place.

Parameters:

mode – Must be value in [0,…self.ndims].

Returns:

Self for chained operations.

Example

Create a pyttb.ktensor:

>>> weights = np.array([1.0, 2.0])
>>> fm0 = np.array([[1.0, 2.0], [3.0, 4.0]])
>>> fm1 = np.array([[5.0, 6.0], [7.0, 8.0]])
>>> factor_matrices = [fm0, fm1]
>>> K = ttb.ktensor(factor_matrices, weights)
>>> print(K)
ktensor of shape (2, 2) with order F
weights=[1. 2.]
factor_matrices[0] =
[[1. 2.]
 [3. 4.]]
factor_matrices[1] =
[[5. 6.]
 [7. 8.]]

Distribute weights of that pyttb.ktensor to mode 0:

>>> K.redistribute(0)
ktensor of shape (2, 2) with order F
weights=[1. 1.]
factor_matrices[0] =
[[1. 4.]
 [3. 8.]]
factor_matrices[1] =
[[5. 6.]
 [7. 8.]]
property shape: Tuple[int, ...]

Shape of a pyttb.ktensor.

Returns the lengths of all dimensions of the pyttb.ktensor.

score(other: ktensor, weight_penalty: bool = True, threshold: float | None = None, greedy: bool = True) Tuple[float, ktensor, bool, ndarray][source]

Check if two pyttb.ktensor with the same shape match.

Matching is defined as follows. If self and other are single- component pyttb.ktensor instances that have been normalized so that their weights are self.weights and other.weights, and their factor matrices are single column vectors containing [a1,a2,…,an] and [b1,b2,…bn], rescpetively, then the score is defined as

score = penalty * (a1.T*b1) * (a2.T*b2) * … * (an.T*bn),

where the penalty is defined by the weights such that

max_weights = max(self.weights, other.weights)

penalty = 1 - abs(self.weights - other.weights) / max_weights.

The score of multi-component pyttb.ktensor instances is a normalized sum of the scores across the best permutation of the components of self. self can have more components than other; any extra components are ignored in terms of the matching score.

Parameters:
  • otherpyttb.ktensor with which to match.

  • weight_penalty – Flag indicating whether or not to consider the weights in the calculations.

  • threshold – Threshold specified in the formula above for determining a match. (defaults to: 0.99**self.ndims)

  • greedy – Flag indicating whether or not to consider all possible matchings (exponentially expensive) or just do a greedy matching.

Returns:

  • Score – Between 0 and 1.

  • Copy of self – Which has been normalized and permuted to best match other.

  • Flag – Indicating a match according to a user-specified threshold.

  • Permutation – (i.e. array of indices of the modes of self) of the components of self that was used to best match other.

Examples

Create two pyttb.ktensor instances and compute the score between them:

>>> factors = [np.ones((3, 3)), np.ones((4, 3)), np.ones((5, 3))]
>>> weights = np.array([2.0, 1.0, 3.0])
>>> K = ttb.ktensor(factors, weights)
>>> factors_2 = [np.ones((3, 2)), np.ones((4, 2)), np.ones((5, 2))]
>>> weights_2 = np.array([2.0, 4.0])
>>> K2 = ttb.ktensor(factors_2, weights_2)
>>> score, Kperm, flag, perm = K.score(K2)
>>> print(score)
0.875
>>> print(perm)
[0 2 1]

Compute score without using weights:

>>> score, Kperm, flag, perm = K.score(K2, weight_penalty=False)
>>> print(score)
1.0
>>> print(perm)
[0 1 2]
symmetrize() ktensor[source]

Symmetrize a pyttb.ktensor in all modes.

Symmetrize a pyttb.ktensor with respect to all modes so that the resulting pyttb.ktensor is symmetric with respect to any permutation of indices.

Returns:

pyttb.ktensor

Examples

Create a pyttb.ktensor:

>>> weights = np.array([1.0, 2.0])
>>> fm0 = np.array([[1.0, 2.0], [3.0, 4.0]])
>>> fm1 = np.array([[5.0, 6.0], [7.0, 8.0]])
>>> factor_matrices = [fm0, fm1]
>>> K = ttb.ktensor(factor_matrices, weights)
>>> print(K)
ktensor of shape (2, 2) with order F
weights=[1. 2.]
factor_matrices[0] =
[[1. 2.]
 [3. 4.]]
factor_matrices[1] =
[[5. 6.]
 [7. 8.]]

Make the factor matrices of the pyttb.ktensor symmetric with respect to any permutation of the factor matrices:

>>> K1 = K.symmetrize()
>>> print(K1)  
ktensor of shape (2, 2) with order F
weights=[1. 1.]
factor_matrices[0] =
[[2.3404... 4.9519...]
 [4.5960... 8.0124...]]
factor_matrices[1] =
[[2.3404... 4.9519...]
 [4.5960... 8.0124...]]
tolist(mode: int | None = None) List[ndarray][source]

Convert pyttb.ktensor to a list of factor matrices.

Eevenly distributes the weights across factors. Optionally absorb the weights into a single mode.

Parameters:

mode – Index of factor matrix to absorb all of the weights.

Returns:

Distributed factor matrices.

Examples

Create a pyttb.ktensor of all ones:

>>> weights = np.array([1.0, 2.0])
>>> fm0 = np.array([[1.0, 2.0], [3.0, 4.0]])
>>> fm1 = np.array([[5.0, 6.0], [7.0, 8.0]])
>>> factor_matrices = [fm0, fm1]
>>> K = ttb.ktensor(factor_matrices, weights)
>>> print(K)
ktensor of shape (2, 2) with order F
weights=[1. 2.]
factor_matrices[0] =
[[1. 2.]
 [3. 4.]]
factor_matrices[1] =
[[5. 6.]
 [7. 8.]]

Spread weights equally to all factors and return list of factor matrices:

>>> fm_list = K.tolist()
>>> for fm in fm_list:
...     print(fm)  
[[1. 2.8284...]
 [3. 5.6568...]]
[[ 5. 8.4852...]
 [ 7. 11.313...]]

Shift weight to single factor matrix and return list of factor matrices:

>>> fm_list = K.tolist(0)
>>> for fm in fm_list:
...     print(fm)  
[[ 8.6023... 40. ]
 [25.8069... 80. ]]
[[0.5812... 0.6...]
 [0.8137... 0.8...]]
tovec(include_weights: bool = True) ndarray[source]

Convert pyttb.ktensor to column vector.

Optionally include or exclude the weights. The output of this method can be consumed by from_vector().

Parameters:

include_weights – Flag to specify whether or not to include weights in output.

Returns:

The length of the column vector is (sum(self.shape)+1)*self.ncomponents. The vector contains the weights (if requested) stacked on top of each of the columns of the factor_matrices in order.

Examples

Create a pyttb.ktensor from a vector:

>>> rank = 2
>>> shape = (2, 3, 4)
>>> data = np.arange(1, rank*sum(shape)+1)
>>> weights = 2 * np.ones(rank)
>>> weights_and_data = np.concatenate((weights, data), axis=0)
>>> K = ttb.ktensor.from_vector(weights_and_data[:], shape, True)
>>> print(K)
ktensor of shape (2, 3, 4) with order F
weights=[2. 2.]
factor_matrices[0] =
[[1. 3.]
 [2. 4.]]
factor_matrices[1] =
[[ 5.  8.]
 [ 6.  9.]
 [ 7. 10.]]
factor_matrices[2] =
[[11. 15.]
 [12. 16.]
 [13. 17.]
 [14. 18.]]

Create a pyttb.ktensor from a vector of data extracted from another pyttb.ktensor:

>>> K2 = ttb.ktensor.from_vector(K.tovec(), shape, True)
>>> print(K2)
ktensor of shape (2, 3, 4) with order F
weights=[2. 2.]
factor_matrices[0] =
[[1. 3.]
 [2. 4.]]
factor_matrices[1] =
[[ 5.  8.]
 [ 6.  9.]
 [ 7. 10.]]
factor_matrices[2] =
[[11. 15.]
 [12. 16.]
 [13. 17.]
 [14. 18.]]
ttv(vector: Sequence[ndarray] | ndarray, dims: int | float | Iterable[int] | Iterable[float] | ndarray | None = None, exclude_dims: int | float | Iterable[int] | Iterable[float] | ndarray | None = None) float | ktensor[source]

Tensor times vector for a pyttb.ktensor.

Computes the product of a pyttb.ktensor with a vector (i.e., np.array). If dims is an integer, it specifies the dimension in the pyttb.ktensor along which the vector is multiplied. If the shape of the vector is = (I,), then the length of dimension dims of the pyttb.ktensor must be I. Note that the number of dimensions of the returned pyttb.ktensor is 1 less than the dimension of the pyttb.ktensor used in the multiplication because dimension dims is removed.

If vector is a list of np.array instances, the pyttb.ktensor is multiplied with each vector in the list. The products are computed sequentially along all dimensions (or modes) of the pyttb.ktensor, and thus the list must contain self.ndims vectors.

When dims is not None, compute the products along the dimensions specified by dims. In this case, the number of products can be less than self.ndims and the order of the sequence does not need to match the order of the dimensions in the pyttb.ktensor. Note that the number of vectors must match the number of dimensions provided, and the length of each vector must match the size of each dimension of the pyttb.ktensor specified in dims.

Parameters:
  • vector – Vector to multiply by.

  • dims

    Dimension(s) along which to multiply.

    Exclusively provide dims or exclude_dims.

  • exclude_dims

    Multiply by all but excluded dimension(s).

    Exclusively provide dims or exclude_dims.

Returns:

float or pyttb.ktensor – The number of dimensions of the returned pyttb.ktensor is n-k, where n = self.ndims and k = number of vectors provided as input. If k == n, a scalar is returned.

Examples

Compute the product of a pyttb.ktensor and a single vector (results in a pyttb.ktensor):

>>> rank = 2
>>> shape = (2, 3, 4)
>>> data = np.arange(1, rank * sum(shape) + 1)
>>> weights = 2 * np.ones(rank)
>>> weights_and_data = np.concatenate((weights, data), axis=0)
>>> K = ttb.ktensor.from_vector(weights_and_data[:], shape, True)
>>> K0 = K.ttv(np.array([1, 1, 1]), dims=1)  # compute along a single dimension
>>> print(K0)
ktensor of shape (2, 4) with order F
weights=[36. 54.]
factor_matrices[0] =
[[1. 3.]
 [2. 4.]]
factor_matrices[1] =
[[11. 15.]
 [12. 16.]
 [13. 17.]
 [14. 18.]]

Compute the product of a pyttb.ktensor and a vector for each dimension (results in a float):

>>> vec2 = np.array([1, 1])
>>> vec3 = np.array([1, 1, 1])
>>> vec4 = np.array([1, 1, 1, 1])
>>> K1 = K.ttv([vec2, vec3, vec4])
>>> print(K1)
30348.0

Compute the product of a pyttb.ktensor and multiple vectors out of order (results in a pyttb.ktensor):

>>> K2 = K.ttv([vec4, vec3], np.array([2, 1]))
>>> print(K2)
ktensor of shape (2,) with order F
weights=[1800. 3564.]
factor_matrices[0] =
[[1. 3.]
 [2. 4.]]
update(modes: int | float | Iterable[int] | Iterable[float] | ndarray, data: ndarray) ktensor[source]

Update a pyttb.ktensor in the specific dimensions.

Updates with the values in data (in vector or matrix form). The value of modes must be a value in [-1,…,self.ndims]. If the Further, the number of elements in data must equal self.shape[modes] * self.ncomponents. The update is performed in place.

Parameters:
  • modes – List of dimensions to update; values must be in ascending order. If the first element of the list is -1, then update the weights. All other integer values values must be sorted and in [0,…,self.ndims].

  • data – Data values to use in the update.

Returns:

Self for chained operations.

Examples

Create a pyttb.ktensor of all ones:

>>> K = ttb.ktensor.from_function(np.ones, (2, 3, 4), 2)

Create vectors for updating various factor matrices of the pyttb.ktensor:

>>> vec0 = 2 * np.ones(K.shape[0] * K.ncomponents)
>>> vec1 = 3 * np.ones(K.shape[1] * K.ncomponents)
>>> vec2 = 4 * np.ones(K.shape[2] * K.ncomponents)

Update a single factor matrix:

>>> K1 = K.copy()
>>> K1 = K1.update(0, vec0)
>>> print(K1)
ktensor of shape (2, 3, 4) with order F
weights=[1. 1.]
factor_matrices[0] =
[[2. 2.]
 [2. 2.]]
factor_matrices[1] =
[[1. 1.]
 [1. 1.]
 [1. 1.]]
factor_matrices[2] =
[[1. 1.]
 [1. 1.]
 [1. 1.]
 [1. 1.]]

Update all factor matrices:

>>> K2 = K.copy()
>>> vec_all = np.concatenate((vec0, vec1, vec2))
>>> K2 = K2.update([0, 1, 2], vec_all)
>>> print(K2)
ktensor of shape (2, 3, 4) with order F
weights=[1. 1.]
factor_matrices[0] =
[[2. 2.]
 [2. 2.]]
factor_matrices[1] =
[[3. 3.]
 [3. 3.]
 [3. 3.]]
factor_matrices[2] =
[[4. 4.]
 [4. 4.]
 [4. 4.]
 [4. 4.]]

Update some but not all factor matrices:

>>> K3 = K.copy()
>>> vec_some = np.concatenate((vec0, vec2))
>>> K3 = K3.update([0, 2], vec_some)
>>> print(K3)
ktensor of shape (2, 3, 4) with order F
weights=[1. 1.]
factor_matrices[0] =
[[2. 2.]
 [2. 2.]]
factor_matrices[1] =
[[1. 1.]
 [1. 1.]
 [1. 1.]]
factor_matrices[2] =
[[4. 4.]
 [4. 4.]
 [4. 4.]
 [4. 4.]]
viz(plots: tuple | list | None = None, show_figure: bool = True, normalize: bool = True, norm: int | float = 2, rel_weights: bool = True, rel_heights: tuple | list | None = None, rel_widths: tuple | list | None = None, horz_space: float | None = None, vert_space: float | None = None, left_space: float | None = None, right_space: float | None = None, top_space: float | None = None, bot_space: float | None = None, mode_titles: tuple | list | None = None, title=None) Tuple[Figure, Axes][source]

Visualize factors for pyttb.ktensor.

Parameters:
  • plots – List of functions (one per mode) which visualize the respective vectors of a factor. Function for mode i must have signature f(v_i,ax) where v_i is numpy.ndarray vector of dimension n_i and ax is a :class:`matplotlib.axes.Axes’ on which to plot.

  • show_figure – Boolean determining if the resulting figure should be shown.

  • normalize – Boolean controlling whether to normalize factors and generate a compensating weight, then sort components by weight.

  • norm – Norm used to normalize factors; 1 for 1-norm, 2 for 2-norm, etc.

  • rel_weights – Boolean determining whether weights should be made relative by dividing by largest weight.

  • rel_widths – List of numbers (one per mode) specifying relative widths of each plot column.

  • rel_heights – List of numbers (one per component) specifying relative height of each plot row.

  • horz/vert_space – Number determining amount of space between subplots (horizontally/vertically) as a fraction of the average axis width/height.

  • left/right/top/bot_space – Extent of subplots as fraction of figure width or height.

  • mode_titles – List of strings used as titles for each column (mode).

  • title – String containing overall figure title.

Returns:

  • fig – :class:`matplotlib.figure.Figure’ handle for the generated figure

  • axs – :class:`matplotlib.axes.Axes’ for the generated figure

Examples

Set up a pyttb.ktensor to plot:

>>> np.random.seed(1)
>>> K = ttb.ktensor.from_function(np.random.random_sample, (2, 3, 10), 2)

Use plot K using default behavior K.viz()

>>> fig, axs = K.viz(show_figure=False)  
>>> plt.close(fig)

Define a more realistic plot functions with x labels, control relative widths of each plot, and set mode titles.

>>> def mode_1_plot(v, ax):
...     ax.bar([1, 2], v, width=0.2)
...     ax.set_xticks([1, 2], labels=["neutron", "electron"], rotation=45)
>>> def mode_2_plot(v, ax):
...     ax.plot(np.arange(v.shape[0]), v)
...     ax.set_xlabel("$v$, [m/s]")
>>> def mode_3_plot(v, ax):
...     ax.semilogx(np.logspace(-2, 2, v.shape[0]), v)
...     ax.set_xlabel("$E$, [kJ]")
>>> plots = [mode_1_plot, mode_2_plot, mode_3_plot]
>>> fig, axs = K.viz(
...     show_figure=False,
...     plots=plots,
...     rel_widths=[1, 2, 3],
...     horz_space=0.4,
...     left_space=0.2,
...     bot_space=0.2,
...     mode_titles=["Particle", "Velocity", "Energy"],
... )  
>>> plt.close(fig)
__add__(other)[source]

Binary addition for pyttb.ktensor.

Parameters:

other (pyttb.ktensor, required) – pyttb.ktensor to add to self.

Returns:

pyttb.ktensor

__neg__()[source]

Unary minus (negative) for pyttb.ktensor instances.

Returns:

pyttb.ktensor

__pos__()[source]

Unary plus (positive) for pyttb.ktensor instances.

Returns:

pyttb.ktensor

__sub__(other)[source]

Binary subtraction for pyttb.ktensor.

Parameters:

other (pyttb.ktensor)

Returns:

pyttb.ktensor

__mul__(other)[source]

Elementwise (including scalar) multiplication for pyttb.ktensor.

Parameters:

other (pyttb.tensor, pyttb.sptensor, float, int)

Returns:

pyttb.ktensor

__rmul__(other)[source]

Elementwise (including scalar) multiplication for pyttb.ktensor.

Parameters:

other (pyttb.tensor, pyttb.sptensor, float, int)

Returns:

pyttb.ktensor

__repr__()[source]

Return string representation of a pyttb.ktensor.

Returns:

str

__str__()

Return string representation of a pyttb.ktensor.

Returns:

str