Пример #1
0
def svds_scipy(A, k=6, *, return_vecs=True, **svds_opts):
    """Compute a number of singular value pairs

    Parameters
    ----------
    A : (m, n) dense, sparse or linear operator.
        The operator to solve.
    k : int
        Number of requested singular values.
    return_vecs : bool, optional
        Whether to return the singular vectors.

    Returns
    -------
    U : (m, k) array
        Left singular vectors (if ``return_vecs=True``) as columns.
    s : (k,) array
        Singular values.
    VH : (k, n) array
        Right singular vectors (if ``return_vecs=True``) as rows.
    """
    settings = {'k': k, 'return_singular_vectors': return_vecs, **svds_opts}

    # avoid matrix like behaviour
    if isinstance(A, qu.qarray):
        A = A.A

    if return_vecs:
        uk, sk, vtk = spla.svds(A, **settings)
        so = np.argsort(-sk)
        return qu.qarray(uk[:, so]), sk[so], qu.qarray(vtk[so, :])
    else:
        sk = spla.svds(A, **settings)
        return sk[np.argsort(-sk)]
Пример #2
0
 def to_dense(self):
     """Convert this TN to a dense array.
     """
     t = self.TN.contract_tags(...)
     t.fuse_([('k', list(map(self.lower_ind_id.format, self.TN.sites))),
              ('b', list(map(self.upper_ind_id.format, self.TN.sites)))])
     return qu.qarray(t.data)
Пример #3
0
 def test_convert_vector_to_dop(self):
     x = [1, 2, 3j]
     p = qu.qu(x, qtype='r')
     assert_allclose(
         p,
         qu.qarray([[1. + 0.j, 2. + 0.j, 0. - 3.j],
                    [2. + 0.j, 4. + 0.j, 0. - 6.j],
                    [0. + 3.j, 0. + 6.j, 9. + 0.j]]))
Пример #4
0
def maybe_sort_and_project(lk, vk, P, sort=True):
    if sort:
        sortinds = np.argsort(lk)
        lk, vk = lk[sortinds], vk[:, sortinds]

    # map eigenvectors out of subspace
    if P is not None:
        vk = P @ vk

    return lk, qu.qarray(vk)
Пример #5
0
def svds_numpy(a, k, return_vecs=True, **_):
    """Partial singular value decomposition using numpys (full) singular value
    decomposition.

    Parameters
    ----------
    a : array_like
        Operator to decompose.
    k : int, optional
        Number of singular value triplets to retrieve.
    return_vecs : bool, optional
        whether to return the computed vecs or values only

    Returns
    -------
    (uk,) sk (, vkt) :
        Singlar value triplets.
    """
    if return_vecs:
        uk, sk, vkt = nla.svd(a.A if qu.issparse(a) else a, compute_uv=True)
        return qu.qarray(uk[:, :k]), sk[:k], qu.qarray(vkt[:k, :])
    else:
        sk = nla.svd(a.A if qu.issparse(a) else a, compute_uv=False)
        return sk[:k]
Пример #6
0
def eig_numpy(A, sort=True, isherm=True, return_vecs=True, autoblock=False):
    """Numpy based dense eigensolve.

    Parameters
    ----------
    A : array_like
        The operator to decompose.
    sort : bool, optional
        Whether to sort into ascending order.
    isherm : bool, optional
        Whether ``A`` is hermitian.
    return_vecs : bool, optional
        Whether to return the eigenvectors.
    autoblock : bool, optional
        If true, automatically identify and exploit symmetries appearing in the
        current basis as block diagonals formed via permutation of rows and
        columns.

    Returns
    -------
    evals : 1D-array
        The eigenvalues.
    evecs : qarray
        If ``return_vecs=True``, the eigenvectors.
    """
    if autoblock:
        return eigensystem_autoblocked(A,
                                       sort=sort,
                                       isherm=isherm,
                                       return_vecs=return_vecs)

    evals = _NUMPY_EIG_FUNCS[return_vecs, isherm](A)

    if return_vecs:
        evals, evecs = evals

        if sort:
            sortinds = np.argsort(evals)
            evals, evecs = evals[sortinds], evecs[:, sortinds]

        return evals, qu.qarray(evecs)

    if sort:
        evals.sort()

    return evals
Пример #7
0
def eig_numpy(A, sort=True, isherm=True, return_vecs=True):
    """Numpy based dense eigensolve.

    Parameters
    ----------
    A : array_like
        The operator to decompose.
    sort : bool, optional
        Whether to sort into ascending order.
    isherm : bool, optional
        Whether ``A`` is hermitian.
    return_vecs : bool, optional
        Whether to return the eigenvectors.

    Returns
    -------
    evals : 1D-array
        The eigenvalues.
    evecs : qarray
        If ``return_vecs=True``, the eigenvectors.
    """
    evals = _NUMPY_EIG_FUNCS[return_vecs, isherm](A)

    if return_vecs:
        evals, evecs = evals

        if sort:
            sortinds = np.argsort(evals)
            evals, evecs = evals[sortinds], evecs[:, sortinds]

        return evals, qu.qarray(evecs)

    if sort:
        return np.sort(evals)

    return evals
Пример #8
0
def eigs_scipy(A,
               k,
               *,
               B=None,
               which=None,
               return_vecs=True,
               sigma=None,
               isherm=True,
               sort=True,
               P=None,
               tol=None,
               **eigs_opts):
    """Returns a few eigenpairs from a possibly sparse hermitian operator

    Parameters
    ----------
    A : array_like, sparse_matrix, LinearOperator or quimb.Lazy
        The operator to solve for.
    k : int
        Number of eigenpairs to return
    B : array_like, sparse_matrix, LinearOperator or quimb.Lazy, optional
        If given, the RHS operator (which should be positive) defining a
        generalized eigen problem.
    which : str, optional
        where in spectrum to take eigenvalues from (see
        :func:`scipy.sparse.linalg.eigsh`).
    return_vecs : bool, optional
        Whether to return the eigenvectors as well.
    sigma : float, optional
        Shift, if targeting interior eigenpairs.
    isherm : bool, optional
        Whether ``A`` is hermitian.
    P : array_like, sparse_matrix, LinearOperator or quimb.Lazy, optional
        Perform the eigensolve in the subspace defined by this projector.
    sort : bool, optional
        Whether to ensure the eigenvalues are sorted in ascending value.
    eigs_opts
        Supplied to :func:`scipy.sparse.linalg.eigsh` or
        :func:`scipy.sparse.linalg.eigs`.

    Returns
    -------
    lk : (k,) array
        The eigenvalues.
    vk : (m, k) array
        Corresponding eigenvectors (if ``return_vecs=True``).
    """
    if isinstance(A, qu.Lazy):
        A = A()
    if isinstance(B, qu.Lazy):
        B = B()
    if isinstance(P, qu.Lazy):
        P = P()

    # avoid matrix like behaviour
    if isinstance(A, qu.qarray):
        A = A.A

    # project into subspace
    if P is not None:
        A = qu.dag(P) @ (A @ P)

    # Options that might get passed that scipy doesn't support
    eigs_opts.pop('EPSType', None)

    # convert certain options for scipy
    settings = {
        'k':
        k,
        'M':
        B,
        'which': (
            'SA' if (which is None) and (sigma is None) else 'LM' if
            (which is None) and (sigma is not None) else
            # For target using shift-invert scipy requires 'LM' ->
            'LM' if ('T' in which.upper()) and (sigma is not None) else which),
        'sigma':
        sigma,
        'return_eigenvectors':
        return_vecs,
        'tol':
        0 if tol is None else tol
    }

    eig_fn = spla.eigsh if isherm else spla.eigs

    if return_vecs:
        lk, vk = eig_fn(A, **settings, **eigs_opts)
        vk = qu.qarray(vk)
        return maybe_sort_and_project(lk, vk, P, sort)
    else:
        lk = eig_fn(A, **settings, **eigs_opts)
        return np.sort(lk) if sort else lk
Пример #9
0
def eigs_lobpcg(A,
                k,
                *,
                B=None,
                v0=None,
                which=None,
                return_vecs=True,
                sigma=None,
                isherm=True,
                P=None,
                sort=True,
                **lobpcg_opts):
    """Interface to scipy's lobpcg eigensolver, which can be good for
    generalized eigenproblems with matrix-free operators. Seems to a be a bit
    innacurate though (e.g. on the order of ~ 1e-6 for eigenvalues). Also only
    takes real, symmetric problems, targeting smallest eigenvalues (though
    scipy will soon have complex support, and its easy to add oneself).

    Note that the slepc eigensolver also has a lobpcg backend
    (``EPSType='lobpcg'``) which accepts complex input and is more accurate -
    though seems slower.

    Parameters
    ----------
    A : array_like, sparse_matrix, LinearOperator or callable
        The operator to solve for.
    k : int
        Number of eigenpairs to return
    B : array_like, sparse_matrix, LinearOperator or callable, optional
        If given, the RHS operator (which should be positive) defining a
        generalized eigen problem.
    v0 : array_like (d, k), optional
        The initial subspace to iterate with.
    which : {'SA', 'LA'}, optional
        Find the smallest or largest eigenvalues.
    return_vecs : bool, optional
        Whether to return the eigenvectors found.
    P : array_like, sparse_matrix, LinearOperator or callable, optional
        Perform the eigensolve in the subspace defined by this projector.
    sort : bool, optional
        Whether to ensure the eigenvalues are sorted in ascending value.
    lobpcg_opts
        Supplied to :func:`scipy.sparse.linagl.lobpcg`.

    Returns
    -------
    lk : array_like (k,)
        The eigenvalues.
    vk : array_like (d, k)
        The eigenvectors, if `return_vecs=True`.

    See Also
    --------
    eigs_scipy, eigs_numpy, eigs_slepc
    """
    if not isherm:
        raise ValueError("lobpcg can only solve symmetric problems.")

    if sigma is not None:
        raise ValueError("lobpcg can only solve extremal eigenvalues.")

    # remove invalid options for lobpcg
    lobpcg_opts.pop('ncv', None)
    lobpcg_opts.pop('EPSType', None)

    # convert some arguments and defaults
    lobpcg_opts.setdefault('maxiter', 30)
    if lobpcg_opts['maxiter'] is None:
        lobpcg_opts['maxiter'] = 30
    largest = {'SA': False, 'LA': True}[which]

    if isinstance(A, qu.Lazy):
        A = A()
    if isinstance(B, qu.Lazy):
        B = B()
    if isinstance(P, qu.Lazy):
        P = P()

    # project into subspace
    if P is not None:
        A = qu.dag(P) @ (A @ P)

    # avoid matrix like behaviour
    if isinstance(A, qu.qarray):
        A = A.A

    d = A.shape[0]

    # set up the initial subsspace to iterate with
    if v0 is None:
        v0 = qu.randn((d, k), dtype=A.dtype)
    else:
        # check if intial space should be projected too
        if P is not None and v0.shape[0] != d:
            v0 = qu.dag(P) @ v0

        v0 = v0.reshape(d, -1)

        # if not enough initial states given, flesh out with random
        if v0.shape[1] != k:
            v0 = np.hstack(v0, qu.randn((d, k - v0.shape[1]), dtype=A.dtype))

    lk, vk = spla.lobpcg(A=A, X=v0, B=B, largest=largest, **lobpcg_opts)

    if return_vecs:
        vk = qu.qarray(vk)
        return maybe_sort_and_project(lk, vk, P, sort)
    else:
        return np.sort(lk) if sort else lk
Пример #10
0
 def test_norm_trace_dense(self):
     a = qu.qarray(np.diag([-3, 1, 7]))
     assert qu.norm(a, "trace") == 11
     a = qu.rand_product_state(1, qtype="dop")
     assert_allclose(qu.norm(a, "nuc"), 1)
Пример #11
0
 def to_dense(self):
     t = self.contract_tags(...)
     t.fuse_([('k', list(map(self.site_ind_id.format, self.sites)))])
     return qu.qarray(t.data.reshape(-1, 1))
Пример #12
0
def svds_slepc(A, k=6, ncv=None, return_vecs=True, SVDType='cross',
               return_all_conv=False, tol=None, maxiter=None, comm=None):
    """Find the singular values for sparse matrix `a`.

    Parameters
    ----------
    A : sparse matrix in csr format
        The matrix to solve.
    k : int
        Number of requested singular values.
    method : {"cross", "cyclic", "lanczos", "trlanczos"}
        Solver method to use.

    Returns
    -------
    U : (m, k) array
        Left singular vectors (if ``return_vecs=True``) as columns.
    s : (k,) array
        Singular values.
    VH : (k, n) array
        Right singular vectors (if ``return_vecs=True``) as rows.
    """
    if comm is None:
        comm = get_default_comm()

    pA = convert_mat_to_petsc(A, comm=comm)

    svd_solver = _init_svd_solver(nsv=k, SVDType=SVDType, tol=tol,
                                  maxiter=maxiter, ncv=ncv, comm=comm)
    svd_solver.setOperator(pA)
    svd_solver.solve()

    nconv = svd_solver.getConverged()
    k = nconv if return_all_conv else k
    if nconv < k:
        raise RuntimeError("SLEPC svds did not find enough singular triplets, "
                           f"wanted: {k}, found: {nconv}.")

    rank = comm.Get_rank()

    if return_vecs:
        def usv_getter():
            v, u = pA.createVecs()
            for i in range(k):
                s = svd_solver.getSingularTriplet(i, u, v)
                lu = gather_petsc_array(u, comm=comm, out_shape=(-1, 1))
                lv = gather_petsc_array(v, comm=comm, out_shape=(1, -1))
                yield lu, s, lv

        lus, sk, lvs = zip(*usv_getter())
        sk = np.asarray(sk)

        if rank == 0:
            uk = qu.qarray(np.concatenate(lus, axis=1))
            vtk = qu.qarray(np.concatenate(lvs, axis=0).conjugate())

            # # check if input matrix was real -> use real output when
            # #   petsc compiled with complex scalars and thus outputs complex
            # convert = (np.issubdtype(A.dtype, np.floating) and
            #            np.issubdtype(uk.dtype, np.complexfloating))
            # if convert:
            #     uk = normalize_real_part(uk)
            #     vtk = normalize_real_part(uk, transposed=True)

            res = uk, sk, vtk
    else:
        res = np.asarray([svd_solver.getValue(i) for i in range(k)])

    comm.Barrier()
    svd_solver.destroy()
    return res if rank == 0 else None
Пример #13
0
def eigs_slepc(A, k, *, B=None, which=None, sigma=None, isherm=True, P=None,
               v0=None, ncv=None, return_vecs=True, sort=True, EPSType=None,
               return_all_conv=False, st_opts=None, tol=None, maxiter=None,
               l_win=None, comm=None):
    """Solve a matrix using the advanced eigensystem solver

    Parameters
    ----------
    A : dense-matrix, sparse-matrix, LinearOperator or callable
        Operator to solve.
    k : int, optional
        Number of requested eigenpairs.
    B : dense-matrix, sparse-matrix, LinearOperator or callable
        The RHS operator defining a generalized eigenproblem.
    which : {"LM": "SM", "LR", "LA", "SR", "SA", "LI", "SI", "TM", "TR", "TI"}
        Which eigenpairs to target. See :func:`scipy.sparse.linalg.eigs`.
    sigma : float, optional
        Target eigenvalue, implies ``which='TR'`` if this is not set but
        ``sigma`` is.
    isherm : bool, optional
        Whether problem is hermitian or not.
    P : dense-matrix, sparse-matrix, LinearOperator or callable
        Perform the eigensolve in the subspace defined by this projector.
    v0 : 1D-array like, optional
        Initial iteration vector, e.g., informed guess at eigenvector.
    ncv : int, optional
        Subspace size, defaults to ``min(20, 2 * k)``.
    return_vecs : bool, optional
        Whether to return the eigenvectors.
    sort : bool, optional
        Whether to sort the eigenpairs in ascending real value.
    EPSType : {"krylovschur", 'gd', 'lobpcg', 'jd', 'ciss'}, optional
        SLEPc eigensolver type to use, see slepc4py.EPSType.
    return_all_conv : bool, optional
        Whether to return converged eigenpairs beyond requested subspace size
    st_opts : dict, optional
        options to send to the eigensolver internal inverter.
    tol : float, optional
        Tolerance.
    maxiter : int, optional
        Maximum number of iterations.
    comm : mpi4py communicator, optional
        MPI communicator, defaults to ``COMM_SELF`` for a single process solve.

    Returns
    -------
    lk : (k,) array
        The eigenvalues.
    vk : (m, k) array
        Corresponding eigenvectors (if ``return_vecs=True``).
    """
    if comm is None:
        comm = get_default_comm()

    A_is_linop = isinstance(A, sp.linalg.LinearOperator)
    B_is_linop = isinstance(B, sp.linalg.LinearOperator)
    isgen = B is not None

    eigensolver = _init_eigensolver(
        which=("SA" if (which is None) and (sigma is None) else
               "TR" if (which is None) and (sigma is not None) else which),
        EPSType=EPSType, k=k, sigma=sigma, isherm=isherm, tol=tol, ncv=ncv,
        maxiter=maxiter, st_opts=st_opts, comm=comm, l_win=l_win, isgen=isgen,
        A_is_linop=A_is_linop, B_is_linop=B_is_linop)

    # set up the initial operators and solver
    pA = convert_mat_to_petsc(A, comm=comm)
    pB = convert_mat_to_petsc(B, comm=comm) if isgen else None

    if P is not None:
        pP = convert_mat_to_petsc(P, comm=comm)
        pA = pP.transposeMatMult(pA.matMult(pP))

    eigensolver.setOperators(pA, pB)
    if v0 is not None:
        eigensolver.setInitialSpace(convert_vec_to_petsc(v0, comm=comm))
    eigensolver.solve()

    # work out how many eigenpairs to retrieve
    nconv = eigensolver.getConverged()
    k = nconv if (return_all_conv or l_win is not None) else k
    if nconv < k:
        raise RuntimeError("SLEPC eigs did not find enough eigenpairs, "
                           f"wanted: {k}, found: {nconv}.")

    # get eigenvalues
    rank = comm.Get_rank()
    if rank == 0:
        lk = np.asarray([eigensolver.getEigenvalue(i) for i in range(k)])
        lk = lk.real if isherm else lk
    else:
        res = None

    # gather eigenvectors
    if return_vecs:
        pvec = pA.getVecLeft()

        def get_vecs_local():
            for i in range(k):
                eigensolver.getEigenvector(i, pvec)

                if P is not None:
                    pvecP = pP.getVecLeft()
                    pP.mult(pvec, pvecP)

                yield gather_petsc_array(
                    pvecP if P is not None else pvec,
                    comm=comm, out_shape=(-1, 1))

        lvecs = tuple(get_vecs_local())
        if rank == 0:
            vk = np.concatenate(lvecs, axis=1)
            if sort:
                sortinds = np.argsort(lk)
                lk, vk = lk[sortinds], vk[:, sortinds]

            # check if input matrix was real -> use real output when
            #   petsc compiled with complex scalars and thus outputs complex
            convert = (isherm and np.issubdtype(A.dtype, np.floating) and
                       np.issubdtype(vk.dtype, np.complexfloating))
            if convert:
                vk = normalize_real_part(vk)

            res = lk, qu.qarray(vk)
    elif rank == 0:
        res = np.sort(lk) if sort else lk

    eigensolver.destroy()
    return res
Пример #14
0
def eigs_numpy(A,
               k,
               B=None,
               which=None,
               return_vecs=True,
               sigma=None,
               isherm=True,
               P=None,
               sort=True,
               **eig_opts):
    """Partial eigen-decomposition using numpy's dense linear algebra.

    Parameters
    ----------
    A : array_like or quimb.Lazy
        Operator to partially eigen-decompose.
    k : int
        Number of eigenpairs to return.
    B : array_like or quimb.Lazy
        If given, the RHS operator defining a generalized eigen problem.
    which : str, optional
        Which part of the spectrum to target.
    return_vecs : bool, optional
        Whether to return eigenvectors.
    sigma : None or float, optional
        Target eigenvalue.
    isherm : bool, optional
        Whether `a` is hermitian.
    P : array_like or quimb.Lazy
        Perform the eigensolve in the subspace defined by this projector.
    sort : bool, optional
        Whether to sort reduced list of eigenpairs into ascending order.
    eig_opts
        Settings to pass to numpy.eig... functions.

    Returns
    -------
        lk, (vk): k eigenvalues (and eigenvectors) sorted according to which
    """
    if isinstance(A, qu.Lazy):
        A = A()
    if isinstance(B, qu.Lazy):
        B = B()
    if isinstance(P, qu.Lazy):
        P = P()

    # project into subspace
    if P is not None:
        A = qu.dag(P) @ (A @ P)

    generalized = B is not None

    eig_fn = _DENSE_EIG_METHODS[(isherm, return_vecs, generalized)]

    if generalized:
        eig_opts['b'] = B

    # these might be given for partial eigsys but not relevant for numpy
    eig_opts.pop('ncv', None)
    eig_opts.pop('v0', None)
    eig_opts.pop('tol', None)
    eig_opts.pop('maxiter', None)
    eig_opts.pop('EPSType', None)

    if return_vecs:
        # get all eigenpairs
        lk, vk = eig_fn(A.A if qu.issparse(A) else A, **eig_opts)

        # sort and trim according to which k we want
        sk = sort_inds(lk, method=which, sigma=sigma)[:k]
        lk, vk = lk[sk], vk[:, sk]

        # also potentially sort into ascending order
        if sort:
            so = np.argsort(lk)
            lk, vk = lk[so], vk[:, so]

        # map eigenvectors out of subspace
        if P is not None:
            vk = P @ vk

        return lk, qu.qarray(vk)

    else:
        # get all eigenvalues
        lk = eig_fn(A.A if qu.issparse(A) else A, **eig_opts)

        # sort and trim according to which k we want
        sk = sort_inds(lk, method=which, sigma=sigma)[:k]
        lk = lk[sk]

        # also potentially sort into ascending order
        return np.sort(lk) if sort else lk