
    5[g}                         d Z ddlmc mZ ddlZddlZg dZ e	d      Z
 ed      Z e	d      Zej                  dk  Zd Zdd	Zd
 ZddZd Zd Zd Zd ZddZddZddZd Zy)ac(  
======================================================================
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:

>>> eps = 1e-3
>>> 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:

>>> approx = U @ np.diag(S) @ 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 deterministic 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:

>>> A1, A2 = A**2, A
>>> 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:

>>> s = 42
>>> 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:

>>> arr = 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.

    N)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svdz9invalid input dtype (input must be float64 or complex128)z4invalid input type (must be array or LinearOperator)zFinterpolative decomposition on 32-bit systems with complex128 is buggyl        c                     	 | j                   t        j                  k(  ry| j                   t        j                  k(  ryt        # t
        $ r}t        |d }~ww xY w)NFT)dtypenp
complex128float64_DTYPE_ERRORAttributeError_TYPE_ERROR)Aes     U/var/www/html/bid-api/venv/lib/python3.12/site-packages/scipy/linalg/interpolative.py_is_realr     sK    !77bmm#WW

" !q !s!   A A A 	AAAc                 X   t        | t              r| dk(  rt        j                          y	t	        | d      r|t        j                  | t              }|j                  dk7  rt        d      |j                         dk  s|j                         dkD  rt        d      t        j                  |       y	| 3t        j                  t
        j                  j                  d
             y	t
        j                  j                  |       }t        j                  |j                  d
             y	)a  
    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.

    default__len__)r   )7   zinvalid input sizer      zvalues not in range [0,1]Nr   )
isinstancestr_backend	id_srandohasattrr   asfortranarrayfloatshape
ValueErrorminmax	id_srandirandomr   RandomState)r   staternds      r   r   r     s    4 $!2	y	!!!$e4;;%122YY[1_		a8995!	299>>"-.ii##D)388B<(    c                  p    t        j                  t        j                  |             j	                  |       S )a%  
    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

    )r!   id_srandr   prodreshape)r&   s    r   r   r     s(     RWWU^,44U;;r/   c                 <   ddl m} t        |       }t        | t        j
                        r|dk  r|}|rD|rt        j                  ||       \  }}}n^t        rt        t        j                  ||       \  }}}n7|rt        j                  ||       \  }}}nt        j                  ||       \  }}}||dz
  |fS t        |      }|rB|rt        j                  | |      \  }}n[t        rt        t        j                  | |      \  }}n5|rt        j                   | |      \  }}nt        j"                  | |      \  }}|dz
  |fS t        | |      r| j$                  \  }	}
| j&                  }|dk  rQ|}|rt        j(                  ||	|
|      \  }}}n(t        rt        t        j*                  ||	|
|      \  }}}||dz
  |fS t        |      }|rt        j,                  |	|
||      \  }}n't        rt        t        j.                  |	|
||      \  }}|dz
  |fS t0        )aP
  
    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.
    r   LinearOperatorr   )scipy.sparse.linalgr6   r   r   r   ndarrayr!   iddp_aid	_IS_32BIT_32BIT_ERRORidzp_aididdp_ididzp_idintiddr_aididzr_aididdr_ididzr_idr&   rmatveciddp_rididzp_rididdr_rididzr_ridr   )r   eps_or_kr   r6   realepskidxprojmnmatvecas               r   r   r     s   V 3A;D!RZZ a<C#+#4#4S!#<LAsD **#+#4#4S!#<LAsD#+#3#3C#;LAsD#+#3#3C#;LAsDcAgt##HA ( 1 1!Q 7IC ** ( 1 1!Q 7IC ( 0 0A 6IC ( 0 0A 6IC7D= 	A~	&ww1))a<C'00aGD3&&'00aGD3cAgt##HA$--aGQ?	T&&$--aGQ?	T7D= r/   c                     t        |       rt        j                  | |dz   |      S t        j                  | |dz   |      S )aA  
    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.
    r   )r   r!   idd_reconididz_reconid)BrM   rN   s      r   r
   r
   l  s=    < {##AsQw55##AsQw55r/   c                 |    t        |      rt        j                  | dz   |      S t        j                  | dz   |      S )a  
    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.
    r   )r   r!   idd_reconintidz_reconint)rM   rN   s     r   r	   r	     s:    B ~$$S1Wd33$$S1Wd33r/   c                     t        |       rt        j                  | ||dz         S t        j                  | ||dz         S )aw  
    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.
    r   )r   r!   idd_copycolsidz_copycols)r   rL   rM   s      r   r   r     s>    D {$$Q3733$$Q3733r/   c                     t        |       rt        j                  | |dz   |      \  }}}nt        j                  | |dz   |      \  }}}|||fS )a  
    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.
    r   )r   r!   
idd_id2svd
idz_id2svd)rU   rM   rN   UVSs         r   r   r     sS    D {%%aq$71a%%aq$71aa7Nr/   c                      ddl m}  |         j                  \  }} fd} fd}t               rt	        j
                  |||||      S t	        j                  |||||      S )a  
    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.
    r   aslinearoperatorc                 &    j                  |       S Nmatvecxr   s    r   rh   z&estimate_spectral_norm.<locals>.matvec       xx{r/   c                 &    j                  |       S rf   rD   ri   s    r   rQ   z'estimate_spectral_norm.<locals>.matveca"      yy|r/   its)r7   rd   r&   r   r!   	idd_snorm	idz_snorm)r   rp   rd   rO   rP   rh   rQ   s   `      r   r   r     sb    * 5A77DAq{!!!QSAA!!!QSAAr/   c           	           ddl m}  |         |       j                  \  }} fd} fd}fd}fd}	t               rt	        j
                  ||||	|||      S t	        j                  ||||	|||      S )a  
    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.
    r   rc   c                 &    j                  |       S rf   rg   ri   s    r   matvec1z,estimate_spectral_norm_diff.<locals>.matvec1G  rk   r/   c                 &    j                  |       S rf   rm   ri   s    r   matveca1z-estimate_spectral_norm_diff.<locals>.matveca1I  rn   r/   c                 &    j                  |       S rf   rg   rj   rU   s    r   matvec2z,estimate_spectral_norm_diff.<locals>.matvec2K  rk   r/   c                 &    j                  |       S rf   rm   ry   s    r   matveca2z-estimate_spectral_norm_diff.<locals>.matveca2M  rn   r/   ro   )r7   rd   r&   r   r!   idd_diffsnormidz_diffsnorm)
r   rU   rp   rd   rO   rP   ru   rw   rz   r|   s
   ``        r   r   r   *  s    2 5AA77DAq{%%q(HgwCA 	A %%q(HgwCA 	Ar/   c                     ddl m} t               }t         t        j
                        rQ|dk  r|}|rF|rt        j                  |       \  }}}nt        rt        t        j                  |       \  }}}n|rt        j                  |       \  }}}nt        j                  |       \  }}}nt        |      }	|	t         j                        kD  r%t!        d|	 dt         j                         d      |rF|rt        j"                   |	      \  }}}n1t        rt        t        j$                   |	      \  }}}n	|rt        j&                   |	      \  }}}nt        j(                   |	      \  }}}nt         |      r j                  \  }
} fd} fd}|dk  rL|}|rt        j*                  ||
|||      \  }}}nt        rt        t        j,                  ||
|||      \  }}}n[t        |      }	|rt        j.                  |
||||	      \  }}}n0t        rt        t        j0                  |
||||	      \  }}}nt2        |||fS )	a  
    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.
    r   r5   r   zApproximation rank z exceeds min(A.shape) =   c                 &    j                  |       S rf   rg   ri   s    r   rh   zsvd.<locals>.matvec  s    88A;r/   c                 &    j                  |       S rf   rm   ri   s    r   rQ   zsvd.<locals>.matveca  s    99Q<r/   )r7   r6   r   r   r   r8   r!   	iddp_asvdr:   r;   	idzp_asvdiddp_svdidzp_svdr?   r(   r&   r'   	iddr_asvd	idzr_asvdiddr_svdidzr_svd	iddp_rsvd	idzp_rsvd	iddr_rsvd	idzr_rsvdr   )r   rI   r   r6   rJ   rK   r_   r`   ra   rL   rO   rP   rh   rQ   s   `             r   r   r   W  s-   ^ 3A;D!RZZ a<C&00a8GAq! **&00a8GAq!&//Q7GAq!&//Q7GAq!HA3qww< #6qc :%%(\N!"5 6 6&00A6GAq! **&00A6GAq!&//15GAq!&//15GAq!	A~	&ww1		 a<C",,S!QH1a&&",,S!QH1aHA",,Q7FAF1a&&",,Q7FAF1aa7Nr/   c                    ddl m} t        |       }t        | t        j
                        rK|rt        j                  ||       }nt        j                  ||       }|dk(  rt        | j                        }|S t        | |      rM| j                  \  }}| j                  }|rt        j                  ||||      S t        j                  ||||      S t        )an  
    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.
    r   r5   )r7   r6   r   r   r   r8   r!   idd_estrankidz_estrankr(   r&   rD   idd_findrankidz_findrankr   )r   rK   r6   rJ   rankrO   rP   rQ   s           r   r   r     s    : 3A;D!RZZ ''Q/D''Q/D19qww<D	A~	&ww1))((aG<<((aG<<r/   rf   )T)   )__doc__#scipy.linalg._interpolative_backendlinalg_interpolative_backendr!   numpyr   sys__all__r'   r   	TypeErrorr   r;   maxsizer:   r   r   r   r   r
   r	   r   r   r   r   r   r    r/   r   <module>r      s   @bH 7 6  
 UVNO 5 6[[5 		!')T<$DN!6H$4N%4P&RBD*AZk\2r/   