diff --git a/.gitignore b/.gitignore index e18a7476..a1f4ed2e 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ htmlcov __pycache__ .vs* TestResults +.DS_Store \ No newline at end of file diff --git a/pyttb/cp_als.py b/pyttb/cp_als.py index 3286ddf0..34daa478 100644 --- a/pyttb/cp_als.py +++ b/pyttb/cp_als.py @@ -68,7 +68,7 @@ def cp_als( >>> 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]) + >>> K = ttb.ktensor([fm0, fm1], weights) >>> np.random.seed(1) >>> M, Minit, output = ttb.cp_als(K.full(), 2) # doctest: +ELLIPSIS CP_ALS: @@ -76,7 +76,7 @@ def cp_als( Iter 1: f = ... f-delta = ... Final f = ... >>> print(M) # doctest: +ELLIPSIS - ktensor of shape 2 x 2 + ktensor of shape (2, 2) weights=[108.4715... 8.6114...] factor_matrices[0] = [[0.4187... 0.3989...] @@ -85,7 +85,7 @@ def cp_als( [[0.6188... 0.2581...] [0.7854... 0.9661...]] >>> print(Minit) # doctest: +ELLIPSIS - ktensor of shape 2 x 2 + ktensor of shape (2, 2) weights=[1. 1.] factor_matrices[0] = [[4.1702...e-01 7.2032...e-01] @@ -147,12 +147,12 @@ def cp_als( factor_matrices.append( np.random.uniform(0, 1, (input_tensor.shape[n], rank)) ) - init = ttb.ktensor.from_factor_matrices(factor_matrices) + init = ttb.ktensor(factor_matrices) elif isinstance(init, str) and init.lower() == "nvecs": factor_matrices = [] for n in range(N): factor_matrices.append(input_tensor.nvecs(n, rank)) - init = ttb.ktensor.from_factor_matrices(factor_matrices) + init = ttb.ktensor(factor_matrices) else: assert False, "The selected initialization method is not supported" @@ -208,7 +208,7 @@ def cp_als( U[n] = Unew UtU[:, :, n] = U[n].T @ U[n] - M = ttb.ktensor.from_data(weights, U) + M = ttb.ktensor(U, weights) # This is equivalent to innerprod(X,P). iprod = np.sum( diff --git a/pyttb/cp_apr.py b/pyttb/cp_apr.py index 003b6204..72544567 100644 --- a/pyttb/cp_apr.py +++ b/pyttb/cp_apr.py @@ -116,7 +116,7 @@ def cp_apr( factor_matrices.append( np.random.uniform(0, 1, (input_tensor.shape[n], rank)) ) - init = ttb.ktensor.from_factor_matrices(factor_matrices) + init = ttb.ktensor(factor_matrices) # Call solver based on the couce of algorithm parameter, passing all the other input parameters if algorithm.lower() == "mu": @@ -243,8 +243,7 @@ def tt_cp_apr_mu( nTimes = np.zeros((maxiters,)) # Set up for iteration - initializing M and Phi. - # TODO replace with copy - M = ttb.ktensor.from_tensor_type(init) + M = init.copy() M.normalize(normtype=1) Phi = [] # np.zeros((N,))#cell(N,1) for n in range(N): @@ -453,8 +452,7 @@ def tt_cp_apr_pdnr( init[n][tmpIdx, 0] = 1e-8 # Start with the initial guess, normalized using the vector L1 norm - # TODO replace with copy - M = ttb.ktensor.from_tensor_type(init) + M = init.copy() M.normalize(normtype=1) # Sparse tensor flag affects how Pi and Phi are computed. @@ -824,8 +822,7 @@ def tt_cp_apr_pqnr( init[n][tmpIdx, 0] = 1e-8 # Start with the initial guess, normalized using the vector L1 norm - # TODO replace with copy - M = ttb.ktensor.from_tensor_type(init) + M = init.copy() M.normalize(normtype=1) # Sparse tensor flag affects how Pi and Phi are computed. diff --git a/pyttb/import_data.py b/pyttb/import_data.py index ffbec480..54b2ec0a 100644 --- a/pyttb/import_data.py +++ b/pyttb/import_data.py @@ -31,14 +31,14 @@ def import_data(filename, index_base=1): shape = import_shape(fp) data = import_array(fp, np.prod(shape)) fp.close() - return ttb.tensor().from_data(data, shape) + return ttb.tensor.from_data(data, shape) elif data_type == "sptensor": shape = import_shape(fp) nz = import_nnz(fp) subs, vals = import_sparse_array(fp, len(shape), nz, index_base) fp.close() - return ttb.sptensor().from_data(subs, vals, shape) + return ttb.sptensor.from_data(subs, vals, shape) elif data_type == "matrix": shape = import_shape(fp) @@ -59,7 +59,7 @@ def import_data(filename, index_base=1): fac = np.reshape(fac, np.array(fac_shape)) factor_matrices.append(fac) fp.close() - return ttb.ktensor().from_data(weights, factor_matrices) + return ttb.ktensor(factor_matrices, weights, copy=False) def import_type(fp): diff --git a/pyttb/ktensor.py b/pyttb/ktensor.py index 1c12883a..432119ae 100644 --- a/pyttb/ktensor.py +++ b/pyttb/ktensor.py @@ -36,9 +36,6 @@ class ktensor(object): :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` @@ -52,46 +49,45 @@ class ktensor(object): __slots__ = ("weights", "factor_matrices") - def __init__(self): + def __init__(self, factor_matrices=None, weights=None, copy=True): """ - Construct an empty :class:`pyttb.ktensor` + Create a :class:`pyttb.ktensor` in one of the following ways: + - With no inputs (or `weights` and `factor_matrices` both None), + return an empty :class:`pyttb.ktensor`. + - If `weights` is None, return a :class:`pyttb.ktensor` with + `weights` all equal to 1 and `factor_matrices` as provided. + - Otherwise, return a :class:`pyttb.ktensor` with `weights` and + `factor_matrices` as provided. - The constructor takes no arguments and returns an empty - :class:`pyttb.ktensor`. - """ - # Empty constructor - 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`. See - :class:`pyttb.ktensor` for parameter descriptions. + If `copy` is True, return a :class:`pyttb.ktensor` with copies + of `weights` and `factor_matrices`, otherwise just use references + to the `weights` and `factor_matrices` provided. Parameters ---------- - 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` + factor_matrices: :class:`list` of :class:`numpy.ndarray` with `dtype`=:class:`float`, optional + weights: :class:`numpy.ndarray`, optional + copy: :class:`bool`, optional Examples -------- + Create an empty :class:`pyttb.ktensor`: + + >>> K = ttb.ktensor() + >>> print(K) + ktensor of shape () + weights=[] + factor_matrices=[] + 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 = ttb.ktensor.from_data(weights, [fm0, fm1]) + >>> K = ttb.ktensor([fm0, fm1], weights) >>> print(K) - ktensor of shape 2 x 2 + ktensor of shape (2, 2) weights=[1. 2.] factor_matrices[0] = [[1. 2.] @@ -100,101 +96,15 @@ def from_data(cls, weights, *factor_matrices): [[5. 6.] [7. 8.]] - Create a :class:`pyttb.ktensor` from weights and factor matrices passed as - arguments: - - >>> 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.]] - """ - # Check individual input parameters - assert isinstance(weights, np.ndarray) and isvector( - weights - ), "Input parameter 'weights' must be a numpy.array type vector." - - # Input can be a list or sequences of factor_matrices - if isinstance(factor_matrices[0], list): - _factor_matrices = [f.copy() for f in factor_matrices[0]] - else: - _factor_matrices = [f.copy() for f in factor_matrices[0:]] - for fm in _factor_matrices: - assert isinstance( - fm, np.ndarray - ), "Input parameter 'factor_matrices' must be a list of numpy.array's." - - # Check dimensions of weights and factor_matrices - num_weights = len(weights) - for i, fm in enumerate(_factor_matrices): - assert ( - num_weights == fm.shape[1] - ), "Size of factor_matrix {} does not match number of weights ({}).".format( - i, num_weights - ) - - # Create ktensor and populate data members - k = cls() - k.weights = weights.copy() - if k.weights.dtype != float: - print("converting weights from {} to float".format(k.weights.dtype)) - k.weights = k.weights.astype(float) - k.factor_matrices = _factor_matrices - for i in range(len(k.factor_matrices)): - if k.factor_matrices[i].dtype != float: - print( - "converting factor_matrices[{}] from {} to float".format( - i, k.factor_matrices[i].dtype - ) - ) - k.factor_matrices[i] = k.factor_matrices[i].astype(float) - - return k - - @classmethod - def from_tensor_type(cls, source) -> 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`, required - - Returns - ------- - :class:`pyttb.ktensor` - - Examples - -------- - Create an instance of a :class:`pyttb.ktensor`: + Create a :class:`pyttb.ktensor` from a :class:`list` of factor + matrices (without providing weights): >>> fm0 = np.array([[1., 2.], [3., 4.]]) >>> fm1 = np.array([[5., 6.], [7., 8.]]) >>> factor_matrices = [fm0, fm1] - >>> K_source = ttb.ktensor.from_factor_matrices(factor_matrices) - >>> print(K_source) - ktensor of shape 2 x 2 - weights=[1. 1.] - factor_matrices[0] = - [[1. 2.] - [3. 4.]] - factor_matrices[1] = - [[5. 6.] - [7. 8.]] - - Create another instance of a :class:`pyttb.ktensor` from the original - one above: - - >>> K = ttb.ktensor.from_tensor_type(K_source) + >>> K = ttb.ktensor([fm0, fm1]) >>> print(K) - ktensor of shape 2 x 2 + ktensor of shape (2, 2) weights=[1. 1.] factor_matrices[0] = [[1. 2.] @@ -202,84 +112,58 @@ def from_tensor_type(cls, source) -> ktensor: factor_matrices[1] = [[5. 6.] [7. 8.]] - - See also :func:`pyttb.ktensor.copy` """ - if isinstance(source, ktensor): - return cls().from_data( - source.weights.copy(), [f.copy() for f in source.factor_matrices] - ) - - # TODO impement conversion when symktensor has been implemented - # if isinstance(source, ttb.symktensor): - # # self.weights = args[0].weights; - # # # MATLAB=> [t.u{1:varargin{1}.m,1}] = deal(varargin{1}.u); - # # return - # raise NotImplementedError - - assert False, "Cannot convert from {} to ktensor".format(str(source.__class__)) - - @classmethod - def from_factor_matrices(cls, *factor_matrices): - """ - 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`, required - The number of columns of each of the factor matrices must be the - same. - Returns - ------- - :class:`pyttb.ktensor` + # Cannot specify weights and not factor_matrices + if factor_matrices is None and weights is not None: + assert False, "factor_matrices cannot be None if weights are provided." - 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 = ttb.ktensor.from_factor_matrices(factor_matrices) - >>> print(K) - ktensor of shape 2 x 2 - weights=[1. 1.] - factor_matrices[0] = - [[1. 2.] - [3. 4.]] - factor_matrices[1] = - [[5. 6.] - [7. 8.]] + # Empty constructor + if factor_matrices is None and weights is None: + self.weights = np.array([]) + self.factor_matrices = [] + return + + # 'factor_matrices' must be a list + if not isinstance(factor_matrices, list): + assert False, "Input 'factor_matrices' must be a list." + # each factor matrix should be a np.ndarray + if not ( + all(isinstance(fm, np.ndarray) for fm in factor_matrices) + and all(fm.dtype == float for fm in factor_matrices) + ): + assert ( + False + ), "Each item in 'factor_matrices' must be a numpy.ndarray object with dtype=float." + # the number of columns of all factor_matrices must be equal + num_components = factor_matrices[0].shape[1] + if not all(fm.shape[1] == num_components for fm in factor_matrices): + assert ( + False + ), "The number of columns each item in 'factor_matrices' must be the same." - Create a :class:`pyttb.ktensor` from factor matrices passed as - arguments: + # process weights + if weights is not None: + # check if weights are the correct type and shape + assert ( + isinstance(weights, np.ndarray) + and weights.dtype == float + and weights.shape == (num_components,) + ), "Input 'weights' must be a numpy.ndarray object with dtype=float and length equal to the number of columns in each factor matrix." + # make copy or use reference + if copy: + self.weights = weights.copy() + else: + self.weights = weights + else: + # create weights if not provided + self.weights = np.ones(num_components) - >>> K = ttb.ktensor.from_factor_matrices(fm0, fm1) - >>> print(K) - ktensor of shape 2 x 2 - weights=[1. 1.] - factor_matrices[0] = - [[1. 2.] - [3. 4.]] - factor_matrices[1] = - [[5. 6.] - [7. 8.]] - """ - # CONSTRUCTOR FROM LIST OF FACTOR MATRICES - # Input can be a list or sequences of factor_matrices - if isinstance(factor_matrices[0], list): - _factor_matrices = factor_matrices[0] + # process factor_matrices + if copy: + self.factor_matrices = [fm.copy() for fm in factor_matrices] else: - _factor_matrices = [f for f in factor_matrices[0:]] - for fm in _factor_matrices: - assert isinstance( - fm, np.ndarray - ), "Input parameter 'factor_matrices' must be a list of numpy.array's." - nc = _factor_matrices[0].shape[1] - return cls().from_data(np.ones(nc), _factor_matrices) + self.factor_matrices = factor_matrices @classmethod def from_function(cls, fun, shape, num_components): @@ -310,7 +194,7 @@ def from_function(cls, fun, shape, num_components): >>> np.random.seed(1) >>> 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 + ktensor of shape (2, 3, 4) weights=[1. 1.] factor_matrices[0] = [[4.1702...e-01 7.2032...e-01] @@ -329,7 +213,7 @@ def from_function(cls, fun, shape, num_components): >>> K = ttb.ktensor.from_function(np.ones, (2, 3, 4), 2) >>> print(K) - ktensor of shape 2 x 3 x 4 + ktensor of shape (2, 3, 4) weights=[1. 1.] factor_matrices[0] = [[1. 1.] @@ -348,7 +232,7 @@ def from_function(cls, fun, shape, num_components): >>> K = ttb.ktensor.from_function(np.zeros, (2, 3, 4), 2) >>> print(K) - ktensor of shape 2 x 3 x 4 + ktensor of shape (2, 3, 4) weights=[1. 1.] factor_matrices[0] = [[0. 0.] @@ -374,7 +258,7 @@ def from_function(cls, fun, shape, num_components): factor_matrices = [] for i in range(nd): factor_matrices.append(fun((shape[i], num_components))) - return cls().from_data(weights, factor_matrices) + return cls(factor_matrices, weights, copy=False) @classmethod def from_vector(cls, data, shape, contains_weights): @@ -413,7 +297,7 @@ def from_vector(cls, data, shape, contains_weights): >>> data = np.arange(1, rank*sum(shape)+1).astype(float) >>> K = ttb.ktensor.from_vector(data[:], shape, False) >>> print(K) - ktensor of shape 2 x 3 x 4 + ktensor of shape (2, 3, 4) weights=[1. 1.] factor_matrices[0] = [[1. 3.] @@ -435,7 +319,7 @@ def from_vector(cls, data, shape, contains_weights): >>> 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 x 3 x 4 + ktensor of shape (2, 3, 4) weights=[2. 2.] factor_matrices[0] = [[1. 3.] @@ -491,7 +375,7 @@ def from_vector(cls, data, shape, contains_weights): ) factor_matrices.append(factor_matrix) - return cls().from_data(weights, factor_matrices) + return cls(factor_matrices, weights, copy=False) def arrange(self, weight_factor=None, permutation=None): """ @@ -524,9 +408,9 @@ def arrange(self, weight_factor=None, permutation=None): >>> 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]) + >>> K = ttb.ktensor([fm0, fm1], weights) >>> print(K) - ktensor of shape 2 x 2 + ktensor of shape (2, 2) weights=[1. 2.] factor_matrices[0] = [[1. 2.] @@ -540,7 +424,7 @@ def arrange(self, weight_factor=None, permutation=None): >>> p = [1,0] >>> K.arrange(permutation=p) >>> print(K) - ktensor of shape 2 x 2 + ktensor of shape (2, 2) weights=[2. 1.] factor_matrices[0] = [[2. 1.] @@ -554,7 +438,7 @@ def arrange(self, weight_factor=None, permutation=None): >>> K.arrange() >>> print(K) # doctest: +ELLIPSIS - ktensor of shape 2 x 2 + ktensor of shape (2, 2) weights=[89.4427... 27.2029...] factor_matrices[0] = [[0.4472... 0.3162...] @@ -567,7 +451,7 @@ def arrange(self, weight_factor=None, permutation=None): >>> K.arrange(weight_factor=1) >>> print(K) # doctest: +ELLIPSIS - ktensor of shape 2 x 2 + ktensor of shape (2, 2) weights=[1. 1.] factor_matrices[0] = [[0.4472... 0.3162...] @@ -613,7 +497,7 @@ def arrange(self, weight_factor=None, permutation=None): return - def copy(self): + def copy(self) -> ktensor: """ Make a deep copy of a :class:`pyttb.ktensor`. @@ -628,7 +512,7 @@ def copy(self): >>> np.random.seed(1) >>> 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 + ktensor of shape (2, 3, 4) weights=[1. 1.] factor_matrices[0] = [[4.1702...e-01 7.2032...e-01] @@ -648,7 +532,7 @@ def copy(self): >>> K2 = K.copy() >>> K2.weights = np.array([2., 3.]) >>> print(K2) # doctest: +ELLIPSIS - ktensor of shape 2 x 3 x 4 + ktensor of shape (2, 3, 4) weights=[2. 3.] factor_matrices[0] = [[4.1702...e-01 7.2032...e-01] @@ -666,7 +550,7 @@ def copy(self): Show that the original :class:`pyttb.ktensor` is unchanged: >>> print(K) # doctest: +ELLIPSIS - ktensor of shape 2 x 3 x 4 + ktensor of shape (2, 3, 4) weights=[1. 1.] factor_matrices[0] = [[4.1702...e-01 7.2032...e-01] @@ -681,7 +565,7 @@ def copy(self): [0.0273... 0.6704...] [0.4173... 0.5586...]] """ - return ttb.ktensor.from_tensor_type(self) + return ttb.ktensor(self.factor_matrices, self.weights, copy=True) def double(self): """ @@ -697,7 +581,7 @@ def double(self): >>> fm0 = np.array([[1., 2.], [3., 4.]]) >>> fm1 = np.array([[5., 6.], [7., 8.]]) >>> factor_matrices = [fm0, fm1] - >>> K = ttb.ktensor.from_data(weights, factor_matrices) + >>> K = ttb.ktensor(factor_matrices, weights) >>> K.double() array([[29., 39.], [63., 85.]]) @@ -755,9 +639,9 @@ def extract(self, idx=None): >>> 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]) + >>> K = ttb.ktensor([fm0, fm1], weights) >>> print(K) - ktensor of shape 2 x 2 + ktensor of shape (2, 2) weights=[1. 2.] factor_matrices[0] = [[1. 2.] @@ -770,7 +654,7 @@ def extract(self, idx=None): component from each factor of the original :class:`pyttb.ktensor`: >>> K.extract([1]) - ktensor of shape 2 x 2 + ktensor of shape (2, 2) weights=[2.] factor_matrices[0] = [[2.] @@ -781,7 +665,7 @@ def extract(self, idx=None): """ # return a copy if no components have been specified if idx is None: - return self.from_tensor_type(self) + return self.copy() if isinstance(idx, (int, tuple, list, np.ndarray)): if isinstance(idx, int): @@ -810,7 +694,7 @@ def extract(self, idx=None): new_factor_matrices = [] for i in range(self.ndims): new_factor_matrices.append(self.factor_matrices[i][:, components]) - return self.from_data(new_weights, new_factor_matrices) + return ttb.ktensor(new_factor_matrices, new_weights) else: assert False, "Input parameter must be an int, tuple, list or numpy.ndarray" @@ -843,11 +727,11 @@ def fixsigns(self, other=None): >>> 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]) + >>> 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 x 2 + ktensor of shape (2, 2) weights=[1. 2.] factor_matrices[0] = [[ 1. 2.] @@ -859,7 +743,7 @@ def fixsigns(self, other=None): Fix the signs of the largest magnitude entries: >>> print(K.fixsigns()) - ktensor of shape 2 x 2 + ktensor of shape (2, 2) weights=[1. 2.] factor_matrices[0] = [[ 1. -2.] @@ -870,13 +754,13 @@ def fixsigns(self, other=None): Fix the signs using another :class:`pyttb.ktensor`: - >>> K = ttb.ktensor.from_data(weights, [fm0, fm1]) + >>> 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) # doctest: +ELLIPSIS - ktensor of shape 2 x 2 + ktensor of shape (2, 2) weights=[27.2029... 89.4427...] factor_matrices[0] = [[ 0.3162... -0.4472...] @@ -973,9 +857,9 @@ def full(self): >>> 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]) + >>> K = ttb.ktensor([fm0, fm1], weights) >>> print(K) - ktensor of shape 2 x 2 + ktensor of shape (2, 2) weights=[1. 2.] factor_matrices[0] = [[1. 2.] @@ -1053,9 +937,9 @@ def isequal(self, other): Examples -------- >>> K1 = ttb.ktensor.from_function(np.ones, (2,3,4), 2) - >>> weights = np.ones((2, 1)) + >>> weights = np.ones((2,)) >>> factor_matrices = [np.ones((2, 2)), np.ones((3, 2)), np.ones((4, 2))] - >>> K2 = ttb.ktensor.from_data(weights, factor_matrices) + >>> K2 = ttb.ktensor(factor_matrices, weights) >>> print(K1.isequal(K2)) True """ @@ -1102,7 +986,7 @@ def issymmetric(self, return_diffs=False): >>> weights = np.array([1., 2.]) >>> fm0 = np.array([[1., 2.], [3., 4.]]) >>> fm1 = np.array([[5., 6.], [7., 8.]]) - >>> K2 = ttb.ktensor.from_data(weights, [fm0, fm1]) + >>> K2 = ttb.ktensor([fm0, fm1], weights) >>> issym, diffs = K2.issymmetric(return_diffs=True) >>> print(diffs) [[0. 8.] @@ -1149,7 +1033,7 @@ def mask(self, W): >>> 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]) + >>> K = ttb.ktensor([fm0, fm1], weights) Create a mask :class:`pyttb.tensor` and extract the elements of the :class:`pyttb.ktensor` using the mask: @@ -1311,7 +1195,7 @@ def normalize(self, weight_factor=None, sort=False, normtype=2, mode=None): -------- >>> K = ttb.ktensor.from_function(np.ones, (2, 3, 4), 2) >>> print(K.normalize()) # doctest: +ELLIPSIS - ktensor of shape 2 x 3 x 4 + ktensor of shape (2, 3, 4) weights=[4.898... 4.898...] factor_matrices[0] = [[0.7071... 0.7071...] @@ -1452,7 +1336,7 @@ def permute(self, order): Permute :class:`pyttb.ktensor` dimensions. Rearranges the dimensions of a :class:`pyttb.ktensor` so that they are - in the order specified by `order`. The corresponding tensor has the + 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`. @@ -1471,9 +1355,9 @@ def permute(self, order): >>> fm0 = np.array([[1., 2.], [3., 4.]]) >>> fm1 = np.array([[5., 6.], [7., 8.]]) >>> factor_matrices = [fm0, fm1] - >>> K = ttb.ktensor.from_data(weights, factor_matrices) + >>> K = ttb.ktensor(factor_matrices, weights) >>> print(K) - ktensor of shape 2 x 2 + ktensor of shape (2, 2) weights=[1. 2.] factor_matrices[0] = [[1. 2.] @@ -1486,7 +1370,7 @@ def permute(self, order): >>> K1 = K.permute(np.array([1, 0])) >>> print(K1) - ktensor of shape 2 x 2 + ktensor of shape (2, 2) weights=[1. 2.] factor_matrices[0] = [[5. 6.] @@ -1499,7 +1383,7 @@ def permute(self, order): if tuple(range(self.ndims)) != tuple(sorted(order.tolist())): assert False, "Invalid permutation" - return ktensor.from_data(self.weights, [self.factor_matrices[i] for i in order]) + return ttb.ktensor([self.factor_matrices[i] for i in order], self.weights) def redistribute(self, mode): """ @@ -1519,9 +1403,9 @@ def redistribute(self, mode): >>> fm0 = np.array([[1., 2.], [3., 4.]]) >>> fm1 = np.array([[5., 6.], [7., 8.]]) >>> factor_matrices = [fm0, fm1] - >>> K = ttb.ktensor.from_data(weights, factor_matrices) + >>> K = ttb.ktensor(factor_matrices, weights) >>> print(K) - ktensor of shape 2 x 2 + ktensor of shape (2, 2) weights=[1. 2.] factor_matrices[0] = [[1. 2.] @@ -1534,7 +1418,7 @@ def redistribute(self, mode): >>> K.redistribute(0) >>> print(K) - ktensor of shape 2 x 2 + ktensor of shape (2, 2) weights=[1. 1.] factor_matrices[0] = [[1. 4.] @@ -1615,8 +1499,8 @@ def score(self, other, weight_penalty=True, threshold=0.99, greedy=True): Create two :class:`pyttb.ktensor` instances and compute the score between them: - >>> 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))) + >>> K = ttb.ktensor([np.ones((3,3)), np.ones((4,3)), np.ones((5,3))], np.array([2., 1., 3.])) + >>> K2 = ttb.ktensor([np.ones((3,2)), np.ones((4,2)), np.ones((5,2))], np.array([2., 4.])) >>> score,Kperm,flag,perm = K.score(K2) >>> print(score) 0.875 @@ -1653,8 +1537,8 @@ def score(self, other, weight_penalty=True, threshold=0.99, greedy=True): assert False, "Tensor A must have at least as many components as tensor B" # Make sure columns of factor matrices are normalized - A = ttb.ktensor.from_tensor_type(self).normalize() - B = ttb.ktensor.from_tensor_type(other).normalize() + A = self.copy().normalize() + B = other.copy().normalize() # Compute all possible vector-vector congruences. @@ -1725,9 +1609,9 @@ def symmetrize(self): >>> fm0 = np.array([[1., 2.], [3., 4.]]) >>> fm1 = np.array([[5., 6.], [7., 8.]]) >>> factor_matrices = [fm0, fm1] - >>> K = ttb.ktensor.from_data(weights, factor_matrices) + >>> K = ttb.ktensor(factor_matrices, weights) >>> print(K) - ktensor of shape 2 x 2 + ktensor of shape (2, 2) weights=[1. 2.] factor_matrices[0] = [[1. 2.] @@ -1741,7 +1625,7 @@ def symmetrize(self): >>> K1 = K.symmetrize() >>> print(K1) # doctest: +ELLIPSIS - ktensor of shape 2 x 2 + ktensor of shape (2, 2) weights=[1. 1.] factor_matrices[0] = [[2.3404... 4.9519...] @@ -1756,7 +1640,7 @@ def symmetrize(self): ).all(), "Tensor is not cubic -- cannot be symmetrized" # Distribute lambda evenly into factors - K = self.from_tensor_type(self) + K = self.copy() K.normalize("all") weights = K.weights @@ -1780,7 +1664,7 @@ def symmetrize(self): weights[j] = -weights[j] V[:, [j]] = -V[:, [j]] - return self.from_data(weights, [V.copy() for i in range(K.ndims)]) + return ttb.ktensor([V.copy() for i in range(K.ndims)], weights) def tolist(self, mode=None): """ @@ -1805,9 +1689,9 @@ def tolist(self, mode=None): >>> fm0 = np.array([[1., 2.], [3., 4.]]) >>> fm1 = np.array([[5., 6.], [7., 8.]]) >>> factor_matrices = [fm0, fm1] - >>> K = ttb.ktensor.from_data(weights, factor_matrices) + >>> K = ttb.ktensor(factor_matrices, weights) >>> print(K) - ktensor of shape 2 x 2 + ktensor of shape (2, 2) weights=[1. 2.] factor_matrices[0] = [[1. 2.] @@ -1884,7 +1768,7 @@ def tovec(self, include_weights=True): >>> 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 x 3 x 4 + ktensor of shape (2, 3, 4) weights=[2. 2.] factor_matrices[0] = [[1. 3.] @@ -1904,7 +1788,7 @@ def tovec(self, include_weights=True): >>> K2 = ttb.ktensor.from_vector(K.tovec(), shape, True) >>> print(K2) - ktensor of shape 2 x 3 x 4 + ktensor of shape (2, 3, 4) weights=[2. 2.] factor_matrices[0] = [[1. 3.] @@ -1987,7 +1871,7 @@ def ttv(self, vector, dims=None, exclude_dims=None): >>> 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 x 4 + ktensor of shape (2, 4) weights=[36. 54.] factor_matrices[0] = [[1. 3.] @@ -2013,7 +1897,7 @@ def ttv(self, vector, dims=None, exclude_dims=None): >>> K2 = K.ttv([vec4, vec3],np.array([2, 1])) >>> print(K2) - ktensor of shape 2 + ktensor of shape (2,) weights=[1800. 3564.] factor_matrices[0] = [[1. 3.] @@ -2057,7 +1941,7 @@ def ttv(self, vector, dims=None, exclude_dims=None): factor_matrices = [] for i in remdims: factor_matrices.append(self.factor_matrices[i]) - return ktensor.from_data(new_weights, factor_matrices) + return ttb.ktensor(factor_matrices, new_weights, copy=False) def update(self, modes, data): """ @@ -2099,7 +1983,7 @@ def update(self, modes, data): >>> K1 = K.copy() >>> K1 = K1.update(0, vec0) >>> print(K1) - ktensor of shape 2 x 3 x 4 + ktensor of shape (2, 3, 4) weights=[1. 1.] factor_matrices[0] = [[2. 2.] @@ -2120,7 +2004,7 @@ def update(self, modes, data): >>> vec_all = np.concatenate((vec0, vec1, vec2)) >>> K2 = K2.update([0, 1, 2], vec_all) >>> print(K2) - ktensor of shape 2 x 3 x 4 + ktensor of shape (2, 3, 4) weights=[1. 1.] factor_matrices[0] = [[2. 2.] @@ -2141,7 +2025,7 @@ def update(self, modes, data): >>> vec_some = np.concatenate((vec0, vec2)) >>> K3 = K3.update([0, 2], vec_some) >>> print(K3) - ktensor of shape 2 x 3 x 4 + ktensor of shape (2, 3, 4) weights=[1. 1.] factor_matrices[0] = [[2. 2.] @@ -2217,7 +2101,7 @@ def __add__(self, other): (self.factor_matrices[k], other.factor_matrices[k]), axis=1 ) ) - return ktensor.from_data(weights, factor_matrices) + return ttb.ktensor(factor_matrices, weights) def __getitem__(self, item): """ @@ -2287,7 +2171,7 @@ def __neg__(self): ------- :class:`pyttb.ktensor` """ - return ktensor.from_data(-self.weights, self.factor_matrices) + return ttb.ktensor(self.factor_matrices, -self.weights) def __pos__(self): """ @@ -2297,7 +2181,7 @@ def __pos__(self): ------- :class:`pyttb.ktensor` """ - return ktensor.from_tensor_type(self) + return self.copy() def __setitem__(self, key, value): """ @@ -2309,12 +2193,12 @@ def __setitem__(self, key, value): Example ------- - >>> 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 = ttb.ktensor([np.random.random((2,4)), np.random.random((3,4)), np.random.random((4,4))], np.ones((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 + ktensor of shape (2, 3, 4) weights=[[2.] [2.] [2.] @@ -2362,7 +2246,7 @@ def __sub__(self, other): (self.factor_matrices[k], other.factor_matrices[k]), axis=1 ) ) - return ktensor.from_data(weights, factor_matrices) + return ttb.ktensor(factor_matrices, weights) def __mul__(self, other): """ @@ -2381,7 +2265,7 @@ def __mul__(self, other): return other.__mul__(self) if isinstance(other, (float, int)): - return ktensor.from_data(other * self.weights, self.factor_matrices) + return ttb.ktensor(self.factor_matrices, other * self.weights) assert ( False @@ -2410,15 +2294,14 @@ def __repr__(self): ------- str: """ - s = "" - s += "ktensor of shape " - s += (" x ").join([str(int(d)) for d in self.shape]) - s += "\n" - s += "weights=" - s += str(self.weights) - for i in range(len(self.factor_matrices)): - s += "\nfactor_matrices[{}] =\n".format(i) - s += str(self.factor_matrices[i]) + s = f"ktensor of shape {self.shape}\n" + s += f"weights={str(self.weights)}" + if len(self.shape) == 0: + s += "\nfactor_matrices=[]" + else: + for i in range(len(self.factor_matrices)): + s += "\nfactor_matrices[{}] =\n".format(i) + s += str(self.factor_matrices[i]) return s __str__ = __repr__ diff --git a/pyttb/tensor.py b/pyttb/tensor.py index 187af709..55a597b2 100644 --- a/pyttb/tensor.py +++ b/pyttb/tensor.py @@ -671,7 +671,7 @@ def mttkrp(self, U: Union[ttb.ktensor, List[np.ndarray]], n: int) -> np.ndarray: # extract the list of factor matrices if given a ktensor if isinstance(U, ttb.ktensor): - U = ttb.ktensor.from_tensor_type(U) + U = U.copy() if n == 0: U.redistribute(1) else: diff --git a/tests/test_cp_apr.py b/tests/test_cp_apr.py index 7e1a7fc4..48ff7c95 100644 --- a/tests/test_cp_apr.py +++ b/tests/test_cp_apr.py @@ -22,7 +22,7 @@ def test_loglikelihood(): fm0 = np.array([[0.0, 0.0], [3.0, 4.0]]) fm1 = np.array([[0.0, 6.0], [7.0, 8.0]]) factor_matrices = [fm0, fm1] - ktensorInstance = ttb.ktensor.from_data(weights, factor_matrices) + ktensorInstance = ttb.ktensor(factor_matrices, weights) tensorInstance = ktensorInstance.full() sptensorInstance = ttb.sptensor.from_tensor_type(tensorInstance) @@ -45,7 +45,7 @@ def test_loglikelihood(): factor_matrices = [] for i in range(n): factor_matrices.append(np.abs(np.random.normal(size=(5, n)))) - ktensorInstance = ttb.ktensor.from_data(weights, factor_matrices) + ktensorInstance = ttb.ktensor(factor_matrices, weights) tensorInstance = ttb.tensor.from_data( np.abs(np.random.normal(size=ktensorInstance.shape)) ) @@ -75,7 +75,7 @@ def test_calculatePi(): fm0 = np.array([[0.0, 0.0], [3.0, 4.0]]) fm1 = np.array([[0.0, 6.0], [7.0, 8.0]]) factor_matrices = [fm0, fm1] - ktensorInstance = ttb.ktensor.from_data(weights, factor_matrices) + ktensorInstance = ttb.ktensor(factor_matrices, weights) tensorInstance = ktensorInstance.full() sptensorInstance = ttb.sptensor.from_tensor_type(tensorInstance) answer = np.array([[0, 6], [7, 8]]) @@ -104,7 +104,7 @@ def test_calculatePi(): factor_matrices = [] for i in range(n): factor_matrices.append(np.abs(np.random.normal(size=(5, n)))) - ktensorInstance = ttb.ktensor.from_data(weights, factor_matrices) + ktensorInstance = ttb.ktensor(factor_matrices, weights) tensorInstance = ttb.tensor.from_data(np.abs(np.random.normal(size=ktensorInstance.shape))) sptensorInstance = ttb.sptensor.from_tensor_type(tensorInstance) @@ -125,7 +125,7 @@ def test_calculatePhi(): fm0 = np.array([[0.0, 0.0], [3.0, 4.0]]) fm1 = np.array([[0.0, 6.0], [7.0, 8.0]]) factor_matrices = [fm0, fm1] - ktensorInstance = ttb.ktensor.from_data(weights, factor_matrices) + ktensorInstance = ttb.ktensor(factor_matrices, weights) tensorInstance = ktensorInstance.full() sptensorInstance = ttb.sptensor.from_tensor_type(tensorInstance) answer = np.array([[0, 0], [11.226415094339623, 24.830188679245282]]) @@ -145,7 +145,7 @@ def test_cpapr_mu(capsys): fm0 = np.array([[0.0, 0.0], [3.0, 4.0]]) fm1 = np.array([[0.0, 6.0], [7.0, 8.0]]) factor_matrices = [fm0, fm1] - ktensorInstance = ttb.ktensor.from_data(weights, factor_matrices) + ktensorInstance = ttb.ktensor(factor_matrices, weights) tensorInstance = ktensorInstance.full() np.random.seed(123) M, _, _ = ttb.cp_apr(tensorInstance, 2, printinneritn=1) @@ -172,7 +172,7 @@ def test_cpapr_pdnr(capsys): fm0 = np.array([[0.0, 0.0], [3.0, 4.0]]) fm1 = np.array([[0.0, 6.0], [7.0, 8.0]]) factor_matrices = [fm0, fm1] - ktensorInstance = ttb.ktensor.from_data(weights, factor_matrices) + ktensorInstance = ttb.ktensor(factor_matrices, weights) tensorInstance = ktensorInstance.full() np.random.seed(123) M, _, _ = ttb.cp_apr( @@ -208,7 +208,7 @@ def test_cpapr_pqnr(capsys): fm0 = np.array([[0.0, 0.0], [3.0, 4.0]]) fm1 = np.array([[0.0, 6.0], [7.0, 8.0]]) factor_matrices = [fm0, fm1] - ktensorInstance = ttb.ktensor.from_data(weights, factor_matrices) + ktensorInstance = ttb.ktensor(factor_matrices, weights) tensorInstance = ktensorInstance.full() np.random.seed(123) with pytest.raises(AssertionError) as excinfo: @@ -220,7 +220,7 @@ def test_cpapr_pqnr(capsys): fm0 = np.array([[1.0, 1.0], [3.0, 4.0]]) fm1 = np.array([[1.0, 6.0], [7.0, 8.0]]) factor_matrices = [fm0, fm1] - ktensorInstance = ttb.ktensor.from_data(weights, factor_matrices) + ktensorInstance = ttb.ktensor(factor_matrices, weights) tensorInstance = ktensorInstance.full() np.random.seed(123) M, _, _ = ttb.cp_apr(tensorInstance, 2, algorithm="pqnr", printinneritn=1) @@ -252,7 +252,7 @@ def test_calculatepi_prowsubprob(): fm0 = np.array([[0.0, 0.0], [3.0, 4.0]]) fm1 = np.array([[0.0, 6.0], [7.0, 8.0]]) factor_matrices = [fm0, fm1] - ktensorInstance = ttb.ktensor.from_data(weights, factor_matrices) + ktensorInstance = ttb.ktensor(factor_matrices, weights) tensorInstance = ktensorInstance.full() sptensorInstance = ttb.sptensor.from_tensor_type(tensorInstance) answer = np.array([[0, 6], [7, 8]]) @@ -287,7 +287,7 @@ def test_calc_partials(): fm0 = np.array([[0.0, 0.0], [3.0, 4.0]]) fm1 = np.array([[0.0, 6.0], [7.0, 8.0]]) factor_matrices = [fm0, fm1] - ktensorInstance = ttb.ktensor.from_data(weights, factor_matrices) + ktensorInstance = ttb.ktensor(factor_matrices, weights) tensorInstance = ktensorInstance.full() # print(tensorInstance[:, 0]) sptensorInstance = ttb.sptensor.from_tensor_type(tensorInstance) @@ -350,7 +350,7 @@ def test_getHessian(): fm0 = np.array([[0.0, 0.0], [3.0, 4.0]]) fm1 = np.array([[0.0, 6.0], [7.0, 8.0]]) factor_matrices = [fm0, fm1] - ktensorInstance = ttb.ktensor.from_data(weights, factor_matrices) + ktensorInstance = ttb.ktensor(factor_matrices, weights) tensorInstance = ktensorInstance.full() # print(tensorInstance[:, 0]) free_indices = [0, 1] @@ -396,7 +396,7 @@ def test_getSearchDirPdnr(): fm0 = np.array([[0.0, 0.0], [3.0, 4.0]]) fm1 = np.array([[0.0, 6.0], [7.0, 8.0]]) factor_matrices = [fm0, fm1] - ktensorInstance = ttb.ktensor.from_data(weights, factor_matrices) + ktensorInstance = ttb.ktensor(factor_matrices, weights) tensorInstance = ktensorInstance.full() # print(tensorInstance[:, 0]) sptensorInstance = ttb.sptensor.from_tensor_type(tensorInstance) @@ -435,7 +435,7 @@ def test_tt_loglikelihood_row(): fm0 = np.array([[0.0, 0.0], [3.0, 4.0]]) fm1 = np.array([[0.0, 6.0], [7.0, 8.0]]) factor_matrices = [fm0, fm1] - ktensorInstance = ttb.ktensor.from_data(weights, factor_matrices) + ktensorInstance = ttb.ktensor(factor_matrices, weights) tensorInstance = ktensorInstance.full() # print(tensorInstance[:, 0]) sptensorInstance = ttb.sptensor.from_tensor_type(tensorInstance) @@ -453,7 +453,7 @@ def test_tt_linesearch_prowsubprob(): fm0 = np.array([[0.0, 0.0], [3.0, 4.0]]) fm1 = np.array([[0.0, 6.0], [7.0, 8.0]]) factor_matrices = [fm0, fm1] - ktensorInstance = ttb.ktensor.from_data(weights, factor_matrices) + ktensorInstance = ttb.ktensor(factor_matrices, weights) tensorInstance = ktensorInstance.full() # print(tensorInstance[:, 0]) sptensorInstance = ttb.sptensor.from_tensor_type(tensorInstance) @@ -490,7 +490,7 @@ def test_getSearchDirPqnr(): fm0 = np.array([[0.0, 0.0], [3.0, 4.0]]) fm1 = np.array([[0.0, 6.0], [7.0, 8.0]]) factor_matrices = [fm0, fm1] - ktensorInstance = ttb.ktensor.from_data(weights, factor_matrices) + ktensorInstance = ttb.ktensor(factor_matrices, weights) tensorInstance = ktensorInstance.full() # print(tensorInstance[:, 0]) sptensorInstance = ttb.sptensor.from_tensor_type(tensorInstance) @@ -511,19 +511,15 @@ def test_cp_apr_negative_tests(): dense_tensor = ttb.tensor.from_data(np.ones((2, 2, 2))) bad_weights = np.array([8.0]) bad_factors = [np.array([[1.0]])] * 3 - bad_initial_guess_shape = ttb.ktensor.from_data(bad_weights, bad_factors) + bad_initial_guess_shape = ttb.ktensor(bad_factors, bad_weights) with pytest.raises(AssertionError): ttb.cp_apr(dense_tensor, init=bad_initial_guess_shape, rank=1) good_weights = np.array([8.0] * 3) good_factor = np.array([[1.0, 1.0, 1.0], [1.0, 1.0, 1.0]]) - bad_initial_guess_factors = ttb.ktensor.from_data( - good_weights, [-1.0 * good_factor] * 3 - ) + bad_initial_guess_factors = ttb.ktensor([-1.0 * good_factor] * 3, good_weights) with pytest.raises(AssertionError): ttb.cp_apr(dense_tensor, init=bad_initial_guess_factors, rank=3) - bad_initial_guess_weight = ttb.ktensor.from_data( - -1.0 * good_weights, [good_factor] * 3 - ) + bad_initial_guess_weight = ttb.ktensor([good_factor] * 3, -1.0 * good_weights) with pytest.raises(AssertionError): ttb.cp_apr(dense_tensor, init=bad_initial_guess_weight, rank=3) diff --git a/tests/test_import_export_data.py b/tests/test_import_export_data.py index 2771fa05..c45006b6 100644 --- a/tests/test_import_export_data.py +++ b/tests/test_import_export_data.py @@ -51,12 +51,12 @@ def sample_sptensor(): @pytest.fixture() def sample_ktensor(): # truth data - weights = np.array([3, 2]) + weights = np.array([3.0, 2.0]) fm0 = np.array([[1.0, 5.0], [2.0, 6.0], [3.0, 7.0], [4.0, 8.0]]) fm1 = np.array([[2.0, 7.0], [3.0, 8.0], [4.0, 9.0], [5.0, 10.0], [6.0, 11.0]]) fm2 = np.array([[3.0, 6.0], [4.0, 7.0], [5.0, 8.0]]) factor_matrices = [fm0, fm1, fm2] - K = ttb.ktensor.from_data(weights, factor_matrices) + K = ttb.ktensor(factor_matrices, weights) return K diff --git a/tests/test_ktensor.py b/tests/test_ktensor.py index 5444344c..31352f2d 100644 --- a/tests/test_ktensor.py +++ b/tests/test_ktensor.py @@ -17,7 +17,7 @@ def sample_ktensor_2way(): fm1 = np.array([[5.0, 6.0], [7.0, 8.0]]) factor_matrices = [fm0, fm1] data = {"weights": weights, "factor_matrices": factor_matrices} - ktensorInstance = ttb.ktensor.from_data(weights, factor_matrices) + ktensorInstance = ttb.ktensor(factor_matrices, weights) return data, ktensorInstance @@ -41,7 +41,7 @@ def sample_ktensor_3way(): "vector_with_weights": vector_with_weights, "shape": shape, } - ktensorInstance = ttb.ktensor.from_data(weights, factor_matrices) + ktensorInstance = ttb.ktensor(factor_matrices, weights) return data, ktensorInstance @@ -56,88 +56,80 @@ def sample_ktensor_symmetric(): ) factor_matrices = [fm0, fm1] data = {"weights": weights, "factor_matrices": factor_matrices} - ktensorInstance = ttb.ktensor.from_data(weights, factor_matrices) + ktensorInstance = ttb.ktensor(factor_matrices, weights) return data, ktensorInstance -@pytest.mark.indevelopment -def test_ktensor_init(): +def test_ktensor_init(sample_ktensor_2way): empty = np.array([]) - # No args - K0 = ttb.ktensor() - assert (K0.weights == empty).all() - assert K0.factor_matrices == [] + # Input: none (empty ktensor) + K = ttb.ktensor() + assert (K.weights == empty).all() + assert K.factor_matrices == [] + # Input: factor_matrices, weights + (data, K) = sample_ktensor_2way + K = ttb.ktensor(data["factor_matrices"], data["weights"]) + assert (K.weights == data["weights"]).all() + assert (K.factor_matrices[0] == data["factor_matrices"][0]).all() + assert (K.factor_matrices[1] == data["factor_matrices"][1]).all() -@pytest.mark.indevelopment -def test_ktensor_from_tensor_type(sample_ktensor_2way): - (data, K0) = sample_ktensor_2way - K1 = ttb.ktensor.from_tensor_type(K0) - assert (K0.weights == K1.weights).all() - assert (K0.factor_matrices[0] == K1.factor_matrices[0]).all() - assert (K0.factor_matrices[1] == K1.factor_matrices[1]).all() - # won't work with instances other than ktensors + # Input: factor_matrices provided, weights = None + K = ttb.ktensor(data["factor_matrices"]) + assert ( + K.weights + == np.ones( + 2, + ) + ).all() + assert (K.factor_matrices[0] == data["factor_matrices"][0]).all() + assert (K.factor_matrices[1] == data["factor_matrices"][1]).all() + + # Input: copy=True + K = ttb.ktensor(data["factor_matrices"], data["weights"]) + K2 = ttb.ktensor(K.factor_matrices, K.weights, copy=True) + assert not (K.weights is K2.weights) + assert not (K.factor_matrices[0] is K2.factor_matrices[0]) + assert not (K.factor_matrices[1] is K2.factor_matrices[1]) + + # Input: copy=False + K = ttb.ktensor(data["factor_matrices"], data["weights"]) + K3 = ttb.ktensor(K.factor_matrices, K.weights, copy=False) + assert K.weights is K3.weights + assert K.factor_matrices[0] is K3.factor_matrices[0] + assert K.factor_matrices[1] is K3.factor_matrices[1] + + # Errors + # Cannot specify weights and not factor_matrices with pytest.raises(AssertionError) as excinfo: - K2 = ttb.ktensor.from_tensor_type(np.ones((2, 2))) - assert "Cannot convert from to ktensor" in str(excinfo) - - -@pytest.mark.indevelopment -def test_ktensor_from_factor_matrices(sample_ktensor_2way): - (data, K0) = sample_ktensor_2way - K0 = ttb.ktensor.from_factor_matrices(data["factor_matrices"]) - assert (K0.weights == np.ones(2)).all() - assert (K0.factor_matrices[0] == data["factor_matrices"][0]).all() - assert (K0.factor_matrices[1] == data["factor_matrices"][1]).all() - - # Create ktensor with weights and multiple factor matrices as arguments - K1 = ttb.ktensor.from_factor_matrices( - data["factor_matrices"][0], data["factor_matrices"][1] - ) - assert (K1.weights == np.ones(2)).all() - assert (K1.factor_matrices[0] == data["factor_matrices"][0]).all() - assert (K1.factor_matrices[1] == data["factor_matrices"][1]).all() - - -@pytest.mark.indevelopment -def test_ktensor_from_data(sample_ktensor_2way, capsys): - (data, K0) = sample_ktensor_2way - K0 = ttb.ktensor.from_data(data["weights"], data["factor_matrices"]) - assert (K0.weights == data["weights"]).all() - assert (K0.factor_matrices[0] == data["factor_matrices"][0]).all() - assert (K0.factor_matrices[1] == data["factor_matrices"][1]).all() + KE = ttb.ktensor(None, np.array([2.0, 1.0])) + assert "factor_matrices cannot be None if weights are provided." in str(excinfo) - # Create ktensor with weights and multiple factor matrices as arguments - K1 = ttb.ktensor.from_data( - data["weights"], data["factor_matrices"][0], data["factor_matrices"][1] - ) - assert (K1.weights == data["weights"]).all() - assert (K1.factor_matrices[0] == data["factor_matrices"][0]).all() - assert (K1.factor_matrices[1] == data["factor_matrices"][1]).all() + # 'factor_matrices' must be a list + with pytest.raises(AssertionError) as excinfo: + KE = ttb.ktensor(np.ones((2, 2)), np.array([2.0])) + assert "Input 'factor_matrices' must be a list." in str(excinfo) - # Weights that are int should be converted - weights_int = np.array([1, 2]) - K2 = ttb.ktensor.from_data(weights_int, data["factor_matrices"]) - out, err = capsys.readouterr() + # each factor matrix should be a np.ndarray + with pytest.raises(AssertionError) as excinfo: + KE = ttb.ktensor( + [np.ones((2, 2)), np.ones((2, 2)).astype(int)], np.array([2.0, 1.0]) + ) assert ( - "converting weights from int64 to float" in out - or "converting weights from int32 to float" in out + "Each item in 'factor_matrices' must be a numpy.ndarray object with dtype=float." + in str(excinfo) ) - # Weights that are int should be converted - fm0 = np.array([[1, 2], [3, 4]]) - fm1 = np.array([[5, 6], [7, 8]]) - factor_matrices = [fm0, fm1] - K3 = ttb.ktensor.from_data(data["weights"], factor_matrices) - out, err = capsys.readouterr() + # the number of columns of all factor_matrices must be equal + with pytest.raises(AssertionError) as excinfo: + KE = ttb.ktensor([np.ones((2, 2)), np.ones((2, 1))], np.array([2.0, 1.0])) assert ( - "converting factor_matrices[0] from int64 to float" in out - or "converting factor_matrices[0] from int32 to float" in out + "The number of columns each item in 'factor_matrices' must be the same." + in str(excinfo) ) -@pytest.mark.indevelopment def test_ktensor_from_function(): K0 = ttb.ktensor.from_function(np.ones, (2, 3, 4), 2) assert (K0.weights == np.array([1.0, 1.0])).all() @@ -165,7 +157,6 @@ def test_ktensor_from_function(): assert np.linalg.norm(K1.factor_matrices[2] - fm2) < 1e-8 -@pytest.mark.indevelopment def test_ktensor_from_vector(sample_ktensor_3way): (data, K0) = sample_ktensor_3way @@ -197,12 +188,11 @@ def test_ktensor_from_vector(sample_ktensor_3way): assert "Input parameter 'data' is not the right length." in str(excinfo) -@pytest.mark.indevelopment def test_ktensor_arrange(sample_ktensor_2way): (data, K) = sample_ktensor_2way # permutation only - K0 = ttb.ktensor.from_tensor_type(K) + K0 = K.copy() p = [1, 0] K0.arrange(permutation=p) assert (K0.weights == data["weights"][p]).all() @@ -210,7 +200,7 @@ def test_ktensor_arrange(sample_ktensor_2way): assert (K0.factor_matrices[1] == data["factor_matrices"][1][:, p]).all() # normalize and arrange by sorting weights (default) - K1 = ttb.ktensor.from_tensor_type(K) + K1 = K.copy() K1.arrange() weights = np.array([89.4427191, 27.20294102]) fm0 = np.array([[0.4472136, 0.31622777], [0.89442719, 0.9486833]]) @@ -236,7 +226,6 @@ def test_ktensor_arrange(sample_ktensor_2way): ) -@pytest.mark.indevelopment def test_ktensor_copy(sample_ktensor_2way): (data, K0) = sample_ktensor_2way K1 = K0.copy() @@ -249,7 +238,6 @@ def test_ktensor_copy(sample_ktensor_2way): assert not (K0.weights[0] == K1.weights[0]) -@pytest.mark.indevelopment def test_ktensor_double(sample_ktensor_2way, sample_ktensor_3way): (data2, K2) = sample_ktensor_2way assert (K2.double() == np.array([[29.0, 39.0], [63.0, 85.0]])).all() @@ -285,7 +273,6 @@ def test_ktensor_double(sample_ktensor_2way, sample_ktensor_3way): assert (K3.double() == A).all() -@pytest.mark.indevelopment def test_ktensor_end(sample_ktensor_3way): (data, K) = sample_ktensor_3way assert K.end() == 23 @@ -294,7 +281,6 @@ def test_ktensor_end(sample_ktensor_3way): assert K.end(k=2) == 3 -@pytest.mark.indevelopment def test_ktensor_extract(sample_ktensor_3way): (data, K) = sample_ktensor_3way weights = data["weights"][[1]] @@ -302,7 +288,7 @@ def test_ktensor_extract(sample_ktensor_3way): fm1 = data["factor_matrices"][1][:, [1]] fm2 = data["factor_matrices"][2][:, [1]] factor_matrices = [fm0, fm1, fm2] - K_new = ttb.ktensor.from_data(weights, factor_matrices) + K_new = ttb.ktensor(factor_matrices, weights) # int K_extracted = K.extract(1) @@ -344,11 +330,10 @@ def test_ktensor_extract(sample_ktensor_3way): ) -@pytest.mark.indevelopment def test_ktensor_fixsigns(sample_ktensor_2way): (data, K) = sample_ktensor_2way - K1 = ttb.ktensor.from_tensor_type(K) - K2 = ttb.ktensor.from_tensor_type(K) + K1 = K.copy() + K2 = K.copy() # use same ktensor K1.factor_matrices[0][1, 1] = -K1.factor_matrices[0][1, 1] @@ -380,8 +365,8 @@ def test_ktensor_fixsigns(sample_ktensor_2way): assert "other must be a ktensor" in str(excinfo) # test odd number of vectors to flip - K = ttb.ktensor.from_data( - np.array([3, 2, 1]), np.ones((3, 3)), np.ones((4, 3)), np.ones((5, 3)) + K = ttb.ktensor( + [np.ones((3, 3)), np.ones((4, 3)), np.ones((5, 3))], np.array([3.0, 2.0, 1.0]) ) K2 = K.copy() # one column to flip @@ -393,7 +378,6 @@ def test_ktensor_fixsigns(sample_ktensor_2way): K = K.fixsigns(K2) -@pytest.mark.indevelopment def test_ktensor_full(sample_ktensor_2way, sample_ktensor_3way): (data, K2) = sample_ktensor_2way assert K2.full().isequal( @@ -403,7 +387,6 @@ def test_ktensor_full(sample_ktensor_2way, sample_ktensor_3way): print(K3.full()) -@pytest.mark.indevelopment def test_ktensor_innerprod(sample_ktensor_2way): (data, K) = sample_ktensor_2way assert K.innerprod(K) == 13556 @@ -411,14 +394,14 @@ def test_ktensor_innerprod(sample_ktensor_2way): # test with tensor Tdata = np.array([[1, 2], [3, 4]]) Tshape = (2, 2) - T = ttb.tensor().from_data(Tdata, Tshape) + T = ttb.tensor.from_data(Tdata, Tshape) assert K.innerprod(T) == 636 # test with sptensor Ssubs = np.array([[0, 0], [0, 1], [1, 1]]) Svals = np.array([[0.5], [1.0], [1.5]]) Sshape = (2, 2) - S = ttb.sptensor().from_data(Ssubs, Svals, Sshape) + S = ttb.sptensor.from_data(Ssubs, Svals, Sshape) assert K.innerprod(S) == 181 # Wrong shape @@ -428,28 +411,26 @@ def test_ktensor_innerprod(sample_ktensor_2way): assert "Innerprod can only be computed for tensors of the same size" in str(excinfo) -@pytest.mark.indevelopment def test_ktensor_isequal(sample_ktensor_2way): (data, K0) = sample_ktensor_2way # should be equal - K1 = ttb.ktensor.from_tensor_type(K0) + K1 = K0.copy() assert K0.isequal(K1) # ncomponents don't match K2 = ttb.ktensor.from_function(np.ones, (2, 2), 3) assert ~(K0.isequal(K2)) # weights don't match - K3 = ttb.ktensor.from_tensor_type(K0) + K3 = K0.copy() K3.weights[0] = 10 assert ~(K0.isequal(K3)) # types don't match assert ~(K0.isequal(np.array([]))) # factor_matrices don't match - K4 = ttb.ktensor.from_tensor_type(K0) + K4 = K0.copy() K4.factor_matrices[0] = np.zeros((2, 2)) assert ~(K0.isequal(K4)) -@pytest.mark.indevelopment def test_ktensor_issymetric(sample_ktensor_2way, sample_ktensor_symmetric): # should not be symmetric (data, K) = sample_ktensor_2way @@ -470,7 +451,6 @@ def test_ktensor_issymetric(sample_ktensor_2way, sample_ktensor_symmetric): assert (diffs2 == np.array([[0.0, np.inf], [0.0, 0]])).all() -@pytest.mark.indevelopment def test_ktensor_mask(sample_ktensor_2way): (data, K) = sample_ktensor_2way W = ttb.tensor.from_data(np.array([[0, 1], [1, 0]])) @@ -482,7 +462,6 @@ def test_ktensor_mask(sample_ktensor_2way): assert "Mask cannot be bigger than the data tensor" in str(excinfo) -@pytest.mark.indevelopment def test_ktensor_mttkrp(sample_ktensor_3way): (data, K) = sample_ktensor_3way K1 = ttb.ktensor.from_function(np.ones, (2, 3, 4), 4) @@ -529,13 +508,11 @@ def test_ktensor_mttkrp(sample_ktensor_3way): assert "Second argument must be list of numpy.ndarray's" in str(excinfo) -@pytest.mark.indevelopment def test_ktensor_ncomponents(sample_ktensor_2way): (data, K0) = sample_ktensor_2way assert K0.ncomponents == 2 -@pytest.mark.indevelopment def test_ktensor_ndims(sample_ktensor_2way, sample_ktensor_3way): (data, K0) = sample_ktensor_2way assert K0.ndims == 2 @@ -543,7 +520,6 @@ def test_ktensor_ndims(sample_ktensor_2way, sample_ktensor_3way): assert K1.ndims == 3 -@pytest.mark.indevelopment def test_ktensor_norm(): K0 = ttb.ktensor.from_function(np.zeros, (2, 3, 4), 2) assert pytest.approx(K0.norm(), 1e-8) == 0 @@ -560,14 +536,13 @@ def test_ktensor_norm(): assert pytest.approx(K2.norm(), 1e-8) == 6.337788257744180e03 -@pytest.mark.indevelopment def test_ktensor_normalize(sample_ktensor_2way, sample_ktensor_3way): # get data and make several copies, so that all ktensor tests use the same data, as normalize computes in place data0, K0 = sample_ktensor_3way - K1 = ttb.ktensor.from_tensor_type(K0) - K2 = ttb.ktensor.from_tensor_type(K0) - K3 = ttb.ktensor.from_tensor_type(K0) - K4 = ttb.ktensor.from_tensor_type(K0) + K1 = K0.copy() + K2 = K0.copy() + K3 = K0.copy() + K4 = K0.copy() # normalize a single mode mode = 1 @@ -724,7 +699,6 @@ def test_ktensor_normalize(sample_ktensor_2way, sample_ktensor_3way): assert np.allclose(K5.factor_matrices[1], fm1) -@pytest.mark.indevelopment def test_ktensor_nvecs(sample_ktensor_3way): (data, K) = sample_ktensor_3way @@ -783,12 +757,11 @@ def test_ktensor_nvecs(sample_ktensor_3way): K.nvecs(1, 3) -@pytest.mark.indevelopment def test_ktensor_permute(sample_ktensor_3way): (data, K) = sample_ktensor_3way order = np.array([2, 0, 1]) fm = [data["factor_matrices"][i] for i in order] - K0 = ttb.ktensor.from_data(data["weights"], fm) + K0 = ttb.ktensor(fm, data["weights"]) assert K0.isequal(K.permute(order)) # invalid permutation @@ -798,7 +771,6 @@ def test_ktensor_permute(sample_ktensor_3way): assert "Invalid permutation" in str(excinfo) -@pytest.mark.indevelopment def test_ktensor_redistribute(sample_ktensor_2way): (data, K) = sample_ktensor_2way K.redistribute(0) @@ -807,15 +779,12 @@ def test_ktensor_redistribute(sample_ktensor_2way): assert (np.array([1, 1]) == K.weights).all() -pytest.mark.indevelopment - - def test_ktensor_score(): - A = ttb.ktensor.from_data( - np.array([2, 1, 3]), np.ones((3, 3)), np.ones((4, 3)), np.ones((5, 3)) + A = ttb.ktensor( + [np.ones((3, 3)), np.ones((4, 3)), np.ones((5, 3))], np.array([2.0, 1.0, 3.0]) ) - B = ttb.ktensor.from_data( - np.array([2, 4]), np.ones((3, 2)), np.ones((4, 2)), np.ones((5, 2)) + B = ttb.ktensor( + [np.ones((3, 2)), np.ones((4, 2)), np.ones((5, 2))], np.array([2.0, 4.0]) ) # defaults @@ -833,11 +802,11 @@ def test_ktensor_score(): assert (best_perm == np.array([0, 1, 2])).all() # zero lambda values lead to equal components - A0 = ttb.ktensor.from_data( - np.array([2, 0]), np.ones((3, 2)), np.ones((4, 2)), np.ones((5, 2)) + A0 = ttb.ktensor( + [np.ones((3, 2)), np.ones((4, 2)), np.ones((5, 2))], np.array([2.0, 0.0]) ) - B0 = ttb.ktensor.from_data( - np.array([2, 0]), np.ones((3, 2)), np.ones((4, 2)), np.ones((5, 2)) + B0 = ttb.ktensor( + [np.ones((3, 2)), np.ones((4, 2)), np.ones((5, 2))], np.array([2.0, 0.0]) ) score, Aperm, flag, best_perm = A0.score(B0) assert score == 1.0 @@ -851,14 +820,14 @@ def test_ktensor_score(): # try to compute score with tensor type other than ktensor with pytest.raises(AssertionError) as excinfo: - score, Aperm, flag, best_perm = A.score(ttb.tensor.from_tensor_type(B)) + score, Aperm, flag, best_perm = A.score(B.full()) assert "The first input should be a ktensor" in str(excinfo) # try to compute score when ktensor dimensions do not match with pytest.raises(AssertionError) as excinfo: # A is 3x4x5; B is 3x4x4 - B = ttb.ktensor.from_data( - np.array([2, 4]), np.ones((3, 2)), np.ones((4, 2)), np.ones((4, 2)) + B = ttb.ktensor( + [np.ones((3, 2)), np.ones((4, 2)), np.ones((4, 2))], np.array([2.0, 4.0]) ) score, Aperm, flag, best_perm = A.score(B) assert "Size mismatch" in str(excinfo) @@ -866,16 +835,13 @@ def test_ktensor_score(): # invalid: number of compnents of first ktensor must be greater than or # equal to number of components of second ktensor with pytest.raises(AssertionError) as excinfo: - B = ttb.ktensor.from_data( - np.array([2, 4]), np.ones((3, 2)), np.ones((4, 2)), np.ones((5, 2)) + B = ttb.ktensor( + [np.ones((3, 2)), np.ones((4, 2)), np.ones((5, 2))], np.array([2.0, 4.0]) ) score, Aperm, flag, best_perm = B.score(A) assert "Tensor A must have at least as many components as tensor B" in str(excinfo) -pytest.mark.indevelopment - - def test_ktensor_shape(sample_ktensor_2way, sample_ktensor_3way): (data, K0) = sample_ktensor_2way assert K0.shape == (2, 2) @@ -893,7 +859,6 @@ def test_ktensor_shape(sample_ktensor_2way, sample_ktensor_3way): assert "tuple index out of range" in str(excinfo) -@pytest.mark.indevelopment def test_ktensor_symmetrize(sample_ktensor_2way): (data, K) = sample_ktensor_2way K1 = K.symmetrize() @@ -911,7 +876,7 @@ def test_ktensor_symmetrize(sample_ktensor_2way): fm0 = np.ones((4, 3)) fm1 = np.ones((4, 3)) fm2 = -np.ones((4, 3)) - K2 = ttb.ktensor.from_data(weights, [fm0, fm1, fm2]) + K2 = ttb.ktensor([fm0, fm1, fm2], weights) K3 = K2.symmetrize() out_fm = np.array( [ @@ -928,7 +893,6 @@ def test_ktensor_symmetrize(sample_ktensor_2way): assert K3.issymmetric() -@pytest.mark.indevelopment def test_ktensor_tolist(sample_ktensor_3way): (data, K) = sample_ktensor_3way @@ -960,7 +924,7 @@ def test_ktensor_tolist(sample_ktensor_3way): assert np.allclose(fm0[2], m2) # weights are all 1 - K1 = ttb.ktensor.from_factor_matrices(fm0) + K1 = ttb.ktensor(fm0) fm0_tolist = K1.tolist() for i in range(len(fm0)): assert np.allclose(fm0[i], fm0_tolist[i]) @@ -998,14 +962,12 @@ def test_ktensor_tolist(sample_ktensor_3way): assert "Input parameter'mode' must be in the range of self.ndims" in str(excinfo) -@pytest.mark.indevelopment def test_ktensor_tovec(sample_ktensor_3way): (data, K0) = sample_ktensor_3way assert (data["vector_with_weights"] == K0.tovec()).all() assert (data["vector"] == K0.tovec(include_weights=False)).all() -@pytest.mark.indevelopment def test_ktensor_ttv(sample_ktensor_3way): (data, K) = sample_ktensor_3way K0 = K.ttv(np.array([1, 1, 1]), dims=1) @@ -1013,7 +975,7 @@ def test_ktensor_ttv(sample_ktensor_3way): fm0 = np.array([[1.0, 3.0], [2.0, 4.0]]) fm1 = np.array([[11.0, 15.0], [12.0, 16.0], [13.0, 17.0], [14.0, 18.0]]) factor_matrices = [fm0, fm1] - K1 = ttb.ktensor.from_data(weights, factor_matrices) + K1 = ttb.ktensor(factor_matrices, weights) assert K0.isequal(K1) # Empty dims requires that # vectors == # dimensions @@ -1035,16 +997,15 @@ def test_ktensor_ttv(sample_ktensor_3way): # Multiple dimensions, but fewer than all dimensions, not in same order as ktensor dimensions K2 = K.ttv([vec4, vec3], dims=np.array([2, 1])) weights = np.array([1800.0, 3564.0]) - fm0 = np.array([[1.0, 3.0], [2.0, 4.0]]) - assert K2.isequal(ttb.ktensor.from_data(weights, fm0)) + fm0 = [np.array([[1.0, 3.0], [2.0, 4.0]])] + assert K2.isequal(ttb.ktensor(fm0, weights)) -@pytest.mark.indevelopment def test_ktensor_update(sample_ktensor_3way): (data, K) = sample_ktensor_3way # single factor matrix updates - K1 = ttb.ktensor.from_tensor_type(K) + K1 = K.copy() vec0 = np.random.randn(K.shape[0] * K.ncomponents) vec1 = np.random.randn(K.shape[1] * K.ncomponents) vec2 = np.random.randn(K.shape[2] * K.ncomponents) @@ -1056,7 +1017,7 @@ def test_ktensor_update(sample_ktensor_3way): assert (K1.factor_matrices[2] == vec2.reshape((K1.shape[2], K1.ncomponents))).all() # all factor matrix updates - K2 = ttb.ktensor.from_tensor_type(K) + K2 = K.copy() vec_all = np.concatenate((vec0, vec1, vec2)) K2.update([0, 1, 2], vec_all) assert (K2.factor_matrices[0] == vec0.reshape((K2.shape[0], K2.ncomponents))).all() @@ -1064,7 +1025,7 @@ def test_ktensor_update(sample_ktensor_3way): assert (K2.factor_matrices[2] == vec2.reshape((K2.shape[2], K2.ncomponents))).all() # multiple but not all factor matrix updates - K3 = ttb.ktensor.from_tensor_type(K) + K3 = K.copy() vec_some = np.concatenate((vec0, vec2)) K3.update([0, 2], vec_some) assert (K3.factor_matrices[0] == vec0.reshape((K3.shape[0], K3.ncomponents))).all() @@ -1102,7 +1063,6 @@ def test_ktensor_update(sample_ktensor_3way): assert "Failed to consume all of the input data" in str(record[0].message) -@pytest.mark.indevelopment def test_ktensor__add__(sample_ktensor_2way, sample_ktensor_3way): (data0, K0) = sample_ktensor_2way (data1, K1) = sample_ktensor_3way @@ -1134,7 +1094,6 @@ def test_ktensor__add__(sample_ktensor_2way, sample_ktensor_3way): assert "Cannot add instance of this type to a ktensor" in str(excinfo) -@pytest.mark.indevelopment def test_ktensor__getitem__(sample_ktensor_2way): (data, K) = sample_ktensor_2way # adding ktensor to itself @@ -1157,7 +1116,6 @@ def test_ktensor__getitem__(sample_ktensor_2way): ) -@pytest.mark.indevelopment def test_ktensor__neg__(sample_ktensor_2way): (data0, K0) = sample_ktensor_2way # adding ktensor to itself @@ -1169,7 +1127,6 @@ def test_ktensor__neg__(sample_ktensor_2way): assert K2.isequal(K0) -@pytest.mark.indevelopment def test_ktensor__pos__(sample_ktensor_2way): (data0, K0) = sample_ktensor_2way # adding ktensor to itself @@ -1180,7 +1137,6 @@ def test_ktensor__pos__(sample_ktensor_2way): assert K1.isequal(K0) -@pytest.mark.indevelopment def test_ktensor__setitem__(sample_ktensor_2way): (data, K) = sample_ktensor_2way # adding ktensor to itself @@ -1192,7 +1148,6 @@ def test_ktensor__setitem__(sample_ktensor_2way): ) -@pytest.mark.indevelopment def test_ktensor__sub__(sample_ktensor_2way, sample_ktensor_3way): (data0, K0) = sample_ktensor_2way (data1, K1) = sample_ktensor_3way @@ -1223,7 +1178,6 @@ def test_ktensor__sub__(sample_ktensor_2way, sample_ktensor_3way): assert "Cannot subtract instance of this type from a ktensor" in str(excinfo) -@pytest.mark.indevelopment def test_ktensor__mul__(sample_ktensor_2way, sample_ktensor_3way): (data0, K0) = sample_ktensor_2way K1 = 2 * K0 @@ -1244,7 +1198,7 @@ def test_ktensor__mul__(sample_ktensor_2way, sample_ktensor_3way): # test with tensor Tdata = np.array([[1, 2], [3, 4]]) Tshape = (2, 2) - T = ttb.tensor().from_data(Tdata, Tshape) + T = ttb.tensor.from_data(Tdata, Tshape) K0T = K0 * T assert (K0T.double() == np.array([[29.0, 78.0], [189.0, 340.0]])).all() @@ -1252,13 +1206,12 @@ def test_ktensor__mul__(sample_ktensor_2way, sample_ktensor_3way): Ssubs = np.array([[0, 0], [0, 1], [1, 1]]) Svals = np.array([[0.5], [1.0], [1.5]]) Sshape = (2, 2) - S = ttb.sptensor().from_data(Ssubs, Svals, Sshape) + S = ttb.sptensor.from_data(Ssubs, Svals, Sshape) K0S = S * K0 assert (K0S.double() == np.array([[14.5, 39.0], [0.0, 127.5]])).all() -@pytest.mark.indevelopment def test_ktensor__str__(sample_ktensor_2way): (data0, K0) = sample_ktensor_2way - s = """ktensor of shape 2 x 2\nweights=[1. 2.]\nfactor_matrices[0] =\n[[1. 2.]\n [3. 4.]]\nfactor_matrices[1] =\n[[5. 6.]\n [7. 8.]]""" + s = """ktensor of shape (2, 2)\nweights=[1. 2.]\nfactor_matrices[0] =\n[[1. 2.]\n [3. 4.]]\nfactor_matrices[1] =\n[[5. 6.]\n [7. 8.]]""" assert K0.__str__() == s diff --git a/tests/test_sptensor.py b/tests/test_sptensor.py index a8317044..4b06b851 100644 --- a/tests/test_sptensor.py +++ b/tests/test_sptensor.py @@ -914,7 +914,7 @@ def test_sptensor__mul__(sample_sptensor): 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.from_data(weights, factor_matrices) + K = ttb.ktensor(factor_matrices, weights) subs = np.array([[0, 0], [0, 1], [1, 1]]) vals = np.array([[0.5], [1.0], [1.5]]) shape = (2, 2) @@ -944,7 +944,7 @@ def test_sptensor__rmul__(sample_sptensor): 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.from_data(weights, factor_matrices) + K = ttb.ktensor(factor_matrices, weights) subs = np.array([[0, 0], [0, 1], [1, 1]]) vals = np.array([[0.5], [1.0], [1.5]]) shape = (2, 2) @@ -1402,7 +1402,7 @@ def test_sptensor__truediv__(sample_sptensor): 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.from_data(weights, factor_matrices) + K = ttb.ktensor(factor_matrices, weights) subs = np.array([[0, 0], [0, 1], [1, 1]]) vals = np.array([[0.5], [1.0], [1.5]]) shape = (2, 2) @@ -1591,7 +1591,7 @@ def test_sptensor_mttkrp(sample_sptensor): ).all() # MTTKRP with factor matrices from ktensor - K = ttb.ktensor.from_factor_matrices([matrix, matrix, matrix]) + K = ttb.ktensor([matrix, matrix, matrix]) assert ( sptensorInstance.mttkrp(np.array([matrix, matrix, matrix]), 0) == sptensorInstance.mttkrp(K, 0) diff --git a/tests/test_tensor.py b/tests/test_tensor.py index c7832b53..6d92e090 100644 --- a/tests/test_tensor.py +++ b/tests/test_tensor.py @@ -1583,7 +1583,7 @@ def test_tensor_mttkrp(sample_tensor_2way): fm1 = np.array([[5.0, 8.0], [6.0, 9.0], [7.0, 10.0]]) fm2 = np.array([[11.0, 15.0], [12.0, 16.0], [13.0, 17.0], [14.0, 18.0]]) factor_matrices = [fm0, fm1, fm2] - ktensorInstance = ttb.ktensor.from_data(weights, factor_matrices) + ktensorInstance = ttb.ktensor(factor_matrices, weights) m0 = np.array([[1800.0, 3564.0], [1800.0, 3564.0]]) m1 = np.array([[300.0, 924.0], [300.0, 924.0], [300.0, 924.0]]) diff --git a/tests/test_ttensor.py b/tests/test_ttensor.py index b7a304e6..605a9562 100644 --- a/tests/test_ttensor.py +++ b/tests/test_ttensor.py @@ -174,7 +174,7 @@ def test_ttensor_innerproduct(sample_ttensor, random_ttensor): ) # ttensr innerprod ktensor - ktensorInstance = ttb.ktensor.from_data(np.array([8.0]), [np.array([[1.0]])] * 3) + ktensorInstance = ttb.ktensor([np.array([[1.0]])] * 3, np.array([8.0])) assert ttensorInstance.innerprod(ktensorInstance) == ttensorInstance.double() ** 2 # ttensor innerprod tensor (shape larger than core)