123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429 |
- """QR decomposition functions."""
- import numpy
- # Local imports
- from .lapack import get_lapack_funcs
- from ._misc import _datacopied
- __all__ = ['qr', 'qr_multiply', 'rq']
- def safecall(f, name, *args, **kwargs):
- """Call a LAPACK routine, determining lwork automatically and handling
- error return values"""
- lwork = kwargs.get("lwork", None)
- if lwork in (None, -1):
- kwargs['lwork'] = -1
- ret = f(*args, **kwargs)
- kwargs['lwork'] = ret[-2][0].real.astype(numpy.int_)
- ret = f(*args, **kwargs)
- if ret[-1] < 0:
- raise ValueError("illegal value in %dth argument of internal %s"
- % (-ret[-1], name))
- return ret[:-2]
- def qr(a, overwrite_a=False, lwork=None, mode='full', pivoting=False,
- check_finite=True):
- """
- Compute QR decomposition of a matrix.
- Calculate the decomposition ``A = Q R`` where Q is unitary/orthogonal
- and R upper triangular.
- Parameters
- ----------
- a : (M, N) array_like
- Matrix to be decomposed
- overwrite_a : bool, optional
- Whether data in `a` is overwritten (may improve performance if
- `overwrite_a` is set to True by reusing the existing input data
- structure rather than creating a new one.)
- lwork : int, optional
- Work array size, lwork >= a.shape[1]. If None or -1, an optimal size
- is computed.
- mode : {'full', 'r', 'economic', 'raw'}, optional
- Determines what information is to be returned: either both Q and R
- ('full', default), only R ('r') or both Q and R but computed in
- economy-size ('economic', see Notes). The final option 'raw'
- (added in SciPy 0.11) makes the function return two matrices
- (Q, TAU) in the internal format used by LAPACK.
- pivoting : bool, optional
- Whether or not factorization should include pivoting for rank-revealing
- qr decomposition. If pivoting, compute the decomposition
- ``A P = Q R`` as above, but where P is chosen such that the diagonal
- of R is non-increasing.
- 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
- -------
- Q : float or complex ndarray
- Of shape (M, M), or (M, K) for ``mode='economic'``. Not returned
- if ``mode='r'``.
- R : float or complex ndarray
- Of shape (M, N), or (K, N) for ``mode='economic'``. ``K = min(M, N)``.
- P : int ndarray
- Of shape (N,) for ``pivoting=True``. Not returned if
- ``pivoting=False``.
- Raises
- ------
- LinAlgError
- Raised if decomposition fails
- Notes
- -----
- This is an interface to the LAPACK routines dgeqrf, zgeqrf,
- dorgqr, zungqr, dgeqp3, and zgeqp3.
- If ``mode=economic``, the shapes of Q and R are (M, K) and (K, N) instead
- of (M,M) and (M,N), with ``K=min(M,N)``.
- Examples
- --------
- >>> import numpy as np
- >>> from scipy import linalg
- >>> rng = np.random.default_rng()
- >>> a = rng.standard_normal((9, 6))
- >>> q, r = linalg.qr(a)
- >>> np.allclose(a, np.dot(q, r))
- True
- >>> q.shape, r.shape
- ((9, 9), (9, 6))
- >>> r2 = linalg.qr(a, mode='r')
- >>> np.allclose(r, r2)
- True
- >>> q3, r3 = linalg.qr(a, mode='economic')
- >>> q3.shape, r3.shape
- ((9, 6), (6, 6))
- >>> q4, r4, p4 = linalg.qr(a, pivoting=True)
- >>> d = np.abs(np.diag(r4))
- >>> np.all(d[1:] <= d[:-1])
- True
- >>> np.allclose(a[:, p4], np.dot(q4, r4))
- True
- >>> q4.shape, r4.shape, p4.shape
- ((9, 9), (9, 6), (6,))
- >>> q5, r5, p5 = linalg.qr(a, mode='economic', pivoting=True)
- >>> q5.shape, r5.shape, p5.shape
- ((9, 6), (6, 6), (6,))
- """
- # 'qr' was the old default, equivalent to 'full'. Neither 'full' nor
- # 'qr' are used below.
- # 'raw' is used internally by qr_multiply
- if mode not in ['full', 'qr', 'r', 'economic', 'raw']:
- raise ValueError("Mode argument should be one of ['full', 'r',"
- "'economic', 'raw']")
- if check_finite:
- a1 = numpy.asarray_chkfinite(a)
- else:
- a1 = numpy.asarray(a)
- if len(a1.shape) != 2:
- raise ValueError("expected a 2-D array")
- M, N = a1.shape
- overwrite_a = overwrite_a or (_datacopied(a1, a))
- if pivoting:
- geqp3, = get_lapack_funcs(('geqp3',), (a1,))
- qr, jpvt, tau = safecall(geqp3, "geqp3", a1, overwrite_a=overwrite_a)
- jpvt -= 1 # geqp3 returns a 1-based index array, so subtract 1
- else:
- geqrf, = get_lapack_funcs(('geqrf',), (a1,))
- qr, tau = safecall(geqrf, "geqrf", a1, lwork=lwork,
- overwrite_a=overwrite_a)
- if mode not in ['economic', 'raw'] or M < N:
- R = numpy.triu(qr)
- else:
- R = numpy.triu(qr[:N, :])
- if pivoting:
- Rj = R, jpvt
- else:
- Rj = R,
- if mode == 'r':
- return Rj
- elif mode == 'raw':
- return ((qr, tau),) + Rj
- gor_un_gqr, = get_lapack_funcs(('orgqr',), (qr,))
- if M < N:
- Q, = safecall(gor_un_gqr, "gorgqr/gungqr", qr[:, :M], tau,
- lwork=lwork, overwrite_a=1)
- elif mode == 'economic':
- Q, = safecall(gor_un_gqr, "gorgqr/gungqr", qr, tau, lwork=lwork,
- overwrite_a=1)
- else:
- t = qr.dtype.char
- qqr = numpy.empty((M, M), dtype=t)
- qqr[:, :N] = qr
- Q, = safecall(gor_un_gqr, "gorgqr/gungqr", qqr, tau, lwork=lwork,
- overwrite_a=1)
- return (Q,) + Rj
- def qr_multiply(a, c, mode='right', pivoting=False, conjugate=False,
- overwrite_a=False, overwrite_c=False):
- """
- Calculate the QR decomposition and multiply Q with a matrix.
- Calculate the decomposition ``A = Q R`` where Q is unitary/orthogonal
- and R upper triangular. Multiply Q with a vector or a matrix c.
- Parameters
- ----------
- a : (M, N), array_like
- Input array
- c : array_like
- Input array to be multiplied by ``q``.
- mode : {'left', 'right'}, optional
- ``Q @ c`` is returned if mode is 'left', ``c @ Q`` is returned if
- mode is 'right'.
- The shape of c must be appropriate for the matrix multiplications,
- if mode is 'left', ``min(a.shape) == c.shape[0]``,
- if mode is 'right', ``a.shape[0] == c.shape[1]``.
- pivoting : bool, optional
- Whether or not factorization should include pivoting for rank-revealing
- qr decomposition, see the documentation of qr.
- conjugate : bool, optional
- Whether Q should be complex-conjugated. This might be faster
- than explicit conjugation.
- overwrite_a : bool, optional
- Whether data in a is overwritten (may improve performance)
- overwrite_c : bool, optional
- Whether data in c is overwritten (may improve performance).
- If this is used, c must be big enough to keep the result,
- i.e. ``c.shape[0]`` = ``a.shape[0]`` if mode is 'left'.
- Returns
- -------
- CQ : ndarray
- The product of ``Q`` and ``c``.
- R : (K, N), ndarray
- R array of the resulting QR factorization where ``K = min(M, N)``.
- P : (N,) ndarray
- Integer pivot array. Only returned when ``pivoting=True``.
- Raises
- ------
- LinAlgError
- Raised if QR decomposition fails.
- Notes
- -----
- This is an interface to the LAPACK routines ``?GEQRF``, ``?ORMQR``,
- ``?UNMQR``, and ``?GEQP3``.
- .. versionadded:: 0.11.0
- Examples
- --------
- >>> import numpy as np
- >>> from scipy.linalg import qr_multiply, qr
- >>> A = np.array([[1, 3, 3], [2, 3, 2], [2, 3, 3], [1, 3, 2]])
- >>> qc, r1, piv1 = qr_multiply(A, 2*np.eye(4), pivoting=1)
- >>> qc
- array([[-1., 1., -1.],
- [-1., -1., 1.],
- [-1., -1., -1.],
- [-1., 1., 1.]])
- >>> r1
- array([[-6., -3., -5. ],
- [ 0., -1., -1.11022302e-16],
- [ 0., 0., -1. ]])
- >>> piv1
- array([1, 0, 2], dtype=int32)
- >>> q2, r2, piv2 = qr(A, mode='economic', pivoting=1)
- >>> np.allclose(2*q2 - qc, np.zeros((4, 3)))
- True
- """
- if mode not in ['left', 'right']:
- raise ValueError("Mode argument can only be 'left' or 'right' but "
- "not '{}'".format(mode))
- c = numpy.asarray_chkfinite(c)
- if c.ndim < 2:
- onedim = True
- c = numpy.atleast_2d(c)
- if mode == "left":
- c = c.T
- else:
- onedim = False
- a = numpy.atleast_2d(numpy.asarray(a)) # chkfinite done in qr
- M, N = a.shape
- if mode == 'left':
- if c.shape[0] != min(M, N + overwrite_c*(M-N)):
- raise ValueError('Array shapes are not compatible for Q @ c'
- ' operation: {} vs {}'.format(a.shape, c.shape))
- else:
- if M != c.shape[1]:
- raise ValueError('Array shapes are not compatible for c @ Q'
- ' operation: {} vs {}'.format(c.shape, a.shape))
- raw = qr(a, overwrite_a, None, "raw", pivoting)
- Q, tau = raw[0]
- gor_un_mqr, = get_lapack_funcs(('ormqr',), (Q,))
- if gor_un_mqr.typecode in ('s', 'd'):
- trans = "T"
- else:
- trans = "C"
- Q = Q[:, :min(M, N)]
- if M > N and mode == "left" and not overwrite_c:
- if conjugate:
- cc = numpy.zeros((c.shape[1], M), dtype=c.dtype, order="F")
- cc[:, :N] = c.T
- else:
- cc = numpy.zeros((M, c.shape[1]), dtype=c.dtype, order="F")
- cc[:N, :] = c
- trans = "N"
- if conjugate:
- lr = "R"
- else:
- lr = "L"
- overwrite_c = True
- elif c.flags["C_CONTIGUOUS"] and trans == "T" or conjugate:
- cc = c.T
- if mode == "left":
- lr = "R"
- else:
- lr = "L"
- else:
- trans = "N"
- cc = c
- if mode == "left":
- lr = "L"
- else:
- lr = "R"
- cQ, = safecall(gor_un_mqr, "gormqr/gunmqr", lr, trans, Q, tau, cc,
- overwrite_c=overwrite_c)
- if trans != "N":
- cQ = cQ.T
- if mode == "right":
- cQ = cQ[:, :min(M, N)]
- if onedim:
- cQ = cQ.ravel()
- return (cQ,) + raw[1:]
- def rq(a, overwrite_a=False, lwork=None, mode='full', check_finite=True):
- """
- Compute RQ decomposition of a matrix.
- Calculate the decomposition ``A = R Q`` where Q is unitary/orthogonal
- and R upper triangular.
- Parameters
- ----------
- a : (M, N) array_like
- Matrix to be decomposed
- overwrite_a : bool, optional
- Whether data in a is overwritten (may improve performance)
- lwork : int, optional
- Work array size, lwork >= a.shape[1]. If None or -1, an optimal size
- is computed.
- mode : {'full', 'r', 'economic'}, optional
- Determines what information is to be returned: either both Q and R
- ('full', default), only R ('r') or both Q and R but computed in
- economy-size ('economic', see Notes).
- 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
- -------
- R : float or complex ndarray
- Of shape (M, N) or (M, K) for ``mode='economic'``. ``K = min(M, N)``.
- Q : float or complex ndarray
- Of shape (N, N) or (K, N) for ``mode='economic'``. Not returned
- if ``mode='r'``.
- Raises
- ------
- LinAlgError
- If decomposition fails.
- Notes
- -----
- This is an interface to the LAPACK routines sgerqf, dgerqf, cgerqf, zgerqf,
- sorgrq, dorgrq, cungrq and zungrq.
- If ``mode=economic``, the shapes of Q and R are (K, N) and (M, K) instead
- of (N,N) and (M,N), with ``K=min(M,N)``.
- Examples
- --------
- >>> import numpy as np
- >>> from scipy import linalg
- >>> rng = np.random.default_rng()
- >>> a = rng.standard_normal((6, 9))
- >>> r, q = linalg.rq(a)
- >>> np.allclose(a, r @ q)
- True
- >>> r.shape, q.shape
- ((6, 9), (9, 9))
- >>> r2 = linalg.rq(a, mode='r')
- >>> np.allclose(r, r2)
- True
- >>> r3, q3 = linalg.rq(a, mode='economic')
- >>> r3.shape, q3.shape
- ((6, 6), (6, 9))
- """
- if mode not in ['full', 'r', 'economic']:
- raise ValueError(
- "Mode argument should be one of ['full', 'r', 'economic']")
- if check_finite:
- a1 = numpy.asarray_chkfinite(a)
- else:
- a1 = numpy.asarray(a)
- if len(a1.shape) != 2:
- raise ValueError('expected matrix')
- M, N = a1.shape
- overwrite_a = overwrite_a or (_datacopied(a1, a))
- gerqf, = get_lapack_funcs(('gerqf',), (a1,))
- rq, tau = safecall(gerqf, 'gerqf', a1, lwork=lwork,
- overwrite_a=overwrite_a)
- if not mode == 'economic' or N < M:
- R = numpy.triu(rq, N-M)
- else:
- R = numpy.triu(rq[-M:, -M:])
- if mode == 'r':
- return R
- gor_un_grq, = get_lapack_funcs(('orgrq',), (rq,))
- if N < M:
- Q, = safecall(gor_un_grq, "gorgrq/gungrq", rq[-N:], tau, lwork=lwork,
- overwrite_a=1)
- elif mode == 'economic':
- Q, = safecall(gor_un_grq, "gorgrq/gungrq", rq, tau, lwork=lwork,
- overwrite_a=1)
- else:
- rq1 = numpy.empty((N, N), dtype=rq.dtype)
- rq1[-M:] = rq
- Q, = safecall(gor_un_grq, "gorgrq/gungrq", rq1, tau, lwork=lwork,
- overwrite_a=1)
- return R, Q
|