示例#1
0
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]
示例#2
0
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]
示例#3
0
def ijacobi(sym_mat_in, tri_diag=False, skipthr=1E-10, verbosity='silent'):

    nline = len(sym_mat_in)
    mat_op_on = deepcopy(sym_mat_in)

    op_accum = mlib.eye(n=nline)

    if tri_diag:
        row_skip = 1
    else:
        row_skip = 0

    for icol in range(nline - 1):
        for irow in range(icol + 1 + row_skip, nline):

            if abs(mat_op_on[irow][icol] / nline**2) < skipthr:
                continue
            # guarantee irow >= icol, i.e., only concentrate on lower triangonal part
            sub_mat = [[mat_op_on[icol][icol], mat_op_on[icol][irow]],
                       [mat_op_on[irow][icol], mat_op_on[irow][irow]]]

            [diag_mat, Uso2] = orthso2(sub_mat)
            if verbosity == 'debug':
                print(
                    '\nJacobi (sequential 2-dimensional) diagonalization report\n'
                    + '-' * 50)
                print('Present matrix:')
                mlib.matrix_print(mat_op_on, decimal=4)
                print('Matrix to diagonalize:')
                mlib.matrix_print(sub_mat, decimal=4)
                print('Diagonalized matrix:')
                mlib.matrix_print(diag_mat, decimal=4)
                print('Unitary operator at present step:')
                mlib.matrix_print(Uso2, decimal=4)

            #mat_op_on[icol][icol] = diag_mat[0][0]
            #mat_op_on[icol][irow] = diag_mat[0][1]
            #mat_op_on[irow][icol] = diag_mat[1][0]
            #mat_op_on[irow][irow] = diag_mat[1][1]

            op = op_gen(size=nline, U=Uso2, irow=irow, icol=icol)

            mat_op_on = mlib.unitary_transform(U=op, mat=mat_op_on)

            op_accum = mlib.dot(op_accum, op)

            if verbosity == 'debug':
                print('Unitary operator that operates on whole matrix:')
                mlib.matrix_print(op, decimal=4)
                print('Accumulative unitary operator:')
                mlib.matrix_print(op_accum, decimal=4)

    if verbosity == 'debug':
        print('-' * 30)
        print('JACOBI-DIAG| Final report:\nDiagonalized matrix:')
        mlib.matrix_print(mat_op_on, decimal=4)
        print('Accumulative unitary operator:')
        mlib.matrix_print(op_accum, decimal=4)

    return [sym_mat_in, mat_op_on, op_accum]
示例#4
0
def op_gen(size, U, irow, icol):

    # generate one Jacobi operator by reading one SO(2) operator
    # and one (irow, icol)-pair, where irow >= icol
    P = mlib.eye(n=size)
    P[icol][icol] = U[0][0]
    P[icol][irow] = U[0][1]
    P[irow][icol] = U[1][0]
    P[irow][irow] = U[1][1]

    return P
示例#5
0
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]
示例#6
0
def householder(vec_in, vec_desti=[], mode='reduce', verbosity='silent'):
    """
    Householder algorithm\n
    Householder build a reflection operator that can transform one vector to one wanted
    direction\n
    # input requirement\n
    vec_in: one vector that want to reflect, SHOULD BE INPUT AS <|, 1d list\n
    vec_desti: one vector whose direction will vec_in be reflected onto, if not given
    explicitly, program will quit unless KEYWORD mode is set to 'reduce'\n
    mode: 'reduce' or anything else. 'reduce' mode means to reflect vec_in to direction
    along x-axis, i.e., [1, 0, 0, ...]\n
    # output description\n
    Householder operator P
    """
    len_vec = len(vec_in)
    mod_vec_in = mlib.mod_of_vec(vec_in)

    if mode == 'reduce':

        vec_desti = mlib.zeros(n=1, m=len_vec)[0][:]
        vec_desti[0] = mod_vec_in

    u = mlib.minus(vec_in, vec_desti)

    mod_u = mlib.mod_of_vec(u)
    v = mlib.zeros(n=1, m=len_vec)[0][:]
    for i in range(len(u)):

        if (u[i] == 0) and (mod_u == 0):
            v[i] = 1.0  # normalize manually
        else:
            v[i] = u[i] / mod_u
    # v = [iterm/mod_u for iterm in u]

    I = mlib.eye(len_vec)
    vvT = mlib.ketbra(v, v, mode='2bra', amplify=2)
    P = mlib.minus(I, vvT)
    if verbosity == 'debug':
        print('HOUSEHOLDER| Comprehensive report\n' + 'input check:\n' +
              'vector = {}\n'.format(vec_in) +
              'destination = {}\n'.format(vec_desti) +
              'norm vector (original) = {}\n'.format(u) +
              'normalized norm vector = {}'.format(v))
        print('2|v><v| tensor product  =')
        mlib.matrix_print(vvT)
        print('identity operator generated:\n{}\n'.format(I) +
              'Householder operator:\n{}'.format(P))
    return P
示例#7
0
def tri_diag(mat_in, verbosity='silent'):
    """
    Tri-diagonalization (Givens method)\n
    # input requirement\n
    mat_in: symmetrical, real matrix, FLOAT data type\n
    # output description\n
    [tdiag, U]\n
    tdiag: tri-diagonalized matrix\n
    U: unitary operator (accumulative Givens operator, in present context)\n
    # formulation\n
    see LATEX formatted text at the end of present function and\n
    mat_in = U·tdiag·U', where U' denotes transpose of unitary operator
    """
    nline = len(mat_in)
    mat_op_on = deepcopy(mat_in)

    op_accum = mlib.eye(nline)
    for irow in range(1, nline - 1):
        for icol in range(irow + 1, nline):

            s = mat_op_on[irow - 1][icol] / sqrt(mat_op_on[irow - 1][irow]**2 +
                                                 mat_op_on[irow - 1][icol]**2)
            if s == 0:
                c = 1.0
            else:
                c = -(mat_op_on[irow - 1][irow] /
                      mat_op_on[irow - 1][icol]) * s

            P = [[c, s], [-s, c]]
            op = op_gen(size=nline, U=P, irow=icol, icol=irow)

            op_accum = mlib.dot(op_accum, op)
            mat_op_on = mlib.unitary_transform(U=op, mat=mat_op_on)

            if verbosity == 'debug':

                print('\nGivens tri-diagonalization comprehensive report\n' +
                      '-' * 50 +
                      '\nGivens operator (2x2) P({}, {}) print:'.format(
                          irow, icol))
                mlib.matrix_print(P, decimal=4)
                print('Embedded Givens operator print:')
                mlib.matrix_print(op, decimal=4)
                print('Matrix print:')
                mlib.matrix_print(mat_op_on, decimal=4)

    return [mat_op_on, op_accum]
示例#8
0
def ludecomp(mat_in, pivot=False, iexpr=False):
    """
    Lower triangonal-Upper triangonal matrix decomposition, Crout Algorithm\n
    # input requirement\n
    mat_in: squared matrix\n
    pivot (not implemented): for actual use, there may be zero diagonal element that will cause numerical failure, 
    use pivot to swap rows of original matrix, therefore it is possible when zero diagonal element emerges, 
    program will return a result of LU decomposition of row-swapped original matrix, not that of original matrix. 
    If so, program will pop a warning on this unexpected condition\n
    iexpr: only available if mode == 'recursive', print expression of every element of lower and upper 
    triangonal matrices\n
    # output description\n
    [original matrix, Lower triangonal matrix, Upper triangonal matrix]
    """
    # in principle, should check if singular in advance.

    if mlib.det(mat_in=mat_in) == 0:

        print(
            '***error*** Singular matrix! LU decomposition doesn\'t support present matrix!'
        )
        exit()

    # deepcopy = save as
    mat_in_bak = deepcopy(mat_in)
    nline = len(mat_in)

    L = mlib.eye(nline)
    U = mlib.zeros(nline)

    if iexpr:
        print(
            '-' * 80 + '\n' +
            '>> Lower-Upper triangonal matrix decomposition (recursive mode): EXPRESSIONS <<\n'
            + '-' * 80)
    for i in range(nline):
        for j in range(i, nline):

            term_Aij = 0
            expr_str_U = 'U({}, {}) = A({}, {})'.format(i, j, i, j)
            for k in range(i):
                term_Aij += L[i][k] * U[k][j]
                word = ' - L({}, {})*U({}, {})'.format(i, k, k, j)
                expr_str_U += word
            mat_in[i][j] -= term_Aij
            U[i][j] = mat_in[i][j]
            if iexpr:
                print(expr_str_U)

        for j in range(i, nline):
            if j != i:

                term_Aji = 0
                expr_str_L_off_diag = 'L({}, {}) = [A({}, {})'.format(
                    j, i, j, i)
                for k in range(i):
                    term_Aji += L[j][k] * U[k][i]
                    word = ' - L({}, {})*U({}, {})'.format(j, k, k, i)
                    expr_str_L_off_diag += word
                mat_in[j][i] -= term_Aji
                L[j][i] = mat_in[j][i] / U[i][i]
                expr_str_L_off_diag = expr_str_L_off_diag + ']/U({}, {})'.format(
                    i, i)
                if iexpr:
                    print(expr_str_L_off_diag)
    if iexpr:
        print(
            'Numerical results can be collected by line: [A0, L, U] = ludecomp(A, mode = \'recursive\', iexpr = True)'
        )

    return [mat_in_bak, L, U]
示例#9
0
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]]
示例#10
0
def gauss_jordan(mat_in, mode='inversion', b=[], verbosity='silent'):
    """
    Gauss-Jordan inversion\n
    # input requirement\n
    mat_in: squared matrix in 2-dimension\n
    mode: 'inversion' or 'backsubstitution', for the former, will return an inversed mat_in
    while for the latter, will return semi-inversed mat_in, transformed b-vector\n
    b: if mode == 'backsubstitution', this keyword must be specified explicitly. However, if mode == 'inversion'
    and b is given correctly, equation Ax = b will be solved by x = A-1b, x will also be returned.\n
    # output description\n
    Has been introduced in section "input description"
    """

    mat_op_on = deepcopy(mat_in)

    if det(mat_in=mat_op_on):
        # main text of this function
        if verbosity == 'debug':
            print(
                'GAU-JOR INV| non-singluar matrix, safe to calculate inverse...'
            )
        nline = len(mat_op_on)
        record = eye(n=nline)

        zero_diag = 0
        for iline in range(nline):
            if mat_op_on[iline][iline] == 0:
                zero_diag += 1
        if zero_diag > 0:
            print(
                'GAU-JOR INV| zero diagonal element(s) detected: {}\nPartial pivot method will be used...'
                .format(zero_diag))
            # pivot
        for irow in range(nline):
            # OPERATION 1: PIVOT SWAP
            if mat_op_on[irow][irow] == 0:
                print(
                    'GAU-JOR INV| zero diagonal element encounted at row {}, try to swap with other row...'
                    .format(irow))
                pivot = 0
                row2swap = irow
                while pivot == 0 and row2swap <= nline:
                    if mat_op_on[row2swap][irow] != 0:
                        pivot = mat_op_on[row2swap][irow]
                        if verbosity == 'debug':
                            print(
                                'GAU-JOR INV| [OPERATION 1] exchange row {} with row {}'
                                .format(row2swap, irow))
                        temp = mat_op_on[row2swap][:]
                        mat_op_on[row2swap][:] = mat_op_on[irow][:]
                        mat_op_on[irow][:] = temp
                        # same exchange operates on element matrix...
                        temp = record[row2swap][:]
                        record[row2swap][:] = record[irow][:]
                        record[irow][:] = temp
                        if mode == 'backsubstitution':
                            temp = b[row2swap]
                            b[row2swap] = b[irow]
                            b[irow] = temp
                    else:
                        row2swap += 1
            else:
                # OPERATION 2: NORMALIZE DIAGONAL ELEMENT
                diag = mat_op_on[irow][irow]
                for icol in range(nline):
                    if verbosity == 'debug':
                        print(
                            'GAU-JOR INV| [OPERATION 2] STATUS: normalize row {} with factor {}.'
                            .format(irow, diag))
                        print('GAU-JOR INV| [OPERATION 2] row {}: {}'.format(
                            irow, mat_op_on[irow][:]))
                    mat_op_on[irow][icol] /= diag
                    if verbosity == 'debug':
                        print('GAU-JOR INV| [OPERATION 2] RESULTANT row: {}'.
                              format(mat_op_on[irow][:]))
                    record[irow][icol] /= diag
                if mode == 'backsubstitution':
                    b[irow] /= diag

                # OPERATION 3: ELIMINATE ALL OFF-DIAGONAL ELEMENT TO 0
                if mode == 'backsubstitution':
                    line2sub_start = irow
                else:
                    line2sub_start = 0

                for irow2sub in range(line2sub_start, nline):
                    if irow2sub == irow:
                        continue
                    else:

                        factor = mat_op_on[irow2sub][irow]
                        if verbosity == 'debug':
                            print(
                                'GAU-JOR INV| [OPERATION 3] STATUS: subtracting row {} from row {}'
                                .format(mat_op_on[irow][:],
                                        mat_op_on[irow2sub][:]))
                            print(
                                'GAU-JOR INV| [OPERATION 3] row number: {}, {}'
                                .format(irow, irow2sub))
                            print('GAU-JOR INV| [OPERATION 3] FACTOR = {}'.
                                  format(factor))
                        for icol in range(nline):

                            mat_op_on[irow2sub][
                                icol] -= mat_op_on[irow][icol] * factor
                            record[irow2sub][
                                icol] -= record[irow][icol] * factor
                        if mode == 'backsubstitution':
                            b[irow2sub] -= b[irow] * factor
                        if verbosity == 'debug':
                            print(
                                'GAU-JOR INV| [OPERATION 3] RESULTANT ROW: {}'.
                                format(mat_op_on[irow2sub][:]))
                            print(
                                'GAU-JOR INV| [OPERATION 3] RESULTANT MATRIX: {}'
                                .format(mat_op_on))
        if mode == 'inversion':
            if len(b) != nline:
                return record
            else:
                b_in_2d = [[b[i]] for i in range(nline)]
                x = dot(record, b_in_2d)
                x_in_1d = [x[i][0] for i in range(nline)]
                return [record, x_in_1d]
        elif mode == 'backsubstitution':

            return [mat_op_on, b]
    else:
        print(
            'Singular matrix! Not suitable for Gauss-Jordan method to find its inverse, quit.'
        )
        exit()