def arlsnn(A, b): """Solves Ax = b in the least squares sense, with the solution constrained to be non-negative. For a nonpositive solution, use x = -arlsnn(A,-b,0) Parameters ---------- A : (m, n) array_like "Coefficient" matrix, type float. b : (m) array_like column of dependent variables, type float. Returns ------- x : (n) array_like column, type float. Raises ------ LinAlgError If A is not 2-D. If A is empty. If A and b do not have the same row size. If b has more than one column. If SCIPY's SVD() does not converge. Example ------- Let A = [[2., 2., 1.], [2., 1., 0.], [1., 1., 0.]] and b = [3.9, 3., 2.] Then any least-squares solver will produce x = [1. ,1., -0.1] But arlsnn() produces x = [ 1.0322, 0.9093, 0.]. arlsnn() tries to produce a small residual for the final solution, while being based toward making the fewest changes feasible to the problem. Most older solvers try to minimize the residual at the expense of extra interference with the user's model. Arls, arlsgt, and arlsnn seek a better balance. """ A = atleast_2d(_asarray_validated(A, check_finite=True)) b = atleast_1d(_asarray_validated(b, check_finite=True)) if np.count_nonzero(A) == 0 or np.count_nonzero(b) == 0: return np.zeros(A.shape[1]) checkAb(A, b, 1) n = A.shape[1] AA = A.copy() bb = b.copy() G = np.eye(n) h = np.zeros(n) x = arlsgt(AA, bb, G, h) return x
def prepeq(E, f, neglect): """ a utility routine for arlseq() below that prepares the equality constraints for use""" E = atleast_2d(_asarray_validated(E, check_finite=True)) f = atleast_1d(_asarray_validated(f, check_finite=True)) EE = E.copy() ff = f.copy() m, n = EE.shape for i in range(0, m): # determine new best row and put it next if i == 0: imax = find_max_sense(EE, ff) else: rnmax = -1.0 imax = -1 for k in range(i, m): rn = norm(EE[k, :]) if imax < 0 or rn > rnmax: rnmax = rn imax = k EE[[i, imax], :] = EE[[imax, i], :] ff[[i, imax]] = ff[[imax, i]] # normalize rin = norm(EE[i, :]) if rin > 0.0: EE[i, :] /= rin ff[i] /= rin else: EE[i, :] = 0.0 # will be culled below ff[i] = 0.0 # subtract projections onto EE[i,:] for k in range(i + 1, m): d = np.dot(EE[k, :], EE[i, :]) EE[k, :] -= d * EE[i, :] ff[k] -= d * ff[i] # reject ill-conditioned rows if m > 2: g = np.zeros(m) for k in range(0, m): g[k] = abs(ff[k]) m1 = splita(m, g) mm = splitb(m1, g) if mm < m: EE = np.resize(EE, (mm, n)) ff = np.resize(ff, mm) return EE, ff
def arlsqr(A, b): """Solves the linear system of equation, Ax = b, for any shape matrix. Arlsqr() is called exactly like arls() above, and the returns are exactly the same. The difference is that if b is only one column then arlsqr() first performs a quick assessment of the system to see if it appears to be ill-conditioned. If not, qr() is called for a solution. If the resulting solution from qr() appears to be good, then it is returned. Otherwise, arls() continues itself to to get a solution based on the SVD. The purpose of this difference is to execute signficantly faster than arlsqr() does if the problems given to it are usually well behaved. """ A = atleast_2d(_asarray_validated(A, check_finite=True)) b = atleast_1d(_asarray_validated(b, check_finite=True)) nz = np.count_nonzero(A) if nz == 0 or np.count_nonzero(b) == 0: return np.zeros(A.shape[1]), 0, 0, 0.0, 0.0 checkAb(A, b, 2) m, n = A.shape nr = min(m, n) # is b a single column? if len(b.shape) > 1: return arls(A, b) odd, maxnorm = strange(A, b) if not odd or nz < int( (m * n) / 2): # max norm tests fails for sparse-like if (m >= n): Q, R = qr(A, mode='economic') Qb = Q.T @ b try: x = solve_triangular(R, Qb) except LinAlgError: return arls(A, b) else: Q, R = qr(A.T, mode='economic') try: y = solve_triangular(R, b, 'T') except LinAlgError: return arls(A, b) x = Q @ y if mynorm(x) < maxnorm or nz < int((m * n) / 2): return x, nr, nr, 0.0, 0.0 return arls(A, b)
def arlsgt(A, b, G, h): """Solves the double linear system of equations Ax = b (least squares) Gx >= h ("greater than" inequality constraints) Both Ax=b and Gx>=h can be underdetermined, square, or over-determined. Arguments b and h must be single columns. Arlsgt() uses arls(), above, as the core solver, and iteratively selects rows of Gx>=h to move to a growing list of equality constraints, choosing first whatever equation in Gx>=h most violates its requirement. Note that "less than" equations can be included by negating both sides of the equation, thus turning it into a "greater than". If either A or b is all zeros then the solution will be all zeros. Parameters ---------- A : (m, n) array_like "Coefficient" matrix, type float. b : (m) array_like column of dependent variables, type float. G : (mg, n) array_like "Coefficient" matrix, type float. b : (mg) array_like column of dependent variables, type float. Returns ------- x : (n) array_like column, type float. Raises ------ LinAlgError If A is not 2-D. If A is empty. If A and b do not have the same row size. If b has more than one column. If G is not 2-D. If G is empty. If G and h do not have the same row size. If h has more than one column. If A and G do not have the same number of columns. If SCIPY's SVD() does not converge. Example ------- Let A = [[1,1,1], [0,1,1], [1,0,1]] and b = [5.9, 5.0, 3.9] Then any least-squares solver would produce x = [0.9, 2., 3.] But if we happen to know that all the answers should be at least 1.0 then we can add inequalites to insure that: x[0] >= 1 x[1] >= 1 x[2] >= 1 This can be expressed in the matrix equation Gx>=h where G = [[1,0,0], [0,1,0], [0,0,1]] h = [1,1,1] Then arlsgt(A,b,G,h) produces x = [1., 2.0375, 2.8508]. The residual vector and its norm would be res = [-0.011, -0.112 -0.049] norm(res) = 0.122 If the user had just forced the least-squares answer of [0.9, 2., 3.] to [1., 2., 3.] without re-solving then the residual vector and its norm would be res = [0.1, 0, 0.1] norm(res) = 0.141 which is significantly larger. """ A = atleast_2d(_asarray_validated(A, check_finite=True)) b = atleast_1d(_asarray_validated(b, check_finite=True)) if np.count_nonzero(A) == 0 or np.count_nonzero(b) == 0: return np.zeros(A.shape[1]) AA = A.copy() bb = b.copy() checkAb(AA, bb, 1) m, n = AA.shape G = atleast_2d(_asarray_validated(G, check_finite=True)) h = atleast_1d(_asarray_validated(h, check_finite=True)) GG = G.copy() hh = h.copy() checkAb(GG, hh, 1) mg, ng = GG.shape if n != ng: raise LinAlgError( "The two matrices do not have the same number of unknowns.") EE = [] ff = [] me = 0 ne = 0 # get initial solution... it might actually be right x = arls(AA, bb) nx = norm(x) if nx <= 0.0: return x # while constraints are not fully satisfied: while True: # assess state of inequalities p = -1 mg = GG.shape[0] rhs = GG @ x worst = 0.0 for i in range(0, mg): if rhs[i] < hh[i]: diff = hh[i] - rhs[i] if p < 0 or diff > worst: p = i worst = diff if p < 0: break # delete row from GGx=hh row = GG[p, :] rhs = hh[p] GG = np.delete(GG, p, 0) hh = np.delete(hh, p, 0) # add row to Ex>=f if me == 0: EE = np.zeros((1, ng)) EE[0, :] = row ff = np.zeros(1) ff[0] = rhs me = 1 ne = ng else: me += 1 EE = np.resize(EE, (me, ne)) for j in range(0, ne): EE[me - 1, j] = row[j] ff = np.resize(ff, me) ff[me - 1] = rhs # re-solve modified system x = arlseq(AA, bb, EE, ff) return x
def arlseq(A, b, E, f): """Solves the double linear system of equations Ax = b (least squares) Ex = f (exact) Both Ax=b and Ex=f system can be underdetermined, square, or over-determined. Arguments b and f must be single columns. Ax=b is handled as a least-squares problem, using arls() above, after an appropriate othogonalization process removes the projection of each row of Ax=b onto the set of equality constraints in Ex=f. The solution to the equality constraints is then added back to the solution of the reduced Ax=b system. Ex=f is treated as a set of equality constraints. These constraints are usually few in number and well behaved. But clearly the caller can easily provide equations in Ex=f that are impossible to satisfy as a group... for example, by one equation requiring x[0]=0, and another requiring x[0]=1. So the solution process will either solve each equation in Ex=f exactly (within roundoff) or if that is impossible, arlseq() will discard one or more equations until the remaining equations are solvable. In the event that Ex=f is actually ill-conditioned in the manner that arls() is expected to handle, then arlseq() will delete offending rows of Ex=f. If either A or b is all zeros then the solution will be all zeros. Parameters ---------- A : (m, n) array_like "Coefficient" matrix, type float. b : (m) array_like column of dependent variables, type float. E : (me, n) array_like "Coefficient" matrix, type float. f : (me) array_like column of dependent variables, type float. Returns ------- x : (n) array_like column, type float. Raises ------ LinAlgError If A is not 2-D. If A is empty. If A and b do not have the same row size. If b has more than one column. If E is not 2-D. If E is empty. If E and f do not have the same row size. If f has more than one column. If A and E do not have the same number of columns. If SCIPY's SVD() does not converge. Examples -------- Here is a tiny example of a problem which has an "unknown" amount of error in the right hand side, but for which the user knows that the correct SUM of the unknowns must be 3: x + 2 y = 5.3 (Least Squares) 2 x + 3 y = 7.8 x + y = 3 ( Exact ) Then the arrays for arlseq are: A = array([[ 1., 2.0], [ 2., 3.0]]) b = array([5.3, 7.8]) E = array([[ 1., 1.0]]) f = array([3.0]) Without using the equality constraint we are given here, standard solvers will return [x,y] = [-.3 , 2.8]. Even arls() will return the same [x,y] = [-.3 , 2.8]. Arlsnn() could help here by disallowing presumably unacceptable negative values, producing [x,y] = [0. , 2.615]. If we solve with arlseq(A,b,E,f) then we get [x,y] = [1.00401 1.99598]. This answer is very close to the correct answer of [x,y] = [1.0 , 2.0] if the right hand side had been the correct [5.,8.] instead of [5.3,7.8]. It is constructive to look at residuals to understand more about the problem: For [x,y] = [-.3 , 2.8], the residual is [0.0 , 0.0] (exact). But of course x + y = 2.5, not the 3.0 we really want. For [x,y] = [0. , 2.615], the residual is [0.07 , 0.045], which is of course an increase from zero, but this is natural since we have forced the solution away from being the "exact" result, for good reason. Note that x + y = 2.615, which is a little better. For [x,y] = [1.00401 1.99598], the residual is [0.004 , 0.196] which is even larger. Again, by adding extra information to the problem the residual typically increases, but the solution becomes more acceptable. Note the arlseq() achieved x + y = 3 within output format limits. Notes: ----- See arls() above for notes and references. """ A = atleast_2d(_asarray_validated(A, check_finite=True)) b = atleast_1d(_asarray_validated(b, check_finite=True)) if np.count_nonzero(A) == 0 or np.count_nonzero(b) == 0: return np.zeros(A.shape[1]) AA = A.copy() bb = b.copy() checkAb(AA, bb, 1) m, n = AA.shape rnmax = find_max_row_norm(AA) neglect = rnmax * myepsilon() E = atleast_2d(_asarray_validated(E, check_finite=True)) f = atleast_1d(_asarray_validated(f, check_finite=True)) EE = E.copy() ff = f.copy() checkAb(EE, ff, 1) me, ne = EE.shape if n != ne: raise LinAlgError( "The two matrices do not have the same number of unknowns.") EE, ff = prepeq(EE, ff, neglect) mEE = EE.shape[0] # decouple AAx=bb from EEx=ff i = 0 while i < m: for j in range(0, mEE): d = np.dot(AA[i, :], EE[j, :]) AA[i, :] -= d * EE[j, :] bb[i] -= d * ff[j] nm = norm(AA[i, :]) if nm < neglect: AA = np.delete(AA, i, 0) bb = np.delete(bb, i, 0) m = AA.shape[0] else: AA[i, :] = AA[i, :] / nm bb[i] = bb[i] / nm i += 1 # final solution xe = np.transpose(EE) @ ff if AA.shape[0] > 0: xt = arls(AA, bb) else: xt = np.zeros(n) return xt + xe
def arls(A, b): """Solves the linear system of equation, Ax = b, for any shape matrix. The system can be underdetermined, square, or over-determined. That is, A(m,n) can be such that m < n, m = n, or m > n. Argument b is a matrix of size(n,p) of p right-hand-side columns. This solver automatically detects if each system is ill-conditioned or not. Then -- If the equations are consistent then the solution will usually be exact within round-off error. -- If the equations are inconsistent then the the solution will be by least-squares. That is, it solves ``min ||b - Ax||_2``. -- If the equations are inconsistent and diagnosable as ill-conditioned using the principles of the first reference below, the system will be automatically regularized and the residual will be larger than minimum. -- If either A or b is all zeros then the solution will be all zeros. Parameters ---------- A : (m, n) array_like "Coefficient" matrix, type float. b : (m, p) array_like Set of columns of dependent variables, type float. Returns ------- x : (n, p) array_like set of columns, type float. Each column will be the solution corresponding to the same column of b. Raises ------ LinAlgError If A is not 2-D. If A is empty. If A and b do not have the same row size. If b has more than one column. If SCIPY's SVD() does not converge. Examples -------- Arls() will behave like any good least-squares solver when the system is well conditioned. Here is a tiny example of an ill-conditioned system as handled by arls(), x + y = 2 x + 1.01 y =3 Then A = array([[ 1., 1.], [ 1., 1.01.]]) and b = array([2.0, 3.0]) Then standard solvers will return: x = [-98. , 100.] But arls() will see the violation of the Picard Condition and return x = [1.12216 , 1.12779] Notes: ----- 1. When the system is ill-conditioned, the process works best when the rows of A are scaled so that the elements of b have similar estimated errors. 2. Arls() occasionally may produce a smoother (i.e., more regularized) solution than desired. In this case please try scipy routine lsmr. 3. With any linear equation solver, check that the solution is reasonable. In particular, you should check the residual vector, Ax - b. 4. Arls() neither needs nor accepts optional parameters such as iteration limits, error estimates, variable bounds, condition number limits, etc. It also does not return any error flags as there are no error states. As long as the SVD converges (and SVD failure is remarkably rare) then arls() and other routines in this package will complete normally. 5. Arls()'s intent (and the intent of all routines in this module) is to find a reasonable solution even in the midst of excessive inaccuracy, ill-conditioning, singularities, duplicated data, etc. Its performance is often very like that of lsmr, but from a completely different approach. 6. In view of note 5, arls() is not appropriate for situations where the requirements are more for high accuracy rather than robustness. So, we assume, in the coding, where needed, that no data needs to be considered more accurate than 8 significant figures. References ---------- About the Picard Condition: "The discrete picard condition for discrete ill-posed problems", Per Christian Hansen, 1990. https://link.springer.com/article/10.1007/BF01933214 About Nonnegative solutions: "Solving Least Squares Problems", by Charles L. Lawson and Richard J. Hanson. Prentice-Hall 1974 About our algorithm: "Solving Linear Algebraic Systems Arising in the Solution of Integral Equations of the First Kind", Dissertation by Rondall E. Jones, 1985, U. of N.M. Advisor: Cleve B. Moler, creator of MatLab and co-founder of MathWorks. For further information on the Picard Condition please see http://www.rejones7.net/autorej/What_Is_The_Picard_Condition.htm """ A = atleast_2d(_asarray_validated(A, check_finite=True)) b = atleast_1d(_asarray_validated(b, check_finite=True)) if np.count_nonzero(A) == 0 or np.count_nonzero(b) == 0: return np.zeros(A.shape[1]) AA = A.copy() bb = b.copy() checkAb(AA, bb, 2) n = AA.shape[1] if len(bb.shape) == 2: nrhs = bb.shape[1] else: nrhs = 1 # call for each solution U, S, Vt = np.linalg.svd(AA, full_matrices=False) xx = np.zeros((n, nrhs)) if nrhs == 1: return arlsusv(AA, bb, U, S, Vt)[0] for p in range(0, nrhs): xx[:, p] = arlsusv(AA, bb[:, p], U, S, Vt)[0] return xx
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`. 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 -------- A 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_T_QB = dot(QA.T, QB) sigma = svdvals(QA_T_QB) # 3. Compute matrix B if QA.shape[1] >= QB.shape[1]: B = QB - dot(QA, QA_T_QB) else: B = QA - dot(QB, QA_T_QB.T) del QA, QB, QA_T_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 theta = where(mask, mu_arcsin, arccos(clip(sigma, -1., 1.))) return theta
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, string_types): 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 %d-th 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 arlsnn(A, b): """Solves Ax = b in the least squares sense, with the solution constrained to be non-negative. For a nonpositive solution, use x = -arlsnn(A,-b) Parameters ---------- A : (m, n) array_like "Coefficient" matrix, type float. b : (m) array_like column of dependent variables, type float. Returns ------- x : (n) array_like column, type float. nr : int The numerical rank of the matrix, A. ur : int The usable rank of the problem, Ax=b. Note that "numerical rank" is an attribute of a matrix but the "usable rank" that arls computes is an attribute of the problem, Ax=b. sigma : float The estimated right-hand-side root-mean-square error. lambda : float The estimated Tikhonov regularization. Raises ------ LinAlgError If A is not 2-D. If A is empty. If A and b do not have the same row size. If b has more than one column. If SCIPY's SVD() does not converge. Example ------- Let A = [[2., 2., 1.], [2., 1., 0.], [1., 1., 0.]] and b = [3.9, 3., 2.] Then any least-squares solver will produce x = [1. ,1., -0.1] But arlsnn() produces x = [ 1.0322, 0.9093, 0.]. Arlsnn() tries to produce a small residual for the final solution, while being based toward making the fewest changes feasible to the problem. Most older solvers try to minimize the residual at the expense of extra interference with the user's model. Arls, arlsgt, and arlsnn seek a better balance. """ A = atleast_2d(_asarray_validated(A, check_finite=True)) b = atleast_1d(_asarray_validated(b, check_finite=True)) if np.count_nonzero(A) == 0 or np.count_nonzero(b) == 0: return np.zeros(A.shape[1]), 0, 0, 0.0, 0.0 checkAb(A, b, 1) n = A.shape[1] G = np.eye(n) h = np.zeros(n) return arlsgt(A, b, G, h)
def arlseq(A, b, E, f): """Solves the double linear system of equations Ax = b (least squares) Ex = f (exact) Both Ax=b and Ex=f system can be underdetermined, square, or over-determined. Arguments b and f must be single columns. Ex=f is treated as a set of equality constraints. These constraints are usually few in number and well behaved. But clearly the caller can easily provide equations in Ex=f that are impossible to satisfy as a group. For example, there could be one equation requiring x[0]=0, and another requiring x[0]=1. And, the solver must deal with there being redundant or other pathological situations within the E matrix. So the solution process will either solve each equation in Ex=f exactly (within roundoff) or if that is impossible, arlseq() will discard one or more equations until the remaining equations are solvable exactly (within roundoff). We will refer below to the solution of this reduced system as "xe". After Ex=f is processed as above, the rows of Ax=b will have their projections onto every row of Ex=f subtracted from them. We will call this reduced set of equations A'x = b'. (Thus, the rows of A' will all be orthogonal to the rows of E.) This reduced problem A'x = b', will then be solved with arls(). We will refer to the solution of this system as "xt". The final solution will be x = xe + xt. (Since E and A' are mutually orthogonal matrices.) If A'x = b' is NOT ill-conditioned (or singular) then x = xe + xt will satisfy all the equality constraints that were not rejected. However, if A'x = b' is ill-conditioned, then xt will be a regularized solution, not an exact solution, and thus x = xe + xt will not in general satisfy the equality constraints exactly. Parameters ---------- A : (m, n) array_like "Coefficient" matrix, type float. b : (m) array_like column of dependent variables, type float. E : (me, n) array_like "Coefficient" matrix, type float. f : (me) array_like column of dependent variables, type float. Returns ------- x : (n) array_like column, type float. nr : int The numerical rank of the matrix, A, after its projection onto the rows of E are subtracted. ur : int The "usable" rank of the "reduced" problem, Ax=b, after its projection onto the rows of Ex=f are subtracted. Note that "numerical rank" is an attribute of a matrix but the "usable rank" that arls computes is an attribute of the problem, Ax=b. sigma : float The estimated right-hand-side root-mean-square error. lambda : float The estimated Tikhonov regularization. Raises ------ LinAlgError If A is not 2-D. If A is empty. If A and b do not have the same row size. If b has more than one column. If E is not 2-D. If E is empty. If E and f do not have the same row size. If f has more than one column. If A and E do not have the same number of columns. If SCIPY's SVD() does not converge. Examples -------- Here is a tiny example of a problem which has an "unknown" amount of error in the right hand side, but for which the user knows that the correct SUM of the unknowns must be 3: x + 2 y = 5.3 (Least Squares) 2 x + 3 y = 7.8 x + y = 3 ( Exact ) Then the arrays for arlseq are: A = array([[ 1., 2.0], [ 2., 3.0]]) b = array([5.3, 7.8]) E = array([[ 1.0, 1.0]]) f = array([3.0]) Without using the equality constraint we are given here, standard solvers will return [x,y] = [-.3 , 2.8]. Even arls() will return the same [x,y] = [-.3 , 2.8]. The residual for this solution is [0.0 , 0.0] (within roundoff). But of course x + y = 2.5, not the 3.0 we really want. Arlsnn() could help here by disallowing presumably unacceptable negative values, producing [x,y] = [0. , 2.6]. The residual for this solution is [-0.1 , 0.] which is of course an increase from zero, but this is natural since we have forced the solution away from being the "exact" result, for good reason. Note that x + y = 2.6, which is a little better. If we solve with arlseq(A,b,E,f) then we get [x,y] = [1.004, 1.996]. This answer is close to the "correct" answer of [x,y] = [1.0 , 2.0] if the right hand side had been the correct [5.,8.] instead of [5.3,7.8]. The residual for this solution is [-0.3 , 0.2] which is yet larger. Again, when adding constraints to the problem the residual typically increases, but the solution becomes more acceptable. Note that x + y = 3 exactly. Notes: ----- See arls() above for notes and references. """ A = atleast_2d(_asarray_validated(A, check_finite=True)) b = atleast_1d(_asarray_validated(b, check_finite=True)) if np.count_nonzero(A) == 0 or np.count_nonzero(b) == 0: return np.zeros(A.shape[1]), 0, 0, 0.0, 0.0 AA = A.copy() bb = b.copy() checkAb(AA, bb, 1) m, n = AA.shape rnmax = find_max_row_norm(AA) neglect = rnmax * 0.00000001 # see Note 6. for arls() E = atleast_2d(_asarray_validated(E, check_finite=True)) f = atleast_1d(_asarray_validated(f, check_finite=True)) EE = E.copy() ff = f.copy() checkAb(EE, ff, 1) me, ne = EE.shape if n != ne: raise LinAlgError( "The two matrices do not have the same number of unknowns.") EE, ff = prepeq(EE, ff, neglect) mEE = EE.shape[0] # decouple AAx=bb from EEx=ff i = 0 while i < m: for j in range(0, mEE): d = np.dot(AA[i, :], EE[j, :]) AA[i, :] -= d * EE[j, :] bb[i] -= d * ff[j] nm = mynorm(AA[i, :]) if nm < neglect: AA = np.delete(AA, i, 0) bb = np.delete(bb, i, 0) m = AA.shape[0] else: AA[i, :] = AA[i, :] / nm bb[i] = bb[i] / nm i += 1 # final solution xe = np.transpose(EE) @ ff if AA.shape[0] > 0: xt, nr, ur, sigma, lambdah = arls(AA, bb) return xt + xe, nr, ur, sigma, lambdah else: return xe, 0, 0, 0., 0.
def arls(A, b): """Solves the linear system of equation, Ax = b, for any shape matrix. The system can be underdetermined, square, or over-determined. That is, A(m,n) can be such that m < n, m = n, or m > n. Argument b is a matrix of size(m,p) of p right-hand-side columns. This solver automatically detects if each system is ill-conditioned or not. Then -- If the equations are consistent then the solution will usually be exact within round-off error. -- If the equations are inconsistent then the the solution will be by least-squares. That is, it solves ``min ||b - Ax||_2``. -- If the equations are inconsistent and diagnosable as ill-conditioned using the principles of the first reference below, the system will be automatically regularized and the residual will be larger than minimum. -- If either A or b is all zeros then the solution will be all zeros. Parameters ---------- A : (m, n) array_like Coefficient matrix b : (m, p) array_like Set of columns of dependent variables. Returns ------- x : (n, p) array_like set of columns, type float. Each column will be the solution corresponding to a column of b. nr : int The Numerical Rank of A. mur : int The Minimum Usable Rank seen in solving all the problems. Note that "numerical rank" is an attribute of a matrix but the "usable rank" that arls computes is an attribute of the problem, Ax=b. msigma : float The largest estimated right-hand-side root-mean-square error seen. mlambda : float The largest estimated Tikhonov regularization parameter seen. Raises ------ LinAlgError If A is not 2-D. If A is empty. If A and b do not have the same row size. If b has more than one column. If SCIPY's SVD() does not converge. Examples -------- Arls() will behave like any good least-squares solver when the system is well conditioned. Here is a tiny example of an ill-conditioned system as handled by arls(), x + y = 2 x + 1.01 y =3 Then A = array([[ 1., 1.], [ 1., 1.01.]]) and b = array([2.0, 3.0]) Then standard solvers will return: x = [-98. , 100.] But arls() will see the violation of the Picard Condition and return x = [1.12216 , 1.12779] Notes: ----- 1. When the system is ill-conditioned, the process works best when the rows of A are scaled so that the elements of b have similar estimated errors. 2. Arls() occasionally may produce a smoother (i.e., more regularized) solution than desired. In this case please try scipy routine lsmr. 3. With any linear equation solver, check that the solution is reasonable. In particular, you should check the residual vector, Ax - b. 4. Arls() neither needs nor accepts optional parameters such as iteration limits, error estimates, variable bounds, condition number limits, etc. It also does not return any error flags as there are no error states. As long as the SVD converges (and SVD failure is remarkably rare) then arls() and other routines in this package will complete normally. 5. Arls()'s intent (and the intent of all routines in this module) is to find a reasonable solution even in the midst of excessive inaccuracy, ill-conditioning, singularities, duplicated data, etc. Its performance is often very like that of lsmr, but from a completely different approach. 6. In view of note 5, arls() is not appropriate for situations where the requirements are more for high accuracy rather than robustness. So, we assume, in the coding, where needed, that no data needs to be considered more accurate than 8 significant figures. References ---------- The auto-regularization algorithm in this software arose from the research for my dissertation, "Solving Linear Algebraic Systems Arising in the Solution of Integral Equations of the First Kind", University of New Mexico, Albuquerque, NM, 1985. Many thanks to Cleve B. Moler, MatLab creater and co-founder of MathWorks for his energy and insights in guiding my dissertation research. My thanks also to Richard Hanson (deceased), co-author of the classic "Solving Least Squares Problems", co-creater of BLAS, and co-advisor for the last year of my dissertation work. My thanks also to Per Christian Hansen for reviewing an early version of this software which resulted in my creating the crucial two-phase matrix "split" process. And my thanks to the central computing department at Sandia National Labs where I had the opportunity to contribute to the "MATHLIB" Fortran library in the 1960's and 1970's. MATLIB evolved into part of the Fortran "SLATEC" library. See http://www.netlib.org/slatec/src/ For for a short presentation on the Picard Condition which is at the heart of this package's algorithms, please see See www.rejones7.net/Arls/What_Is_The_Picard_Condition.htm For a complete description, see "The Discrete Picard Condition for Discrete Ill-posed Problems", Per Christian Hansen, 1990. See link.springer.com/article/10.1007/BF01933214 For discussion of incorporating equality and inequality constraints (including nonnegativity) in solving linear algebraic problems, see "Solving Least Squares Problems", by Charles L. Lawson and Richard J. Hanson, Prentice-Hall 1974. My implementation of these features has evolved somewhat from that fine book, but is based on those algorithms. Rondall E. Jones, Ph.D. [email protected] """ A = atleast_2d(_asarray_validated(A, check_finite=True)) b = atleast_1d(_asarray_validated(b, check_finite=True)) if np.count_nonzero(A) == 0 or np.count_nonzero(b) == 0: return np.zeros(A.shape[1]), 0, 0, 0.0, 0.0 checkAb(A, b, 2) m, n = A.shape nrhs = 1 if len(b.shape) == 2: nrhs = b.shape[1] U, S, Vt = np.linalg.svd(A, full_matrices=False) # one right hand side if nrhs == 1: return arlsusv(A, b, U, S, Vt) # multiple right hand sides xx = np.zeros((n, nrhs)) nr = min(A.shape) mur = nr # track minimum usable rank msigma = 0.0 # track maximum estimated RHS error mlamda = 0.0 # track maximum Tikhonov parameter for p in range(0, nrhs): xx[:, p], nr, ur, sigma, lambdah = arlsusv(A, b[:, p], U, S, Vt) mur = min(mur, ur) msigma = max(msigma, sigma) mlamda = max(mlamda, lambdah) return xx, nr, mur, msigma, mlamda
def pinv2(a, rank=None, cond=None, rcond=None, return_rank=False, check_finite=True): """ Compute the (Moore-Penrose) pseudo-inverse of a matrix. Calculate a generalized inverse of a matrix using its singular-value decomposition and including all 'large' singular values. Parameters ---------- a : (M, N) array_like Matrix to be pseudo-inverted. cond, rcond : float or None Cutoff for 'small' singular values; singular values smaller than this value are considered as zero. If both are omitted, the default value ``max(M,N)*largest_singular_value*eps`` is used where ``eps`` is the machine precision value of the datatype of ``a``. .. versionchanged:: 1.3.0 Previously the default cutoff value was just ``eps*f`` where ``f`` was ``1e3`` for single precision and ``1e6`` for double precision. return_rank : bool, optional If True, return the effective rank of the matrix. 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 ------- B : (N, M) ndarray The pseudo-inverse of matrix `a`. rank : int The effective rank of the matrix. Returned if `return_rank` is True. Raises ------ LinAlgError If SVD computation does not converge. Examples -------- >>> from scipy import linalg >>> a = np.random.randn(9, 6) >>> B = linalg.pinv2(a) >>> np.allclose(a, np.dot(a, np.dot(B, a))) True >>> np.allclose(B, np.dot(B, np.dot(a, B))) True """ a = _asarray_validated(a, check_finite=check_finite) u, s, vh = decomp_svd.svd(a, full_matrices=False, check_finite=False) if rank is None: if rcond is not None: cond = np.max(s) * rcond if cond in [None, -1]: t = u.dtype.char.lower() cond = np.max(s) * max(a.shape) * np.finfo(t).eps rank = np.sum(s > cond) elif rank == float('inf'): assert (cond is not None) rank = len(s) s[s < cond] = cond print(rank, flush='True') u = u[:, :rank] u /= s[:rank] B = np.transpose(np.conjugate(np.dot(u, vh[:rank]))) if return_rank: return B, rank else: return B
def pinvh(a, cond=None, rcond=None, lower=True, return_rank=False, check_finite=True): """ Compute the (Moore-Penrose) pseudo-inverse of a Hermitian matrix. Copied in from scipy==1.2.2, in order to preserve the default choice of the `cond` and `above_cutoff` values which determine which values of the matrix inversion lie below threshold and are so set to zero. Changes in scipy 1.3 resulted in a smaller default threshold and thus slower convergence of dependent algorithms in some cases (see Sklearn github issue #14055). Calculate a generalized inverse of a Hermitian or real symmetric matrix using its eigenvalue decomposition and including all eigenvalues with 'large' absolute value. Parameters ---------- a : (N, N) array_like Real symmetric or complex hermetian matrix to be pseudo-inverted cond, rcond : float or None Cutoff for 'small' eigenvalues. Singular values smaller than rcond * largest_eigenvalue are considered zero. If None or -1, suitable machine precision is used. lower : bool, optional Whether the pertinent array data is taken from the lower or upper triangle of a. (Default: lower) return_rank : bool, optional if True, return the effective rank of the matrix 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 ------- B : (N, N) ndarray The pseudo-inverse of matrix `a`. rank : int The effective rank of the matrix. Returned if return_rank == True Raises ------ LinAlgError If eigenvalue does not converge Examples -------- >>> from scipy.linalg import pinvh >>> a = np.random.randn(9, 6) >>> a = np.dot(a, a.T) >>> B = pinvh(a) >>> np.allclose(a, np.dot(a, np.dot(B, a))) True >>> np.allclose(B, np.dot(B, np.dot(a, B))) True """ a = decomp._asarray_validated(a, check_finite=check_finite) s, u = decomp.eigh(a, lower=lower, check_finite=False) if rcond is not None: cond = rcond if cond in [None, -1]: t = u.dtype.char.lower() factor = {'f': 1E3, 'd': 1E6} cond = factor[t] * np.finfo(t).eps # For Hermitian matrices, singular values equal abs(eigenvalues) above_cutoff = (abs(s) > cond * np.max(abs(s))) psigma_diag = 1.0 / s[above_cutoff] u = u[:, above_cutoff] B = np.dot(u * psigma_diag, np.conjugate(u).T) if return_rank: return B, len(psigma_diag) else: return B
def lstsq(a, b, cond=None, overwrite_a=False, overwrite_b=False, check_finite=True, lapack_driver=None): """ Compute least-squares solution to equation Ax = b. Compute a vector x such that the 2-norm ``|b - A x|`` is minimized. This code was adapted from the Scipy distribution: https://github.com/scipy/scipy/blob/v1.2.1/scipy/linalg/basic.py#L1047-L1264 Parameters ---------- a : (M, N) array_like Left hand side matrix (2-D array). b : (M,) or (M, K) array_like Right hand side matrix or vector (1-D or 2-D array). cond : float, optional Cutoff for 'small' singular values; used to determine effective rank of a. Singular values smaller than ``rcond * largest_singular_value`` are considered zero. overwrite_a : bool, optional Discard data in `a` (may enhance performance). Default is False. overwrite_b : bool, optional Discard data in `b` (may enhance performance). Default is False. check_finite : bool, optional Whether to check that the input matrices contain 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 : str, optional Which LAPACK driver is used to solve the least-squares problem. Options are ``'gelsd'``, ``'gelsy'``, ``'gelss'``. Default (``'gelsd'``) is a good choice. However, ``'gelsy'`` can be slightly faster on many problems. ``'gelss'`` was used historically. It is generally slow but uses less memory. .. versionadded:: 0.17.0 Returns ------- x : (N,) or (N, K) ndarray Least-squares solution. Return shape matches shape of `b`. residues : (0,) or () or (K,) ndarray Sums of residues, squared 2-norm for each column in ``b - a x``. If rank of matrix a is ``< N`` or ``N > M``, or ``'gelsy'`` is used, this is a length zero array. If b was 1-D, this is a () shape array (numpy scalar), otherwise the shape is (K,). rank : int Effective rank of matrix `a`. s : (min(M,N),) ndarray or None Singular values of `a`. The condition number of a is ``abs(s[0] / s[-1])``. None is returned when ``'gelsy'`` is used. Raises ------ LinAlgError If computation does not converge. ValueError When parameters are wrong. See Also -------- optimize.nnls : linear least squares with non-negativity constraint Examples -------- >>> from scipy.linalg import lstsq >>> import matplotlib.pyplot as plt Suppose we have the following data: >>> x = np.array([1, 2.5, 3.5, 4, 5, 7, 8.5]) >>> y = np.array([0.3, 1.1, 1.5, 2.0, 3.2, 6.6, 8.6]) We want to fit a quadratic polynomial of the form ``y = a + b*x**2`` to this data. We first form the "design matrix" M, with a constant column of 1s and a column containing ``x**2``: >>> M = x[:, np.newaxis]**[0, 2] >>> M array([[ 1. , 1. ], [ 1. , 6.25], [ 1. , 12.25], [ 1. , 16. ], [ 1. , 25. ], [ 1. , 49. ], [ 1. , 72.25]]) We want to find the least-squares solution to ``M.dot(p) = y``, where ``p`` is a vector with length 2 that holds the parameters ``a`` and ``b``. >>> p, res, rnk, s = lstsq(M, y) >>> p array([ 0.20925829, 0.12013861]) Plot the data and the fitted curve. >>> plt.plot(x, y, 'o', label='data') >>> xx = np.linspace(0, 9, 101) >>> yy = p[0] + p[1]*xx**2 >>> plt.plot(xx, yy, label='least squares fit, $y = a + bx^2$') >>> plt.xlabel('x') >>> plt.ylabel('y') >>> plt.legend(framealpha=1, shadow=True) >>> plt.grid(alpha=0.25) >>> plt.show() """ a1 = _asarray_validated(a, check_finite=check_finite) b1 = _asarray_validated(b, check_finite=check_finite) if len(a1.shape) != 2: raise ValueError('expected matrix') m, n = a1.shape if len(b1.shape) == 2: nrhs = b1.shape[1] else: nrhs = 1 if m != b1.shape[0]: raise ValueError('incompatible dimensions') if m == 0 or n == 0: # Zero-sized problem, confuses LAPACK x = np.zeros((n, ) + b1.shape[1:], dtype=np.common_type(a1, b1)) if n == 0: residues = np.linalg.norm(b1, axis=0)**2 else: residues = np.empty((0, )) return x, residues, 0, np.empty((0, )) driver = lapack_driver if driver is None: global default_lapack_driver driver = default_lapack_driver if driver not in ('gelsd', 'gelsy', 'gelss'): raise ValueError('LAPACK driver "%s" is not found' % driver) lapack_func, lapack_lwork = get_lapack_funcs( (driver, '%s_lwork' % driver), (a1, b1)) real_data = True if (lapack_func.dtype.kind == 'f') else False if m < n: # need to extend b matrix as it will be filled with # a larger solution matrix if len(b1.shape) == 2: b2 = np.zeros((n, nrhs), dtype=lapack_func.dtype) b2[:m, :] = b1 else: b2 = np.zeros(n, dtype=lapack_func.dtype) b2[:m] = b1 b1 = b2 overwrite_a = overwrite_a or _datacopied(a1, a) overwrite_b = overwrite_b or _datacopied(b1, b) if cond is None: cond = np.finfo(lapack_func.dtype).eps a1_wrk = np.copy(a1) b1_wrk = np.copy(b1) lwork, iwork = _compute_lwork(lapack_lwork, m, n, nrhs, cond) x_check, s_check, rank_check, info = lapack_func( a1_wrk, b1_wrk, lwork, iwork, cond, False, False) driver = 'gelss' if driver in ('gelss', 'gelsd'): if driver == 'gelss': if not context: a1_wrk = np.copy(a1) b1_wrk = np.copy(b1) lwork, iwork = _compute_lwork(lapack_lwork, m, n, nrhs, cond) x, s, rank, info = lapack_func(a1_wrk, b1_wrk, lwork, iwork, cond, False, False) else: try: # Check that we aren't dealing with an underconstrained problem ... if m < n: pkg.log.error( Exception( "Underconstrained problems not yet supported by Magma." )) # Initialize a1_trans = np.copy(a1, order='F') a1_gpu = gpuarray.to_gpu(a1_trans) # Note that the result for 'x' gets written to the vector inputted for b x_trans = np.copy(b1, order='F') x_gpu = gpuarray.to_gpu(x_trans) # Init singular-value decomposition (SVD) output & buffer arrays s = np.zeros(min(m, n), np.float32) u = np.zeros((m, m), np.float32) vh = np.zeros((n, n), np.float32) # Query and allocate optimal workspace # n.b.: - the result for 'x' gets written to the input vector for b, so we just label b->x # - assume magma variables lda=ldb=m throughout here lwork_SVD = magma.magma_sgesvd_buffersize( 'A', 'A', m, n, a1_trans.ctypes.data, m, s.ctypes.data, u.ctypes.data, m, vh.ctypes.data, n) # For some reason, magma_sgels_buffersize() does not return the right value for large problems, so # we compute the values used for the validation check (see Magma SGELS documentation) directly and use that #lwork_LS = magma.magma_sgels_buffersize('n', m, n, nrhs, a1_trans.ctypes.data, m, x_trans.ctypes.data, m) nb = magma.magma_get_sgeqrf_nb(m, n) check = (m - n + nb) * (nrhs + nb) + nrhs * nb lwork_LS = check # Allocate workspaces hwork_SVD = np.zeros(lwork_SVD, np.float32, order='F') hwork_LS = np.zeros(lwork_LS, np.float32) # Compute SVD timer.start("SVD") magma.magma_sgesvd('A', 'A', m, n, a1_trans.ctypes.data, m, s.ctypes.data, u.ctypes.data, m, vh.ctypes.data, n, hwork_SVD.ctypes.data, lwork_SVD) timer.stop("SVD") # Note, the use of s_i>rcond here; this is meant to select # values that are effectively non-zero. Results will depend # somewhat on the choice for this value. This criterion was # adopted from that utilized by scipy.linalg.basic.lstsq() rcond = np.finfo(lapack_func.dtype).eps * s[0] rank = sum(1 for s_i in s if s_i > rcond) # Run LS solver timer.start("LS") magma.magma_sgels_gpu('n', m, n, nrhs, a1_gpu.gpudata, m, x_gpu.gpudata, m, hwork_LS.ctypes.data, lwork_LS) timer.stop("LS") # Unload result from GPU x = x_gpu.get() except magma.MagmaError as e: info = e._status else: info = 0 elif driver == 'gelsd': if real_data: if not context: raise Exception( "For some reason, the CUDA implementation of fit() is being called when context is False." ) else: raise Exception( "gelsd not supported using Cuda yet") else: # complex data raise LinAlgError( "driver=%s not yet supported for complex data" % (driver)) if info > 0: raise LinAlgError( "SVD did not converge in Linear Least Squares") if info < 0: raise ValueError( 'illegal value in %d-th argument of internal %s' % (-info, lapack_driver)) resids = np.asarray([], dtype=x.dtype) if m > n: x1 = x[:n] if rank == n: resids = np.sum(np.abs(x[n:])**2, axis=0) x = x1 elif driver == 'gelsy': raise LinAlgError("driver=%s not yet supported" % (driver)) #pkg.log.close("Done", time_elapsed=True) return x, resids, rank, s