def METHOD_NAME(x, n_pcs, svd_solver='auto', random_state=0):
    """
    Principal component analysis.
    :param x: 2D array, shape (M, N)
    :param n_pcs: the number of features for a return array after reducing.
    :param svd_solver: {'auto', 'full', 'arpack', 'randomized'}, default to 'auto'
                If auto :
                    The solver is selected by a default policy based on `X.shape` and
                    `n_pcs`: if the input data is larger than 500x500 and the
                    number of components to extract is lower than 80% of the smallest
                    dimension of the data, then the more efficient 'randomized'
                    method is enabled. Otherwise the exact full SVD is computed and
                    optionally truncated afterwards.
                If full :
                    run exact full SVD calling the standard LAPACK solver via
                    `scipy.linalg.svd` and select the components by postprocessing
                If arpack :
                    run SVD truncated to n_pcs calling ARPACK solver via
                    `scipy.sparse.linalg.svds`. It requires strictly
                    0 < n_pcs < min(x.shape)
                If randomized :
                    run randomized SVD by the method of Halko et al.
    :param random_state : int, RandomState instance
    :return:  ndarray of shape (n_samples, n_pcs) Embedding of the training data in low-dimensional space.
    """
    if issparse(x):
        if svd_solver != 'arpack':
            logger.warning(
                f'svd_solver: {svd_solver} can not be used with sparse input.\n'
                'Use "arpack" (the default) instead.'
            )
            svd_solver = 'arpack'
        if x.dtype.char not in "fFdD":
            x = x.astype(np.float32)
            logger.info('exp_matrix dType is changed to float32')
        output = _pca_with_sparse(x, n_pcs, solver=svd_solver, random_state=random_state)
        # this is just a wrapper for the results
        # pca_ = PCA(n_components=n_pcs, svd_solver=svd_solver)
        # pca_.components_ = output['components']
        # pca_.explained_variance_ = output['variance']
        # pca_.explained_variance_ratio_ = output['variance_ratio']
        # return dict([('x_pca', output['X_pca']), ('variance', output['variance']), ('variance_ratio', output['variance_ratio']), ('pcs', pca_.components_.T)])
        return dict([('x_pca', output['X_pca']), ('variance', output['variance']), ('variance_ratio', output['variance_ratio']), ('pcs', output['components'].T)])
    else:
        pca_obj = PCA(n_components=n_pcs, svd_solver=svd_solver, random_state=random_state)
        x_pca = pca_obj.fit_transform(x)
        variance = pca_obj.explained_variance_
        variance_ratio = pca_obj.explained_variance_ratio_
        pcs = pca_obj.components_.T
        return dict([('x_pca', x_pca), ('variance', variance), ('variance_ratio', variance_ratio), ('pcs', pcs)])