forinv.f90 Source File


Files dependent on this one

sourcefile~~forinv.f90~~AfferentGraph sourcefile~forinv.f90 forinv.f90 sourcefile~test1.f90 test1.f90 sourcefile~test1.f90->sourcefile~forinv.f90 sourcefile~test2.f90 test2.f90 sourcefile~test2.f90->sourcefile~forinv.f90 sourcefile~test3.f90 test3.f90 sourcefile~test3.f90->sourcefile~forinv.f90

Source Code

module forinv

   !! This module provides functions and subroutines for inverse and pseudo-inverse calculations.

   use kinds
   use forsvd, only: svd

   implicit none

   private

   public :: inv

   !===============================================================================
   interface inv
      procedure :: pinv_rel
   end interface
   !===============================================================================

contains

   !===============================================================================
   !> author: Seyed Ali Ghasemi
   !> Calculates the pseudoinverse of a matrix A.
#if defined (PURE)
   pure function pinv_rel(A, method, tol) result(Apinv)
#elif defined (IMPURE)
   impure function pinv_rel(A, method, tol) result(Apinv)
#endif

      ! Inputs:
      real(rk),     dimension(:, :), contiguous, intent(in) :: A
      real(rk),     intent(in), optional                    :: tol
      character(*), intent(in), optional                    :: method

      ! Outputs:
      real(rk), dimension(size(A,2), size(A,1))          :: Apinv ! Pseudoinverse of A


      if (present(method)) then
         select case (method)
            case('gesvd','gesdd')
            Apinv = pinvSVD_rel(A, method, tol)
            case('getrf')
            Apinv = pinvLU_rel(A)
            case default
            error stop 'method is not valid.'
         end select
      else
         Apinv = pinvSVD_rel(A, tol=tol)
      end if

   end function pinv_rel
   !===============================================================================


   !===============================================================================
   !> author: Seyed Ali Ghasemi
   !> Calculates the pseudoinverse of a matrix A using the SVD.
#if defined (PURE)
   pure function pinvSVD_rel(A, method, tol) result(Apinv)
#elif defined (IMPURE)
   impure function pinvSVD_rel(A, method, tol) result(Apinv)
#endif

      ! Inputs:
      real(rk), dimension(:, :), contiguous, intent(in)  :: A     ! Input matrix A
      real(rk),     intent(in), optional                 :: tol
      character(*), intent(in), optional                 :: method

      ! Outputs:
      real(rk), dimension(size(A,2), size(A,1))          :: Apinv ! Pseudoinverse of A

      ! Local variables
      real(rk), dimension(size(A,1), size(A,1))          :: U    ! Left singular vectors
      real(rk), dimension(size(A,2), size(A,2))          :: VT   ! Right singular vectors
      real(rk), dimension(min(size(A,1), size(A,2)))     :: S    ! Singular values
      integer                                            :: m, n, i, j, irank, rank

      m = size(A, 1)
      n = size(A, 2)

      call svd(A, U,S,VT, method)

      if (.not. present(tol)) then
         rank = min(m,n)
      else
         rank = count(S > tol)
      end if

      Apinv = 0.0_rk

      do irank = 1, rank
         do j = 1, m
            do i = 1, n
               Apinv(i, j) = Apinv(i, j) + VT(irank, i) * U(j, irank) / S(irank)
            end do
         end do
      end do

   end function pinvSVD_rel
   !===============================================================================


   !===============================================================================
   !> author: ZUO Zhihua, Seyed Ali Ghasemi
   !> Calculates the pseudoinverse of a matrix A using the LU decomposition.
#if defined (PURE)
   pure function pinvLU_rel(A) result(Apinv)
#elif defined (IMPURE)
   impure function pinvLU_rel(A) result(Apinv)
#endif

      ! Inputs:
      real(rk), dimension(:, :), contiguous, intent(in) :: A     ! Input matrix A

      ! Outputs:
      real(rk), dimension(size(A,2), size(A,1))         :: Apinv ! Pseudoinverse of A

      if (size(A, 1) == size(A, 2)) then
         Apinv = invLU_rel(A)
      elseif (size(A, 1) > size(A, 2)) then
         Apinv = transpose(A)
         Apinv = gemm(invLU_rel(gemm(Apinv, A)), Apinv)
      else
         Apinv = transpose(A)
         Apinv = gemm(Apinv, invLU_rel(gemm(A, Apinv)))
      end if

   end function pinvLU_rel
   !===============================================================================


   !===============================================================================
   !> author: ZUO Zhihua, Seyed Ali Ghasemi
   !> Calculates the inverse of a matrix A using the LU decomposition.
#if defined (PURE)
   pure function invLU_rel(A) result(Ainv)
#elif defined (IMPURE)
   impure function invLU_rel(A) result(Ainv)
#endif

      ! Inputs:
      real(rk), dimension(:, :), contiguous, intent(in) :: A     ! Input matrix A

      ! Outputs:
      real(rk), dimension(size(A,2), size(A,1))         :: Ainv  ! Inverse of A

      ! Local variables
      integer                                            :: ipiv(size(A, 1)), info
      real(rk)                                           :: work(size(A, 2))

      ! External subroutine for calculating the inverse of a matrix A using the LU decomposition.
      interface
#if defined (PURE)
         pure subroutine dgetrf(f_m, f_n, f_a, f_lda, f_ipiv, f_info)
#elif defined (IMPURE)
         impure subroutine dgetrf(f_m, f_n, f_a, f_lda, f_ipiv, f_info)
#endif
            import rk
            integer,  intent(in)    :: f_m
            integer,  intent(in)    :: f_n
            integer,  intent(in)    :: f_lda
            integer,  intent(out)   :: f_ipiv(*)
            integer,  intent(out)   :: f_info
            real(rk), intent(inout) :: f_a(f_lda, *)
         end subroutine dgetrf
#if defined (PURE)
         pure subroutine dgetri(f_n, f_a, f_lda, f_ipiv, f_work, f_lwork, f_info)
#elif defined (IMPURE)
         impure subroutine dgetri(f_n, f_a, f_lda, f_ipiv, f_work, f_lwork, f_info)
#endif
            import rk
            integer,  intent(in)    :: f_n
            integer,  intent(in)    :: f_lda
            integer,  intent(in)    :: f_lwork
            integer,  intent(out)   :: f_ipiv(*)
            integer,  intent(out)   :: f_info
            real(rk), intent(inout) :: f_a(f_lda, *)
            real(rk), intent(out)   :: f_work(*)
         end subroutine dgetri
      end interface

      Ainv = A
      call dgetrf(size(A, 1), size(A, 2), Ainv, size(A, 1), ipiv, info)
      call dgetri(size(A, 2), Ainv, size(A, 1), ipiv, work, size(A, 2), info)

   end function invLU_rel
   !===============================================================================


   !===============================================================================
   !> author: ZUO Zhihua, Seyed Ali Ghasemi
   !> Calculates the matrix-matrix product of two matrices A and B.
#if defined (PURE)
   pure function gemm(A, B) result(C)
#elif defined (IMPURE)
   impure function gemm(A, B) result(C)
#endif

      ! Inputs:
      real(rk), dimension(:, :), contiguous, intent(in) :: A     ! Input matrix A
      real(rk), dimension(:, :), contiguous, intent(in) :: B     ! Input matrix B

      ! Outputs:
      real(rk), dimension(size(A,1), size(B,2))          :: C     ! Matrix-matrix product of A and B

      ! Local variables
      integer                                            :: m, n, k

      ! External subroutine for calculating the matrix-matrix product of two matrices A and B.
      interface
#if defined (PURE)
         pure subroutine dgemm(f_transa, f_transb, f_m, f_n, f_k, f_alpha, f_a, f_lda, f_b, f_ldb, f_beta, f_c, f_ldc)
#elif defined (IMPURE)
         impure subroutine dgemm(f_transa, f_transb, f_m, f_n, f_k, f_alpha, f_a, f_lda, f_b, f_ldb, f_beta, f_c, f_ldc)
#endif
            import rk
            integer,   intent(in)    :: f_ldc
            integer,   intent(in)    :: f_ldb
            integer,   intent(in)    :: f_lda
            character, intent(in)    :: f_transa
            character, intent(in)    :: f_transb
            integer,   intent(in)    :: f_m
            integer,   intent(in)    :: f_n
            integer,   intent(in)    :: f_k
            real(rk),  intent(in)    :: f_alpha
            real(rk),  intent(in)    :: f_a(f_lda, *)
            real(rk),  intent(in)    :: f_b(f_ldb, *)
            real(rk),  intent(in)    :: f_beta
            real(rk),  intent(inout) :: f_c(f_ldc, *)
         end subroutine dgemm
      end interface

      m = size(A, 1)
      n = size(B, 2)
      k = size(A, 2)

      call dgemm('N', 'N', m, n, k, 1.0_rk, A, m, B, k, 0.0_rk, C, m)

   end function gemm
   !===============================================================================

end module forinv