forlab_linalg_inv.f90 Source File


Contents

Source Code


Source Code

submodule(forlab_linalg) forlab_linalg_inv

    implicit none

contains

    module procedure inv_rsp
    !! inv0 computes the real matrix inverse.
    integer :: i, j, k, m
    real(sp) :: D
    real(sp), dimension(:), allocatable :: x, y, e
    real(sp), dimension(:, :), allocatable :: L, U

    if (is_square(A)) then
        m = size(A, 1)
        if (m .le. 3) then
            D = det(A)
        else
            D = det(A, L, U)
        end if
        if (D .ne. 0._sp) then
            inv = zeros(m, m)
            if (m .eq. 2) then
                inv(1, 1) = A(2, 2)
                inv(1, 2) = -A(1, 2)
                inv(2, 1) = -A(2, 1)
                inv(2, 2) = A(1, 1)
                inv = inv/D
            elseif (m .eq. 3) then
                inv(1, 1) = A(2, 2)*A(3, 3) - A(2, 3)*A(3, 2)
                inv(1, 2) = A(1, 3)*A(3, 2) - A(1, 2)*A(3, 3)
                inv(1, 3) = A(1, 2)*A(2, 3) - A(1, 3)*A(2, 2)
                inv(2, 1) = A(2, 3)*A(3, 1) - A(2, 1)*A(3, 3)
                inv(2, 2) = A(1, 1)*A(3, 3) - A(1, 3)*A(3, 1)
                inv(2, 3) = A(1, 3)*A(2, 1) - A(1, 1)*A(2, 3)
                inv(3, 1) = A(2, 1)*A(3, 2) - A(2, 2)*A(3, 1)
                inv(3, 2) = A(1, 2)*A(3, 1) - A(1, 1)*A(3, 2)
                inv(3, 3) = A(1, 1)*A(2, 2) - A(1, 2)*A(2, 1)
                inv = inv/D
            else
                do k = 1, m
                    x = zeros(m)
                    y = zeros(m)
                    e = zeros(m)
                    e(k) = 1.
                    y(1) = e(1)

                    ! Forward substitution: Ly = e
                    !==============================
                    do i = 2, m
                        y(i) = e(i)
                        do j = 1, i - 1
                            y(i) = y(i) - y(j)*L(i, j)
                        end do
                    end do

                    ! Back substitution: Ux = y
                    !===========================
                    x(m) = y(m)/U(m, m)
                    do i = m - 1, 1, -1
                        x(i) = y(i)
                        do j = m, i + 1, -1
                            x(i) = x(i) - x(j)*U(i, j)
                        end do
                        x(i) = x(i)/U(i, i)
                    end do

                    ! The column k of the inverse is x
                    !==================================
                    inv(:, k) = x
                end do
            end if
        else
            call error_stop("Error: in det(A), A is not inversible (= 0).")
        end if
    else
        call error_stop("Error: in inv(A), A should be square.")
    end if
    return
    end procedure
    module procedure inv_rdp
    !! inv0 computes the real matrix inverse.
    integer :: i, j, k, m
    real(dp) :: D
    real(dp), dimension(:), allocatable :: x, y, e
    real(dp), dimension(:, :), allocatable :: L, U

    if (is_square(A)) then
        m = size(A, 1)
        if (m .le. 3) then
            D = det(A)
        else
            D = det(A, L, U)
        end if
        if (D .ne. 0._dp) then
            inv = zeros(m, m)
            if (m .eq. 2) then
                inv(1, 1) = A(2, 2)
                inv(1, 2) = -A(1, 2)
                inv(2, 1) = -A(2, 1)
                inv(2, 2) = A(1, 1)
                inv = inv/D
            elseif (m .eq. 3) then
                inv(1, 1) = A(2, 2)*A(3, 3) - A(2, 3)*A(3, 2)
                inv(1, 2) = A(1, 3)*A(3, 2) - A(1, 2)*A(3, 3)
                inv(1, 3) = A(1, 2)*A(2, 3) - A(1, 3)*A(2, 2)
                inv(2, 1) = A(2, 3)*A(3, 1) - A(2, 1)*A(3, 3)
                inv(2, 2) = A(1, 1)*A(3, 3) - A(1, 3)*A(3, 1)
                inv(2, 3) = A(1, 3)*A(2, 1) - A(1, 1)*A(2, 3)
                inv(3, 1) = A(2, 1)*A(3, 2) - A(2, 2)*A(3, 1)
                inv(3, 2) = A(1, 2)*A(3, 1) - A(1, 1)*A(3, 2)
                inv(3, 3) = A(1, 1)*A(2, 2) - A(1, 2)*A(2, 1)
                inv = inv/D
            else
                do k = 1, m
                    x = zeros(m)
                    y = zeros(m)
                    e = zeros(m)
                    e(k) = 1.
                    y(1) = e(1)

                    ! Forward substitution: Ly = e
                    !==============================
                    do i = 2, m
                        y(i) = e(i)
                        do j = 1, i - 1
                            y(i) = y(i) - y(j)*L(i, j)
                        end do
                    end do

                    ! Back substitution: Ux = y
                    !===========================
                    x(m) = y(m)/U(m, m)
                    do i = m - 1, 1, -1
                        x(i) = y(i)
                        do j = m, i + 1, -1
                            x(i) = x(i) - x(j)*U(i, j)
                        end do
                        x(i) = x(i)/U(i, i)
                    end do

                    ! The column k of the inverse is x
                    !==================================
                    inv(:, k) = x
                end do
            end if
        else
            call error_stop("Error: in det(A), A is not inversible (= 0).")
        end if
    else
        call error_stop("Error: in inv(A), A should be square.")
    end if
    return
    end procedure

    module procedure inv_csp
    !! inv computes the complex matrix inverse.
    real(sp), dimension(:, :), allocatable :: ar, ai
    !! AR stores the real part, AI stores the imaginary part
    integer :: flag, n
    real(sp) :: d, p, t, q, s, b
    integer, dimension(:), allocatable :: is, js
    integer :: i, j, k

    if (is_square(A)) then
        n = size(A, 1)
        inv = zeros(n, n)
        ar = zeros(n, n)
        ai = zeros(n, n)

        is = ones(n)
        js = ones(n)

        forall (i=1:n, j=1:n)
            ar(i, j) = real(A(i, j)); ai(i, j) = imag(A(i, j))
        end forall
        flag = 1
        do k = 1, n
            d = 0.0
            do i = k, n
                do j = k, n
                    p = ar(i, j)*ar(i, j) + ai(i, j)*ai(i, j)
                    if (p .gt. d) then
                        d = p
                        is(k) = i
                        js(k) = j
                    end if
                end do
            end do
            if (d + 1.0_sp .eq. 1.0_sp) then
                flag = 0
                call error_stop('ERROR: A is not inversible (= 0)')
            end if
            do j = 1, n
                t = ar(k, j)
                ar(k, j) = ar(is(k), j)
                ar(is(k), j) = t
                t = ai(k, j)
                ai(k, j) = ai(is(k), j)
                ai(is(k), j) = t
            end do
            do i = 1, n
                t = ar(i, k)
                ar(i, k) = ar(i, js(k))
                ar(i, js(k)) = t
                t = ai(i, k)
                ai(i, k) = ai(i, js(k))
                ai(i, js(k)) = t
            end do
            ar(k, k) = ar(k, k)/d
            ai(k, k) = -ai(k, k)/d
            do j = 1, n
                if (j .ne. k) then
                    p = ar(k, j)*ar(k, k)
                    q = ai(k, j)*ai(k, k)
                    s = (ar(k, j) + ai(k, j))*(ar(k, k) + ai(k, k))
                    ar(k, j) = p - q
                    ai(k, j) = s - p - q
                end if
            end do
            do i = 1, n
                if (i .ne. k) then
                    do j = 1, n
                        if (j .ne. k) then
                            p = ar(k, j)*ar(i, k)
                            q = ai(k, j)*ai(i, k)
                            s = (ar(k, j) + ai(k, j))*(ar(i, k) + ai(i, k))
                            t = p - q
                            b = s - p - q
                            ar(i, j) = ar(i, j) - t
                            ai(i, j) = ai(i, j) - b
                        end if
                    end do
                end if
            end do
            do i = 1, n
                if (i .ne. k) then
                    p = ar(i, k)*ar(k, k)
                    q = ai(i, k)*ai(k, k)
                    s = (ar(i, k) + ai(i, k))*(ar(k, k) + ai(k, k))
                    ar(i, k) = q - p
                    ai(i, k) = p + q - s
                end if
            end do
        end do
        do k = n, 1, -1
            do j = 1, n
                t = ar(k, j)
                ar(k, j) = ar(js(k), j)
                ar(js(k), j) = t
                t = ai(k, j)
                ai(k, j) = ai(js(k), j)
                ai(js(k), j) = t
            end do
            do i = 1, n
                t = ar(i, k)
                ar(i, k) = ar(i, is(k))
                ar(i, is(k)) = t
                t = ai(i, k)
                ai(i, k) = ai(i, is(k))
                ai(i, is(k)) = t
            end do
        end do
        forall (i=1:n, j=1:n)
            inv(i, j) = cmplx(ar(i, j), ai(i, j), sp)
        end forall
    else
        call error_stop('Error: in inv(A), A should be square.')
    end if
    return
    end procedure inv_csp
    module procedure inv_cdp
    !! inv computes the complex matrix inverse.
    real(dp), dimension(:, :), allocatable :: ar, ai
    !! AR stores the real part, AI stores the imaginary part
    integer :: flag, n
    real(dp) :: d, p, t, q, s, b
    integer, dimension(:), allocatable :: is, js
    integer :: i, j, k

    if (is_square(A)) then
        n = size(A, 1)
        inv = zeros(n, n)
        ar = zeros(n, n)
        ai = zeros(n, n)

        is = ones(n)
        js = ones(n)

        forall (i=1:n, j=1:n)
            ar(i, j) = real(A(i, j)); ai(i, j) = imag(A(i, j))
        end forall
        flag = 1
        do k = 1, n
            d = 0.0
            do i = k, n
                do j = k, n
                    p = ar(i, j)*ar(i, j) + ai(i, j)*ai(i, j)
                    if (p .gt. d) then
                        d = p
                        is(k) = i
                        js(k) = j
                    end if
                end do
            end do
            if (d + 1.0_dp .eq. 1.0_dp) then
                flag = 0
                call error_stop('ERROR: A is not inversible (= 0)')
            end if
            do j = 1, n
                t = ar(k, j)
                ar(k, j) = ar(is(k), j)
                ar(is(k), j) = t
                t = ai(k, j)
                ai(k, j) = ai(is(k), j)
                ai(is(k), j) = t
            end do
            do i = 1, n
                t = ar(i, k)
                ar(i, k) = ar(i, js(k))
                ar(i, js(k)) = t
                t = ai(i, k)
                ai(i, k) = ai(i, js(k))
                ai(i, js(k)) = t
            end do
            ar(k, k) = ar(k, k)/d
            ai(k, k) = -ai(k, k)/d
            do j = 1, n
                if (j .ne. k) then
                    p = ar(k, j)*ar(k, k)
                    q = ai(k, j)*ai(k, k)
                    s = (ar(k, j) + ai(k, j))*(ar(k, k) + ai(k, k))
                    ar(k, j) = p - q
                    ai(k, j) = s - p - q
                end if
            end do
            do i = 1, n
                if (i .ne. k) then
                    do j = 1, n
                        if (j .ne. k) then
                            p = ar(k, j)*ar(i, k)
                            q = ai(k, j)*ai(i, k)
                            s = (ar(k, j) + ai(k, j))*(ar(i, k) + ai(i, k))
                            t = p - q
                            b = s - p - q
                            ar(i, j) = ar(i, j) - t
                            ai(i, j) = ai(i, j) - b
                        end if
                    end do
                end if
            end do
            do i = 1, n
                if (i .ne. k) then
                    p = ar(i, k)*ar(k, k)
                    q = ai(i, k)*ai(k, k)
                    s = (ar(i, k) + ai(i, k))*(ar(k, k) + ai(k, k))
                    ar(i, k) = q - p
                    ai(i, k) = p + q - s
                end if
            end do
        end do
        do k = n, 1, -1
            do j = 1, n
                t = ar(k, j)
                ar(k, j) = ar(js(k), j)
                ar(js(k), j) = t
                t = ai(k, j)
                ai(k, j) = ai(js(k), j)
                ai(js(k), j) = t
            end do
            do i = 1, n
                t = ar(i, k)
                ar(i, k) = ar(i, is(k))
                ar(i, is(k)) = t
                t = ai(i, k)
                ai(i, k) = ai(i, is(k))
                ai(i, is(k)) = t
            end do
        end do
        forall (i=1:n, j=1:n)
            inv(i, j) = cmplx(ar(i, j), ai(i, j), dp)
        end forall
    else
        call error_stop('Error: in inv(A), A should be square.')
    end if
    return
    end procedure inv_cdp

end submodule forlab_linalg_inv