def hooi(X, rank, **kwargs): """ Compute Tucker decomposition of a tensor using Higher-Order Orthogonal Iterations. Parameters ---------- X : tensor_mixin The tensor to be decomposed rank : array_like The rank of the decomposition for each mode of the tensor. The length of ``rank`` must match the number of modes of ``X``. init : {'random', 'nvecs'}, optional The initialization method to use. - random : Factor matrices are initialized randomly. - nvecs : Factor matrices are initialzed via HOSVD. default : 'nvecs' Examples -------- Create dense tensor >>> T = np.zeros((3, 4, 2)) >>> T[:, :, 0] = [[ 1, 4, 7, 10], [ 2, 5, 8, 11], [3, 6, 9, 12]] >>> T[:, :, 1] = [[13, 16, 19, 22], [14, 17, 20, 23], [15, 18, 21, 24]] >>> T = dtensor(T) Compute Tucker decomposition of ``T`` with n-rank [2, 3, 1] via higher-order orthogonal iterations >>> Y = hooi(T, [2, 3, 1], init='nvecs') Shape of the core tensor matches n-rank of the decomposition. >>> Y['core'].shape (2, 3, 1) >>> Y['U'][1].shape (3, 2) References ---------- .. [1] L. De Lathauwer, B. De Moor, J. Vandewalle: On the best rank-1 and rank-(R_1, R_2, \ldots, R_N) approximation of higher order tensors; IEEE Trans. Signal Process. 49 (2001), pp. 2262-2271 """ # init options ainit = kwargs.pop('init', __DEF_INIT) maxIter = kwargs.pop('maxIter', __DEF_MAXITER) conv = kwargs.pop('conv', __DEF_CONV) dtype = kwargs.pop('dtype', X.dtype) if not len(kwargs) == 0: raise ValueError('Unknown keywords (%s)' % (kwargs.keys())) ndims = X.ndim if isNumberType(rank): rank = rank * ones(ndims) normX = norm(X) U = __init(ainit, X, ndims, rank, dtype) fit = 0 exectimes = [] for itr in xrange(maxIter): tic = time.clock() fitold = fit for n in range(ndims): Utilde = ttm(X, U, n, transp=True, without=True) U[n] = nvecs(Utilde, n, rank[n]) # compute core tensor to get fit core = ttm(Utilde, U, n, transp=True) # since factors are orthonormal, compute fit on core tensor normresidual = sqrt(normX ** 2 - norm(core) ** 2) # fraction explained by model fit = 1 - (normresidual / normX) fitchange = abs(fitold - fit) exectimes.append(time.clock() - tic) _log.debug( '[%3d] fit: %.5f | delta: %7.1e | secs: %.5f' % (itr, fit, fitchange, exectimes[-1]) ) if itr > 1 and fitchange < conv: break return core, U
def als(X, rank, dtype=np.float, **kwargs): """ Alternating least-sqaures algorithm to compute the CP decomposition. Parameters ---------- X : tensor_mixin The tensor to be decomposed. rank : int Tensor rank of the decomposition. init : {'random', 'nvecs'}, optional The initialization method to use. - random : Factor matrices are initialized randomly. - nvecs : Factor matrices are initialzed via HOSVD. (default 'nvecs') max_iter : int, optional Maximium number of iterations of the ALS algorithm. (default 500) fit_method : {'full', None} The method to compute the fit of the factorization - 'full' : Compute least-squares fit of the dense approximation of. X and X. - None : Do not compute the fit of the factorization, but iterate until ``max_iter`` (Useful for large-scale tensors). (default 'full') conv : float Convergence tolerance on difference of fit between iterations (default 1e-5) Returns ------- P : ktensor Rank ``rank`` factorization of X. ``P.U[i]`` corresponds to the factor matrix for the i-th mode. ``P.lambda[i]`` corresponds to the weight of the i-th mode. fit : float Fit of the factorization compared to ``X`` itr : int Number of iterations that were needed until convergence exectimes : ndarray of floats Time needed for each single iteration Examples -------- Create random dense tensor >>> from sktensor import dtensor >>> U = [np.random.rand(i,3) for i in (20, 10, 14)] >>> T = dtensor(ktensor(U).toarray()) Compute rank-3 CP decomposition of ``T`` with ALS >>> P, fit, itr, _ = als(T, 3) Result is a decomposed tensor stored as a Kruskal operator >>> type(P) <class 'sktensor.ktensor.ktensor'> Factorization should be close to original data >>> np.allclose(T, P.totensor()) True References ---------- .. [1] Kolda, T. G. & Bader, B. W. Tensor Decompositions and Applications. SIAM Rev. 51, 455–500 (2009). .. [2] Harshman, R. A. Foundations of the PARAFAC procedure: models and conditions for an 'explanatory' multimodal factor analysis. UCLA Working Papers in Phonetics 16, (1970). .. [3] Carroll, J. D., Chang, J. J. Analysis of individual differences in multidimensional scaling via an N-way generalization of 'Eckart-Young' decomposition. Psychometrika 35, 283–319 (1970). """ N = len(X.shape) normX = norm(X) # init options ainit = kwargs.pop('init', __DEF_INIT) maxiter = kwargs.pop('maxIter', __DEF_MAXITER) fit_method = kwargs.pop('fit_method', __DEF_FIT_METHOD) conv = kwargs.pop('conv', __DEF_CONV) if not len(kwargs) == 0: raise ValueError('Unknown keywords (%s)' % (kwargs.keys())) U = __init(ainit, X, N, rank, dtype) fit = 0 exectimes = [] for itr in xrange(maxiter): tic = time.clock() fitold = fit for n in range(N): Unew = X.uttkrp(U, n) Y = ones((rank, rank), dtype=dtype) for i in (range(n) + range(n + 1, N)): Y = Y * dot(U[i].T, U[i]) Unew = Unew.dot(pinv(Y)) # Normalize if itr == 0: lmbda = sqrt((Unew ** 2).sum(axis=0)) else: lmbda = Unew.max(axis=0) lmbda[lmbda < 1] = 1 U[n] = Unew / lmbda P = ktensor(U, lmbda) if fit_method == 'full': normresidual = normX ** 2 + P.norm() ** 2 - 2 * P.innerprod(X) fit = 1 - (normresidual / normX ** 2) else: fit = itr fitchange = abs(fitold - fit) exectimes.append(time.clock() - tic) _log.debug( '[%3d] fit: %.5f | delta: %7.1e | secs: %.5f' % (itr, fit, fitchange, exectimes[-1]) ) if itr > 0 and fitchange < conv: break return P, fit, itr, array(exectimes)
def als(X, rank, **kwargs): """ Alternating least-sqaures algorithm to compute the CP decomposition. Parameters ---------- X : tensor_mixin The tensor to be decomposed. rank : int Tensor rank of the decomposition. init : {'random', 'nvecs'}, optional The initialization method to use. - random : Factor matrices are initialized randomly. - nvecs : Factor matrices are initialzed via HOSVD. (default 'nvecs') max_iter : int, optional Maximium number of iterations of the ALS algorithm. (default 500) fit_method : {'full', None} The method to compute the fit of the factorization - 'full' : Compute least-squares fit of the dense approximation of. X and X. - None : Do not compute the fit of the factorization, but iterate until ``max_iter`` (Useful for large-scale tensors). (default 'full') conv : float Convergence tolerance on difference of fit between iterations (default 1e-5) Returns ------- P : ktensor Rank ``rank`` factorization of X. ``P.U[i]`` corresponds to the factor matrix for the i-th mode. ``P.lambda[i]`` corresponds to the weight of the i-th mode. fit : float Fit of the factorization compared to ``X`` itr : int Number of iterations that were needed until convergence exectimes : ndarray of floats Time needed for each single iteration Examples -------- Create random dense tensor >>> from sktensor import dtensor, ktensor >>> U = [np.random.rand(i,3) for i in (20, 10, 14)] >>> T = dtensor(ktensor(U).toarray()) Compute rank-3 CP decomposition of ``T`` with ALS >>> P, fit, itr, _ = als(T, 3) Result is a decomposed tensor stored as a Kruskal operator >>> type(P) <class 'sktensor.ktensor.ktensor'> Factorization should be close to original data >>> np.allclose(T, P.totensor()) True References ---------- .. [1] Kolda, T. G. & Bader, B. W. Tensor Decompositions and Applications. SIAM Rev. 51, 455–500 (2009). .. [2] Harshman, R. A. Foundations of the PARAFAC procedure: models and conditions for an 'explanatory' multimodal factor analysis. UCLA Working Papers in Phonetics 16, (1970). .. [3] Carroll, J. D., Chang, J. J. Analysis of individual differences in multidimensional scaling via an N-way generalization of 'Eckart-Young' decomposition. Psychometrika 35, 283–319 (1970). """ # init options ainit = kwargs.pop('init', _DEF_INIT) maxiter = kwargs.pop('max_iter', _DEF_MAXITER) fit_method = kwargs.pop('fit_method', _DEF_FIT_METHOD) conv = kwargs.pop('conv', _DEF_CONV) dtype = kwargs.pop('dtype', _DEF_TYPE) if not len(kwargs) == 0: raise ValueError('Unknown keywords (%s)' % (kwargs.keys())) N = X.ndim normX = norm(X) U = _init(ainit, X, N, rank, dtype) fit = 0 exectimes = [] for itr in range(maxiter): tic = time.clock() fitold = fit for n in range(N): Unew = X.uttkrp(U, n) Y = ones((rank, rank), dtype=dtype) for i in (list(range(n)) + list(range(n + 1, N))): Y = Y * dot(U[i].T, U[i]) Unew = Unew.dot(pinv(Y)) # Normalize if itr == 0: lmbda = sqrt((Unew**2).sum(axis=0)) else: lmbda = Unew.max(axis=0) lmbda[lmbda < 1] = 1 U[n] = Unew / lmbda P = ktensor(U, lmbda) if fit_method == 'full': normresidual = normX**2 + P.norm()**2 - 2 * P.innerprod(X) fit = 1 - (normresidual / normX**2) else: fit = itr fitchange = abs(fitold - fit) exectimes.append(time.clock() - tic) _log.debug('[%3d] fit: %.5f | delta: %7.1e | secs: %.5f' % (itr, fit, fitchange, exectimes[-1])) if itr > 0 and fitchange < conv: break return P, fit, itr, array(exectimes)