def initialize_factors(tensor, rank, init='svd', svd='numpy_svd', random_state=None, non_negative=False): r"""Initialize factors used in `parafac`. The type of initialization is set using `init`. If `init == 'random'` then initialize factor matrices using `random_state`. If `init == 'svd'` then initialize the `m`th factor matrix using the `rank` left singular vectors of the `m`th unfolding of the input tensor. Parameters ---------- tensor : ndarray rank : int init : {'svd', 'random'}, optional svd : str, default is 'numpy_svd' function to use to compute the SVD, acceptable values in tensorly.SVD_FUNS non_negative : bool, default is False if True, non-negative factors are returned Returns ------- factors : ndarray list List of initialized factors of the CP decomposition where element `i` is of shape (tensor.shape[i], rank) """ rng = check_random_state(random_state) if init == 'random': factors = [tl.tensor(rng.random_sample((tensor.shape[i], rank)), **tl.context(tensor)) for i in range(tl.ndim(tensor))] if non_negative: return [tl.abs(f) for f in factors] else: return factors elif init == 'svd': try: svd_fun = tl.SVD_FUNS[svd] except KeyError: message = 'Got svd={}. However, for the current backend ({}), the possible choices are {}'.format( svd, tl.get_backend(), tl.SVD_FUNS) raise ValueError(message) factors = [] for mode in range(tl.ndim(tensor)): U, _, _ = svd_fun(unfold(tensor, mode), n_eigenvecs=rank) if tensor.shape[mode] < rank: # TODO: this is a hack but it seems to do the job for now # factor = tl.tensor(np.zeros((U.shape[0], rank)), **tl.context(tensor)) # factor[:, tensor.shape[mode]:] = tl.tensor(rng.random_sample((U.shape[0], rank - tl.shape(tensor)[mode])), **tl.context(tensor)) # factor[:, :tensor.shape[mode]] = U random_part = tl.tensor(rng.random_sample((U.shape[0], rank - tl.shape(tensor)[mode])), **tl.context(tensor)) U = tl.concatenate([U, random_part], axis=1) if non_negative: factors.append(tl.abs(U[:, :rank])) else: factors.append(U[:, :rank]) return factors raise ValueError('Initialization method "{}" not recognized'.format(init))
def initialize_cp(tensor: np.ndarray, matrix: np.ndarray, rank: int): r"""Initialize factors used in `parafac`. Parameters ---------- tensor : ndarray rank : int Returns ------- factors : CPTensor An initial cp tensor. """ factors = [] for mode in range(tl.ndim(tensor)): unfold = tl.unfold(tensor, mode) if mode == 0 and (matrix is not None): unfold = np.hstack((unfold, matrix)) # Remove completely missing columns unfold = unfold[:, np.sum(np.isfinite(unfold), axis=0) > 2] # Impute by PCA outt = PCA(unfold, ncomp=1, method="nipals", missing="fill-em", standardize=False, demean=False, normalize=False, max_em_iter=1000) recon_pca = outt.scores @ outt.loadings.T unfold[np.isnan(unfold)] = recon_pca[np.isnan(unfold)] U = np.linalg.svd(unfold)[0] if U.shape[1] < rank: # This is a hack but it seems to do the job for now pad_part = np.random.rand(U.shape[0], rank - U.shape[1]) U = tl.concatenate([U, pad_part], axis=1) factors.append(U[:, :rank]) return tl.cp_tensor.CPTensor((None, factors))
def factors2vec(factors): """Wrapper function detailed in Appendix C [1] Stacks the column vectors of a set of matrices into a single vecto Parameters --------- factors : list of ndarrays Factor matrices or Gradient wrt factor gradient Returns ------- vec : ndarry column-wise vectorization of a list of matrices """ vec = None for factor in factors: if vec is None: vec = tl.tensor_to_vec(tl.transpose(factor)) else: vec = tl.concatenate([vec, tl.tensor_to_vec(tl.transpose(factor))]) return vec
def initialize_cp(tensor, rank, init='svd', svd='numpy_svd', random_state=None, non_negative=False, normalize_factors=False): r"""Initialize factors used in `parafac`. The type of initialization is set using `init`. If `init == 'random'` then initialize factor matrices using `random_state`. If `init == 'svd'` then initialize the `m`th factor matrix using the `rank` left singular vectors of the `m`th unfolding of the input tensor. Parameters ---------- tensor : ndarray rank : int init : {'svd', 'random'}, optional svd : str, default is 'numpy_svd' function to use to compute the SVD, acceptable values in tensorly.SVD_FUNS non_negative : bool, default is False if True, non-negative factors are returned Returns ------- factors : CPTensor An initial cp tensor. """ rng = check_random_state(random_state) if init == 'random': # factors = [tl.tensor(rng.random_sample((tensor.shape[i], rank)), **tl.context(tensor)) for i in range(tl.ndim(tensor))] # kt = CPTensor((None, factors)) return random_cp(tl.shape(tensor), rank, normalise_factors=False, random_state=rng, **tl.context(tensor)) elif init == 'svd': try: svd_fun = tl.SVD_FUNS[svd] except KeyError: message = 'Got svd={}. However, for the current backend ({}), the possible choices are {}'.format( svd, tl.get_backend(), tl.SVD_FUNS) raise ValueError(message) factors = [] for mode in range(tl.ndim(tensor)): U, S, _ = svd_fun(unfold(tensor, mode), n_eigenvecs=rank) # Put SVD initialization on the same scaling as the tensor in case normalize_factors=False if mode == 0: idx = min(rank, tl.shape(S)[0]) U = tl.index_update(U, tl.index[:, :idx], U[:, :idx] * S[:idx]) if tensor.shape[mode] < rank: # TODO: this is a hack but it seems to do the job for now # factor = tl.tensor(np.zeros((U.shape[0], rank)), **tl.context(tensor)) # factor[:, tensor.shape[mode]:] = tl.tensor(rng.random_sample((U.shape[0], rank - tl.shape(tensor)[mode])), **tl.context(tensor)) # factor[:, :tensor.shape[mode]] = U random_part = tl.tensor( rng.random_sample( (U.shape[0], rank - tl.shape(tensor)[mode])), **tl.context(tensor)) U = tl.concatenate([U, random_part], axis=1) factors.append(U[:, :rank]) kt = CPTensor((None, factors)) elif isinstance(init, (tuple, list, CPTensor)): # TODO: Test this try: kt = CPTensor(init) except ValueError: raise ValueError( 'If initialization method is a mapping, then it must ' 'be possible to convert it to a CPTensor instance') else: raise ValueError( 'Initialization method "{}" not recognized'.format(init)) if non_negative: kt.factors = [tl.abs(f) for f in kt[1]] if normalize_factors: kt = cp_normalize(kt) return kt
def coupled_matrix_tensor_3d_factorization(tensor_3d, matrix, rank, init='svd', n_iter_max=100, normalize_factors=False): """ Calculates a coupled matrix and tensor factorization of 3rd order tensor and matrix which are coupled in first mode. Assume you have tensor_3d = [[lambda; A, B, C]] and matrix = [[gamma; A, V]], which are coupled in 1st mode. With coupled matrix and tensor factorization (CTMF), the normalized factor matrices A, B, C for the CP decomposition of X, the normalized matrix V and the weights lambda_ and gamma are found. This implementation only works for a coupling in the first mode. Solution is found via alternating least squares (ALS) as described in Figure 5 of @article{acar2011all, title={All-at-once optimization for coupled matrix and tensor factorizations}, author={Acar, Evrim and Kolda, Tamara G and Dunlavy, Daniel M}, journal={arXiv preprint arXiv:1105.3422}, year={2011} } Notes ----- In the paper, the columns of the factor matrices are not normalized and therefore weights are not included in the algorithm. Parameters ---------- tensor_3d : tl.tensor or CP tensor 3rd order tensor X = [[A, B, C]] matrix : tl.tensor or CP tensor matrix that is coupled with tensor in first mode: Y = [[A, V]] rank : int rank for CP decomposition of X Returns ------- tensor_3d_pred : CPTensor tensor_3d_pred = [[lambda; A,B,C]] matrix_pred : CPTensor matrix_pred = [[gamma; A,V]] rec_errors : list contains the reconstruction error of each iteration: error = 1 / 2 * | X - [[ lambda_; A, B, C ]] | ^ 2 + 1 / 2 * | Y - [[ gamma; A, V ]] | ^ 2 Examples -------- A = tl.tensor([[1, 2], [3, 4]]) B = tl.tensor([[1, 0], [0, 2]]) C = tl.tensor([[2, 0], [0, 1]]) V = tl.tensor([[2, 0], [0, 1]]) R = 2 X = (None, [A, B, C]) Y = (None, [A, V]) tensor_3d_pred, matrix_pred = cmtf_als_for_third_order_tensor(X, Y, R) """ rank = validate_cp_rank(tl.shape(tensor_3d), rank=rank) # initialize values tensor_cp = initialize_cp(tensor_3d, rank, init=init) rec_errors = [] # alternating least squares # note that the order of the khatri rao product is reversed since tl.unfold has another order # than assumed in paper for iteration in range(n_iter_max): V = tl.transpose(tl.lstsq(tensor_cp.factors[0], matrix)[0]) # Loop over modes of the tensor for ii in range(tl.ndim(tensor_3d)): kr = khatri_rao(tensor_cp.factors, skip_matrix=ii) unfolded = tl.unfold(tensor_3d, ii) # If we are at the coupled mode, concat the matrix if ii == 0: kr = tl.concatenate((kr, V), axis=0) unfolded = tl.concatenate((unfolded, matrix), axis=1) tensor_cp.factors[ii] = tl.transpose( tl.lstsq(kr, tl.transpose(unfolded))[0]) error_new = tl.norm(tensor_3d - cp_to_tensor(tensor_cp))**2 + tl.norm( matrix - cp_to_tensor((None, [tensor_cp.factors[0], V])))**2 if iteration > 0 and (tl.abs(error_new - error_old) / error_old <= 1e-8 or error_new < 1e-5): break error_old = error_new rec_errors.append(error_new) matrix_pred = CPTensor((None, [tensor_cp.factors[0], V])) if normalize_factors: tensor_cp = cp_normalize(tensor_cp) matrix_pred = cp_normalize(matrix_pred) return tensor_cp, matrix_pred, rec_errors
def online_tensor_decomposition(dataset, X, X_stream, rank, n_iter=1, ul=-1, ll=-1, verbose=False, methods=['dao', 'dtd', 'ocp', 'fcp']): results = {} start = time.time() (weights, factors_old) = parafac(X_stream[0], rank, init='random') init_time = time.time() - start for method in methods: print('-------------------------------------') mem_usage = sys.getsizeof(X_stream[0]) if method in ['dao', 'dtd']: print(f'>> {method}: rank-{rank} n_iter-{n_iter}') elif method in ['ocp', 'fcp']: print(f'>> {method}: rank-{rank}') factors = factors_old X_old = X_stream[0] n_dim = tl.ndim(X_old) if not method in ['dao', 'dtd', 'ocp', 'fcp']: raise ValueError('The method does not exist.') if method == 'fcp': mem_usage = sys.getsizeof(X) ktensor = parafac(X, rank, init='random') (weights, factors) = ktensor mem_usage += sys.getsizeof(factors) X_est = construct_tensor(factors) err_norm = tl.norm(X - X_est) global_rt = time.time() - start global_fit = 1 - (err_norm / tl.norm(X)) print('Global Fitness :', format(global_fit * 100, '.4f'), '%') print('Global Running Time :', format(global_rt, '.4f'), 'sec') print('Memory Usage :', mem_usage, 'bytes') results[method] = [ktensor] continue ktensors = [] verbose_list = [] split_points = [] refine_points = [] fitness = [] running_time = [] begin = time.time() - init_time welford = Welford() X_est = construct_tensor(factors) err_norm = tl.norm(X_old - X_est) welford(err_norm * 1.2) if method == 'ocp': start = time.time() K = get_KhatriRao_except0(factors) H = get_Hadamard(factors) P = np.empty((n_dim), dtype=object) Q = np.empty((n_dim), dtype=object) for mode in range(1, n_dim): P[mode] = tl.dot(tl.unfold(X_old, mode), tl.tenalg.khatri_rao((factors[0], K[mode]))) Q[mode] = H / tl.dot(tl.transpose(factors[mode]), factors[mode]) #print('init_time:', time.time()-start) mem_usage += sys.getsizeof(K) mem_usage += sys.getsizeof(H) mem_usage += sys.getsizeof(P) mem_usage += sys.getsizeof(Q) iter_mem_usage = 0 for i, X_new in enumerate(X_stream[1:]): i_mem = sys.getsizeof(X_new) start = time.time() if method == 'dao': (weights, factors0) = data_adaptive_online_cp(factors.copy(), X_old, X_new, rank, n_iter=n_iter, mu=0.8, verbose=False) elif method == 'ocp': ((weights, factors0), P0, Q0) = online_cp(factors.copy(), X_old, X_new, rank, P, Q, verbose=False) elif method == 'dtd': (weights, factors0) = dtd(factors.copy(), X_old, X_new, rank, n_iter=n_iter, mu=1, verbose=False) U = factors0.copy() U[0] = U[0][-X_new.shape[0] - 1:-1] i_mem += sys.getsizeof(U) dX_est = construct_tensor(U) err_norm = tl.norm(X_new - dX_est) z_score = get_z_score(err_norm, welford.mean, welford.std) if method == 'dao' and ul > 0 and z_score > ul: weights = tl.ones(rank) ktensors.append(KruskalTensor((weights, factors.copy()))) split_points.append(i + 1) X_old = X_stream[i + 1] (weights, factors0) = parafac(X_old, rank, init='random') elapsed_time = time.time() - start verbose_list.append([i + 1, elapsed_time, err_norm, z_score]) i_mem += sys.getsizeof(factors0) start = time.time() X_est = construct_tensor(factors0) err_norm = tl.norm(X_old - X_est) welford = Welford() welford(err_norm * 1.2) z_score = get_z_score(err_norm, welford.mean, welford.std) factors = factors0.copy() welford(err_norm) elapsed_time = time.time() - start verbose_list.append([i + 1, elapsed_time, err_norm, z_score]) fitness.append(err_norm / tl.norm(X_new)) running_time.append(elapsed_time) continue elif method == 'dao' and ll > 0 and z_score > ll: refine_points.append(i + 1) elapsed_time = time.time() - start verbose_list.append([i + 1, elapsed_time, err_norm, z_score]) (weights, factors) = data_adaptive_online_cp(factors, X_old, X_new, rank, n_iter=n_iter * 2, mu=0.5, verbose=False) i_mem += sys.getsizeof(factors) U = factors.copy() U[0] = U[0][-X_new.shape[0] - 1:-1] dX_est = construct_tensor(U) err_norm = tl.norm(X_new - dX_est) welford(err_norm) else: if method == 'ocp': P = P0 Q = Q0 factors = factors0.copy() welford(err_norm) elapsed_time = time.time() - start verbose_list.append([i + 1, elapsed_time, err_norm, z_score]) fitness.append(err_norm / tl.norm(X_new)) running_time.append(elapsed_time) X_old = tl.concatenate((X_old, X_new)) iter_mem_usage = max(iter_mem_usage, i_mem) if verbose: X_est = construct_tensor(factors) compare_tensors(X_old, X_est) mem_usage += iter_mem_usage weights = tl.ones(rank) ktensors.append(KruskalTensor((weights, factors))) mem_usage += sys.getsizeof(ktensors) global_rt = time.time() - begin tensor_est = construct_tensor(ktensors[0][1]) for (weights, factors) in ktensors[1:]: tensor_est = tl.tensor( tl.concatenate((tensor_est, construct_tensor(factors)))) global_error_norm = compare_tensors(X, tensor_est) if method == 'dao': print(f'SPLIT: {len(split_points)}, REFINE: {len(refine_points)}') if method != 'fcp': verbose_list = np.asarray(verbose_list, dtype=float) fitness = np.asarray(fitness, dtype=float) running_time = np.asarray(running_time, dtype=float) tot_norm = tl.norm(X) local_fit = 1 - np.mean(fitness) local_rt = np.mean(running_time) global_fit = 1 - (global_error_norm / tot_norm) print('Global Fitness :', format(global_fit * 100, '.4f'), '%') print('Avg Local Fitness :', format(local_fit * 100, '.4f'), '%') print('Global Running Time :', format(global_rt, '.4f'), 'sec') print('Avg Local Running Time :', format(local_rt, '.4f'), 'sec') print('Memory Usage :', mem_usage, 'bytes') results[method] = ktensors return results
def __initialize_factors(self, tensor, svd='numpy_svd', non_negative=False, custom=None): """Initialize random or SVD-guided factors for TCA depending on TCA type Parameters ---------- tensor : torch.Tensor The tensor of activity of N neurons, T timepoints and K trials of shape N, T, K svd : str, optional Type of SVD algorithm to use (default is numpy_svd) non_negative : bool, optional A flag used to specify if factors generated must be strictyl positive (default is False) custom : int, optional A flag used to specify which factor should be strictly positive for 'custom parafac' (default is None) Raises ------ ValueError If svd does not contain a valid SVD algorithm reference If self.init variable does not contain a valid intialization method Returns ------- list List of initialized tensors """ rng = tensorly.random.check_random_state(self.random_state) if self.init == 'random': if custom: factors = [ tl.tensor( rng.random_sample( (tensor.shape[i], self.rank)) * 2 - 1, **tl.context(tensor)) for i in range(self.dimension) ] factors = [ f if int(i) == int(custom) else tl.abs(f) for i, f in enumerate(factors) ] elif non_negative: factors = [ tl.tensor(rng.random_sample((tensor.shape[i], self.rank)), **tl.context(tensor)) for i in range(self.dimension) ] factors = [ tl.abs(f) for f in factors ] # See if this line is useful depending on random function used else: factors = [ tl.tensor( rng.random_sample( (tensor.shape[i], self.rank)) * 2 - 1, **tl.context(tensor)) for i in range(self.dimension) ] return factors elif self.init == 'svd': try: svd_fun = tl.SVD_FUNS[svd] except KeyError: message = 'Got svd={}. However, for the current backend ({}), the possible choices are {}'.format( svd, tl.get_backend(), tl.SVD_FUNS) raise ValueError(message) factors = [] for mode in range(tl.ndim(tensor)): U, *_ = svd_fun(unfold(tensor, mode), n_eigenvecs=rank) if tensor.shape[mode] < rank: random_part = tl.tensor( rng.random_sample( (U.shape[0], rank - tl.shape(tensor)[mode])), **tl.context(tensor)) U = tl.concatenate([U, random_part], axis=1) if non_negative or custom == mode: factors.append(tl.abs(U[:, :rank])) else: factors.append(U[:, :rank]) return factors else: raise ValueError( 'Initialization method "{}" not recognized'.format(self.init))
def initialize_nn_cp(tensor, rank, init='svd', svd='numpy_svd', random_state=None, normalize_factors=False, nntype='nndsvda'): r"""Initialize factors used in `parafac`. The type of initialization is set using `init`. If `init == 'random'` then initialize factor matrices using `random_state`. If `init == 'svd'` then initialize the `m`th factor matrix using the `rank` left singular vectors of the `m`th unfolding of the input tensor. Parameters ---------- tensor : ndarray rank : int init : {'svd', 'random'}, optional svd : str, default is 'numpy_svd' function to use to compute the SVD, acceptable values in tensorly.SVD_FUNS nntype : {'nndsvd', 'nndsvda'} Whether to fill small values with 0.0 (nndsvd), or the tensor mean (nndsvda, default). Returns ------- factors : CPTensor An initial cp tensor. """ rng = tl.check_random_state(random_state) if init == 'random': kt = random_cp(tl.shape(tensor), rank, normalise_factors=False, random_state=rng, **tl.context(tensor)) elif init == 'svd': try: svd_fun = tl.SVD_FUNS[svd] except KeyError: message = 'Got svd={}. However, for the current backend ({}), the possible choices are {}'.format( svd, tl.get_backend(), tl.SVD_FUNS) raise ValueError(message) factors = [] for mode in range(tl.ndim(tensor)): U, S, V = svd_fun(unfold(tensor, mode), n_eigenvecs=rank) # Apply nnsvd to make non-negative U = make_svd_non_negative(tensor, U, S, V, nntype) if tensor.shape[mode] < rank: # TODO: this is a hack but it seems to do the job for now random_part = tl.tensor( rng.random_sample( (U.shape[0], rank - tl.shape(tensor)[mode])), **tl.context(tensor)) U = tl.concatenate([U, random_part], axis=1) factors.append(U[:, :rank]) kt = CPTensor((None, factors)) # If the initialisation is a precomputed decomposition, we double check its validity and return it elif isinstance(init, (tuple, list, CPTensor)): # TODO: Test this try: kt = CPTensor(init) except ValueError: raise ValueError( 'If initialization method is a mapping, then it must ' 'be possible to convert it to a CPTensor instance') return kt else: raise ValueError( 'Initialization method "{}" not recognized'.format(init)) # Make decomposition feasible by taking the absolute value of all factor matrices kt.factors = [tl.abs(f) for f in kt[1]] if normalize_factors: kt = cp_normalize(kt) return kt
def initialize_constrained_parafac(tensor, rank, init='svd', svd='numpy_svd', random_state=None, non_negative=None, l1_reg=None, l2_reg=None, l2_square_reg=None, unimodality=None, normalize=None, simplex=None, normalized_sparsity=None, soft_sparsity=None, smoothness=None, monotonicity=None, hard_sparsity=None): r"""Initialize factors used in `constrained_parafac`. Parameters ---------- The type of initialization is set using `init`. If `init == 'random'` then initialize factor matrices with uniform distribution using `random_state`. If `init == 'svd'` then initialize the `m`th factor matrix using the `rank` left singular vectors of the `m`th unfolding of the input tensor. If init is a previously initialized `cp tensor`, all the weights are pulled in the last factor and then the weights are set to "1" for the output tensor. Lastly, factors are updated with proximal operator according to the selected constraint(s), so that they satisfy the imposed constraints (does not apply to cptensor initialization). Parameters ---------- tensor : ndarray rank : int random_state : {None, int, np.random.RandomState} init : {'svd', 'random', cptensor}, optional svd : str, default is 'numpy_svd' function to use to compute the SVD, acceptable values in tensorly.SVD_FUNS non_negative : bool or dictionary This constraint is clipping negative values to '0'. If it is True non-negative constraint is applied to all modes. l1_reg : float or list or dictionary, optional l2_reg : float or list or dictionary, optional l2_square_reg : float or list or dictionary, optional unimodality : bool or dictionary, optional If it is True unimodality constraint is applied to all modes. normalize : bool or dictionary, optional This constraint divides all the values by maximum value of the input array. If it is True normalize constraint is applied to all modes. simplex : float or list or dictionary, optional normalized_sparsity : float or list or dictionary, optional soft_sparsity : float or list or dictionary, optional smoothness : float or list or dictionary, optional monotonicity : bool or dictionary, optional hard_sparsity : float or list or dictionary, optional Returns ------- factors : CPTensor An initial cp tensor. """ n_modes = tl.ndim(tensor) rng = tl.check_random_state(random_state) if init == 'random': weights, factors = random_cp(tl.shape(tensor), rank, normalise_factors=False, **tl.context(tensor)) elif init == 'svd': try: svd_fun = tl.SVD_FUNS[svd] except KeyError: message = 'Got svd={}. However, for the current backend ({}), the possible choices are {}'.format( svd, tl.get_backend(), tl.SVD_FUNS) raise ValueError(message) factors = [] for mode in range(tl.ndim(tensor)): U, S, _ = svd_fun(unfold(tensor, mode), n_eigenvecs=rank) # Put SVD initialization on the same scaling as the tensor in case normalize_factors=False if mode == 0: idx = min(rank, tl.shape(S)[0]) U = tl.index_update(U, tl.index[:, :idx], U[:, :idx] * S[:idx]) if tensor.shape[mode] < rank: random_part = tl.tensor(rng.random_sample((U.shape[0], rank - tl.shape(tensor)[mode])), **tl.context(tensor)) U = tl.concatenate([U, random_part], axis=1) factors.append(U[:, :rank]) elif isinstance(init, (tuple, list, CPTensor)): try: weights, factors = CPTensor(init) if tl.all(weights == 1): weights, factors = CPTensor((None, factors)) else: weights_avg = tl.prod(weights) ** (1.0 / tl.shape(weights)[0]) for i in range(len(factors)): factors[i] = factors[i] * weights_avg kt = CPTensor((None, factors)) return kt except ValueError: raise ValueError( 'If initialization method is a mapping, then it must ' 'be possible to convert it to a CPTensor instance' ) else: raise ValueError('Initialization method "{}" not recognized'.format(init)) for i in range(n_modes): factors[i] = proximal_operator(factors[i], non_negative=non_negative, l1_reg=l1_reg, l2_reg=l2_reg, l2_square_reg=l2_square_reg, unimodality=unimodality, normalize=normalize, simplex=simplex, normalized_sparsity=normalized_sparsity, soft_sparsity=soft_sparsity, smoothness=smoothness, monotonicity=monotonicity, hard_sparsity=hard_sparsity, n_const=n_modes, order=i) kt = CPTensor((None, factors)) return kt
def initialize_cp(tensor, rank, init='svd', svd='numpy_svd', random_state=None, normalize_factors=False): r"""Initialize factors used in `parafac`. The type of initialization is set using `init`. If `init == 'random'` then initialize factor matrices with uniform distribution using `random_state`. If `init == 'svd'` then initialize the `m`th factor matrix using the `rank` left singular vectors of the `m`th unfolding of the input tensor. If init is a previously initialized `cp tensor`, all the weights are pulled in the last factor and then the weights are set to "1" for the output tensor. Parameters ---------- tensor : ndarray rank : int init : {'svd', 'random', cptensor}, optional svd : str, default is 'numpy_svd' function to use to compute the SVD, acceptable values in tensorly.SVD_FUNS non_negative : bool, default is False if True, non-negative factors are returned Returns ------- factors : CPTensor An initial cp tensor. """ rng = tl.check_random_state(random_state) if init == 'random': kt = random_cp(tl.shape(tensor), rank, normalise_factors=False, random_state=rng, **tl.context(tensor)) elif init == 'svd': try: svd_fun = tl.SVD_FUNS[svd] except KeyError: message = 'Got svd={}. However, for the current backend ({}), the possible choices are {}'.format( svd, tl.get_backend(), tl.SVD_FUNS) raise ValueError(message) factors = [] for mode in range(tl.ndim(tensor)): U, S, _ = svd_fun(unfold(tensor, mode), n_eigenvecs=rank) # Put SVD initialization on the same scaling as the tensor in case normalize_factors=False if mode == 0: idx = min(rank, tl.shape(S)[0]) U = tl.index_update(U, tl.index[:, :idx], U[:, :idx] * S[:idx]) if tensor.shape[mode] < rank: # TODO: this is a hack but it seems to do the job for now random_part = tl.tensor( rng.random_sample( (U.shape[0], rank - tl.shape(tensor)[mode])), **tl.context(tensor)) U = tl.concatenate([U, random_part], axis=1) factors.append(U[:, :rank]) kt = CPTensor((None, factors)) elif isinstance(init, (tuple, list, CPTensor)): # TODO: Test this try: if normalize_factors is True: warnings.warn( 'It is not recommended to initialize a tensor with normalizing. Consider normalizing the tensor before using this function' ) kt = CPTensor(init) weights, factors = kt if tl.all(weights == 1): kt = CPTensor((None, factors)) else: weights_avg = tl.prod(weights)**(1.0 / tl.shape(weights)[0]) for i in range(len(factors)): factors[i] = factors[i] * weights_avg kt = CPTensor((None, factors)) except ValueError: raise ValueError( 'If initialization method is a mapping, then it must ' 'be possible to convert it to a CPTensor instance') else: raise ValueError( 'Initialization method "{}" not recognized'.format(init)) if normalize_factors: kt = cp_normalize(kt) return kt