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))
Beispiel #2
0
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))
Beispiel #3
0
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
Beispiel #4
0
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
Beispiel #5
0
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
Beispiel #6
0
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
Beispiel #7
0
    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))
Beispiel #8
0
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
Beispiel #9
0
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
Beispiel #10
0
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