diff --git a/pyttb/ktensor.py b/pyttb/ktensor.py index 72c6b0bc..622807fa 100644 --- a/pyttb/ktensor.py +++ b/pyttb/ktensor.py @@ -18,67 +18,79 @@ class ktensor(object): """ - Class for Kruskal tensors (decomposed). + KTENSOR Class for Kruskal tensors (decomposed). Contains the following data members: - * ``weights``: :class:`numpy.ndarray` vector containing the weights of the rank-1 tensors defined by the outer \ - products of the column vectors of the factor_matrices. + ``weights``: :class:`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``: :class:`list` of :class:`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). + ``factor_matrices``: :class:`list` of :class:`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). - Although the constructor, `__init__()`, can be used to create an empty :class:`pyttb.ktensor`, there - are several class methods that can be used to create an instance of this class: + Although the constructor `__init__()` can be used to create an empty + :class:`pyttb.ktensor`, there are several class methods that can be used + to create an instance of this class: * :meth:`from_data` * :meth:`from_tensor_type` * :meth:`from_factor_matrices` * :meth:`from_function` * :meth:`from_vector` + + Examples + -------- + For all examples listed below, the following module imports are assumed: + + >>> import pyttb as ttb + >>> import numpy as np """ __slots__ = ("weights", "factor_matrices") def __init__(self): """ - Constructor for :class:`pyttb.ktensor` + Construct an empty :class:`pyttb.ktensor` - The constructor takes no arguments and returns an empty :class:`pyttb.ktensor`. + The constructor takes no arguments and returns an empty + :class:`pyttb.ktensor`. """ # Empty constructor - self.weights = np.array([]) # renamed from lambda to weights - self.factor_matrices = ( - [] - ) # changed from cell array to list; changed name from 'u' to 'factor_matrices' + self.weights = np.array([]) + self.factor_matrices = [] @classmethod def from_data(cls, weights, *factor_matrices): """ Construct a :class:`pyttb.ktensor` from weights and factor matrices. - The length of the list or the number of arguments specified by `factor_matrices` must equal - the length of `weights` + The length of the list or the number of arguments specified by + `factor_matrices` must equal the length of `weights`. See + :class:`pyttb.ktensor` for parameter descriptions. Parameters ---------- - weights: :class:`numpy.ndarray` - factor_matrices: :class:`list` of :class:`numpy.ndarray` or variable number of :class:`numpy.ndarray` + weights: :class:`numpy.ndarray`, required + factor_matrices: :class:`list` of :class:`numpy.ndarray` or variable number of :class:`numpy.ndarray`, required Returns ------- :class:`pyttb.ktensor` - Example - ------- - Create a `ktensor` from weights and a list of factor matrices: + Examples + -------- + Create a :class:`pyttb.ktensor` from weights and a list of factor + matrices: >>> weights = np.array([1., 2.]) >>> fm0 = np.array([[1., 2.], [3., 4.]]) >>> fm1 = np.array([[5., 6.], [7., 8.]]) - >>> K_from_list = ttb.ktensor.from_data(weights, [fm0, fm1]) - >>> print(K_from_list) + >>> K = ttb.ktensor.from_data(weights, [fm0, fm1]) + >>> print(K) ktensor of shape 2 x 2 weights=[1. 2.] factor_matrices[0] = @@ -88,10 +100,11 @@ def from_data(cls, weights, *factor_matrices): [[5. 6.] [7. 8.]] - Create a `ktensor` from weights and a factor matrices passed as arguments: + Create a :class:`pyttb.ktensor` from weights and factor matrices passed as + arguments: - >>> K_from_args = ttb.ktensor.from_data(weights, fm0, fm1) - >>> print(K_from_args) + >>> K = ttb.ktensor.from_data(weights, fm0, fm1) + >>> print(K) ktensor of shape 2 x 2 weights=[1. 2.] factor_matrices[0] = @@ -146,20 +159,21 @@ def from_data(cls, weights, *factor_matrices): @classmethod def from_tensor_type(cls, source) -> ktensor: """ - Construct a ktensor from another ktensor. A deep copy of the data from the input ktensor - is used for the new ktensor. + Construct a :class:`pyttb.ktensor` from another + :class:`pyttb.ktensor`. A deep copy of the data from the input + :class:`pyttb.ktensor` is used for the new :class:`pyttb.ktensor`. Parameters ---------- - source: :class:`pyttb.ktensor` + source: :class:`pyttb.ktensor`, required Returns ------- :class:`pyttb.ktensor` - Example - ------- - Create an instance of a `ktensor`: + Examples + -------- + Create an instance of a :class:`pyttb.ktensor`: >>> fm0 = np.array([[1., 2.], [3., 4.]]) >>> fm1 = np.array([[5., 6.], [7., 8.]]) @@ -175,10 +189,11 @@ def from_tensor_type(cls, source) -> ktensor: [[5. 6.] [7. 8.]] - Create another instance of a `ktensor` from the original one above: + Create another instance of a :class:`pyttb.ktensor` from the original + one above: - >>> K_copy = ttb.ktensor.from_tensor_type(K_source) - >>> print(K_copy) + >>> K = ttb.ktensor.from_tensor_type(K_source) + >>> print(K) ktensor of shape 2 x 2 weights=[1. 1.] factor_matrices[0] = @@ -188,7 +203,7 @@ def from_tensor_type(cls, source) -> ktensor: [[5. 6.] [7. 8.]] - See also :func:`~pyttb.ktensor.copy` + See also :func:`pyttb.ktensor.copy` """ if isinstance(source, ktensor): return cls().from_data( @@ -207,26 +222,29 @@ def from_tensor_type(cls, source) -> ktensor: @classmethod def from_factor_matrices(cls, *factor_matrices): """ - Construct a ktensor from factor matrices. The weights of the returned ktensor will all be equal to 1. + Construct a :class:`pyttb.ktensor` from factor matrices. The weights + of the returned :class:`pyttb.ktensor` will all be equal to 1. Parameters ---------- - factor_matrices: :class:`list` of :class:`numpy.ndarray` or variable number of :class:`numpy.ndarray` - The number of columns of each of the factor matrices must be the same. + factor_matrices: :class:`list` of :class:`numpy.ndarray` or variable number of :class:`numpy.ndarray`, required + The number of columns of each of the factor matrices must be the + same. Returns ------- :class:`pyttb.ktensor` - Example - ------- - Create a `ktensor` from a list of factor matrices: + Examples + -------- + Create a :class:`pyttb.ktensor` from a :class:`list` of factor + matrices: >>> fm0 = np.array([[1., 2.], [3., 4.]]) >>> fm1 = np.array([[5., 6.], [7., 8.]]) >>> factor_matrices = [fm0, fm1] - >>> K_from_list = ttb.ktensor.from_factor_matrices(factor_matrices) - >>> print(K_from_list) + >>> K = ttb.ktensor.from_factor_matrices(factor_matrices) + >>> print(K) ktensor of shape 2 x 2 weights=[1. 1.] factor_matrices[0] = @@ -236,10 +254,11 @@ def from_factor_matrices(cls, *factor_matrices): [[5. 6.] [7. 8.]] - Create a `ktensor` from a sequence of factor matrices passed as arguments: + Create a :class:`pyttb.ktensor` from factor matrices passed as + arguments: - >>> K_from_args = ttb.ktensor.from_factor_matrices(fm0, fm1) - >>> print(K_from_args) + >>> K = ttb.ktensor.from_factor_matrices(fm0, fm1) + >>> print(K) ktensor of shape 2 x 2 weights=[1. 1.] factor_matrices[0] = @@ -265,27 +284,32 @@ def from_factor_matrices(cls, *factor_matrices): @classmethod def from_function(cls, fun, shape, num_components): """ - Construct a ktensor whose factor matrix entries are set using a function. The weights of the returned ktensor will all be equal to 1. + Construct a :class:`pyttb.ktensor` whose factor matrix entries are + set using a function. The weights of the returned + :class:`pyttb.ktensor` will all be equal to 1. Parameters ---------- - fun: function - A function that can accept a shape (i.e., :class:`tuple` of dimension sizes) and return a :class:`numpy.ndarray` - of that shape. Example functions include `numpy.random.random_sample`, `numpy,zeros`, `numpy.ones`. - shape: :class:`tuple` - num_components: int + fun: function, required + A function that can accept a shape (i.e., :class:`tuple` of + dimension sizes) and return a :class:`numpy.ndarray` of that shape. + Example functions include `numpy.random.random_sample`, + `numpy,zeros`, `numpy.ones`. + shape: :class:`tuple`, required + num_components: int, required Returns ------- :class:`pyttb.ktensor` - Example - ------- - Create a `ktensor` with entries of the factor matrices taken from a uniform random distribution: + Examples + -------- + Create a :class:`pyttb.ktensor` with entries of the factor matrices + taken from a uniform random distribution: >>> np.random.seed(1) - >>> K_random = ttb.ktensor.from_function(np.random.random_sample, (2, 3, 4), 2) - >>> print(K_random) # doctest: +ELLIPSIS + >>> K = ttb.ktensor.from_function(np.random.random_sample, (2, 3, 4), 2) + >>> print(K) # doctest: +ELLIPSIS ktensor of shape 2 x 3 x 4 weights=[1. 1.] factor_matrices[0] = @@ -301,10 +325,10 @@ def from_function(cls, fun, shape, num_components): [0.0273... 0.6704...] [0.4173... 0.5586...]] - Create a `ktensor` with entries equal to 1: + Create a :class:`pyttb.ktensor` with entries equal to 1: - >>> K_ones = ttb.ktensor.from_function(np.ones, (2, 3, 4), 2) - >>> print(K_ones) + >>> K = ttb.ktensor.from_function(np.ones, (2, 3, 4), 2) + >>> print(K) ktensor of shape 2 x 3 x 4 weights=[1. 1.] factor_matrices[0] = @@ -320,10 +344,10 @@ def from_function(cls, fun, shape, num_components): [1. 1.] [1. 1.]] - Create a `ktensor` with entries equal to 0: + Create a :class:`pyttb.ktensor` with entries equal to 0: - >>> K_zeros = ttb.ktensor.from_function(np.zeros, (2, 3, 4), 2) - >>> print(K_zeros) + >>> K = ttb.ktensor.from_function(np.zeros, (2, 3, 4), 2) + >>> print(K) ktensor of shape 2 x 3 x 4 weights=[1. 1.] factor_matrices[0] = @@ -355,33 +379,40 @@ def from_function(cls, fun, shape, num_components): @classmethod def from_vector(cls, data, shape, contains_weights): """ - Construct a ktensor from a vector. The rank of the `ktensor` is inferred from the shape and size of the vector. + Construct a :class:`pyttb.ktensor` from a vector (given as a + :class:`numpy.ndarray`) and shape (given as a + :class:`numpy.ndarray`). The rank of the :class:`pyttb.ktensor` + is inferred from the shape and length of the vector. Parameters ---------- - data: :class:`numpy.ndarray` - Vector containing either elements of the factor matrices (when contains_weights==False) or - elements of the weights and factor matrices (when contains_weights==True). 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: :class:`numpy.ndarray` - Vector containing the shape of the tensor (i.e., sizes of the dimensions). - contains_weights: bool - Flag to specify whether or not ``data`` contains weights. If False, all weights are set to 1. + data: :class:`numpy.ndarray`, required + Vector containing either elements of the factor matrices (when + `contains_weights`==False) or elements of the weights and factor + matrices (when `contains_weights`==True). 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: :class:`numpy.ndarray`, required + Vector containing the shape of the tensor (i.e., lengths of the + dimensions). + contains_weights: bool, required + Flag to specify whether or not `data` contains weights. If False, + all weights are set to 1. Returns ------- :class:`pyttb.ktensor` - Example - ------- - Create a `ktensor` from a vector containing only elements of the factor matrices: + Examples + -------- + Create a :class:`pyttb.ktensor` from a vector containing only + elements of the factor matrices: >>> rank = 2 >>> shape = np.array([2, 3, 4]) >>> data = np.arange(1, rank*sum(shape)+1).astype(float) - >>> K_without_weights = ttb.ktensor.from_vector(data[:], shape, False) - >>> print(K_without_weights) + >>> K = ttb.ktensor.from_vector(data[:], shape, False) + >>> print(K) ktensor of shape 2 x 3 x 4 weights=[1. 1.] factor_matrices[0] = @@ -397,12 +428,13 @@ def from_vector(cls, data, shape, contains_weights): [13. 17.] [14. 18.]] - Create a `ktensor` from a vector containing elements of both the weights and the factor matrices: + Create a :class:`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_with_weights = ttb.ktensor.from_vector(weights_and_data[:], shape, True) - >>> print(K_with_weights) + >>> K = ttb.ktensor.from_vector(weights_and_data[:], shape, True) + >>> print(K) ktensor of shape 2 x 3 x 4 weights=[2. 2.] factor_matrices[0] = @@ -463,21 +495,29 @@ def from_vector(cls, data, shape, contains_weights): def arrange(self, weight_factor=None, permutation=None): """ - Arrange the rank-1 components of a `ktensor`. The columns are permuted in place, so you must make a copy - before calling this method if you want to store the original. + Arrange the rank-1 components of a :class:`pyttb.ktensor`. The + columns are permuted in place, so you must make a copy before calling + this method if you want to store the original :class:`pyttb.ktensor`. + One of the parameters, either `weight_factor` or `permutation` + must be passed, and passing both parameters leads to an error. Parameters ---------- - weight_factor: - permutation: + weight_factor: int, optional + The index of the factor that the weights will be absorbed into + permutation: :class:`tuple`, :class:`list`, :class:`numpy.ndarray`, optional + The new order of the components of the :class:`pyttb.ktensor` + into which to permute. The permutation must be of length equal to + the number of components of the :class:`pyttb.ktensor`, N, and + must be a permutation of 1 to N. Returns ------- :class:`pyttb.ktensor` - Example - ------- - Create the initial `ktensor`: + Examples + -------- + Create the initial :class:`pyttb.ktensor`: >>> weights = np.array([1., 2.]) >>> fm0 = np.array([[1., 2.], [3., 4.]]) @@ -549,15 +589,15 @@ def arrange(self, weight_factor=None, permutation=None): def copy(self): """ - Make a deep copy of a `ktensor`. + Make a deep copy of a :class:`pyttb.ktensor`. Returns ------- :class:`pyttb.ktensor` - Example - ------- - Create a random `ktensor` with weights of 1: + Examples + -------- + Create a random :class:`pyttb.ktensor` with weights of 1: >>> np.random.seed(1) >>> K = ttb.ktensor.from_function(np.random.random_sample, (2, 3, 4), 2) @@ -577,11 +617,11 @@ def copy(self): [0.0273... 0.6704...] [0.4173... 0.5586...]] - Create a copy of the `ktensor` and change the weights: + Create a copy of the :class:`pyttb.ktensor` and change the weights: - >>> K1 = K.copy() - >>> K1.weights = np.array([2., 3.]) - >>> print(K1) # doctest: +ELLIPSIS + >>> K2 = K.copy() + >>> K2.weights = np.array([2., 3.]) + >>> print(K2) # doctest: +ELLIPSIS ktensor of shape 2 x 3 x 4 weights=[2. 3.] factor_matrices[0] = @@ -592,6 +632,24 @@ def copy(self): [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 :class:`pyttb.ktensor` is unchanged: + + >>> print(K) # doctest: +ELLIPSIS + ktensor of shape 2 x 3 x 4 + 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...] @@ -601,14 +659,14 @@ def copy(self): def double(self): """ - Convert `ktensor` to numpy array. + Convert :class:`pyttb.ktensor` to :class:`numpy.ndarray`. Returns ------- :class:`numpy.ndarray` - Example - ------- + Examples + -------- >>> weights = np.array([1., 2.]) >>> fm0 = np.array([[1., 2.], [3., 4.]]) >>> fm1 = np.array([[5., 6.], [7., 8.]]) @@ -617,26 +675,26 @@ def double(self): >>> K.double() array([[29., 39.], [63., 85.]]) + >>> type(K.double()) + """ return self.full().double() def end(self, k=None): """ - Last index of indexing expression for `ktensor`. + Last index of indexing expression for :class:`pyttb.ktensor`. Parameters ---------- - k: int - dimension for subscripted indexing - n: int - dimensions to index + k: int, optional + dimension for subscripted indexing Returns ------- int: index - Example - ------- + Examples + -------- >>> K = ttb.ktensor.from_function(np.ones, (2, 3, 4), 2) >>> print(K.end(2)) 3 @@ -649,21 +707,24 @@ def end(self, k=None): def extract(self, idx=None): """ - Creates a new `ktensor` with only the specified components. + Creates a new :class:`pyttb.ktensor` with only the specified + components. Parameters ---------- - idx: int, tuple, list, :class:`numpy.ndarray` - 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 ktensor is returned. + idx: int, :class:`tuple`, :class:`list`, :class:`numpy.ndarray`, optional + 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 + :class:`pyttb.ktensor` is returned. Returns ------- - :class:`ktensor` + :class:`pyttb.ktensor` - Example - ------- - Create a `ktensor`: + Examples + -------- + Create a :class:`pyttb.ktensor`: >>> weights = np.array([1., 2.]) >>> fm0 = np.array([[1., 2.], [3., 4.]]) @@ -679,7 +740,8 @@ def extract(self, idx=None): [[5. 6.] [7. 8.]] - Create a new `ktensor`, extracting only the second component from each factor of the original `ktensor`: + Create a new :class:`pyttb.ktensor`, extracting only the second + component from each factor of the original :class:`pyttb.ktensor`: >>> K.extract([1]) ktensor of shape 2 x 2 @@ -728,25 +790,29 @@ def extract(self, idx=None): def fixsigns(self, other=None): """ - Change the elements of a `ktensor` in place 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. + Change the elements of a :class:`pyttb.ktensor` in place 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: :class:`pyttb.ktensor` - If not None, returns a version of the `ktensor` where some of the signs of - the columns of the factor matrices have been flipped to better align - with `other`. + other: :class:`pyttb.ktensor`, optional + If not None, returns a version of the :class:`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 + :class:`pyttb.ktensor` objects are first normalized (using + :func:`~pyttb.ktensor.normalize`). Returns ------- :class:`pyttb.ktensor` - The changes are made in place and a reference to the updated tensor is returned + The changes are made in place and a reference to the updated + tensor is returned - Example - ------- - Create a `ktensor` with negative large magnitude entries: + Examples + -------- + Create a :class:`pyttb.ktensor` with negative large magnitude entries: >>> weights = np.array([1., 2.]) >>> fm0 = np.array([[1., 2.], [3., 4.]]) @@ -776,12 +842,12 @@ def fixsigns(self, other=None): [[ 5. -6.] [ 7. 8.]] - Fix the signs using another `ktensor`: + Fix the signs using another :class:`pyttb.ktensor`: >>> K = ttb.ktensor.from_data(weights, [fm0, fm1]) >>> 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] + >>> 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) # doctest: +ELLIPSIS ktensor of shape 2 x 2 @@ -870,18 +936,27 @@ def fixsigns(self, other=None): def full(self): """ - Convert a `ktensor` to a (dense) `tensor`. + Convert a :class:`pyttb.ktensor` to a :class:`pyttb.tensor`. Returns ------- :class:`pyttb.tensor` - Example - ------- + Examples + -------- >>> weights = np.array([1., 2.]) >>> fm0 = np.array([[1., 2.], [3., 4.]]) >>> fm1 = np.array([[5., 6.], [7., 8.]]) >>> K = ttb.ktensor.from_data(weights, [fm0, fm1]) + >>> print(K) + ktensor of shape 2 x 2 + weights=[1. 2.] + factor_matrices[0] = + [[1. 2.] + [3. 4.]] + factor_matrices[1] = + [[5. 6.] + [7. 8.]] >>> print(K.full()) # doctest: +NORMALIZE_WHITESPACE tensor of shape 2 x 2 data[:, :] = @@ -894,22 +969,26 @@ def full(self): def innerprod(self, other): """ - Efficient inner product with a `ktensor`. + Efficient inner product with a :class:`pyttb.ktensor`. - Efficiently computes the inner product between two tensors, self and other. If other is a ktensor, - the inner product is computed using inner products of the factor matrices. Otherwise, the inner product - is computed using ttv with all of the columns of self's factor matrices. + Efficiently computes the inner product between two tensors, `self` + and `other`. If other is a :class:`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: compute inner product of ktensor with other + other: :class:`pyttb.ktensor`, :class:`pyttb.sptensor`, :class:`pyttb.tensor`, or :class:`pyttb.ttensor`, required + Tensor with which to compute the inner product. Returns ------- :float - Example - ------- + Examples + -------- >>> K = ttb.ktensor.from_function(np.ones, (2,3,4), 2) >>> print(K.innerprod(K)) 96.0 @@ -923,7 +1002,7 @@ def innerprod(self, other): M = M * (self.factor_matrices[i].T @ other.factor_matrices[i]) return np.sum(np.sum(M)) - if isinstance(other, (ttb.tensor, ttb.sptensor, ttb.ttensor)): + if isinstance(other, (ttb.sptensor, ttb.tensor, ttb.ttensor)): res = 0.0 for r in range(self.ncomponents): vecs = [] @@ -934,18 +1013,19 @@ def innerprod(self, other): def isequal(self, other): """ - Equal comparator for `ktensors`. + Equal comparator for :class:`pyttb.ktensor` objects. Parameters ---------- - other: compare equality of ktensor to other + other: :class:`pyttb.ktensor`, required + :class:`pyttb.ktensor` with which to compare. Returns ------- :bool - Example - ------- + Examples + -------- >>> K1 = ttb.ktensor.from_function(np.ones, (2,3,4), 2) >>> weights = np.ones((2, 1)) >>> factor_matrices = [np.ones((2, 2)), np.ones((3, 2)), np.ones((4, 2))] @@ -966,34 +1046,38 @@ def isequal(self, other): def issymmetric(self, return_diffs=False): """ - Returns true if the `ktensor` is exactly symmetric for every permutation. + Returns True if the :class:`pyttb.ktensor` is exactly symmetric for + every permutation. Parameters ---------- - return_diffs: bool - If True, returns the matrix of the norm of the differences between the factor matrices + return_diffs: bool, optional + If True, returns the matrix of the norm of the differences between + the factor matrices. Returns ------- :bool - :class:`numpy.ndarray` - Matrix of the norm of the differences between the factor matrices (optional) + :class:`numpy.ndarray`, optional + Matrix of the norm of the differences between the factor matrices - Example - ------- - Create a 'ktensor` that is symmetric and test if it is symmetric: + Examples + -------- + Create a :class:`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 `ktensor` that is not symmetric and return the differences: + Create a :class:`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.]]) - >>> K1 = ttb.ktensor.from_data(weights, [fm0, fm1]) - >>> issym, diffs = K1.issymmetric(return_diffs=True) + >>> K2 = ttb.ktensor.from_data(weights, [fm0, fm1]) + >>> issym, diffs = K2.issymmetric(return_diffs=True) >>> print(diffs) [[0. 8.] [0. 0.]] @@ -1018,26 +1102,31 @@ def issymmetric(self, return_diffs=False): def mask(self, W): """ - Extract `ktensor` values as specified by a mask `tensor` + Extract :class:`pyttb.ktensor` values as specified by `W`, a + :class:`pyttb.tensor` or :class:`pyttb.sptensor` containing + only values of zeros (0) and ones (1). The values in the + :class:`pyttb.ktensor` corresponding to the indices for the + ones (1) in `W` will be returned as a column vector. Parameters ---------- - W: :class:`pyttb.sptensor` + W: :class:`pyttb.tensor` or :class:`pyttb.sptensor`, required Returns ------- - :class:`Numpy.ndarray` + :class:`numpy.ndarray` - Example - ------- - Create a `ktensor`: + Examples + -------- + Create a :class:`pyttb.ktensor`: >>> weights = np.array([1., 2.]) >>> fm0 = np.array([[1., 2.], [3., 4.]]) >>> fm1 = np.array([[5., 6.], [7., 8.]]) >>> K = ttb.ktensor.from_data(weights, [fm0, fm1]) - Create a mask `tensor` and extract the elements of the `ktensor` using the mask: + Create a mask :class:`pyttb.tensor` and extract the elements of the + :class:`pyttb.ktensor` using the mask: >>> W = ttb.tensor.from_data(np.array([[0, 1], [1, 0]])) >>> print(K.mask(W)) @@ -1066,15 +1155,17 @@ def mask(self, W): def mttkrp(self, U, n): """ - Matricized `tensor` times Khatri-Rao product for `ktensor`. + Matricized tensor times Khatri-Rao product for :class:`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 matrices, except the nth. + Efficiently calculates the matrix product of the n-mode matricization + of the `ktensor` with the Khatri-Rao product of all entries in U, + a :class:`list` of factor matrices, except the nth. Parameters ---------- - U: list of factor matrices - n: multiplies by all modes except n + U: :class:`list` of factor matrices, required + n: int, required + Multiply by all modes except n. Returns ------- @@ -1111,34 +1202,51 @@ def mttkrp(self, U, n): @property def ncomponents(self): - """Number of components (i.e., number of columns in each factor matrix) of the `ktensor`. + """ + Number of components in the :class:`pyttb.ktensor` (i.e., number of + columns in each factor matrix) of the :class:`pyttb.ktensor`. Returns ------- :int + + Examples + -------- + >>> K = ttb.ktensor.from_function(np.ones, (2, 3, 4), 2) + >>> print(K.ncomponents) + 2 """ return len(self.weights) @property def ndims(self): - """Number of dimensions (i.e., number of factor_matrices) of the `ktensor`. + """ + Number of dimensions (i.e., number of factor matrices) of the + :class:`pyttb.ktensor`. Returns ------- :int + + Examples + -------- + >>> K = ttb.ktensor.from_function(np.ones, (2, 3, 4), 2) + >>> print(K.ndims) + 3 """ return len(self.factor_matrices) def norm(self): """ - Compute the norm (i.e., square root of the sum of squares of entries) of a `ktensor`. + Compute the norm (i.e., square root of the sum of squares of entries) + of a :class:`pyttb.ktensor`. Returns -------- :int - Example - ------- + Examples + -------- >>> K = ttb.ktensor.from_function(np.ones, (2, 3, 4), 2) >>> K.norm() 9.797958971132712 @@ -1151,27 +1259,30 @@ def norm(self): def normalize(self, weight_factor=None, sort=False, normtype=2, mode=None): """ - Normalize the columns of the factor matrices. + Normalize the columns of the factor matrices of a + :class:`pyttb.ktensor`. Parameters ---------- weight_factor: {"all", int}, optional - Absorb the weights into one or more factors: - * "all": absorb weight equally across all factors - * int: absorb weight into a single dimension (int must be in range(self.ndims) - sort: bool - Sort the columns in descending order of the weights - normtype: {non-zero int, inf, -inf, 'fro', 'nuc'}, optional - Order of the norm (see :func:`numpy.linalg.norm` for possible values) - mode: {int, None} - Index of factor matrix to normalize. A value of `None` means normalize all factor matrices. + 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: bool, optional + Sort the columns in descending order of the weights. + normtype: {non-negative int, -1, -2, np.inf, -np.inf}, optional + Order of the norm (see :func:`numpy.linalg.norm` for possible + values). + mode: {int, None}, optional + Index of factor matrix to normalize. A value of `None` means + normalize all factor matrices. Returns - -------- + ------- :class:`pyttb.ktensor` - Example - ------- + Examples + -------- >>> K = ttb.ktensor.from_function(np.ones, (2, 3, 4), 2) >>> print(K.normalize()) # doctest: +ELLIPSIS ktensor of shape 2 x 3 x 4 @@ -1244,31 +1355,31 @@ def normalize(self, weight_factor=None, sort=False, normtype=2, mode=None): def nvecs(self, n, r, flipsign=True): """ - Compute the leading mode-n vectors for a `ktensor`. + Compute the leading mode-n vectors for a :class:`pyttb.ktensor`. - Computes the `r` leading eigenvectors of Xn*Xn' - (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 the matrix Xn*Xn'. + 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: int - Mode for tensor matricization - r: int - Number of eigenvectors to compute and use - flipsign: bool - Make each column's largest element positive if `True` + n: int, required + Mode for tensor matricization. + r: int, required + Number of eigenvectors to compute and use. + flipsign: bool, optional + If True, make each column's largest element positive. Returns ------- - :class:`Numpy.ndarray` + :class:`numpy.ndarray` - Example - ------- + Examples + -------- Compute single eigenvector for dimension 0: >>> K = ttb.ktensor.from_function(np.ones, (2, 3, 4), 2) @@ -1312,25 +1423,24 @@ def nvecs(self, n, r, flipsign=True): def permute(self, order): """ - Permute `ktensor` dimensions. + Permute :class:`pyttb.ktensor` dimensions. - Rearranges the dimensions of A so that they are in the order specified by the - vector `order`. The output is a `ktensor` with components rearranged as specified - by `order`. The corresponding tensor has the same components as `self` but the order - of the subscripts needed to access any particular element is rearranged as - specified by `order`. + Rearranges the dimensions of a :class:`pyttb.ktensor` so that they are + in the order specified by `order`. The corresponding tensor 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: :class:`Numpy.ndarray` + order: :class:`numpy.ndarray` + Permutation of [0,...,self.ndimensions]. Returns ------- :class:`pyttb.ktensor` - shapeNew == shapePrevious[order] - Example - ------- + Examples + -------- >>> weights = np.array([1., 2.]) >>> fm0 = np.array([[1., 2.], [3., 4.]]) >>> fm1 = np.array([[5., 6.], [7., 8.]]) @@ -1367,15 +1477,17 @@ def permute(self, order): def redistribute(self, mode): """ - Distribute weights of a `ktensor` to a specified mode + Distribute weights of a :class:`pyttb.ktensor` to the specified mode. + The redistribution is performed in place. Parameters ---------- mode: int + Must be value in [0,...self.ndims]. Example ------- - Create a `ktensor`: + Create a :class:`pyttb.ktensor`: >>> weights = np.array([1., 2.]) >>> fm0 = np.array([[1., 2.], [3., 4.]]) @@ -1392,7 +1504,7 @@ def redistribute(self, mode): [[5. 6.] [7. 8.]] - Distribute weights of that `ktensor` to mode 0: + Distribute weights of that :class:`pyttb.ktensor` to mode 0: >>> K.redistribute(0) >>> print(K) @@ -1413,9 +1525,9 @@ def redistribute(self, mode): @property def shape(self): - """Shape of a `ktensor`. + """Shape of a :class:`pyttb.ktensor`. - Returns the sizes of all dimensions. + Returns the lengths of all dimensions of the :class:`pyttb.ktensor`. Returns ------- @@ -1425,64 +1537,69 @@ def shape(self): def score(self, other, weight_penalty=True, threshold=0.99, greedy=True): """ - Checks if two ktensor instances match except for permutation. + Checks if two :class:`pyttb.ktensor` instances with the same shapes + but potentially different number of components match except for + permutation. - We define matching as follows. If A (self) and B (other) are single component - ktensors that have been normalized so that their weights are weights_a and - weights_b, then the score is defined as + Matching is defined as follows. If `self` and `other` are single- + component :class:`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) * ... * (aR.T*bR), + score = penalty * (a1.T*b1) * (a2.T*b2) * ... * (an.T*bn), where the penalty is defined by the weights such that - penalty = 1 - abs(weights_a - weights_b) / max(weights_a, weights_b). + penalty = 1 - abs(self.weights - other.weights) / max(self.weights, other.weights). - The score of multi-component ktensors is a normalized sum of the - scores across the best permutation of the components of A. A can have - more components than B --- any extra components are ignored in terms of - the matching score. + The score of multi-component :class:`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 ---------- - other: :class:`pyttb.ktensor` - `ktensor` to match against - weight_penalty: bool - Flag indicating whether or not to consider the weights in the calculations. - Default: true - threshold: float + other: :class:`pyttb.ktensor`, required + :class:`pyttb.ktensor` with which to match. + weight_penalty: bool, optional + Flag indicating whether or not to consider the weights in the + calculations. + threshold: float, optional Threshold specified in the formula above for determining a match. - Default: 0.99 - greedy: bool + greedy: bool, optional Flag indicating whether or not to consider all possible matchings - (exponentially expensive) or just do a greedy matching. Default: true + (exponentially expensive) or just do a greedy matching. Returns ------- int - Score (between 0 and 1) + Score (between 0 and 1). :class:`pyttb.ktensor` - Copy of `self`, which has been normalized and permuted to best match `other` + Copy of `self`, which has been normalized and permuted to best match + `other`. bool - Flag indicating a match according to a user-specified threshold - :class:`Numpy.ndarray` - Permutation (i.e. array of indices of the modes of self) of the components - of self that was used to best match other + Flag indicating a match according to a user-specified threshold. + :class:`numpy.ndarray` + Permutation (i.e. array of indices of the modes of self) of the + components of `self` that was used to best match `other`. - Example - ------- - Create two `ktensor` instances: + Examples + -------- + Create two :class:`pyttb.ktensor` instances and compute the score + between them: - >>> A = ttb.ktensor.from_data(np.array([2., 1., 3.]), np.ones((3,3)), np.ones((4,3)), np.ones((5,3))) - >>> B = ttb.ktensor.from_data(np.array([2., 4.]), np.ones((3,2)), np.ones((4,2)), np.ones((5,2))) - >>> score,Aperm,flag,perm = A.score(B) # Compute `score` using `ktensor.weights` + >>> K = ttb.ktensor.from_data(np.array([2., 1., 3.]), np.ones((3,3)), np.ones((4,3)), np.ones((5,3))) + >>> K2 = ttb.ktensor.from_data(np.array([2., 4.]), np.ones((3,2)), np.ones((4,2)), np.ones((5,2))) + >>> score,Kperm,flag,perm = K.score(K2) >>> print(score) 0.875 >>> print(perm) [0 2 1] - Compute `score` not using `ktensor.weights`: + Compute score without using weights: - >>> score,Aperm,flag,perm = A.score(B,weight_penalty=False) + >>> score,Kperm,flag,perm = K.score(K2,weight_penalty=False) >>> print(score) 1.0 >>> print(perm) @@ -1523,8 +1640,8 @@ def score(self, other, weight_penalty=True, threshold=0.99, greedy=True): # Collapse across all modes using the product C = Cbig.collapse(np.array([2]), np.prod).double() - # Calculate penalty based on differences in the Lambda's - # Note that we are assuming the the lambda value are positive because the + # Calculate penalty based on differences in the weights + # Note that we are assuming the the weights are positive because the # ktensor's were previously normalized. if weight_penalty: P = np.zeros((RA, RB)) @@ -1564,18 +1681,20 @@ def score(self, other, weight_penalty=True, threshold=0.99, greedy=True): def symmetrize(self): """ - Symmetrize a `ktensor` in all modes. + Symmetrize a :class:`pyttb.ktensor` in all modes. - Symmetrize a `ktensor` with respect to all modes so that the resulting `ktensor` is symmetric with respect - to any permutation of indices. + Symmetrize a :class:`pyttb.ktensor` with respect to all modes so that + the resulting :class:`pyttb.ktensor` is symmetric with respect to any + permutation of indices. Returns ------- :class:`pyttb.ktensor` - A new `ktensor` whose factor matrices are symmetric - Example - ------- + Examples + -------- + Create a :class:`pyttb.ktensor`: + >>> weights = np.array([1., 2.]) >>> fm0 = np.array([[1., 2.], [3., 4.]]) >>> fm1 = np.array([[5., 6.], [7., 8.]]) @@ -1591,7 +1710,8 @@ def symmetrize(self): [[5. 6.] [7. 8.]] - Make the factor matrices of the `ktensor` symmetric: + Make the factor matrices of the :class:`pyttb.ktensor` symmetric with + respect to any permutation of the factor matrices: >>> K1 = K.symmetrize() >>> print(K1) # doctest: +ELLIPSIS @@ -1638,21 +1758,22 @@ def symmetrize(self): def tolist(self, mode=None): """ - Converts `ktensor` to a list of factor matrices, evenly distributing the weights across factors. + Convert :class:`pyttb.ktensor` to a list of factor matrices, evenly + distributing the weights across factors. Optionally absorb the + weights into a single mode. Parameters ---------- - mode: int - Index of factor matrix to absorb all of the weight. + mode: int, optional + Index of factor matrix to absorb all of the weights. Returns ------- - :list of :class:`numpy.ndarray` - List of factor matrices + :class:`list` of :class:`numpy.ndarray` - Example - ------- - Create a `ktensor` of all ones: + Examples + -------- + Create a :class:`pyttb.ktensor` of all ones: >>> weights = np.array([1., 2.]) >>> fm0 = np.array([[1., 2.], [3., 4.]]) @@ -1669,7 +1790,8 @@ def tolist(self, mode=None): [[5. 6.] [7. 8.]] - Spread weights equally to all factors and return list of factor matrices: + Spread weights equally to all factors and return list of factor + matrices: >>> fm_list = K.tolist() >>> for fm in fm_list: print(fm) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE @@ -1678,7 +1800,8 @@ def tolist(self, mode=None): [[ 5. 8.4852...] [ 7. 11.313...]] - Shift weight to single factor matrix and return list of factor matrices: + Shift weight to single factor matrix and return list of factor + matrices: >>> fm_list = K.tolist(0) >>> for fm in fm_list: print(fm) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE @@ -1708,30 +1831,33 @@ def tolist(self, mode=None): def tovec(self, include_weights=True): """ - Convert `ktensor` to column vector. + Convert :class:`pyttb.ktensor` to column vector. Optionally include + or exclude the weights. Parameters ---------- - include_weights: bool - Flag to specify whether or not to include weights in output (default is True). + include_weights: bool, optional + Flag to specify whether or not to include weights in output. Returns ------- :class:`numpy.ndarray` - The length of the column vector is (sum(K.shape)+1)*K.ncomponents. The vector contains the ktensor - weights (if requested) stacked on top of each of the columns of the factor_matrices in order. + 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. - Example - ------- - Create a `ktensor` from a vector: + Examples + -------- + Create a :class:`pyttb.ktensor` from a vector: >>> rank = 2 >>> shape = np.array([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_from_vector = ttb.ktensor.from_vector(weights_and_data[:], shape, True) - >>> print(K_from_vector) + >>> K = ttb.ktensor.from_vector(weights_and_data[:], shape, True) + >>> print(K) ktensor of shape 2 x 3 x 4 weights=[2. 2.] factor_matrices[0] = @@ -1747,10 +1873,11 @@ def tovec(self, include_weights=True): [13. 17.] [14. 18.]] - Create a ktensor from a vector of data extracted from another `ktensor`: + Create a :class:`pyttb.ktensor` from a vector of data extracted from + another :class:`pyttb.ktensor`: - >>> K_from_tovec = ttb.ktensor.from_vector(K_from_vector.tovec(), shape, True) - >>> print(K_from_tovec) + >>> K2 = ttb.ktensor.from_vector(K.tovec(), shape, True) + >>> print(K2) ktensor of shape 2 x 3 x 4 weights=[2. 2.] factor_matrices[0] = @@ -1779,54 +1906,52 @@ def tovec(self, include_weights=True): x[offset : offset + f.shape[0]] = f[:, r].reshape(f.shape[0]) offset += f.shape[0] - # if include_weights: - # x = np.zeros((self.ncomponents * int(sum(self.shape)+1), 1)) - # x[0:self.ncomponents] = self.weights.reshape((len(self.weights), 1)) - # offset = self.ncomponents - # else: - # x = np.zeros((self.ncomponents * int(sum(self.shape)), 1)) - # offset = 0 - # - # for f in self.factor_matrices: - # for r in range(self.ncomponents): - # x[offset:offset+f.shape[0]] = f[:, r].reshape((f.shape[0], 1)) - # offset += f.shape[0] return x def ttv(self, vector, dims=None, exclude_dims=None): """ - `Tensor` times vector for `ktensors`. - - Computes the product of a `ktensor` with a vector. If `dims` is an integer, it specifies - the dimension in the `ktensor` along which the vector is multiplied. If the shape of the vector - is = (I,1), then the size of dimension `dims` of the `ktensor` must have size I. Note that - number of dimensions of the returned `tensor` is 1 less than the dimension of the `ktensor` used - in the multiplication because dimension `dims` is removed. - - ttv(np.array([v1,v2,...,vN])) computes the product of the `ktensor` with a sequence of vectors in the - array. The products are computed sequentially along all dimensions (or modes) of the `ktensor`. The array - contains N=self.ndims vectors. - - ttv(np.array([v1,v2,...,vk]), dims=np.array([I1,I2,...,Ik) computes the sequence of `ktensor`-by-vector - products along the dimensions specified by `dims`. In this case, the number of products, k, can be less - than N=self.ndims and the order of the sequence does not need to match the order of the dimensions in the - `ktensor`. Note that the number of vectors must match the number of dimensions provides, and the length of - each vector must match the size of each dimension of the `ktensor` specified in `dims`. + Tensor times vector for a :class:`pyttb.ktensor`. + + Computes the product of a :class:`pyttb.ktensor` with a vector (i.e., + np.array). If `dims` is an integer, it specifies the dimension in the + :class:`pyttb.ktensor` along which the vector is multiplied. + If the shape of the vector is = (I,1), then the length of dimension + `dims` of the :class:`pyttb.ktensor` must be I. Note that the number + of dimensions of the returned :class:`pyttb.ktensor` is 1 less than + the dimension of the :class:`pyttb.ktensor` used in the + multiplication because dimension `dims` is removed. + + If `vector` is a :class:`list` of np.array instances, the + :class:`pyttb.ktensor` is multiplied with each vector in the list. The + products are computed sequentially along all dimensions (or modes) of + the :class:`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 :class:`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 :class:`pyttb.ktensor` specified in `dims`. Parameters ---------- - vector: :class:`Numpy.ndarray`, list[:class:`Numpy.ndarray`] - dims: int, :class:`Numpy.ndarray` + vector: :class:`numpy.ndarray` or list[:class:`numpy.ndarray`], required + dims: int, :class:`numpy.ndarray`, optional + exclude_dims: Returns ------- float or :class:`pyttb.ktensor` - The number of dimensions of the returned `ktensor` is N-k, where N=self.ndims and k = number of - vectors provided as input. If k == N, a scalar is returned + The number of dimensions of the returned :class:`pyttb.ktensor` is + n-k, where n = self.ndims and k = number of vectors provided as + input. If k == n, a scalar is returned. - Example + Examples ------- - Compute the product of a `ktensor` and a single vector (results in a `ktensor`): + Compute the product of a :class:`pyttb.ktensor` and a single vector + (results in a :class:`pyttb.ktensor`): >>> rank = 2 >>> shape = np.array([2, 3, 4]) @@ -1847,7 +1972,8 @@ def ttv(self, vector, dims=None, exclude_dims=None): [13. 17.] [14. 18.]] - Compute the product of a `ktensor` and a vector for each dimension (results in a `float`): + Compute the product of a :class:`pyttb.ktensor` and a vector for each + dimension (results in a `float`): >>> vec2 = np.array([1, 1]) >>> vec3 = np.array([1, 1, 1]) @@ -1856,7 +1982,8 @@ def ttv(self, vector, dims=None, exclude_dims=None): >>> print(K1) 30348.0 - Compute the product of a `ktensor` and multiple vectors out of order (results in a `ktensor`): + Compute the product of a :class:`pyttb.ktensor` and multiple vectors + out of order (results in a :class:`pyttb.ktensor`): >>> K2 = K.ttv([vec4, vec3],np.array([2, 1])) >>> print(K2) @@ -1908,31 +2035,34 @@ def ttv(self, vector, dims=None, exclude_dims=None): def update(self, modes, data): """ - Updates a `ktensor` in the specific dimensions, `modes`, with the values in `data` - (in vector or matrix form). The value of `mode` must be an integer between 1 - and self.ndims. Further, the number of elements in `data` must equal self.shape[mode] * self.ncomponents. + Updates a :class:`pyttb.ktensor` in the specific dimensions with the + values in `data` (in vector or matrix form). The value of `modes` must + be a value in [-1,...,self.ndoms]. 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 - List of dimensions to update; values must be in ascending order. Can include one or more of the following values: - * -1: update the weights - * `int`: value must be sorted and in `{range(self.ndims)}` - data: :class:numpy.ndarray - Vector of data values to use in the update + modes: int or :class:`list` of int, required + 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: :class:`numpy.ndarray`, required + Data values to use in the update. Results ------- :class:`pyttb.ktensor` - The update is performed in place and a reference to self is returned - Example - ------- - Create a `ktensor` of all ones: + Examples + -------- + Create a :class:`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 `ktensor`: + Create vectors for updating various factor matrices of the + :class:`pyttb.ktensor`: >>> vec0 = 2 * np.ones(K.shape[0] * K.ncomponents) >>> vec1 = 3 * np.ones(K.shape[1] * K.ncomponents) @@ -1979,7 +2109,7 @@ def update(self, modes, data): [4. 4.] [4. 4.]] - Update some but not all factor matrices + Update some but not all factor matrices: >>> K3 = K.copy() >>> vec_some = np.concatenate((vec0, vec2)) @@ -2035,11 +2165,12 @@ def update(self, modes, data): def __add__(self, other): """ - Binary addition for `ktensors`. + Binary addition for :class:`pyttb.ktensor`. Parameters ---------- - other: :class:`pyttb.ktensor` + other: :class:`pyttb.ktensor`, required + :class:`pyttb.ktensor` to add to `self`. Returns ------- @@ -2064,20 +2195,21 @@ def __add__(self, other): def __getitem__(self, item): """ - Subscripted reference for a `ktensor`. + Subscripted reference for a :class:`pyttb.ktensor`. - Subscripted reference is used to query the components of a `ktensor`. + Subscripted reference is used to query the components of a + :class:`pyttb.ktensor`. Parameters ---------- - item: {tuple(int), int} + item: tuple(int) or int, required - Example - ------- + Examples + -------- >>> K = ttb.ktensor.from_function(np.ones, (2, 3, 4), 2) - >>> K.weights #<--returns the weights array (np.array([1., 1.])) + >>> K.weights array([1., 1.]) - >>> K.factor_matrices #<--returns a list of 3 np.ndarrays + >>> K.factor_matrices [array([[1., 1.], [1., 1.]]), array([[1., 1.], [1., 1.], @@ -2085,15 +2217,15 @@ def __getitem__(self, item): [1., 1.], [1., 1.], [1., 1.]])] - >>> K.factor_matrices[0] #<--returns the factor matrix corresponding to the first mode + >>> K.factor_matrices[0] array([[1., 1.], [1., 1.]]) - >>> K[0] #<-returns the factor matrix corresponding to the first mode + >>> K[0] array([[1., 1.], [1., 1.]]) - >>> K[1, 2, 0] #<--calculates and returns a specific single element of K + >>> K[1, 2, 0] 2.0 - >>> K[0][:, [0]] # returns the first column of the factor matrix corresponding to the first mode + >>> K[0][:, [0]] array([[1.], [1.]]) """ @@ -2123,11 +2255,7 @@ def __getitem__(self, item): def __neg__(self): """ - Unary minus (negative) for ktensors. - - Parameters - ---------- - other: :class:`pyttb.ktensor` + Unary minus (negative) for :class:`pyttb.ktensor` instances. Returns ------- @@ -2137,11 +2265,7 @@ def __neg__(self): def __pos__(self): """ - Unary minus (positive) for `ktensors`. - - Parameters - ---------- - other: :class:`pyttb.ktensor` + Unary plus (positive) for :class:`pyttb.ktensor` instances. Returns ------- @@ -2151,27 +2275,44 @@ def __pos__(self): def __setitem__(self, key, value): """ - Subscripted assignment for `ktensors`. + Subscripted assignment for :class:`pyttb.ktensor`. - Subscripted assignment cannot be used to update individual elements of a `ktensor`. However, you - can update the weights vector or the factor matrices of a `ktensor`. The entire factor matrix or weight + Subscripted assignment cannot be used to update individual elements of + a :class:`pyttb.ktensor`. You can update the weights vector or the + factor matrices of a :class:`pyttb.ktensor`. Example ------- - >>> K = ktensor.from_data(np.ones((4,1)), [np.random.random((2,4)), np.random.random((3,4)), np.random.random((4,4))]) + >>> K = ttb.ktensor.from_data(np.ones((4,1)), [np.random.random((2,4)), np.random.random((3,4)), np.random.random((4,4))]) >>> K.weights = 2 * np.ones((4,1)) >>> K.factor_matrices[0] = np.zeros((2, 4)) >>> K.factor_matrices = [np.zeros((2, 4)), np.zeros((3, 4)), np.zeros((4, 4))] + >>> print(K) + ktensor of shape 2 x 3 x 4 + weights=[[2.] + [2.] + [2.] + [2.]] + factor_matrices[0] = + [[0. 0. 0. 0.] + [0. 0. 0. 0.]] + factor_matrices[1] = + [[0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.]] + factor_matrices[2] = + [[0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.] + [0. 0. 0. 0.]] """ assert ( False - ), "Subscripted assignment cannot be used to update individual elements of a ktensor. However, \ - you can update the weights vector or the factor matrices of a ktensor. The entire factor matrix or weight \ - vector must be provided." + ), "Subscripted assignment cannot be used to update individual elements of a ktensor. However, you can update the weights vector or the factor matrices of a ktensor. The entire factor matrix or weight vector must be provided." def __sub__(self, other): """ - Binary subtraction for ktensors. + Binary subtraction for :class:`pyttb.ktensor`. Parameters ---------- @@ -2199,7 +2340,8 @@ def __sub__(self, other): def __mul__(self, other): """ - Elementwise (including scalar) multiplication for ktensors. + Elementwise (including scalar) multiplication for + :class:`pyttb.ktensor` instances. Parameters ---------- @@ -2221,7 +2363,8 @@ def __mul__(self, other): def __rmul__(self, other): """ - Elementwise (including scalar) multiplication for ktensors. + Elementwise (including scalar) multiplication for + :class:`pyttb.ktensor` instances. Parameters ---------- @@ -2235,12 +2378,11 @@ def __rmul__(self, other): def __repr__(self): """ - String representation of a ktensor. + String representation of a :class:`pyttb.ktensor`. Returns ------- str: - Contains the shape, weights and factor_matrices as strings on different lines. """ s = "" s += "ktensor of shape "