Beispiel #1
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]
Beispiel #2
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]
Beispiel #3
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]
Beispiel #4
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]
Beispiel #5
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]
Beispiel #6
0
def jacobi_diag(mat_in,
                tri_diag=False,
                conv_level=3,
                max_iter=10,
                verbosity='default'):
    """
    Jacobi diagonalization (sequential SO2-diagonalization, cyclic, iterative)\n
    # input requirement\n
    mat_in: real, symmetric, squared matrix\n
    tri_diag: if set to True, will return a tri-diagonalized matrix, rather than fully
    diagonalized one\n
    conv_level: convergence level, an positive integar is wanted. The higher this value
    is set, the clearer the off-diagonal parts of produced matrix will be\n
    max_iter: although this algorithm is promised to converge, maximum iteration step is
    still used to prohibit a high time-consuming but in vain calculation\n
    verbosity: if set to anything except 'silent', iteration information will be printed
    on screen. if set to 'debug', huge amount of information will be printed, use with
    caution!\n
    # output description\n
    [diag, U]\n
    diag: diagonalized matrix\n
    U: unitary operator\n
    # formula\n
    mat_in = U·diag·U', where U' denotes transpose of U
    """
    if not mlib.symm_check(mat=mat_in):

        raise TypeError

    if tri_diag:

        tri_diag_str = '-' * 25 + 'Tri-diagonalization mode' + '-' * 25
    else:

        tri_diag_str = ''

    conv_thr = 10.0**(-conv_level)
    sod_log = []  # s.o.d: sum of off-diagonal elements

    isweep = 0
    [_, rawDiag, op] = ijacobi(sym_mat_in=mat_in,
                               tri_diag=tri_diag,
                               skipthr=conv_thr,
                               verbosity=verbosity)

    op_accum = op

    sod = _off_diag_abs_sum(mat=rawDiag)
    conv = sod
    sod_log.append(sod)

    if verbosity != 'silent':
        print('JACOBI-DIAG| Iteration {}:\n'.format(isweep) +
              '             Sum of all off-diagonal elements S = {}\n'.format(
                  sod) +
              '             Convergence:                         {}\n'.format(
                  conv) +
              '             Convergence threshold:               {}\n'.format(
                  conv_thr) + tri_diag_str)

    while (conv > conv_thr) and (isweep < max_iter):

        isweep += 1

        [_, rawDiag, op] = ijacobi(sym_mat_in=rawDiag,
                                   tri_diag=tri_diag,
                                   verbosity=verbosity)

        sod = _off_diag_abs_sum(mat=rawDiag)
        conv = abs(sod - sod_log[-1])
        sod_log.append(sod)
        op_accum = mlib.dot(op_accum, op)

        if verbosity != 'silent':
            print('JACOBI-DIAG| Iteration {}:\n'.format(isweep) +
                  '             Sum of all off-diagonal elements S = {}\n'.
                  format(sod) +
                  '             Convergence:                         {}\n'.
                  format(conv) +
                  '             Convergence threshold:               {}\n'.
                  format(conv_thr) + tri_diag_str)

    if (conv > conv_thr) and (isweep == max_iter):

        print('JACOBI-DIAG| ***WARNING*** Diagonalization non-converged!')

    return [rawDiag, op_accum]
Beispiel #7
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]]
Beispiel #8
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()