Fixed database typo and removed unnecessary class identifier.
This commit is contained in:
parent
00ad49a143
commit
45fb349a7d
5098 changed files with 952558 additions and 85 deletions
493
venv/Lib/site-packages/scipy/linalg/decomp_svd.py
Normal file
493
venv/Lib/site-packages/scipy/linalg/decomp_svd.py
Normal file
|
@ -0,0 +1,493 @@
|
|||
"""SVD decomposition functions."""
|
||||
import numpy
|
||||
from numpy import zeros, r_, diag, dot, arccos, arcsin, where, clip
|
||||
|
||||
# Local imports.
|
||||
from .misc import LinAlgError, _datacopied
|
||||
from .lapack import get_lapack_funcs, _compute_lwork
|
||||
from .decomp import _asarray_validated
|
||||
|
||||
__all__ = ['svd', 'svdvals', 'diagsvd', 'orth', 'subspace_angles', 'null_space']
|
||||
|
||||
|
||||
def svd(a, full_matrices=True, compute_uv=True, overwrite_a=False,
|
||||
check_finite=True, lapack_driver='gesdd'):
|
||||
"""
|
||||
Singular Value Decomposition.
|
||||
|
||||
Factorizes the matrix `a` into two unitary matrices ``U`` and ``Vh``, and
|
||||
a 1-D array ``s`` of singular values (real, non-negative) such that
|
||||
``a == U @ S @ Vh``, where ``S`` is a suitably shaped matrix of zeros with
|
||||
main diagonal ``s``.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
a : (M, N) array_like
|
||||
Matrix to decompose.
|
||||
full_matrices : bool, optional
|
||||
If True (default), `U` and `Vh` are of shape ``(M, M)``, ``(N, N)``.
|
||||
If False, the shapes are ``(M, K)`` and ``(K, N)``, where
|
||||
``K = min(M, N)``.
|
||||
compute_uv : bool, optional
|
||||
Whether to compute also ``U`` and ``Vh`` in addition to ``s``.
|
||||
Default is True.
|
||||
overwrite_a : bool, optional
|
||||
Whether to overwrite `a`; may improve performance.
|
||||
Default is False.
|
||||
check_finite : bool, optional
|
||||
Whether to check that the input matrix contains only finite numbers.
|
||||
Disabling may give a performance gain, but may result in problems
|
||||
(crashes, non-termination) if the inputs do contain infinities or NaNs.
|
||||
lapack_driver : {'gesdd', 'gesvd'}, optional
|
||||
Whether to use the more efficient divide-and-conquer approach
|
||||
(``'gesdd'``) or general rectangular approach (``'gesvd'``)
|
||||
to compute the SVD. MATLAB and Octave use the ``'gesvd'`` approach.
|
||||
Default is ``'gesdd'``.
|
||||
|
||||
.. versionadded:: 0.18
|
||||
|
||||
Returns
|
||||
-------
|
||||
U : ndarray
|
||||
Unitary matrix having left singular vectors as columns.
|
||||
Of shape ``(M, M)`` or ``(M, K)``, depending on `full_matrices`.
|
||||
s : ndarray
|
||||
The singular values, sorted in non-increasing order.
|
||||
Of shape (K,), with ``K = min(M, N)``.
|
||||
Vh : ndarray
|
||||
Unitary matrix having right singular vectors as rows.
|
||||
Of shape ``(N, N)`` or ``(K, N)`` depending on `full_matrices`.
|
||||
|
||||
For ``compute_uv=False``, only ``s`` is returned.
|
||||
|
||||
Raises
|
||||
------
|
||||
LinAlgError
|
||||
If SVD computation does not converge.
|
||||
|
||||
See also
|
||||
--------
|
||||
svdvals : Compute singular values of a matrix.
|
||||
diagsvd : Construct the Sigma matrix, given the vector s.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from scipy import linalg
|
||||
>>> m, n = 9, 6
|
||||
>>> a = np.random.randn(m, n) + 1.j*np.random.randn(m, n)
|
||||
>>> U, s, Vh = linalg.svd(a)
|
||||
>>> U.shape, s.shape, Vh.shape
|
||||
((9, 9), (6,), (6, 6))
|
||||
|
||||
Reconstruct the original matrix from the decomposition:
|
||||
|
||||
>>> sigma = np.zeros((m, n))
|
||||
>>> for i in range(min(m, n)):
|
||||
... sigma[i, i] = s[i]
|
||||
>>> a1 = np.dot(U, np.dot(sigma, Vh))
|
||||
>>> np.allclose(a, a1)
|
||||
True
|
||||
|
||||
Alternatively, use ``full_matrices=False`` (notice that the shape of
|
||||
``U`` is then ``(m, n)`` instead of ``(m, m)``):
|
||||
|
||||
>>> U, s, Vh = linalg.svd(a, full_matrices=False)
|
||||
>>> U.shape, s.shape, Vh.shape
|
||||
((9, 6), (6,), (6, 6))
|
||||
>>> S = np.diag(s)
|
||||
>>> np.allclose(a, np.dot(U, np.dot(S, Vh)))
|
||||
True
|
||||
|
||||
>>> s2 = linalg.svd(a, compute_uv=False)
|
||||
>>> np.allclose(s, s2)
|
||||
True
|
||||
|
||||
"""
|
||||
a1 = _asarray_validated(a, check_finite=check_finite)
|
||||
if len(a1.shape) != 2:
|
||||
raise ValueError('expected matrix')
|
||||
m, n = a1.shape
|
||||
overwrite_a = overwrite_a or (_datacopied(a1, a))
|
||||
|
||||
if not isinstance(lapack_driver, str):
|
||||
raise TypeError('lapack_driver must be a string')
|
||||
if lapack_driver not in ('gesdd', 'gesvd'):
|
||||
raise ValueError('lapack_driver must be "gesdd" or "gesvd", not "%s"'
|
||||
% (lapack_driver,))
|
||||
funcs = (lapack_driver, lapack_driver + '_lwork')
|
||||
gesXd, gesXd_lwork = get_lapack_funcs(funcs, (a1,))
|
||||
|
||||
# compute optimal lwork
|
||||
lwork = _compute_lwork(gesXd_lwork, a1.shape[0], a1.shape[1],
|
||||
compute_uv=compute_uv, full_matrices=full_matrices)
|
||||
|
||||
# perform decomposition
|
||||
u, s, v, info = gesXd(a1, compute_uv=compute_uv, lwork=lwork,
|
||||
full_matrices=full_matrices, overwrite_a=overwrite_a)
|
||||
|
||||
if info > 0:
|
||||
raise LinAlgError("SVD did not converge")
|
||||
if info < 0:
|
||||
raise ValueError('illegal value in %dth argument of internal gesdd'
|
||||
% -info)
|
||||
if compute_uv:
|
||||
return u, s, v
|
||||
else:
|
||||
return s
|
||||
|
||||
|
||||
def svdvals(a, overwrite_a=False, check_finite=True):
|
||||
"""
|
||||
Compute singular values of a matrix.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
a : (M, N) array_like
|
||||
Matrix to decompose.
|
||||
overwrite_a : bool, optional
|
||||
Whether to overwrite `a`; may improve performance.
|
||||
Default is False.
|
||||
check_finite : bool, optional
|
||||
Whether to check that the input matrix contains only finite numbers.
|
||||
Disabling may give a performance gain, but may result in problems
|
||||
(crashes, non-termination) if the inputs do contain infinities or NaNs.
|
||||
|
||||
Returns
|
||||
-------
|
||||
s : (min(M, N),) ndarray
|
||||
The singular values, sorted in decreasing order.
|
||||
|
||||
Raises
|
||||
------
|
||||
LinAlgError
|
||||
If SVD computation does not converge.
|
||||
|
||||
Notes
|
||||
-----
|
||||
``svdvals(a)`` only differs from ``svd(a, compute_uv=False)`` by its
|
||||
handling of the edge case of empty ``a``, where it returns an
|
||||
empty sequence:
|
||||
|
||||
>>> a = np.empty((0, 2))
|
||||
>>> from scipy.linalg import svdvals
|
||||
>>> svdvals(a)
|
||||
array([], dtype=float64)
|
||||
|
||||
See Also
|
||||
--------
|
||||
svd : Compute the full singular value decomposition of a matrix.
|
||||
diagsvd : Construct the Sigma matrix, given the vector s.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from scipy.linalg import svdvals
|
||||
>>> m = np.array([[1.0, 0.0],
|
||||
... [2.0, 3.0],
|
||||
... [1.0, 1.0],
|
||||
... [0.0, 2.0],
|
||||
... [1.0, 0.0]])
|
||||
>>> svdvals(m)
|
||||
array([ 4.28091555, 1.63516424])
|
||||
|
||||
We can verify the maximum singular value of `m` by computing the maximum
|
||||
length of `m.dot(u)` over all the unit vectors `u` in the (x,y) plane.
|
||||
We approximate "all" the unit vectors with a large sample. Because
|
||||
of linearity, we only need the unit vectors with angles in [0, pi].
|
||||
|
||||
>>> t = np.linspace(0, np.pi, 2000)
|
||||
>>> u = np.array([np.cos(t), np.sin(t)])
|
||||
>>> np.linalg.norm(m.dot(u), axis=0).max()
|
||||
4.2809152422538475
|
||||
|
||||
`p` is a projection matrix with rank 1. With exact arithmetic,
|
||||
its singular values would be [1, 0, 0, 0].
|
||||
|
||||
>>> v = np.array([0.1, 0.3, 0.9, 0.3])
|
||||
>>> p = np.outer(v, v)
|
||||
>>> svdvals(p)
|
||||
array([ 1.00000000e+00, 2.02021698e-17, 1.56692500e-17,
|
||||
8.15115104e-34])
|
||||
|
||||
The singular values of an orthogonal matrix are all 1. Here, we
|
||||
create a random orthogonal matrix by using the `rvs()` method of
|
||||
`scipy.stats.ortho_group`.
|
||||
|
||||
>>> from scipy.stats import ortho_group
|
||||
>>> np.random.seed(123)
|
||||
>>> orth = ortho_group.rvs(4)
|
||||
>>> svdvals(orth)
|
||||
array([ 1., 1., 1., 1.])
|
||||
|
||||
"""
|
||||
a = _asarray_validated(a, check_finite=check_finite)
|
||||
if a.size:
|
||||
return svd(a, compute_uv=0, overwrite_a=overwrite_a,
|
||||
check_finite=False)
|
||||
elif len(a.shape) != 2:
|
||||
raise ValueError('expected matrix')
|
||||
else:
|
||||
return numpy.empty(0)
|
||||
|
||||
|
||||
def diagsvd(s, M, N):
|
||||
"""
|
||||
Construct the sigma matrix in SVD from singular values and size M, N.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
s : (M,) or (N,) array_like
|
||||
Singular values
|
||||
M : int
|
||||
Size of the matrix whose singular values are `s`.
|
||||
N : int
|
||||
Size of the matrix whose singular values are `s`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
S : (M, N) ndarray
|
||||
The S-matrix in the singular value decomposition
|
||||
|
||||
See Also
|
||||
--------
|
||||
svd : Singular value decomposition of a matrix
|
||||
svdvals : Compute singular values of a matrix.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from scipy.linalg import diagsvd
|
||||
>>> vals = np.array([1, 2, 3]) # The array representing the computed svd
|
||||
>>> diagsvd(vals, 3, 4)
|
||||
array([[1, 0, 0, 0],
|
||||
[0, 2, 0, 0],
|
||||
[0, 0, 3, 0]])
|
||||
>>> diagsvd(vals, 4, 3)
|
||||
array([[1, 0, 0],
|
||||
[0, 2, 0],
|
||||
[0, 0, 3],
|
||||
[0, 0, 0]])
|
||||
|
||||
"""
|
||||
part = diag(s)
|
||||
typ = part.dtype.char
|
||||
MorN = len(s)
|
||||
if MorN == M:
|
||||
return r_['-1', part, zeros((M, N-M), typ)]
|
||||
elif MorN == N:
|
||||
return r_[part, zeros((M-N, N), typ)]
|
||||
else:
|
||||
raise ValueError("Length of s must be M or N.")
|
||||
|
||||
|
||||
# Orthonormal decomposition
|
||||
|
||||
def orth(A, rcond=None):
|
||||
"""
|
||||
Construct an orthonormal basis for the range of A using SVD
|
||||
|
||||
Parameters
|
||||
----------
|
||||
A : (M, N) array_like
|
||||
Input array
|
||||
rcond : float, optional
|
||||
Relative condition number. Singular values ``s`` smaller than
|
||||
``rcond * max(s)`` are considered zero.
|
||||
Default: floating point eps * max(M,N).
|
||||
|
||||
Returns
|
||||
-------
|
||||
Q : (M, K) ndarray
|
||||
Orthonormal basis for the range of A.
|
||||
K = effective rank of A, as determined by rcond
|
||||
|
||||
See also
|
||||
--------
|
||||
svd : Singular value decomposition of a matrix
|
||||
null_space : Matrix null space
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from scipy.linalg import orth
|
||||
>>> A = np.array([[2, 0, 0], [0, 5, 0]]) # rank 2 array
|
||||
>>> orth(A)
|
||||
array([[0., 1.],
|
||||
[1., 0.]])
|
||||
>>> orth(A.T)
|
||||
array([[0., 1.],
|
||||
[1., 0.],
|
||||
[0., 0.]])
|
||||
|
||||
"""
|
||||
u, s, vh = svd(A, full_matrices=False)
|
||||
M, N = u.shape[0], vh.shape[1]
|
||||
if rcond is None:
|
||||
rcond = numpy.finfo(s.dtype).eps * max(M, N)
|
||||
tol = numpy.amax(s) * rcond
|
||||
num = numpy.sum(s > tol, dtype=int)
|
||||
Q = u[:, :num]
|
||||
return Q
|
||||
|
||||
|
||||
def null_space(A, rcond=None):
|
||||
"""
|
||||
Construct an orthonormal basis for the null space of A using SVD
|
||||
|
||||
Parameters
|
||||
----------
|
||||
A : (M, N) array_like
|
||||
Input array
|
||||
rcond : float, optional
|
||||
Relative condition number. Singular values ``s`` smaller than
|
||||
``rcond * max(s)`` are considered zero.
|
||||
Default: floating point eps * max(M,N).
|
||||
|
||||
Returns
|
||||
-------
|
||||
Z : (N, K) ndarray
|
||||
Orthonormal basis for the null space of A.
|
||||
K = dimension of effective null space, as determined by rcond
|
||||
|
||||
See also
|
||||
--------
|
||||
svd : Singular value decomposition of a matrix
|
||||
orth : Matrix range
|
||||
|
||||
Examples
|
||||
--------
|
||||
1-D null space:
|
||||
|
||||
>>> from scipy.linalg import null_space
|
||||
>>> A = np.array([[1, 1], [1, 1]])
|
||||
>>> ns = null_space(A)
|
||||
>>> ns * np.sign(ns[0,0]) # Remove the sign ambiguity of the vector
|
||||
array([[ 0.70710678],
|
||||
[-0.70710678]])
|
||||
|
||||
2-D null space:
|
||||
|
||||
>>> B = np.random.rand(3, 5)
|
||||
>>> Z = null_space(B)
|
||||
>>> Z.shape
|
||||
(5, 2)
|
||||
>>> np.allclose(B.dot(Z), 0)
|
||||
True
|
||||
|
||||
The basis vectors are orthonormal (up to rounding error):
|
||||
|
||||
>>> Z.T.dot(Z)
|
||||
array([[ 1.00000000e+00, 6.92087741e-17],
|
||||
[ 6.92087741e-17, 1.00000000e+00]])
|
||||
|
||||
"""
|
||||
u, s, vh = svd(A, full_matrices=True)
|
||||
M, N = u.shape[0], vh.shape[1]
|
||||
if rcond is None:
|
||||
rcond = numpy.finfo(s.dtype).eps * max(M, N)
|
||||
tol = numpy.amax(s) * rcond
|
||||
num = numpy.sum(s > tol, dtype=int)
|
||||
Q = vh[num:,:].T.conj()
|
||||
return Q
|
||||
|
||||
|
||||
def subspace_angles(A, B):
|
||||
r"""
|
||||
Compute the subspace angles between two matrices.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
A : (M, N) array_like
|
||||
The first input array.
|
||||
B : (M, K) array_like
|
||||
The second input array.
|
||||
|
||||
Returns
|
||||
-------
|
||||
angles : ndarray, shape (min(N, K),)
|
||||
The subspace angles between the column spaces of `A` and `B` in
|
||||
descending order.
|
||||
|
||||
See Also
|
||||
--------
|
||||
orth
|
||||
svd
|
||||
|
||||
Notes
|
||||
-----
|
||||
This computes the subspace angles according to the formula
|
||||
provided in [1]_. For equivalence with MATLAB and Octave behavior,
|
||||
use ``angles[0]``.
|
||||
|
||||
.. versionadded:: 1.0
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Knyazev A, Argentati M (2002) Principal Angles between Subspaces
|
||||
in an A-Based Scalar Product: Algorithms and Perturbation
|
||||
Estimates. SIAM J. Sci. Comput. 23:2008-2040.
|
||||
|
||||
Examples
|
||||
--------
|
||||
An Hadamard matrix, which has orthogonal columns, so we expect that
|
||||
the suspace angle to be :math:`\frac{\pi}{2}`:
|
||||
|
||||
>>> from scipy.linalg import hadamard, subspace_angles
|
||||
>>> H = hadamard(4)
|
||||
>>> print(H)
|
||||
[[ 1 1 1 1]
|
||||
[ 1 -1 1 -1]
|
||||
[ 1 1 -1 -1]
|
||||
[ 1 -1 -1 1]]
|
||||
>>> np.rad2deg(subspace_angles(H[:, :2], H[:, 2:]))
|
||||
array([ 90., 90.])
|
||||
|
||||
And the subspace angle of a matrix to itself should be zero:
|
||||
|
||||
>>> subspace_angles(H[:, :2], H[:, :2]) <= 2 * np.finfo(float).eps
|
||||
array([ True, True], dtype=bool)
|
||||
|
||||
The angles between non-orthogonal subspaces are in between these extremes:
|
||||
|
||||
>>> x = np.random.RandomState(0).randn(4, 3)
|
||||
>>> np.rad2deg(subspace_angles(x[:, :2], x[:, [2]]))
|
||||
array([ 55.832])
|
||||
"""
|
||||
# Steps here omit the U and V calculation steps from the paper
|
||||
|
||||
# 1. Compute orthonormal bases of column-spaces
|
||||
A = _asarray_validated(A, check_finite=True)
|
||||
if len(A.shape) != 2:
|
||||
raise ValueError('expected 2D array, got shape %s' % (A.shape,))
|
||||
QA = orth(A)
|
||||
del A
|
||||
|
||||
B = _asarray_validated(B, check_finite=True)
|
||||
if len(B.shape) != 2:
|
||||
raise ValueError('expected 2D array, got shape %s' % (B.shape,))
|
||||
if len(B) != len(QA):
|
||||
raise ValueError('A and B must have the same number of rows, got '
|
||||
'%s and %s' % (QA.shape[0], B.shape[0]))
|
||||
QB = orth(B)
|
||||
del B
|
||||
|
||||
# 2. Compute SVD for cosine
|
||||
QA_H_QB = dot(QA.T.conj(), QB)
|
||||
sigma = svdvals(QA_H_QB)
|
||||
|
||||
# 3. Compute matrix B
|
||||
if QA.shape[1] >= QB.shape[1]:
|
||||
B = QB - dot(QA, QA_H_QB)
|
||||
else:
|
||||
B = QA - dot(QB, QA_H_QB.T.conj())
|
||||
del QA, QB, QA_H_QB
|
||||
|
||||
# 4. Compute SVD for sine
|
||||
mask = sigma ** 2 >= 0.5
|
||||
if mask.any():
|
||||
mu_arcsin = arcsin(clip(svdvals(B, overwrite_a=True), -1., 1.))
|
||||
else:
|
||||
mu_arcsin = 0.
|
||||
|
||||
# 5. Compute the principal angles
|
||||
# with reverse ordering of sigma because smallest sigma belongs to largest
|
||||
# angle theta
|
||||
theta = where(mask, mu_arcsin, arccos(clip(sigma[::-1], -1., 1.)))
|
||||
return theta
|
Loading…
Add table
Add a link
Reference in a new issue