def trid(mat_in, mode='givens'): """ Tri-diagonalization of symmetrical real matrix\n # input requirement\n mat_in: symmetrical real matrix, use FLOAT data type, or will emerge huge roundoff error\n mode: 'givens' or 'householder', 'householder' works faster than 'givens' for 'givens' will seperately work on every off-tri-diagonal element in an iterative manner, not recommended for use other than diagonalization, although there are more efficient diagonalization method\n # output description\n [tridiag, U]\n tridiag: tri-diagonalized matrix\n U: unitary operator\n # formula\n mat_in = U·tridiag·U', where U' denotes transpose of U """ if not mlib.symm_check(mat=mat_in): print('***error*** symmetric matrix is demanded.') exit() if mode == 'givens': [tridiag, U] = givens_tdiag(mat_in=mat_in, verbosity='silent') elif mode == 'householder': nline = len(mat_in) mat_op_on = deepcopy(mat_in) mat_op_on_T = mlib.transpose(mat_op_on) U = mlib.eye(nline) for iline in range(nline - 1): vec_op_on = mat_op_on_T[iline][iline + 1::] reflect_op = hh(vec_in=vec_op_on, verbosity='silent') identi_op = mlib.eye(iline + 1) op = mlib.combine_block(identi_op, reflect_op) #mat_op_on = mlib.dot(op, mat_op_on) mat_op_on = mlib.unitary_transform(op, mat_op_on) mat_op_on_T = mlib.transpose(mat_op_on) U = mlib.dot(op, U) U = mlib.transpose(U) tridiag = mat_op_on else: exit() return [tridiag, U]
def qr(mat_in, mode='householder', verbosity='silent'): """ QR decomposition\n A = QR, where R is upper-triangonal matrix\n # input requirement\n mat_in: matrix to perform QR decomposition, only squared matrix is supported\n mode: 'householder' (recommended) or 'schmidt' (not recommended, bug exists, unsolved)\n # output description\n [original matrix, Q matrix, R matrix] """ nline = len(mat_in) mat_op_on = deepcopy(mat_in) if mode == 'householder': mat_op_on_T = mlib.transpose(mat_op_on) op_log = [] for iline in range(nline): vec_op_on = mat_op_on_T[iline][iline::] reflect_op = hh(vec_in=vec_op_on, verbosity=verbosity) identi_op = mlib.eye(iline) op = mlib.combine_block(identi_op, reflect_op) if verbosity == 'debug': print('QR| (householder) matrix before operation:\n{}'.format( mat_op_on)) mat_op_on = mlib.dot(op, mat_op_on) if verbosity == 'debug': print( 'QR| (householder) matrix after operation:\n{}\n OPERATION:\n{}' .format(mat_op_on, op)) mat_op_on_T = mlib.transpose(mat_op_on) op_log.append(op) Q = mlib.eye(nline) for iop in op_log: Q = mlib.dot(iop, Q) Q = mlib.transpose(Q) return [mat_in, Q, mat_op_on] elif mode == 'schmidt': #print('QR| ***warning*** There seems one bug that has not been discovered yet, although Gram-Schmidt works well.') [_, Q] = gs(mat_in, mode='column', verbosity=verbosity) Q_t = mlib.transpose(Q) R = mlib.dot(Q_t, mat_op_on) return [mat_in, Q, R]
def ql(mat_in, verbosity = 'silent'): # use householder by default mat_op_on = deepcopy(mat_in) nline = len(mat_op_on) Qt = mlib.eye(nline) for iline in range(nline): mat_op_on_T = mlib.transpose(mat_op_on) vec_in = mat_op_on_T[-iline-1][:(nline-iline)] norm_vec = mlib.mod_of_vec(vec_in) vec_desti = mlib.zeros(n = 1, m = nline-iline)[0][:] vec_desti[-1] = norm_vec reflect_op = hh(vec_in, vec_desti, mode = 'L', verbosity = verbosity) identi_op = mlib.eye(iline) op = mlib.combine_block(reflect_op, identi_op) if verbosity == 'debug': print('QL| vector read-in: {}\nQL| vector reflected to: {}\n'.format(vec_in, vec_desti)) print('QL| Reflection operator:') mlib.matrix_print(reflect_op, decimal = 4) print('QL| Integrated Householder operator:') mlib.matrix_print(op, decimal = 4) print('QL| Present matrix before operation:') mlib.matrix_print(mat_op_on, decimal = 4) Qt = mlib.dot(op, Qt) mat_op_on = mlib.dot(op, mat_op_on) if verbosity == 'debug': print('Present matrix after operation:') mlib.matrix_print(mat_op_on, decimal = 4) Q = mlib.transpose(Qt) return [Q, mat_op_on]
def gs_orth(vec_set, mode = 'column', start = 0, normalize = True, verbosity = 'silent'): """ Gram-Schmidt orthogonalization\n WARNING: input matrix (I mean vector set) must be float data type. An integar data type will cause unexpected large roundoff error, even totally wrong result will be returned!\n # input requirement\n vec_set: set of vectors that need to be orthogonalized, 2d array (list), FLOAT\n mode: 'column' or 'row', specify how vectors are placed in matrix the first parameter you entered\n start: which vector is selected as a reference to orthogonalize all other vectors, note that this number corresponds to index of vector, so the first vector is '0' rather than '1 '\n normalize: True or False, choose whether all vectors output are already normalized\n # output description\n [original vector set, resultant matrix containing orthogonalized vectors] """ # Gram-Schmidt method len_vec = len(vec_set) nvec = len(vec_set[-1][:]) if mode == 'row': temp = nvec nvec = len_vec len_vec = temp norm_orth_set = mlib.zeros(nvec, m = len_vec) orth_set = mlib.zeros(nvec, m = len_vec) # original vector collection, no matter how they arrange, <| or |>, save them as <| vec_colle = [] vec_set_bak = deepcopy(vec_set) if mode == 'column': for icol in range(nvec): _vec = [] _vec = [vec_set[i][icol] for i in range(len_vec)] vec_colle.append(_vec) if verbosity == 'debug': print('GRAM-SCHMIDT| |i> => <i|, column vectors have been saved as <| for easy calculation:\n{}'.format(vec_colle)) elif mode == 'row': for irow in range(nvec): _vec = [] _vec = [vec_set[irow][i] for i in range(len_vec)] vec_colle.append(_vec) if verbosity == 'debug': print('GRAM-SCHMIDT| row vectors have been saved:{}'.format(vec_colle)) else: print('***error*** invalid mode required in reading input vectors set.') exit() # will save mod of every vector mod_vec = [] for ivec in range(nvec): mod = mlib.mod_of_vec(vec = vec_colle[ivec][:]) mod_vec.append(mod) # select no. start as the first basis and directly normalize it orth_set[start][:] = vec_colle[start][:] norm_orth_set[start][:] = [vec_colle[start][i]/mod_vec[start] for i in range(len_vec)] if verbosity == 'debug': print('GRAM-SCHMIDT| The first basis has been fixed as:\n{}\nGRAM-SCHMIDT| Normalized:\n{}'.format(orth_set[start][:], norm_orth_set[start][:])) orthlog = [start] for i in range(nvec): if i == start: continue vec2orth = vec_colle[i][:] cut = mlib.zeros(n = 1, m = len_vec)[0][:] for index in orthlog: innerprod = mlib.braket(vec2orth, norm_orth_set[index][:]) compo2cut = [innerprod*item for item in norm_orth_set[index][:]] cut = mlib.plus(compo2cut, cut) if verbosity == 'debug': print('GRAM-SCHMIDT| Present vector to perform Gram-Schmidt:\n{}\nGRAM-SCHMIDT| Basis:\n{}'.format(vec2orth, norm_orth_set[index][:])) print('GRAM-SCHMIDT| scalar product between present vector and basis: {}'.format(innerprod)) print('GRAM-SCHMIDT| vector needed to be subtract from original vector is:\n{}'.format(compo2cut)) orth_set[i][:] = mlib.minus(vec2orth, cut) mod_vec[i] = mlib.mod_of_vec(orth_set[i][:]) #refresh mod info norm_orth_set[i][:] = [item/mod_vec[i] for item in orth_set[i][:]] if verbosity == 'debug': print('GRAM-SCHMIDT| Yield new unnormalized and normalized basis:\n{}\n{}'.format(orth_set[i][:], norm_orth_set[i][:])) orthlog.append(i) if mode == 'column': norm_orth_set = mlib.transpose(norm_orth_set) orth_set = mlib.transpose(orth_set) if normalize: return [vec_set_bak, norm_orth_set] else: return [vec_set_bak, orth_set]
def rayleigh_ritz_diag(mat_in, num_eigen, preconditioner='full', diag_mode='householder', dj_solver='lu', sort_eigval='lowest', batch_size=-1, conv_thr=1E-6, conv_calc_mode='norm1', max_iter=50, verbosity='silent'): """ Subspace diagonalization (Rayleigh-Ritz subspace method)\n # input requirement\n mat_in: must be SQUARED matrix and in FLOAT data type\n num_eigen: number of eigen vectors and values want to find\n preconditioner: preconditioner of residual vector, avaliable options: 'full', 'single', 'dj' or 'none'.\n >For 'full' mode (recommended, most stable), (D_A - theta_i*I)|t_i> = |r_i>\n >, where DA is diagonal matrix that only has non-zero element on its diagonal, D_A[i][i] = A[i][i]\n >For 'single' mode (simple but always cannot converge), (A[i][i] - theta_i)|t_i> = |r_i>\n >For 'dj' (Davidson-Jacobi) mode (accurate but singluarity-unstable):\n > (I-|y_i><y_i|)(D_A - theta_i*I)(I-|y_i><y_i|)|t_i> = |r_i>\n > |t_i> will be solved by LU-decomposition and forward/back substitution method, relatively time-costly\n >For 'none' mode, preconditioner won't be used, i.e.: |t_i> = |r_i>\n diag_mode: 'jacobi', 'householder' or 'np' (numpy integrated). Basic algorithm for diagonalize matrix in subspace\n dj_solver: 'lu' or 'np', the most two fast algorithm for solving linear equation\n >For 'lu', use LU-decompsition and forward/backsubstitution\n >For 'np', use numpy.linalg.solve function\n batch_size: total number of dimensions of subspace, only will be read-in if mode is set to 'batch'\n sort_eigval: 'lowest', 'highest' or 'None'\n >>For 'lowest', sort eigenvalues in an increasing order\n >>For 'highest', sort eigenvalues in an decreasing order\n >>For 'None', will not sort eigenvalues\n conv_thr: convergence threshold of eigen values for 'batch' mode, has no effect in other modes\n conv_calc_mode: 'abs', 'sqr', 'sum', 'norm1' or 'norm2', for measuring lists of eigenvalues of adjacent two iteration steps\n >>For 'abs', use absolute value of difference between element in old list and corresponding one in the new list\n >>For 'sqr', use squared value of ...\n >>For 'sum', just sum up all differences\n >>For 'norm1', use norm of difference between two lists that treated as vectors\n >>For 'norm2', only measure difference between norms of vectorized old and new list\n max_iter: maximum number of iterations for 'batch' mode, has no effect in other modes\n # output description\n [eigval_list, eigvec_list]\n eigval_list: list of eigenvalues\n eigvec_list: list of eigenvectors, arranged according to eigval_list\n """ dim = len(mat_in) mat_op_on = deepcopy(mat_in) I = np.eye(dim) buffer_state = 'YES' if batch_size < num_eigen: batch_size = num_eigen buffer_state = 'NO' U = mlib.eye(n=dim, m=batch_size) eigval_list0 = mlib.zeros(n=1, m=num_eigen)[0][:] eigval_list = mlib.zeros(n=1, m=num_eigen)[0][:] eigval_conv = 1 if (verbosity == 'high') or (verbosity == 'debug'): print('Diagonalization in subspace Initialization Information\n' + '-' * 50 + '\n' + 'Preconditioner: {}\n'.format( preconditioner) + 'Number of eigenvalue-vector pairs to find: {}\n'.format( num_eigen) + 'If buffer vectors used for batch mode: {}\n'.format( buffer_state)) istep = 0 while (istep < max_iter) and (eigval_conv > conv_thr): # --------------------subspace generation-------------------- submat = mlib.unitary_transform(U=U, mat=mat_op_on) # ----------------------------------------------------------- # -----------------subspace diagonalization------------------ if diag_mode == 'np': [eigval_list, eigvec_set] = np.linalg.eig(submat) else: [eigval_list, eigvec_set] = hdiag(hmat_in=submat, mode=diag_mode, eigval_format='list', conv_level=8, max_iter=50, verbosity=verbosity) # ----------------------------------------------------------- # ---------subspace eigenvalues and vectors sorting--------- # sort eigenvalues if sort_eigval == 'None': # will not sort eigenvalues... pass elif (sort_eigval == 'lowest') or (sort_eigval == 'highest'): # sort eigenvalues anyway... sort_idx = np.argsort(a=eigval_list, axis=-1) # np.argsort will return a list of indices of elements in list to sort but # in a sorted order, ascendent as default if sort_eigval == 'highest': sort_idx = sort_idx[::-1] # reverse the indices list # ----------------------------------------------------------- # --------------------eigenvalues storage-------------------- templist = [eigval_list[idx] for idx in sort_idx] eigval_list = templist eigval_conv = mlib.list_diff(list_old=eigval_list0, list_new=eigval_list[0:num_eigen], mode=conv_calc_mode) eigval_list0 = eigval_list[0:num_eigen] # ----------------------------------------------------------- # -----------------------preprocessing----------------------- # rearrange of eigenvectors in subspace for idim in range(batch_size): templist = [eigvec_set[idim][idx] for idx in sort_idx] eigvec_set[idim][:] = templist U_new = [] for ivec in range(batch_size): s = eigvec_set[:][ivec] # no. ivec subspace eigenvector, bra y = mlib.dot( U, mlib.bra2ket(s), mode='matket') # no. ivec original space Ritz vector, ket Resi_mat = mlib.minus( mat_op_on, mlib.eye(n=dim, m=dim, amplify=eigval_list[ivec])) r = mlib.dot( Resi_mat, y, mode='matket') # no. ivec residual vector, ket if preconditioner == 'full': t = [] for icompo in range(len(r)): ti = r[icompo][0] / (mat_op_on[icompo][icompo] - eigval_list[ivec]) t.append(ti) # new vector to append to U, bra elif preconditioner == 'single': orig_idx = sort_idx[ivec] t = [ -ri[0] / (mat_op_on[orig_idx][orig_idx] - eigval_list[ivec]) for ri in r ] # bra elif preconditioner == 'dj': r = [[-r[idim][0]] for idim in range(dim)] # (I-|y_i><y_i|)(D_A - theta_i*I)(I-|y_i><y_i|)|t_i> = |r_i> perp_op = mlib.minus( I, mlib.ketbra(ket=y, bra=y, mode='2ket', amplify=1.0)) # preconditioner of full mode full_prcdtnr = mlib.zeros(dim, dim) for idim in range(dim): full_prcdtnr[idim][ idim] = mat_op_on[idim][idim] - eigval_list[ivec] # final assembly dj_op = mlib.unitary_transform(U=perp_op, mat=full_prcdtnr) # solve D|t> = |r> if dj_solver == 'lu': if verbosity == 'high': print( 'RAYLEIGH-RITZ| Start LU-decomposition...\nRAYLEIGH-RITZ| LU-decomposition carried out on matrix:' ) [_, L_dj, U_dj] = lu(mat_in=dj_op) if verbosity == 'high': print('RAYLEIGH-RITZ| Start forwardsubstitution...') y_dj = sbssolv(triang_mat=L_dj, b=mlib.ket2bra(r), mode='lower') if verbosity == 'high': print('RAYLEIGH-RITZ| Start backsubstitution...') t = sbssolv(triang_mat=U_dj, b=y_dj, mode='upper' ) # new vector to append to U, bra elif dj_solver == 'np': t = np.linalg.solve(dj_op, mlib.ket2bra(r)) if verbosity == 'high': print('RAYLEIGH-RITZ| New |t> generated!') elif preconditioner == 'none': t = mlib.ket2bra(r) else: print( 'RAYLEIGH-RITZ| ***WARNING***: preconditioner required is not recognized, use \'none\' instead.' ) t = mlib.ket2bra(r) t = mlib.normalize(t) U_new.append(t) U_new = mlib.transpose(U_new) #[_, U, _] = qr(mat_in = U_new, mode = 'householder', verbosity = verbosity) #[U, _] = np.linalg.qr(U_new) [_, U] = gs(U_new) istep += 1 if verbosity != 'silent': print('RAYLEIGH-RITZ| Step {}: conv = {}, conv_thr = {}'.format( istep, eigval_conv, conv_thr)) return [eigval_list[0:num_eigen], U[:][0:num_eigen]]