def test_ormrz_unmrz(): """ This test performs a matrix multiplication with an arbitrary m x n matric C and a unitary matrix Q without explicitly forming the array. The array data is encoded in the rectangular part of A which is obtained from ?TZRZF. Q size is inferred by m, n, side keywords. """ seed(1234) qm, qn, cn = 10, 15, 15 for ind, dtype in enumerate(DTYPES): tzrzf, tzrzf_lw = get_lapack_funcs(('tzrzf', 'tzrzf_lwork'), dtype=dtype) lwork_rz = _compute_lwork(tzrzf_lw, qm, qn) if ind < 2: A = triu(rand(qm, qn).astype(dtype)) C = rand(cn, cn).astype(dtype) orun_mrz, orun_mrz_lw = get_lapack_funcs(('ormrz', 'ormrz_lwork'), dtype=dtype) else: A = triu((rand(qm, qn) + rand(qm, qn)*1j).astype(dtype)) C = (rand(cn, cn) + rand(cn, cn)*1j).astype(dtype) orun_mrz, orun_mrz_lw = get_lapack_funcs(('unmrz', 'unmrz_lwork'), dtype=dtype) lwork_mrz = _compute_lwork(orun_mrz_lw, cn, cn) rz, tau, info = tzrzf(A, lwork=lwork_rz) # Get Q manually for comparison V = np.hstack((np.eye(qm, dtype=dtype), rz[:, qm:])) Id = np.eye(qn, dtype=dtype) ref = [Id-tau[x]*V[[x], :].T.dot(V[[x], :].conj()) for x in range(qm)] Q = reduce(np.dot, ref) # Now that we have Q, we can test whether lapack results agree with # each case of CQ, CQ^H, QC, and QC^H trans = 'T' if ind < 2 else 'C' tol = 10*np.spacing(dtype(1.0).real) cq, info = orun_mrz(rz, tau, C, lwork=lwork_mrz) assert_(info == 0) assert_allclose(cq - Q.dot(C), zeros_like(C), atol=tol, rtol=0.) cq, info = orun_mrz(rz, tau, C, trans=trans, lwork=lwork_mrz) assert_(info == 0) assert_allclose(cq - Q.conj().T.dot(C), zeros_like(C), atol=tol, rtol=0.) cq, info = orun_mrz(rz, tau, C, side='R', lwork=lwork_mrz) assert_(info == 0) assert_allclose(cq - C.dot(Q), zeros_like(C), atol=tol, rtol=0.) cq, info = orun_mrz(rz, tau, C, side='R', trans=trans, lwork=lwork_mrz) assert_(info == 0) assert_allclose(cq - C.dot(Q.conj().T), zeros_like(C), atol=tol, rtol=0.)
def test_gels(self): for dtype in REAL_DTYPES: a1 = np.array([[1.0, 2.0], [4.0, 5.0], [7.0, 8.0]], dtype=dtype) b1 = np.array([16.0, 17.0, 20.0], dtype=dtype) gels, gels_lwork, geqrf = get_lapack_funcs( ('gels', 'gels_lwork', 'geqrf'), (a1, b1)) m, n = a1.shape if len(b1.shape) == 2: nrhs = b1.shape[1] else: nrhs = 1 # Request of sizes lwork = _compute_lwork(gels_lwork, m, n, nrhs) lqr, x, info = gels(a1, b1, lwork=lwork) assert_allclose(x[:-1], np.array([-14.333333333333323, 14.999999999999991], dtype=dtype), rtol=25 * np.finfo(dtype).eps) lqr_truth, _, _, _ = geqrf(a1) assert_array_equal(lqr, lqr_truth) for dtype in COMPLEX_DTYPES: a1 = np.array([[1.0 + 4.0j, 2.0], [4.0 + 0.5j, 5.0 - 3.0j], [7.0 - 2.0j, 8.0 + 0.7j]], dtype=dtype) b1 = np.array([16.0, 17.0 + 2.0j, 20.0 - 4.0j], dtype=dtype) gels, gels_lwork, geqrf = get_lapack_funcs( ('gels', 'gels_lwork', 'geqrf'), (a1, b1)) m, n = a1.shape if len(b1.shape) == 2: nrhs = b1.shape[1] else: nrhs = 1 # Request of sizes lwork = _compute_lwork(gels_lwork, m, n, nrhs) lqr, x, info = gels(a1, b1, lwork=lwork) assert_allclose(x[:-1], np.array([ 1.161753632288328 - 1.901075709391912j, 1.735882340522193 + 1.521240901196909j ], dtype=dtype), rtol=25 * np.finfo(dtype).eps) lqr_truth, _, _, _ = geqrf(a1) assert_array_equal(lqr, lqr_truth)
def test_gels(self): for dtype in REAL_DTYPES: a1 = np.array([[1.0, 2.0], [4.0, 5.0], [7.0, 8.0]], dtype=dtype) b1 = np.array([16.0, 17.0, 20.0], dtype=dtype) gels, gels_lwork, geqrf = get_lapack_funcs( ('gels', 'gels_lwork', 'geqrf'), (a1, b1)) m, n = a1.shape if len(b1.shape) == 2: nrhs = b1.shape[1] else: nrhs = 1 # Request of sizes lwork = _compute_lwork(gels_lwork, m, n, nrhs) lqr, x, info = gels(a1, b1, lwork=lwork) assert_allclose(x[:-1], np.array([-14.333333333333323, 14.999999999999991], dtype=dtype), rtol=25*np.finfo(dtype).eps) lqr_truth, _, _, _ = geqrf(a1) assert_array_equal(lqr, lqr_truth) for dtype in COMPLEX_DTYPES: a1 = np.array([[1.0+4.0j, 2.0], [4.0+0.5j, 5.0-3.0j], [7.0-2.0j, 8.0+0.7j]], dtype=dtype) b1 = np.array([16.0, 17.0+2.0j, 20.0-4.0j], dtype=dtype) gels, gels_lwork, geqrf = get_lapack_funcs( ('gels', 'gels_lwork', 'geqrf'), (a1, b1)) m, n = a1.shape if len(b1.shape) == 2: nrhs = b1.shape[1] else: nrhs = 1 # Request of sizes lwork = _compute_lwork(gels_lwork, m, n, nrhs) lqr, x, info = gels(a1, b1, lwork=lwork) assert_allclose(x[:-1], np.array([1.161753632288328-1.901075709391912j, 1.735882340522193+1.521240901196909j], dtype=dtype), rtol=25*np.finfo(dtype).eps) lqr_truth, _, _, _ = geqrf(a1) assert_array_equal(lqr, lqr_truth)
def test_tzrzf(): """ This test performs an RZ decomposition in which an m x n upper trapezoidal array M (m <= n) is factorized as M = [R 0] * Z where R is upper triangular and Z is unitary. """ seed(1234) m, n = 10, 15 for ind, dtype in enumerate(DTYPES): tzrzf, tzrzf_lw = get_lapack_funcs(('tzrzf', 'tzrzf_lwork'), dtype=dtype) lwork = _compute_lwork(tzrzf_lw, m, n) if ind < 2: A = triu(rand(m, n).astype(dtype)) else: A = triu((rand(m, n) + rand(m, n)*1j).astype(dtype)) # assert wrong shape arg, f2py returns generic error assert_raises(Exception, tzrzf, A.T) rz, tau, info = tzrzf(A, lwork=lwork) # Check success assert_(info == 0) # Get Z manually for comparison R = np.hstack((rz[:, :m], np.zeros((m, n-m), dtype=dtype))) V = np.hstack((np.eye(m, dtype=dtype), rz[:, m:])) Id = np.eye(n, dtype=dtype) ref = [Id-tau[x]*V[[x], :].T.dot(V[[x], :].conj()) for x in range(m)] Z = reduce(np.dot, ref) assert_allclose(R.dot(Z) - A, zeros_like(A, dtype=dtype), atol=10*np.spacing(dtype(1.0).real), rtol=0.)
def test_sycon_hecon(): seed(1234) for ind, dtype in enumerate(DTYPES+COMPLEX_DTYPES): # DTYPES + COMPLEX DTYPES = <s,d,c,z> sycon + <c,z>hecon n = 10 # For <s,d,c,z>sycon if ind < 4: func_lwork = get_lapack_funcs('sytrf_lwork', dtype=dtype) funcon, functrf = get_lapack_funcs(('sycon', 'sytrf'), dtype=dtype) A = (rand(n, n)).astype(dtype) # For <c,z>hecon else: func_lwork = get_lapack_funcs('hetrf_lwork', dtype=dtype) funcon, functrf = get_lapack_funcs(('hecon', 'hetrf'), dtype=dtype) A = (rand(n, n) + rand(n, n)*1j).astype(dtype) # Since sycon only refers to upper/lower part, conj() is safe here. A = (A + A.conj().T)/2 + 2*np.eye(n, dtype=dtype) anorm = np.linalg.norm(A, 1) lwork = _compute_lwork(func_lwork, n) ldu, ipiv, _ = functrf(A, lwork=lwork, lower=1) rcond, _ = funcon(a=ldu, ipiv=ipiv, anorm=anorm, lower=1) # The error is at most 1-fold assert_(abs(1/rcond - np.linalg.cond(A, p=1))*rcond < 1)
def test_sycon_hecon(): seed(1234) for ind, dtype in enumerate(DTYPES + COMPLEX_DTYPES): # DTYPES + COMPLEX DTYPES = <s,d,c,z> sycon + <c,z>hecon n = 10 # For <s,d,c,z>sycon if ind < 4: func_lwork = get_lapack_funcs('sytrf_lwork', dtype=dtype) funcon, functrf = get_lapack_funcs(('sycon', 'sytrf'), dtype=dtype) A = (rand(n, n)).astype(dtype) # For <c,z>hecon else: func_lwork = get_lapack_funcs('hetrf_lwork', dtype=dtype) funcon, functrf = get_lapack_funcs(('hecon', 'hetrf'), dtype=dtype) A = (rand(n, n) + rand(n, n) * 1j).astype(dtype) # Since sycon only refers to upper/lower part, conj() is safe here. A = (A + A.conj().T) / 2 + 2 * np.eye(n, dtype=dtype) anorm = np.linalg.norm(A, 1) lwork = _compute_lwork(func_lwork, n) ldu, ipiv, _ = functrf(A, lwork=lwork, lower=1) rcond, _ = funcon(a=ldu, ipiv=ipiv, anorm=anorm, lower=1) # The error is at most 1-fold assert_(abs(1 / rcond - np.linalg.cond(A, p=1)) * rcond < 1)
def test_gglse(): # Example data taken from NAG manual for ind, dtype in enumerate(DTYPES): # DTYPES = <s,d,c,z> gglse func, func_lwork = get_lapack_funcs(('gglse', 'gglse_lwork'), dtype=dtype) lwork = _compute_lwork(func_lwork, m=6, n=4, p=2) # For <s,d>gglse if ind < 2: a = np.array([[-0.57, -1.28, -0.39, 0.25], [-1.93, 1.08, -0.31, -2.14], [2.30, 0.24, 0.40, -0.35], [-1.93, 0.64, -0.66, 0.08], [0.15, 0.30, 0.15, -2.13], [-0.02, 1.03, -1.43, 0.50]], dtype=dtype) c = np.array([-1.50, -2.14, 1.23, -0.54, -1.68, 0.82], dtype=dtype) d = np.array([0., 0.], dtype=dtype) # For <s,d>gglse else: a = np.array([[0.96-0.81j, -0.03+0.96j, -0.91+2.06j, -0.05+0.41j], [-0.98+1.98j, -1.20+0.19j, -0.66+0.42j, -0.81+0.56j], [0.62-0.46j, 1.01+0.02j, 0.63-0.17j, -1.11+0.60j], [0.37+0.38j, 0.19-0.54j, -0.98-0.36j, 0.22-0.20j], [0.83+0.51j, 0.20+0.01j, -0.17-0.46j, 1.47+1.59j], [1.08-0.28j, 0.20-0.12j, -0.07+1.23j, 0.26+0.26j]]) c = np.array([[-2.54+0.09j], [1.65-2.26j], [-2.11-3.96j], [1.82+3.30j], [-6.41+3.77j], [2.07+0.66j]]) d = np.zeros(2, dtype=dtype) b = np.array([[1., 0., -1., 0.], [0., 1., 0., -1.]], dtype=dtype) _, _, _, result, _ = func(a, b, c, d, lwork=lwork) if ind < 2: expected = np.array([0.48904455, 0.99754786, 0.48904455, 0.99754786]) else: expected = np.array([1.08742917-1.96205783j, -0.74093902+3.72973919j, 1.08742917-1.96205759j, -0.74093896+3.72973895j]) assert_array_almost_equal(result, expected, decimal=4)
def test_cossin_separate(dtype_): m, p, q = 250, 80, 170 pfx = 'or' if dtype_ in REAL_DTYPES else 'un' X = ortho_group.rvs(m) if pfx == 'or' else unitary_group.rvs(m) X = np.array(X, dtype=dtype_) drv, dlw = get_lapack_funcs((pfx + 'csd', pfx + 'csd_lwork'), [X]) lwval = _compute_lwork(dlw, m, p, q) lwvals = { 'lwork': lwval } if pfx == 'or' else dict(zip(['lwork', 'lrwork'], lwval)) *_, theta, u1, u2, v1t, v2t, _ = \ drv(X[:p, :q], X[:p, q:], X[p:, :q], X[p:, q:], **lwvals) (u1_2, u2_2), theta2, (v1t_2, v2t_2) = cossin(X, p, q, separate=True) assert_allclose(u1_2, u1, rtol=0., atol=10 * np.finfo(dtype_).eps) assert_allclose(u2_2, u2, rtol=0., atol=10 * np.finfo(dtype_).eps) assert_allclose(v1t_2, v1t, rtol=0., atol=10 * np.finfo(dtype_).eps) assert_allclose(v2t_2, v2t, rtol=0., atol=10 * np.finfo(dtype_).eps) assert_allclose(theta2, theta, rtol=0., atol=10 * np.finfo(dtype_).eps)
def schur_sort(a, q, target): n = a.shape[0] try: trexc, = scipy.linalg.get_lapack_funcs(('trexc', ), (a, )) for i in range(n): if i > 0 and a[i, i - 1] != 0: # Complex conjugate eigenpair continue idx = _select(i, n, a, target) if idx == i: continue result = trexc(a, q, idx, i) assert result[-1] == 0 a = result[0] q = result[1] return a, q except ValueError: trsen, trsen_lwork = scipy.linalg.get_lapack_funcs(( 'trsen', 'trsen_lwork', ), (a, )) idx = _select(0, n, a, target) select = numpy.zeros(n) select[idx] = 1 lwork = _compute_lwork(trsen_lwork, select, a) result = trsen(select, a, q, lwork=lwork) assert result[-1] == 0 return result[0], result[1]
def inv(a, overwrite_a=False): """ Inverts a matrix Parameters ---------- a : (N, N) array_like the matrix to be inverted. overwrite_a : bool, optional whether we are allowed to overwrite the matrix `a` Returns ------- x : (N, N) ndarray The inverted matrix """ a1 = atleast_2d(_asarray_validated(a, check_finite=False)) overwrite_a = overwrite_a or _datacopied(a1, a) if a1.shape[0] != a1.shape[1]: raise ValueError('Input a needs to be a square matrix.') getrf, getri, getri_lwork = get_lapack_funcs( ('getrf', 'getri', 'getri_lwork'), (a1, )) lu, piv, info = getrf(a1, overwrite_a=overwrite_a) if info == 0: lwork = _compute_lwork(getri_lwork, a1.shape[0]) lwork = int(1.01 * lwork) x, info = getri(lu, piv, lwork=lwork, overwrite_lu=True) if info > 0: raise LinAlgError("Singular matrix") if info < 0: raise ValueError('illegal value in %d-th argument of internal ' 'getrf|getri' % -info) return x
def test_tzrzf(): """ This test performs an RZ decomposition in which an m x n upper trapezoidal array M (m <= n) is factorized as M = [R 0] * Z where R is upper triangular and Z is unitary. """ seed(1234) m, n = 10, 15 for ind, dtype in enumerate(DTYPES): tzrzf, tzrzf_lw = get_lapack_funcs(('tzrzf', 'tzrzf_lwork'), dtype=dtype) lwork = _compute_lwork(tzrzf_lw, m, n) if ind < 2: A = triu(rand(m, n).astype(dtype)) else: A = triu((rand(m, n) + rand(m, n) * 1j).astype(dtype)) # assert wrong shape arg, f2py returns generic error assert_raises(Exception, tzrzf, A.T) rz, tau, info = tzrzf(A, lwork=lwork) # Check success assert_(info == 0) # Get Z manually for comparison R = np.hstack((rz[:, :m], np.zeros((m, n - m), dtype=dtype))) V = np.hstack((np.eye(m, dtype=dtype), rz[:, m:])) Id = np.eye(n, dtype=dtype) ref = [ Id - tau[x] * V[[x], :].T.dot(V[[x], :].conj()) for x in range(m) ] Z = reduce(np.dot, ref) assert_allclose(R.dot(Z) - A, zeros_like(A, dtype=dtype), atol=10 * np.spacing(dtype(1.0).real), rtol=0.)
def symeig_semidefinite_ldl( A, B = None, eigenvectors=True, turbo="on", rng=None, type=1, overwrite=False, rank_threshold=1e-12, dfc_out=None): """ LDL-based routine to solve generalized symmetric positive semidefinite eigenvalue problems. This can be used in case the normal symeig() call in _stop_training() throws SymeigException ('Covariance matrices may be singular'). This solver uses SciPy's raw LAPACK interface to access LDL decomposition. www.netlib.org/lapack/lug/node54.html describes how to solve a generalized eigenvalue problem with positive definite B using Cholesky/LL decomposition. We extend this method to solve for positive semidefinite B using LDL decomposition, which is a variant of Cholesky/LL decomposition for indefinite Matrices. Accessing raw LAPACK's LDL decomposition (sytrf) is challenging. This code is partly based on code for SciPy 1.1: github.com/scipy/scipy/pull/7941/files#diff-9bf9b4b2f0f40415bc0e72143584c889 We optimized and shortened that code for the real-valued positive semidefinite case. This procedure is almost as efficient as the ordinary eigh implementation. This is because implementations for symmetric generalized eigenvalue problems usually perform the Cholesky approach mentioned above. The more general LDL decomposition is only slightly more expensive than Cholesky, due to pivotization. The signature of this function equals that of mdp.utils.symeig, but has two additional parameters: rank_threshold: A threshold to determine if an eigenvalue counts as zero. dfc_out: If dfc_out is not None dfc_out.rank_deficit will be set to an integer indicating how many zero-eigenvalues were detected. Note: This method requires SciPy >= 1.0. """ if type != 1: raise ValueError('Only type=1 is supported.') # LDL-based method appears to be particularly unstable if blank lines # and columns exist in B. So we circumvent this case: nonzero_idx = _find_blank_data_idx(B, rank_threshold) if not nonzero_idx is None: orig_shape = B.shape B = B[nonzero_idx, :][:, nonzero_idx] A = A[nonzero_idx, :][:, nonzero_idx] # This method has special requirements, which is why we import here # rather than module wide. from scipy.linalg.lapack import get_lapack_funcs, _compute_lwork from scipy.linalg.blas import get_blas_funcs try: inv_tri, solver, solver_lwork = get_lapack_funcs( ('trtri', 'sytrf', 'sytrf_lwork'), (B,)) mult_tri, = get_blas_funcs(('trmm',), (B,)) except ValueError: err_msg = ("ldl method for solving symeig with rank deficit B " "requires at least SciPy 1.0.") raise SymeigException(err_msg) n = B.shape[0] arng = numx.arange(n) lwork = _compute_lwork(solver_lwork, n, lower=1) lu, piv, _ = solver(B, lwork=lwork, lower=1, overwrite_a=overwrite) # using piv properly requires some postprocessing: swap_ = numx.arange(n) pivs = numx.zeros(swap_.shape, dtype=int) skip_2x2 = False for ind in range(n): # If previous spin belonged already to a 2x2 block if skip_2x2: skip_2x2 = False continue cur_val = piv[ind] # do we have a 1x1 block or not? if cur_val > 0: if cur_val != ind+1: # Index value != array value --> permutation required swap_[ind] = swap_[cur_val-1] pivs[ind] = 1 # Not. elif cur_val < 0 and cur_val == piv[ind+1]: # first neg entry of 2x2 block identifier if -cur_val != ind+2: # Index value != array value --> permutation required swap_[ind+1] = swap_[-cur_val-1] pivs[ind] = 2 skip_2x2 = True full_perm = numx.arange(n) for ind in range(n-1, -1, -1): s_ind = swap_[ind] if s_ind != ind: col_s = ind if pivs[ind] else ind-1 # 2x2 block lu[[s_ind, ind], col_s:] = lu[[ind, s_ind], col_s:] full_perm[[s_ind, ind]] = full_perm[[ind, s_ind]] # usually only a few indices actually permute, so we reduce perm: perm = (full_perm-arng).nonzero()[0] perm_idx = full_perm[perm] # end of ldl postprocessing # perm_idx and perm now describe a permutation as dest and source indexes lu[perm_idx, :] = lu[perm, :] dgd = abs(numx.diag(lu)) dnz = (dgd > rank_threshold).nonzero()[0] dgd_sqrt_I = numx.sqrt(1.0/dgd[dnz]) rank_deficit = len(dgd) - len(dnz) # later used # c, lower, unitdiag, overwrite_c LI, _ = inv_tri(lu, 1, 1, 1) # invert triangular # we mainly apply tril here, because we need to make a # copy of LI anyway, because original result from # dtrtri seems to be read-only regarding some operations LI = numx.tril(LI, -1) LI[arng, arng] = 1 LI[dnz, :] *= dgd_sqrt_I.reshape((dgd_sqrt_I.shape[0], 1)) A2 = A if overwrite else A.copy() A2[perm_idx, :] = A2[perm, :] A2[:, perm_idx] = A2[:, perm] # alpha, a, b, side 0=left 1=right, lower, trans_a, diag 1=unitdiag, # overwrite_b A2 = mult_tri(1.0, LI, A2, 1, 1, 1, 0, 1) # A2 = mult(A2, LI.T) A2 = mult_tri(1.0, LI, A2, 0, 1, 0, 0, 1) # A2 = mult(LI, A2) A2 = A2[dnz, :] A2 = A2[:, dnz] # overwrite=True is okay here, because at this point A2 is a copy anyway eg, ev = mdp.utils.symeig(A2, None, True, turbo, rng, overwrite=True) ev = mdp.utils.mult(LI[dnz].T, ev) if rank_deficit \ else mult_tri(1.0, LI, ev, 0, 1, 1, 0, 1) ev[perm] = ev[perm_idx] if not nonzero_idx is None: # restore ev to original size rank_deficit += orig_shape[0]-len(nonzero_idx) ev_tmp = ev ev = numx.zeros((orig_shape[0], ev.shape[1])) ev[nonzero_idx, :] = ev_tmp if not dfc_out is None: dfc_out.rank_deficit = rank_deficit return eg, ev
def test_ormrz_unmrz(): """ This test performs a matrix multiplication with an arbitrary m x n matric C and a unitary matrix Q without explicitly forming the array. The array data is encoded in the rectangular part of A which is obtained from ?TZRZF. Q size is inferred by m, n, side keywords. """ seed(1234) qm, qn, cn = 10, 15, 15 for ind, dtype in enumerate(DTYPES): tzrzf, tzrzf_lw = get_lapack_funcs(('tzrzf', 'tzrzf_lwork'), dtype=dtype) lwork_rz = _compute_lwork(tzrzf_lw, qm, qn) if ind < 2: A = triu(rand(qm, qn).astype(dtype)) C = rand(cn, cn).astype(dtype) orun_mrz, orun_mrz_lw = get_lapack_funcs(('ormrz', 'ormrz_lwork'), dtype=dtype) else: A = triu((rand(qm, qn) + rand(qm, qn) * 1j).astype(dtype)) C = (rand(cn, cn) + rand(cn, cn) * 1j).astype(dtype) orun_mrz, orun_mrz_lw = get_lapack_funcs(('unmrz', 'unmrz_lwork'), dtype=dtype) lwork_mrz = _compute_lwork(orun_mrz_lw, cn, cn) rz, tau, info = tzrzf(A, lwork=lwork_rz) # Get Q manually for comparison V = np.hstack((np.eye(qm, dtype=dtype), rz[:, qm:])) Id = np.eye(qn, dtype=dtype) ref = [ Id - tau[x] * V[[x], :].T.dot(V[[x], :].conj()) for x in range(qm) ] Q = reduce(np.dot, ref) # Now that we have Q, we can test whether lapack results agree with # each case of CQ, CQ^H, QC, and QC^H trans = 'T' if ind < 2 else 'C' tol = 10 * np.spacing(dtype(1.0).real) cq, info = orun_mrz(rz, tau, C, lwork=lwork_mrz) assert_(info == 0) assert_allclose(cq - Q.dot(C), zeros_like(C), atol=tol, rtol=0.) cq, info = orun_mrz(rz, tau, C, trans=trans, lwork=lwork_mrz) assert_(info == 0) assert_allclose(cq - Q.conj().T.dot(C), zeros_like(C), atol=tol, rtol=0.) cq, info = orun_mrz(rz, tau, C, side='R', lwork=lwork_mrz) assert_(info == 0) assert_allclose(cq - C.dot(Q), zeros_like(C), atol=tol, rtol=0.) cq, info = orun_mrz(rz, tau, C, side='R', trans=trans, lwork=lwork_mrz) assert_(info == 0) assert_allclose(cq - C.dot(Q.conj().T), zeros_like(C), atol=tol, rtol=0.)
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
def test_gels(self): seed(1234) # Test fat/tall matrix argument handling - gh-issue #8329 for ind, dtype in enumerate(DTYPES): m = 10 n = 20 nrhs = 1 a1 = rand(m, n).astype(dtype) b1 = rand(n).astype(dtype) gls, glslw = get_lapack_funcs(('gels', 'gels_lwork'), dtype=dtype) # Request of sizes lwork = _compute_lwork(glslw, m, n, nrhs) _, _, info = gls(a1, b1, lwork=lwork) assert_(info >= 0) _, _, info = gls(a1, b1, trans='TTCC'[ind], lwork=lwork) assert_(info >= 0) for dtype in REAL_DTYPES: a1 = np.array([[1.0, 2.0], [4.0, 5.0], [7.0, 8.0]], dtype=dtype) b1 = np.array([16.0, 17.0, 20.0], dtype=dtype) gels, gels_lwork, geqrf = get_lapack_funcs( ('gels', 'gels_lwork', 'geqrf'), (a1, b1)) m, n = a1.shape if len(b1.shape) == 2: nrhs = b1.shape[1] else: nrhs = 1 # Request of sizes lwork = _compute_lwork(gels_lwork, m, n, nrhs) lqr, x, info = gels(a1, b1, lwork=lwork) assert_allclose(x[:-1], np.array([-14.333333333333323, 14.999999999999991], dtype=dtype), rtol=25 * np.finfo(dtype).eps) lqr_truth, _, _, _ = geqrf(a1) assert_array_equal(lqr, lqr_truth) for dtype in COMPLEX_DTYPES: a1 = np.array([[1.0 + 4.0j, 2.0], [4.0 + 0.5j, 5.0 - 3.0j], [7.0 - 2.0j, 8.0 + 0.7j]], dtype=dtype) b1 = np.array([16.0, 17.0 + 2.0j, 20.0 - 4.0j], dtype=dtype) gels, gels_lwork, geqrf = get_lapack_funcs( ('gels', 'gels_lwork', 'geqrf'), (a1, b1)) m, n = a1.shape if len(b1.shape) == 2: nrhs = b1.shape[1] else: nrhs = 1 # Request of sizes lwork = _compute_lwork(gels_lwork, m, n, nrhs) lqr, x, info = gels(a1, b1, lwork=lwork) assert_allclose(x[:-1], np.array([ 1.161753632288328 - 1.901075709391912j, 1.735882340522193 + 1.521240901196909j ], dtype=dtype), rtol=25 * np.finfo(dtype).eps) lqr_truth, _, _, _ = geqrf(a1) assert_array_equal(lqr, lqr_truth)
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 test_gels(self): seed(1234) # Test fat/tall matrix argument handling - gh-issue #8329 for ind, dtype in enumerate(DTYPES): m = 10 n = 20 nrhs = 1 a1 = rand(m, n).astype(dtype) b1 = rand(n).astype(dtype) gls, glslw = get_lapack_funcs(('gels', 'gels_lwork'), dtype=dtype) # Request of sizes lwork = _compute_lwork(glslw, m, n, nrhs) _, _, info = gls(a1, b1, lwork=lwork) assert_(info >= 0) _, _, info = gls(a1, b1, trans='TTCC'[ind], lwork=lwork) assert_(info >= 0) for dtype in REAL_DTYPES: a1 = np.array([[1.0, 2.0], [4.0, 5.0], [7.0, 8.0]], dtype=dtype) b1 = np.array([16.0, 17.0, 20.0], dtype=dtype) gels, gels_lwork, geqrf = get_lapack_funcs( ('gels', 'gels_lwork', 'geqrf'), (a1, b1)) m, n = a1.shape if len(b1.shape) == 2: nrhs = b1.shape[1] else: nrhs = 1 # Request of sizes lwork = _compute_lwork(gels_lwork, m, n, nrhs) lqr, x, info = gels(a1, b1, lwork=lwork) assert_allclose(x[:-1], np.array([-14.333333333333323, 14.999999999999991], dtype=dtype), rtol=25*np.finfo(dtype).eps) lqr_truth, _, _, _ = geqrf(a1) assert_array_equal(lqr, lqr_truth) for dtype in COMPLEX_DTYPES: a1 = np.array([[1.0+4.0j, 2.0], [4.0+0.5j, 5.0-3.0j], [7.0-2.0j, 8.0+0.7j]], dtype=dtype) b1 = np.array([16.0, 17.0+2.0j, 20.0-4.0j], dtype=dtype) gels, gels_lwork, geqrf = get_lapack_funcs( ('gels', 'gels_lwork', 'geqrf'), (a1, b1)) m, n = a1.shape if len(b1.shape) == 2: nrhs = b1.shape[1] else: nrhs = 1 # Request of sizes lwork = _compute_lwork(gels_lwork, m, n, nrhs) lqr, x, info = gels(a1, b1, lwork=lwork) assert_allclose(x[:-1], np.array([1.161753632288328-1.901075709391912j, 1.735882340522193+1.521240901196909j], dtype=dtype), rtol=25*np.finfo(dtype).eps) lqr_truth, _, _, _ = geqrf(a1) assert_array_equal(lqr, lqr_truth)
def symeig_semidefinite_ldl(A, B=None, eigenvectors=True, turbo="on", rng=None, type=1, overwrite=False, rank_threshold=1e-12, dfc_out=None): """LDL-based routine to solve generalized symmetric positive semidefinite eigenvalue problems. This can be used if the normal ``symeig()`` call in ``_stop_training()`` throws ``SymeigException('Covariance matrices may be singular')``. This solver uses SciPy's raw LAPACK interface to access LDL decomposition. http://www.netlib.org/lapack/lug/node54.html describes how to solve a generalized eigenvalue problem with positive definite B using Cholesky/LL decomposition. We extend this method to solve for positive semidefinite B using LDL decomposition, which is a variant of Cholesky/LL decomposition for indefinite Matrices. Accessing raw LAPACK's LDL decomposition (sytrf) is challenging. This code is partly based on code for SciPy 1.1: http://github.com/scipy/scipy/pull/7941/files#diff-9bf9b4b2f0f40415bc0e72143584c889 We optimized and shortened that code for the real-valued positive semidefinite case. This procedure is almost as efficient as the ordinary eigh implementation. This is because implementations for symmetric generalized eigenvalue problems usually perform the Cholesky approach mentioned above. The more general LDL decomposition is only slightly more expensive than Cholesky, due to pivotization. .. note:: This method requires SciPy >= 1.0. The signature of this function equals that of ``mdp.utils.symeig``, but has two additional parameters: :param rank_threshold: A threshold to determine if an eigenvalue counts as zero. :type rank_threshold: float :param dfc_out: If ``dfc_out`` is not ``None``, ``dfc_out.rank_deficit`` will be set to an integer indicating how many zero-eigenvalues were detected. """ if type != 1: raise ValueError('Only type=1 is supported.') # LDL-based method appears to be particularly unstable if blank lines # and columns exist in B. So we circumvent this case: nonzero_idx = _find_blank_data_idx(B, rank_threshold) if not nonzero_idx is None: orig_shape = B.shape B = B[nonzero_idx, :][:, nonzero_idx] A = A[nonzero_idx, :][:, nonzero_idx] # This method has special requirements, which is why we import here # rather than module wide. from scipy.linalg.lapack import get_lapack_funcs, _compute_lwork from scipy.linalg.blas import get_blas_funcs try: inv_tri, solver, solver_lwork = get_lapack_funcs( ('trtri', 'sytrf', 'sytrf_lwork'), (B, )) mult_tri, = get_blas_funcs(('trmm', ), (B, )) except ValueError: err_msg = ("ldl method for solving symeig with rank deficit B " "requires at least SciPy 1.0.") raise SymeigException(err_msg) n = B.shape[0] arng = numx.arange(n) lwork = _compute_lwork(solver_lwork, n, lower=1) lu, piv, _ = solver(B, lwork=lwork, lower=1, overwrite_a=overwrite) # using piv properly requires some postprocessing: swap_ = numx.arange(n) pivs = numx.zeros(swap_.shape, dtype=int) skip_2x2 = False for ind in range(n): # If previous spin belonged already to a 2x2 block if skip_2x2: skip_2x2 = False continue cur_val = piv[ind] # do we have a 1x1 block or not? if cur_val > 0: if cur_val != ind + 1: # Index value != array value --> permutation required swap_[ind] = swap_[cur_val - 1] pivs[ind] = 1 # Not. elif cur_val < 0 and cur_val == piv[ind + 1]: # first neg entry of 2x2 block identifier if -cur_val != ind + 2: # Index value != array value --> permutation required swap_[ind + 1] = swap_[-cur_val - 1] pivs[ind] = 2 skip_2x2 = True full_perm = numx.arange(n) for ind in range(n - 1, -1, -1): s_ind = swap_[ind] if s_ind != ind: col_s = ind if pivs[ind] else ind - 1 # 2x2 block lu[[s_ind, ind], col_s:] = lu[[ind, s_ind], col_s:] full_perm[[s_ind, ind]] = full_perm[[ind, s_ind]] # usually only a few indices actually permute, so we reduce perm: perm = (full_perm - arng).nonzero()[0] perm_idx = full_perm[perm] # end of ldl postprocessing # perm_idx and perm now describe a permutation as dest and source indexes lu[perm_idx, :] = lu[perm, :] dgd = abs(numx.diag(lu)) dnz = (dgd > rank_threshold).nonzero()[0] dgd_sqrt_I = numx.sqrt(1.0 / dgd[dnz]) rank_deficit = len(dgd) - len(dnz) # later used # c, lower, unitdiag, overwrite_c LI, _ = inv_tri(lu, 1, 1, 1) # invert triangular # we mainly apply tril here, because we need to make a # copy of LI anyway, because original result from # dtrtri seems to be read-only regarding some operations LI = numx.tril(LI, -1) LI[arng, arng] = 1 LI[dnz, :] *= dgd_sqrt_I.reshape((dgd_sqrt_I.shape[0], 1)) A2 = A if overwrite else A.copy() A2[perm_idx, :] = A2[perm, :] A2[:, perm_idx] = A2[:, perm] # alpha, a, b, side 0=left 1=right, lower, trans_a, diag 1=unitdiag, # overwrite_b A2 = mult_tri(1.0, LI, A2, 1, 1, 1, 0, 1) # A2 = mult(A2, LI.T) A2 = mult_tri(1.0, LI, A2, 0, 1, 0, 0, 1) # A2 = mult(LI, A2) A2 = A2[dnz, :] A2 = A2[:, dnz] # overwrite=True is okay here, because at this point A2 is a copy anyway eg, ev = mdp.utils.symeig(A2, None, True, turbo, rng, overwrite=True) ev = mdp.utils.mult(LI[dnz].T, ev) if rank_deficit \ else mult_tri(1.0, LI, ev, 0, 1, 1, 0, 1) ev[perm] = ev[perm_idx] if not nonzero_idx is None: # restore ev to original size rank_deficit += orig_shape[0] - len(nonzero_idx) ev_tmp = ev ev = numx.zeros((orig_shape[0], ev.shape[1])) ev[nonzero_idx, :] = ev_tmp if not dfc_out is None: dfc_out.rank_deficit = rank_deficit return eg, ev
def pinv_array(a, tol=None): """Calculate the Moore-Penrose pseudo inverse of each block of the 3D array a. Parameters ---------- a : {dense array} Is of size (n, m, m) tol : {float} Used by gelss to filter numerically zeros singular values. If None, a suitable value is chosen for you. Returns ------- Nothing, a is modified in place so that a[k] holds the pseudoinverse of that block. Notes ----- By using lapack wrappers, this can be much faster for large n, than directly calling a pseudoinverse (SVD) Examples -------- >>> import numpy as np >>> from pyamg.util.linalg import pinv_array >>> a = np.array([[[1.,2.],[1.,1.]], [[1.,1.],[3.,3.]]]) >>> ac = a.copy() >>> # each block of a is inverted in-place >>> pinv_array(a) """ n = a.shape[0] m = a.shape[1] if m == 1: # Pseudo-inverse of 1 x 1 matrices is trivial zero_entries = (a == 0.0).nonzero()[0] a[zero_entries] = 1.0 a[:] = 1.0 / a a[zero_entries] = 0.0 del zero_entries else: # The block size is greater than 1 # Create necessary arrays and function pointers for calculating pinv gelss, gelss_lwork = lapack.get_lapack_funcs( ('gelss', 'gelss_lwork'), (np.ones((1, ), dtype=a.dtype))) RHS = np.eye(m, dtype=a.dtype) # pylint: disable=protected-access lwork = lapack._compute_lwork(gelss_lwork, m, m, m) # pylint: enable=protected-access # Choose tolerance for which singular values are zero in *gelss below if tol is None: tol = set_tol(a.dtype) # Invert each block of a for kk in range(n): gelssoutput = gelss(a[kk], RHS, cond=tol, lwork=lwork, overwrite_a=True, overwrite_b=False) a[kk] = gelssoutput[1]
def pinv_array(a, cond=None): """Calculate the Moore-Penrose pseudo inverse of each block of the three dimensional array a. Parameters ---------- a : {dense array} Is of size (n, m, m) cond : {float} Used by gelss to filter numerically zeros singular values. If None, a suitable value is chosen for you. Returns ------- Nothing, a is modified in place so that a[k] holds the pseudoinverse of that block. Notes ----- By using lapack wrappers, this can be much faster for large n, than directly calling pinv2 Examples -------- >>> import numpy as np >>> from pyamg.util.linalg import pinv_array >>> a = np.array([[[1.,2.],[1.,1.]], [[1.,1.],[3.,3.]]]) >>> ac = a.copy() >>> # each block of a is inverted in-place >>> pinv_array(a) """ n = a.shape[0] m = a.shape[1] if m == 1: # Pseudo-inverse of 1 x 1 matrices is trivial zero_entries = (a == 0.0).nonzero()[0] a[zero_entries] = 1.0 a[:] = 1.0 / a a[zero_entries] = 0.0 del zero_entries else: # The block size is greater than 1 # Create necessary arrays and function pointers for calculating pinv gelss, gelss_lwork = get_lapack_funcs(('gelss', 'gelss_lwork'), (np.ones((1, ), dtype=a.dtype))) RHS = np.eye(m, dtype=a.dtype) lwork = _compute_lwork(gelss_lwork, m, m, m) # Choose tolerance for which singular values are zero in *gelss below if cond is None: t = a.dtype.char eps = np.finfo(np.float).eps feps = np.finfo(np.single).eps geps = np.finfo(np.longfloat).eps _array_precision = {'f': 0, 'd': 1, 'g': 2, 'F': 0, 'D': 1, 'G': 2} cond = { 0: feps * 1e3, 1: eps * 1e6, 2: geps * 1e6 }[_array_precision[t]] # Invert each block of a for kk in range(n): gelssoutput = gelss(a[kk], RHS, cond=cond, lwork=lwork, overwrite_a=True, overwrite_b=False) a[kk] = gelssoutput[1]
def pinv_array(a, cond=None): """Calculate the Moore-Penrose pseudo inverse of each block of the three dimensional array a. Parameters ---------- a : {dense array} Is of size (n, m, m) cond : {float} Used by gelss to filter numerically zeros singular values. If None, a suitable value is chosen for you. Returns ------- Nothing, a is modified in place so that a[k] holds the pseudoinverse of that block. Notes ----- By using lapack wrappers, this can be much faster for large n, than directly calling pinv2 Examples -------- >>> import numpy as np >>> from pyamg.util.linalg import pinv_array >>> a = np.array([[[1.,2.],[1.,1.]], [[1.,1.],[3.,3.]]]) >>> ac = a.copy() >>> # each block of a is inverted in-place >>> pinv_array(a) """ n = a.shape[0] m = a.shape[1] if m == 1: # Pseudo-inverse of 1 x 1 matrices is trivial zero_entries = (a == 0.0).nonzero()[0] a[zero_entries] = 1.0 a[:] = 1.0/a a[zero_entries] = 0.0 del zero_entries else: # The block size is greater than 1 # Create necessary arrays and function pointers for calculating pinv gelss, gelss_lwork = get_lapack_funcs(('gelss', 'gelss_lwork'), (np.ones((1,), dtype=a.dtype))) RHS = np.eye(m, dtype=a.dtype) lwork = _compute_lwork(gelss_lwork, m, m, m) # Choose tolerance for which singular values are zero in *gelss below if cond is None: t = a.dtype.char eps = np.finfo(np.float).eps feps = np.finfo(np.single).eps geps = np.finfo(np.longfloat).eps _array_precision = {'f': 0, 'd': 1, 'g': 2, 'F': 0, 'D': 1, 'G': 2} cond = {0: feps*1e3, 1: eps*1e6, 2: geps*1e6}[_array_precision[t]] # Invert each block of a for kk in range(n): gelssoutput = gelss(a[kk], RHS, cond=cond, lwork=lwork, overwrite_a=True, overwrite_b=False) a[kk] = gelssoutput[1]