NAFPack_Eigen.f90 Source File


This file depends on

sourcefile~~nafpack_eigen.f90~~EfferentGraph sourcefile~nafpack_eigen.f90 NAFPack_Eigen.f90 sourcefile~nafpack_constant.f90 NAFPack_constant.f90 sourcefile~nafpack_eigen.f90->sourcefile~nafpack_constant.f90 sourcefile~nafpack_kinds.f90 NAFPack_kinds.f90 sourcefile~nafpack_eigen.f90->sourcefile~nafpack_kinds.f90 sourcefile~nafpack_matricielle.f90 NAFPack_matricielle.f90 sourcefile~nafpack_eigen.f90->sourcefile~nafpack_matricielle.f90 sourcefile~nafpack_matrix_decomposition.f90 NAFPack_matrix_decomposition.f90 sourcefile~nafpack_eigen.f90->sourcefile~nafpack_matrix_decomposition.f90 sourcefile~nafpack_constant.f90->sourcefile~nafpack_kinds.f90 sourcefile~nafpack_matricielle.f90->sourcefile~nafpack_kinds.f90 sourcefile~nafpack_matrix_decomposition.f90->sourcefile~nafpack_constant.f90 sourcefile~nafpack_matrix_decomposition.f90->sourcefile~nafpack_kinds.f90 sourcefile~nafpack_matrix_decomposition.f90->sourcefile~nafpack_matricielle.f90

Files dependent on this one

sourcefile~~nafpack_eigen.f90~~AfferentGraph sourcefile~nafpack_eigen.f90 NAFPack_Eigen.f90 sourcefile~nafpack_matrix_properties.f90 NAFPack_matrix_properties.f90 sourcefile~nafpack_matrix_properties.f90->sourcefile~nafpack_eigen.f90 sourcefile~nafpack_direct_methode.f90 NAFPack_Direct_methode.f90 sourcefile~nafpack_direct_methode.f90->sourcefile~nafpack_matrix_properties.f90 sourcefile~nafpack_iterative_methods.f90 NAFPack_Iterative_methods.f90 sourcefile~nafpack_iterative_methods.f90->sourcefile~nafpack_matrix_properties.f90 sourcefile~nafpack_linalg.f90 NAFPack_linalg.f90 sourcefile~nafpack_linalg.f90->sourcefile~nafpack_direct_methode.f90 sourcefile~nafpack_linalg.f90->sourcefile~nafpack_iterative_methods.f90

Source Code

!> Module for eigenvalue and eigenvector computations in NAFPack
module NAFPack_Eigen

    use NAFPack_kinds, only: dp
    use NAFPack_constant, only: TOL_CONVERGENCE_dp, MAX_ITERATION
    use NAFPack_matrix_decomposition, only: QR_decomposition
    use NAFPack_matricielle, only: Identity_n, normalise

    implicit none(type, external)

    private

    public :: Eigen

contains

    !================== Eigen ===============================================================

    !> Computes the eigenvalues and eigenvectors of a matrix A
    !> \[ A * \vec{v} = \lambda * \vec{v} \]
    !> with **A** a square matrix, **λ** the eigenvalue, and **v** the eigenvector.
    !> This subroutine allows you to choose the method for computing eigenvalues and eigenvectors:
    !>
    !> - Power iteration
    !> - QR algorithm (with or without shift)
    !> The default method is Power iteration.
    subroutine Eigen(A, lambda, vp, method, k)
        real(dp), dimension(:, :), intent(in) :: A
        character(LEN=*), optional, intent(in) :: method
        integer, optional, intent(in) :: k
        real(dp), dimension(:, :), optional, intent(out) :: vp
        real(dp), dimension(:), intent(out) :: lambda
        real(dp), dimension(size(A, 1), size(A, 1)) :: A_tmp
        real(dp), dimension(size(A, 1), size(A, 1)) :: vp_tmp
        character(LEN=50) :: base_method
        integer :: N, i, k_max, pos

        if (present(k)) then
            if (k <= 0) stop "ERROR :: k must be a positive integer"
            k_max = k
        else
            k_max = MAX_ITERATION
        end if

        N = size(A, 1)
        if (size(A, 2) /= N) stop "ERROR :: Matrix A not square"

        if (size(lambda, 1) /= N) stop "ERROR :: dimension lambda"
        if (present(vp) .and. (size(vp, 1) /= N .or. size(vp, 2) /= N)) stop "ERROR :: dimension vp"

        if (method == "Power_iteration") then

            A_tmp = A
            do i = 1, N
                call Power_iteration(A_tmp, lambda(i), vp_tmp(i, :), k_max)
                A_tmp = deflation(A_tmp, lambda(i), vp_tmp(i, :), k_max)
            end do

            if (present(vp)) vp = vp_tmp

        else if (index(method, "QR") == 1) then

            if (present(vp)) vp = 0
            if (present(vp)) print*,"WARNING :: No solution for eigenvectors with the QR method"

            pos = index(trim(method), "_Shifted")

            if (pos > 0 .and. pos + 7 == len_trim(method)) then
                base_method = method(:pos - 1)
                call Eigen_QR_Shifted(A, lambda, base_method, N, k_max)
            else
                call Eigen_QR(A, lambda, method, N, k_max)
            end if

        else
            stop "ERROR :: Wrong method for Eigen"
        end if

    end subroutine Eigen

    !> QR algorithm for computing eigenvalues
    !>
    !> This subroutine implements the QR algorithm for computing the eigenvalues of a matrix.
    subroutine Eigen_QR(A, lambda, method, N, k)
        real(dp), dimension(:, :), intent(in) :: A
        character(LEN=*), intent(in) :: method
        integer, intent(in) :: N, k
        real(dp), dimension(:), intent(out) :: lambda
        real(dp), dimension(size(A, 1)) :: lambda_old
        real(dp), dimension(size(A, 1), size(A, 1)) :: A_tmp, Q, R
        real(dp) :: diff
        integer :: i, j

        A_tmp = A

        do i = 1, k

            lambda_old = lambda

            call QR_decomposition(A_tmp, method, Q, R)

            A_tmp = matmul(R, Q)

            diff = abs(A_tmp(2, 1))
            do j = 3, N
                if (maxval(abs(A_tmp(j, 1:j - 1))) > diff) then
                    diff = maxval(abs(A_tmp(j, 1:j - 1)))
                end if
            end do

            if (i == k) then
                print*," WARNING :: non-convergence of the QR Algorithm for eigenvalues "//method
                print*,"convergence = ", diff
                exit
            end if

            if (diff <= TOL_CONVERGENCE_dp) exit
        end do

        ! Extract eigenvalues
        lambda = [(A_tmp(i, i), i=1, N)]

    end subroutine Eigen_QR

    !> Shifted QR algorithm for computing eigenvalues
    !>
    !> This subroutine implements the shifted QR algorithm for computing the eigenvalues of a matrix.
    !> The shift is chosen as the last diagonal element of the matrix.
    subroutine Eigen_QR_Shifted(A, lambda, method, N, k)
        integer, intent(in) :: N, k
        character(LEN=*), intent(in) :: method
        real(dp), dimension(N, N), intent(in) :: A
        real(dp), dimension(N), intent(out) :: lambda
        integer :: i, j
        real(dp), dimension(size(A, 1), size(A, 1)) :: A_tmp, Q, R, Id
        real(dp) :: shift, diff

        A_tmp = A
        Id = Identity_n(N)

        do i = 1, k
            !choice of shift: last diagonal element
            shift = A_tmp(N, N)

            ! Gap : A - µI
            A_tmp = A_tmp - shift * Id

            ! QR Decomposition : A - µI = Q * R
            call QR_decomposition(A_tmp, method, Q, R)

            ! A = RQ + µI
            A_tmp = matmul(R, Q) + shift * Id

            diff = abs(A_tmp(2, 1))
            do j = 3, N
                if (maxval(abs(A_tmp(j, 1:j - 1))) > diff) then
                    diff = maxval(abs(A_tmp(j, 1:j - 1)))
                end if
            end do

            if (i == k) then
                print*,"WARNING :: non-convergence of the Shifted QR Algorithm for eigenvalues ", &
                    trim(method)
                print*,"convergence = ", diff
                exit
            end if

            if (diff <= TOL_CONVERGENCE_dp) exit

        end do

        ! Extract eigenvalues
        lambda = [(A_tmp(i, i), i=1, N)]

    end subroutine Eigen_QR_Shifted

    !> Power iteration method for computing the dominant eigenvalue and eigenvector
    !>
    !> This subroutine implements the power iteration method for finding the dominant eigenvalue and eigenvector of a matrix.
    !> It iteratively computes the eigenvector and eigenvalue until convergence
    subroutine Power_iteration(A, lambda, vp, k)
        real(dp), dimension(:, :), intent(in) :: A
        integer, intent(in) :: k
        real(dp), dimension(:), intent(out) :: vp
        real(dp), intent(out) :: lambda
        real(dp), dimension(size(A, 1)) :: u, vp_tmp, r
        integer :: i, N

        N = size(A, 1)
        call random_number(u)

        u = normalise(u)
        vp_tmp = matmul(A, u)
        lambda = dot_product(vp_tmp, u)
        r = vp_tmp - lambda * u

        do i = 1, k
            u = normalise(vp_tmp)
            vp_tmp = matmul(A, u)
            lambda = dot_product(vp_tmp, u)
            if (norm2(r) <= TOL_CONVERGENCE_dp) exit
            r = vp_tmp - lambda * u
            if (i == k) then
                print*,"WARNING :: non-convergence of the power iteration method"
            end if
        end do

        vp = u

    end subroutine Power_iteration

    !> Deflation method for removing the influence of an eigenvalue and eigenvector
    !>
    !> This function performs deflation on a matrix A by removing the influence of an eigenvalue and its corresponding eigenvector.
    function deflation(A, lambda, vp, k) result(result)

        real(dp), dimension(:, :), intent(in) :: A
        real(dp), dimension(:), intent(in) :: vp
        real(dp), intent(in) :: lambda
        integer, intent(in) :: k
        real(dp), dimension(size(A, 1), size(A, 1)) :: result
        real(dp), dimension(size(A, 1)) :: wp
        integer :: i, j, N
        real(dp) :: lambda1

        N = size(A, 1)
        result = A

        call Power_iteration(transpose(A), lambda1, wp, k)
        do i = 1, N
            do j = 1, N
                result(i, j) = result(i, j) - (lambda * vp(i) * wp(j)) / dot_product(vp, wp)
            end do
        end do

    end function deflation

end module NAFPack_Eigen