12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004 |
- #******************************************************************************
- # Copyright (C) 2013 Kenneth L. Ho
- #
- # Redistribution and use in source and binary forms, with or without
- # modification, are permitted provided that the following conditions are met:
- #
- # Redistributions of source code must retain the above copyright notice, this
- # list of conditions and the following disclaimer. Redistributions in binary
- # form must reproduce the above copyright notice, this list of conditions and
- # the following disclaimer in the documentation and/or other materials
- # provided with the distribution.
- #
- # None of the names of the copyright holders may be used to endorse or
- # promote products derived from this software without specific prior written
- # permission.
- #
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- # POSSIBILITY OF SUCH DAMAGE.
- #******************************************************************************
- # Python module for interfacing with `id_dist`.
- r"""
- ======================================================================
- Interpolative matrix decomposition (:mod:`scipy.linalg.interpolative`)
- ======================================================================
- .. moduleauthor:: Kenneth L. Ho <klho@stanford.edu>
- .. versionadded:: 0.13
- .. currentmodule:: scipy.linalg.interpolative
- An interpolative decomposition (ID) of a matrix :math:`A \in
- \mathbb{C}^{m \times n}` of rank :math:`k \leq \min \{ m, n \}` is a
- factorization
- .. math::
- A \Pi =
- \begin{bmatrix}
- A \Pi_{1} & A \Pi_{2}
- \end{bmatrix} =
- A \Pi_{1}
- \begin{bmatrix}
- I & T
- \end{bmatrix},
- where :math:`\Pi = [\Pi_{1}, \Pi_{2}]` is a permutation matrix with
- :math:`\Pi_{1} \in \{ 0, 1 \}^{n \times k}`, i.e., :math:`A \Pi_{2} =
- A \Pi_{1} T`. This can equivalently be written as :math:`A = BP`,
- where :math:`B = A \Pi_{1}` and :math:`P = [I, T] \Pi^{\mathsf{T}}`
- are the *skeleton* and *interpolation matrices*, respectively.
- If :math:`A` does not have exact rank :math:`k`, then there exists an
- approximation in the form of an ID such that :math:`A = BP + E`, where
- :math:`\| E \| \sim \sigma_{k + 1}` is on the order of the :math:`(k +
- 1)`-th largest singular value of :math:`A`. Note that :math:`\sigma_{k
- + 1}` is the best possible error for a rank-:math:`k` approximation
- and, in fact, is achieved by the singular value decomposition (SVD)
- :math:`A \approx U S V^{*}`, where :math:`U \in \mathbb{C}^{m \times
- k}` and :math:`V \in \mathbb{C}^{n \times k}` have orthonormal columns
- and :math:`S = \mathop{\mathrm{diag}} (\sigma_{i}) \in \mathbb{C}^{k
- \times k}` is diagonal with nonnegative entries. The principal
- advantages of using an ID over an SVD are that:
- - it is cheaper to construct;
- - it preserves the structure of :math:`A`; and
- - it is more efficient to compute with in light of the identity submatrix of :math:`P`.
- Routines
- ========
- Main functionality:
- .. autosummary::
- :toctree: generated/
- interp_decomp
- reconstruct_matrix_from_id
- reconstruct_interp_matrix
- reconstruct_skel_matrix
- id_to_svd
- svd
- estimate_spectral_norm
- estimate_spectral_norm_diff
- estimate_rank
- Support functions:
- .. autosummary::
- :toctree: generated/
- seed
- rand
- References
- ==========
- This module uses the ID software package [1]_ by Martinsson, Rokhlin,
- Shkolnisky, and Tygert, which is a Fortran library for computing IDs
- using various algorithms, including the rank-revealing QR approach of
- [2]_ and the more recent randomized methods described in [3]_, [4]_,
- and [5]_. This module exposes its functionality in a way convenient
- for Python users. Note that this module does not add any functionality
- beyond that of organizing a simpler and more consistent interface.
- We advise the user to consult also the `documentation for the ID package
- <http://tygert.com/id_doc.4.pdf>`_.
- .. [1] P.G. Martinsson, V. Rokhlin, Y. Shkolnisky, M. Tygert. "ID: a
- software package for low-rank approximation of matrices via interpolative
- decompositions, version 0.2." http://tygert.com/id_doc.4.pdf.
- .. [2] H. Cheng, Z. Gimbutas, P.G. Martinsson, V. Rokhlin. "On the
- compression of low rank matrices." *SIAM J. Sci. Comput.* 26 (4): 1389--1404,
- 2005. :doi:`10.1137/030602678`.
- .. [3] E. Liberty, F. Woolfe, P.G. Martinsson, V. Rokhlin, M.
- Tygert. "Randomized algorithms for the low-rank approximation of matrices."
- *Proc. Natl. Acad. Sci. U.S.A.* 104 (51): 20167--20172, 2007.
- :doi:`10.1073/pnas.0709640104`.
- .. [4] P.G. Martinsson, V. Rokhlin, M. Tygert. "A randomized
- algorithm for the decomposition of matrices." *Appl. Comput. Harmon. Anal.* 30
- (1): 47--68, 2011. :doi:`10.1016/j.acha.2010.02.003`.
- .. [5] F. Woolfe, E. Liberty, V. Rokhlin, M. Tygert. "A fast
- randomized algorithm for the approximation of matrices." *Appl. Comput.
- Harmon. Anal.* 25 (3): 335--366, 2008. :doi:`10.1016/j.acha.2007.12.002`.
- Tutorial
- ========
- Initializing
- ------------
- The first step is to import :mod:`scipy.linalg.interpolative` by issuing the
- command:
- >>> import scipy.linalg.interpolative as sli
- Now let's build a matrix. For this, we consider a Hilbert matrix, which is well
- know to have low rank:
- >>> from scipy.linalg import hilbert
- >>> n = 1000
- >>> A = hilbert(n)
- We can also do this explicitly via:
- >>> import numpy as np
- >>> n = 1000
- >>> A = np.empty((n, n), order='F')
- >>> for j in range(n):
- >>> for i in range(n):
- >>> A[i,j] = 1. / (i + j + 1)
- Note the use of the flag ``order='F'`` in :func:`numpy.empty`. This
- instantiates the matrix in Fortran-contiguous order and is important for
- avoiding data copying when passing to the backend.
- We then define multiplication routines for the matrix by regarding it as a
- :class:`scipy.sparse.linalg.LinearOperator`:
- >>> from scipy.sparse.linalg import aslinearoperator
- >>> L = aslinearoperator(A)
- This automatically sets up methods describing the action of the matrix and its
- adjoint on a vector.
- Computing an ID
- ---------------
- We have several choices of algorithm to compute an ID. These fall largely
- according to two dichotomies:
- 1. how the matrix is represented, i.e., via its entries or via its action on a
- vector; and
- 2. whether to approximate it to a fixed relative precision or to a fixed rank.
- We step through each choice in turn below.
- In all cases, the ID is represented by three parameters:
- 1. a rank ``k``;
- 2. an index array ``idx``; and
- 3. interpolation coefficients ``proj``.
- The ID is specified by the relation
- ``np.dot(A[:,idx[:k]], proj) == A[:,idx[k:]]``.
- From matrix entries
- ...................
- We first consider a matrix given in terms of its entries.
- To compute an ID to a fixed precision, type:
- >>> k, idx, proj = sli.interp_decomp(A, eps)
- where ``eps < 1`` is the desired precision.
- To compute an ID to a fixed rank, use:
- >>> idx, proj = sli.interp_decomp(A, k)
- where ``k >= 1`` is the desired rank.
- Both algorithms use random sampling and are usually faster than the
- corresponding older, deterministic algorithms, which can be accessed via the
- commands:
- >>> k, idx, proj = sli.interp_decomp(A, eps, rand=False)
- and:
- >>> idx, proj = sli.interp_decomp(A, k, rand=False)
- respectively.
- From matrix action
- ..................
- Now consider a matrix given in terms of its action on a vector as a
- :class:`scipy.sparse.linalg.LinearOperator`.
- To compute an ID to a fixed precision, type:
- >>> k, idx, proj = sli.interp_decomp(L, eps)
- To compute an ID to a fixed rank, use:
- >>> idx, proj = sli.interp_decomp(L, k)
- These algorithms are randomized.
- Reconstructing an ID
- --------------------
- The ID routines above do not output the skeleton and interpolation matrices
- explicitly but instead return the relevant information in a more compact (and
- sometimes more useful) form. To build these matrices, write:
- >>> B = sli.reconstruct_skel_matrix(A, k, idx)
- for the skeleton matrix and:
- >>> P = sli.reconstruct_interp_matrix(idx, proj)
- for the interpolation matrix. The ID approximation can then be computed as:
- >>> C = np.dot(B, P)
- This can also be constructed directly using:
- >>> C = sli.reconstruct_matrix_from_id(B, idx, proj)
- without having to first compute ``P``.
- Alternatively, this can be done explicitly as well using:
- >>> B = A[:,idx[:k]]
- >>> P = np.hstack([np.eye(k), proj])[:,np.argsort(idx)]
- >>> C = np.dot(B, P)
- Computing an SVD
- ----------------
- An ID can be converted to an SVD via the command:
- >>> U, S, V = sli.id_to_svd(B, idx, proj)
- The SVD approximation is then:
- >>> C = np.dot(U, np.dot(np.diag(S), np.dot(V.conj().T)))
- The SVD can also be computed "fresh" by combining both the ID and conversion
- steps into one command. Following the various ID algorithms above, there are
- correspondingly various SVD algorithms that one can employ.
- From matrix entries
- ...................
- We consider first SVD algorithms for a matrix given in terms of its entries.
- To compute an SVD to a fixed precision, type:
- >>> U, S, V = sli.svd(A, eps)
- To compute an SVD to a fixed rank, use:
- >>> U, S, V = sli.svd(A, k)
- Both algorithms use random sampling; for the determinstic versions, issue the
- keyword ``rand=False`` as above.
- From matrix action
- ..................
- Now consider a matrix given in terms of its action on a vector.
- To compute an SVD to a fixed precision, type:
- >>> U, S, V = sli.svd(L, eps)
- To compute an SVD to a fixed rank, use:
- >>> U, S, V = sli.svd(L, k)
- Utility routines
- ----------------
- Several utility routines are also available.
- To estimate the spectral norm of a matrix, use:
- >>> snorm = sli.estimate_spectral_norm(A)
- This algorithm is based on the randomized power method and thus requires only
- matrix-vector products. The number of iterations to take can be set using the
- keyword ``its`` (default: ``its=20``). The matrix is interpreted as a
- :class:`scipy.sparse.linalg.LinearOperator`, but it is also valid to supply it
- as a :class:`numpy.ndarray`, in which case it is trivially converted using
- :func:`scipy.sparse.linalg.aslinearoperator`.
- The same algorithm can also estimate the spectral norm of the difference of two
- matrices ``A1`` and ``A2`` as follows:
- >>> diff = sli.estimate_spectral_norm_diff(A1, A2)
- This is often useful for checking the accuracy of a matrix approximation.
- Some routines in :mod:`scipy.linalg.interpolative` require estimating the rank
- of a matrix as well. This can be done with either:
- >>> k = sli.estimate_rank(A, eps)
- or:
- >>> k = sli.estimate_rank(L, eps)
- depending on the representation. The parameter ``eps`` controls the definition
- of the numerical rank.
- Finally, the random number generation required for all randomized routines can
- be controlled via :func:`scipy.linalg.interpolative.seed`. To reset the seed
- values to their original values, use:
- >>> sli.seed('default')
- To specify the seed values, use:
- >>> sli.seed(s)
- where ``s`` must be an integer or array of 55 floats. If an integer, the array
- of floats is obtained by using ``numpy.random.rand`` with the given integer
- seed.
- To simply generate some random numbers, type:
- >>> sli.rand(n)
- where ``n`` is the number of random numbers to generate.
- Remarks
- -------
- The above functions all automatically detect the appropriate interface and work
- with both real and complex data types, passing input arguments to the proper
- backend routine.
- """
- import scipy.linalg._interpolative_backend as _backend
- import numpy as np
- import sys
- __all__ = [
- 'estimate_rank',
- 'estimate_spectral_norm',
- 'estimate_spectral_norm_diff',
- 'id_to_svd',
- 'interp_decomp',
- 'rand',
- 'reconstruct_interp_matrix',
- 'reconstruct_matrix_from_id',
- 'reconstruct_skel_matrix',
- 'seed',
- 'svd',
- ]
- _DTYPE_ERROR = ValueError("invalid input dtype (input must be float64 or complex128)")
- _TYPE_ERROR = TypeError("invalid input type (must be array or LinearOperator)")
- _32BIT_ERROR = ValueError("interpolative decomposition on 32-bit systems "
- "with complex128 is buggy")
- _IS_32BIT = (sys.maxsize < 2**32)
- def _is_real(A):
- try:
- if A.dtype == np.complex128:
- return False
- elif A.dtype == np.float64:
- return True
- else:
- raise _DTYPE_ERROR
- except AttributeError as e:
- raise _TYPE_ERROR from e
- def seed(seed=None):
- """
- Seed the internal random number generator used in this ID package.
- The generator is a lagged Fibonacci method with 55-element internal state.
- Parameters
- ----------
- seed : int, sequence, 'default', optional
- If 'default', the random seed is reset to a default value.
- If `seed` is a sequence containing 55 floating-point numbers
- in range [0,1], these are used to set the internal state of
- the generator.
- If the value is an integer, the internal state is obtained
- from `numpy.random.RandomState` (MT19937) with the integer
- used as the initial seed.
- If `seed` is omitted (None), ``numpy.random.rand`` is used to
- initialize the generator.
- """
- # For details, see :func:`_backend.id_srand`, :func:`_backend.id_srandi`,
- # and :func:`_backend.id_srando`.
- if isinstance(seed, str) and seed == 'default':
- _backend.id_srando()
- elif hasattr(seed, '__len__'):
- state = np.asfortranarray(seed, dtype=float)
- if state.shape != (55,):
- raise ValueError("invalid input size")
- elif state.min() < 0 or state.max() > 1:
- raise ValueError("values not in range [0,1]")
- _backend.id_srandi(state)
- elif seed is None:
- _backend.id_srandi(np.random.rand(55))
- else:
- rnd = np.random.RandomState(seed)
- _backend.id_srandi(rnd.rand(55))
- def rand(*shape):
- """
- Generate standard uniform pseudorandom numbers via a very efficient lagged
- Fibonacci method.
- This routine is used for all random number generation in this package and
- can affect ID and SVD results.
- Parameters
- ----------
- *shape
- Shape of output array
- """
- # For details, see :func:`_backend.id_srand`, and :func:`_backend.id_srando`.
- return _backend.id_srand(np.prod(shape)).reshape(shape)
- def interp_decomp(A, eps_or_k, rand=True):
- """
- Compute ID of a matrix.
- An ID of a matrix `A` is a factorization defined by a rank `k`, a column
- index array `idx`, and interpolation coefficients `proj` such that::
- numpy.dot(A[:,idx[:k]], proj) = A[:,idx[k:]]
- The original matrix can then be reconstructed as::
- numpy.hstack([A[:,idx[:k]],
- numpy.dot(A[:,idx[:k]], proj)]
- )[:,numpy.argsort(idx)]
- or via the routine :func:`reconstruct_matrix_from_id`. This can
- equivalently be written as::
- numpy.dot(A[:,idx[:k]],
- numpy.hstack([numpy.eye(k), proj])
- )[:,np.argsort(idx)]
- in terms of the skeleton and interpolation matrices::
- B = A[:,idx[:k]]
- and::
- P = numpy.hstack([numpy.eye(k), proj])[:,np.argsort(idx)]
- respectively. See also :func:`reconstruct_interp_matrix` and
- :func:`reconstruct_skel_matrix`.
- The ID can be computed to any relative precision or rank (depending on the
- value of `eps_or_k`). If a precision is specified (`eps_or_k < 1`), then
- this function has the output signature::
- k, idx, proj = interp_decomp(A, eps_or_k)
- Otherwise, if a rank is specified (`eps_or_k >= 1`), then the output
- signature is::
- idx, proj = interp_decomp(A, eps_or_k)
- .. This function automatically detects the form of the input parameters
- and passes them to the appropriate backend. For details, see
- :func:`_backend.iddp_id`, :func:`_backend.iddp_aid`,
- :func:`_backend.iddp_rid`, :func:`_backend.iddr_id`,
- :func:`_backend.iddr_aid`, :func:`_backend.iddr_rid`,
- :func:`_backend.idzp_id`, :func:`_backend.idzp_aid`,
- :func:`_backend.idzp_rid`, :func:`_backend.idzr_id`,
- :func:`_backend.idzr_aid`, and :func:`_backend.idzr_rid`.
- Parameters
- ----------
- A : :class:`numpy.ndarray` or :class:`scipy.sparse.linalg.LinearOperator` with `rmatvec`
- Matrix to be factored
- eps_or_k : float or int
- Relative error (if `eps_or_k < 1`) or rank (if `eps_or_k >= 1`) of
- approximation.
- rand : bool, optional
- Whether to use random sampling if `A` is of type :class:`numpy.ndarray`
- (randomized algorithms are always used if `A` is of type
- :class:`scipy.sparse.linalg.LinearOperator`).
- Returns
- -------
- k : int
- Rank required to achieve specified relative precision if
- `eps_or_k < 1`.
- idx : :class:`numpy.ndarray`
- Column index array.
- proj : :class:`numpy.ndarray`
- Interpolation coefficients.
- """
- from scipy.sparse.linalg import LinearOperator
- real = _is_real(A)
- if isinstance(A, np.ndarray):
- if eps_or_k < 1:
- eps = eps_or_k
- if rand:
- if real:
- k, idx, proj = _backend.iddp_aid(eps, A)
- else:
- if _IS_32BIT:
- raise _32BIT_ERROR
- k, idx, proj = _backend.idzp_aid(eps, A)
- else:
- if real:
- k, idx, proj = _backend.iddp_id(eps, A)
- else:
- k, idx, proj = _backend.idzp_id(eps, A)
- return k, idx - 1, proj
- else:
- k = int(eps_or_k)
- if rand:
- if real:
- idx, proj = _backend.iddr_aid(A, k)
- else:
- if _IS_32BIT:
- raise _32BIT_ERROR
- idx, proj = _backend.idzr_aid(A, k)
- else:
- if real:
- idx, proj = _backend.iddr_id(A, k)
- else:
- idx, proj = _backend.idzr_id(A, k)
- return idx - 1, proj
- elif isinstance(A, LinearOperator):
- m, n = A.shape
- matveca = A.rmatvec
- if eps_or_k < 1:
- eps = eps_or_k
- if real:
- k, idx, proj = _backend.iddp_rid(eps, m, n, matveca)
- else:
- if _IS_32BIT:
- raise _32BIT_ERROR
- k, idx, proj = _backend.idzp_rid(eps, m, n, matveca)
- return k, idx - 1, proj
- else:
- k = int(eps_or_k)
- if real:
- idx, proj = _backend.iddr_rid(m, n, matveca, k)
- else:
- if _IS_32BIT:
- raise _32BIT_ERROR
- idx, proj = _backend.idzr_rid(m, n, matveca, k)
- return idx - 1, proj
- else:
- raise _TYPE_ERROR
- def reconstruct_matrix_from_id(B, idx, proj):
- """
- Reconstruct matrix from its ID.
- A matrix `A` with skeleton matrix `B` and ID indices and coefficients `idx`
- and `proj`, respectively, can be reconstructed as::
- numpy.hstack([B, numpy.dot(B, proj)])[:,numpy.argsort(idx)]
- See also :func:`reconstruct_interp_matrix` and
- :func:`reconstruct_skel_matrix`.
- .. This function automatically detects the matrix data type and calls the
- appropriate backend. For details, see :func:`_backend.idd_reconid` and
- :func:`_backend.idz_reconid`.
- Parameters
- ----------
- B : :class:`numpy.ndarray`
- Skeleton matrix.
- idx : :class:`numpy.ndarray`
- Column index array.
- proj : :class:`numpy.ndarray`
- Interpolation coefficients.
- Returns
- -------
- :class:`numpy.ndarray`
- Reconstructed matrix.
- """
- if _is_real(B):
- return _backend.idd_reconid(B, idx + 1, proj)
- else:
- return _backend.idz_reconid(B, idx + 1, proj)
- def reconstruct_interp_matrix(idx, proj):
- """
- Reconstruct interpolation matrix from ID.
- The interpolation matrix can be reconstructed from the ID indices and
- coefficients `idx` and `proj`, respectively, as::
- P = numpy.hstack([numpy.eye(proj.shape[0]), proj])[:,numpy.argsort(idx)]
- The original matrix can then be reconstructed from its skeleton matrix `B`
- via::
- numpy.dot(B, P)
- See also :func:`reconstruct_matrix_from_id` and
- :func:`reconstruct_skel_matrix`.
- .. This function automatically detects the matrix data type and calls the
- appropriate backend. For details, see :func:`_backend.idd_reconint` and
- :func:`_backend.idz_reconint`.
- Parameters
- ----------
- idx : :class:`numpy.ndarray`
- Column index array.
- proj : :class:`numpy.ndarray`
- Interpolation coefficients.
- Returns
- -------
- :class:`numpy.ndarray`
- Interpolation matrix.
- """
- if _is_real(proj):
- return _backend.idd_reconint(idx + 1, proj)
- else:
- return _backend.idz_reconint(idx + 1, proj)
- def reconstruct_skel_matrix(A, k, idx):
- """
- Reconstruct skeleton matrix from ID.
- The skeleton matrix can be reconstructed from the original matrix `A` and its
- ID rank and indices `k` and `idx`, respectively, as::
- B = A[:,idx[:k]]
- The original matrix can then be reconstructed via::
- numpy.hstack([B, numpy.dot(B, proj)])[:,numpy.argsort(idx)]
- See also :func:`reconstruct_matrix_from_id` and
- :func:`reconstruct_interp_matrix`.
- .. This function automatically detects the matrix data type and calls the
- appropriate backend. For details, see :func:`_backend.idd_copycols` and
- :func:`_backend.idz_copycols`.
- Parameters
- ----------
- A : :class:`numpy.ndarray`
- Original matrix.
- k : int
- Rank of ID.
- idx : :class:`numpy.ndarray`
- Column index array.
- Returns
- -------
- :class:`numpy.ndarray`
- Skeleton matrix.
- """
- if _is_real(A):
- return _backend.idd_copycols(A, k, idx + 1)
- else:
- return _backend.idz_copycols(A, k, idx + 1)
- def id_to_svd(B, idx, proj):
- """
- Convert ID to SVD.
- The SVD reconstruction of a matrix with skeleton matrix `B` and ID indices and
- coefficients `idx` and `proj`, respectively, is::
- U, S, V = id_to_svd(B, idx, proj)
- A = numpy.dot(U, numpy.dot(numpy.diag(S), V.conj().T))
- See also :func:`svd`.
- .. This function automatically detects the matrix data type and calls the
- appropriate backend. For details, see :func:`_backend.idd_id2svd` and
- :func:`_backend.idz_id2svd`.
- Parameters
- ----------
- B : :class:`numpy.ndarray`
- Skeleton matrix.
- idx : :class:`numpy.ndarray`
- Column index array.
- proj : :class:`numpy.ndarray`
- Interpolation coefficients.
- Returns
- -------
- U : :class:`numpy.ndarray`
- Left singular vectors.
- S : :class:`numpy.ndarray`
- Singular values.
- V : :class:`numpy.ndarray`
- Right singular vectors.
- """
- if _is_real(B):
- U, V, S = _backend.idd_id2svd(B, idx + 1, proj)
- else:
- U, V, S = _backend.idz_id2svd(B, idx + 1, proj)
- return U, S, V
- def estimate_spectral_norm(A, its=20):
- """
- Estimate spectral norm of a matrix by the randomized power method.
- .. This function automatically detects the matrix data type and calls the
- appropriate backend. For details, see :func:`_backend.idd_snorm` and
- :func:`_backend.idz_snorm`.
- Parameters
- ----------
- A : :class:`scipy.sparse.linalg.LinearOperator`
- Matrix given as a :class:`scipy.sparse.linalg.LinearOperator` with the
- `matvec` and `rmatvec` methods (to apply the matrix and its adjoint).
- its : int, optional
- Number of power method iterations.
- Returns
- -------
- float
- Spectral norm estimate.
- """
- from scipy.sparse.linalg import aslinearoperator
- A = aslinearoperator(A)
- m, n = A.shape
- matvec = lambda x: A. matvec(x)
- matveca = lambda x: A.rmatvec(x)
- if _is_real(A):
- return _backend.idd_snorm(m, n, matveca, matvec, its=its)
- else:
- return _backend.idz_snorm(m, n, matveca, matvec, its=its)
- def estimate_spectral_norm_diff(A, B, its=20):
- """
- Estimate spectral norm of the difference of two matrices by the randomized
- power method.
- .. This function automatically detects the matrix data type and calls the
- appropriate backend. For details, see :func:`_backend.idd_diffsnorm` and
- :func:`_backend.idz_diffsnorm`.
- Parameters
- ----------
- A : :class:`scipy.sparse.linalg.LinearOperator`
- First matrix given as a :class:`scipy.sparse.linalg.LinearOperator` with the
- `matvec` and `rmatvec` methods (to apply the matrix and its adjoint).
- B : :class:`scipy.sparse.linalg.LinearOperator`
- Second matrix given as a :class:`scipy.sparse.linalg.LinearOperator` with
- the `matvec` and `rmatvec` methods (to apply the matrix and its adjoint).
- its : int, optional
- Number of power method iterations.
- Returns
- -------
- float
- Spectral norm estimate of matrix difference.
- """
- from scipy.sparse.linalg import aslinearoperator
- A = aslinearoperator(A)
- B = aslinearoperator(B)
- m, n = A.shape
- matvec1 = lambda x: A. matvec(x)
- matveca1 = lambda x: A.rmatvec(x)
- matvec2 = lambda x: B. matvec(x)
- matveca2 = lambda x: B.rmatvec(x)
- if _is_real(A):
- return _backend.idd_diffsnorm(
- m, n, matveca1, matveca2, matvec1, matvec2, its=its)
- else:
- return _backend.idz_diffsnorm(
- m, n, matveca1, matveca2, matvec1, matvec2, its=its)
- def svd(A, eps_or_k, rand=True):
- """
- Compute SVD of a matrix via an ID.
- An SVD of a matrix `A` is a factorization::
- A = numpy.dot(U, numpy.dot(numpy.diag(S), V.conj().T))
- where `U` and `V` have orthonormal columns and `S` is nonnegative.
- The SVD can be computed to any relative precision or rank (depending on the
- value of `eps_or_k`).
- See also :func:`interp_decomp` and :func:`id_to_svd`.
- .. This function automatically detects the form of the input parameters and
- passes them to the appropriate backend. For details, see
- :func:`_backend.iddp_svd`, :func:`_backend.iddp_asvd`,
- :func:`_backend.iddp_rsvd`, :func:`_backend.iddr_svd`,
- :func:`_backend.iddr_asvd`, :func:`_backend.iddr_rsvd`,
- :func:`_backend.idzp_svd`, :func:`_backend.idzp_asvd`,
- :func:`_backend.idzp_rsvd`, :func:`_backend.idzr_svd`,
- :func:`_backend.idzr_asvd`, and :func:`_backend.idzr_rsvd`.
- Parameters
- ----------
- A : :class:`numpy.ndarray` or :class:`scipy.sparse.linalg.LinearOperator`
- Matrix to be factored, given as either a :class:`numpy.ndarray` or a
- :class:`scipy.sparse.linalg.LinearOperator` with the `matvec` and
- `rmatvec` methods (to apply the matrix and its adjoint).
- eps_or_k : float or int
- Relative error (if `eps_or_k < 1`) or rank (if `eps_or_k >= 1`) of
- approximation.
- rand : bool, optional
- Whether to use random sampling if `A` is of type :class:`numpy.ndarray`
- (randomized algorithms are always used if `A` is of type
- :class:`scipy.sparse.linalg.LinearOperator`).
- Returns
- -------
- U : :class:`numpy.ndarray`
- Left singular vectors.
- S : :class:`numpy.ndarray`
- Singular values.
- V : :class:`numpy.ndarray`
- Right singular vectors.
- """
- from scipy.sparse.linalg import LinearOperator
- real = _is_real(A)
- if isinstance(A, np.ndarray):
- if eps_or_k < 1:
- eps = eps_or_k
- if rand:
- if real:
- U, V, S = _backend.iddp_asvd(eps, A)
- else:
- if _IS_32BIT:
- raise _32BIT_ERROR
- U, V, S = _backend.idzp_asvd(eps, A)
- else:
- if real:
- U, V, S = _backend.iddp_svd(eps, A)
- else:
- U, V, S = _backend.idzp_svd(eps, A)
- else:
- k = int(eps_or_k)
- if k > min(A.shape):
- raise ValueError("Approximation rank %s exceeds min(A.shape) = "
- " %s " % (k, min(A.shape)))
- if rand:
- if real:
- U, V, S = _backend.iddr_asvd(A, k)
- else:
- if _IS_32BIT:
- raise _32BIT_ERROR
- U, V, S = _backend.idzr_asvd(A, k)
- else:
- if real:
- U, V, S = _backend.iddr_svd(A, k)
- else:
- U, V, S = _backend.idzr_svd(A, k)
- elif isinstance(A, LinearOperator):
- m, n = A.shape
- matvec = lambda x: A.matvec(x)
- matveca = lambda x: A.rmatvec(x)
- if eps_or_k < 1:
- eps = eps_or_k
- if real:
- U, V, S = _backend.iddp_rsvd(eps, m, n, matveca, matvec)
- else:
- if _IS_32BIT:
- raise _32BIT_ERROR
- U, V, S = _backend.idzp_rsvd(eps, m, n, matveca, matvec)
- else:
- k = int(eps_or_k)
- if real:
- U, V, S = _backend.iddr_rsvd(m, n, matveca, matvec, k)
- else:
- if _IS_32BIT:
- raise _32BIT_ERROR
- U, V, S = _backend.idzr_rsvd(m, n, matveca, matvec, k)
- else:
- raise _TYPE_ERROR
- return U, S, V
- def estimate_rank(A, eps):
- """
- Estimate matrix rank to a specified relative precision using randomized
- methods.
- The matrix `A` can be given as either a :class:`numpy.ndarray` or a
- :class:`scipy.sparse.linalg.LinearOperator`, with different algorithms used
- for each case. If `A` is of type :class:`numpy.ndarray`, then the output
- rank is typically about 8 higher than the actual numerical rank.
- .. This function automatically detects the form of the input parameters and
- passes them to the appropriate backend. For details,
- see :func:`_backend.idd_estrank`, :func:`_backend.idd_findrank`,
- :func:`_backend.idz_estrank`, and :func:`_backend.idz_findrank`.
- Parameters
- ----------
- A : :class:`numpy.ndarray` or :class:`scipy.sparse.linalg.LinearOperator`
- Matrix whose rank is to be estimated, given as either a
- :class:`numpy.ndarray` or a :class:`scipy.sparse.linalg.LinearOperator`
- with the `rmatvec` method (to apply the matrix adjoint).
- eps : float
- Relative error for numerical rank definition.
- Returns
- -------
- int
- Estimated matrix rank.
- """
- from scipy.sparse.linalg import LinearOperator
- real = _is_real(A)
- if isinstance(A, np.ndarray):
- if real:
- rank = _backend.idd_estrank(eps, A)
- else:
- rank = _backend.idz_estrank(eps, A)
- if rank == 0:
- # special return value for nearly full rank
- rank = min(A.shape)
- return rank
- elif isinstance(A, LinearOperator):
- m, n = A.shape
- matveca = A.rmatvec
- if real:
- return _backend.idd_findrank(eps, m, n, matveca)
- else:
- return _backend.idz_findrank(eps, m, n, matveca)
- else:
- raise _TYPE_ERROR
|