forpca.f90 Source File


Files dependent on this one

sourcefile~~forpca.f90~~AfferentGraph sourcefile~forpca.f90 forpca.f90 sourcefile~benchmark.f90 benchmark.f90 sourcefile~benchmark.f90->sourcefile~forpca.f90 sourcefile~test1.f90 test1.f90 sourcefile~test1.f90->sourcefile~forpca.f90

Source Code

module forpca

   use kinds
   use foreig

   implicit none

   private
   public tpca

   !===============================================================================
   !> author: Seyed Ali Ghasemi
   type tpca
      integer                               :: ncol, nrow, npc
      character(:),             allocatable :: method
      real(rk), dimension(:,:), allocatable :: matrix
      real(rk), dimension(:,:), allocatable :: coeff
      real(rk), dimension(:,:), allocatable :: score
      real(rk), dimension(:,:), allocatable :: matrix_app
      real(rk), dimension(:,:), allocatable :: mean_data
      real(rk), dimension(:),   allocatable :: latent
      real(rk), dimension(:),   allocatable :: explained_variance
   contains
      procedure :: initialize
      procedure :: compute_coeff
      procedure :: compute_score
      procedure :: reconstruct_data
      procedure :: cmp_explained_variance
      procedure :: pca
      procedure :: finalize => deallocate_tpca
   end type tpca
   !===============================================================================

contains

   !===============================================================================
   !> author: Seyed Ali Ghasemi
#if defined(USE_COARRAY)
   impure subroutine initialize(this, matrix, npc, method)
#else
   pure subroutine initialize(this, matrix, npc, method)
#endif
      class(tpca),              intent(inout)        :: this
      real(rk), dimension(:,:), intent(in)           :: matrix
      integer,                  intent(in), optional :: npc
      character(*),             intent(in), optional :: method


      allocate(this%matrix(size(matrix,1),size(matrix,2)))

#if defined(USE_COARRAY)
      if (this_image() == 1) then
#endif
         this%matrix = matrix
         this%nrow = size(matrix,1)
         this%ncol = size(matrix,2)

         if (.not.present(npc)) then
            this%npc = this%ncol
         else
            this%npc = npc
         end if
#if defined(USE_COARRAY)
      end if
#endif

      if (.not.present(method)) then
         this%method = 'svd'
      else
         this%method = method
      end if

#if defined(USE_COARRAY)
      call co_broadcast(this%matrix, source_image=1)
      call co_broadcast(this%nrow, source_image=1)
      call co_broadcast(this%ncol, source_image=1)
      call co_broadcast(this%npc, source_image=1)
#endif
   end subroutine initialize
   !===============================================================================


   !===============================================================================
   !> author: Seyed Ali Ghasemi
#if defined(USE_COARRAY)
   impure subroutine compute_coeff(this)
#else
   pure subroutine compute_coeff(this)
#endif
      use forsvd
      use formatmul, only: matmul

      class(tpca), intent(inout)               :: this
      real(rk), dimension(this%ncol)           :: mean
      real(rk), dimension(this%ncol,this%ncol) :: cov
      integer                                  :: i
      real(rk), dimension(:,:), allocatable    :: mean_data_T

      allocate(this%mean_data(this%nrow,this%ncol))
      allocate(mean_data_T(this%ncol,this%nrow))

#if defined(USE_COARRAY)
      if (this_image() == 1) then
#endif
         mean = sum(this%matrix, dim=1) / real(this%nrow, kind=rk)

         do i = 1, this%nrow
            this%mean_data(i, :) = this%matrix(i, :) - mean
         end do
         mean_data_T = transpose(this%mean_data)
#if defined(USE_COARRAY)
      end if
#endif

#if defined(USE_COARRAY)
      call co_broadcast(mean_data_T, source_image=1)
      call co_broadcast(this%mean_data, source_image=1)
      cov = matmul(mean_data_T, this%mean_data, method='coarray', option='m2')/real(this%nrow - 1, kind=rk)
#else
      cov = matmul(mean_data_T, this%mean_data, method='default', option='m2')/real(this%nrow - 1, kind=rk)
#endif

      allocate(this%latent(this%ncol))
      allocate(this%coeff(this%ncol,this%ncol))
#if defined(USE_COARRAY)
      if (this_image() == 1) then
#endif
         select case(this%method)
          case('svd')
            block
               real(rk), dimension(this%ncol,this%ncol) :: U
               real(rk), dimension(this%ncol,this%ncol) :: VT
               real(rk), dimension(this%ncol)           :: S

               call svd(cov, U,S,VT)
               this%latent = S**2/(this%ncol-1)
               this%coeff  = transpose(VT)
            end block
          case('eig')
            block
               logical,  dimension(this%ncol) :: mask
               integer,  dimension(this%ncol) :: order

               call eig(cov, this%coeff, this%latent)

               ! Sort
               mask = .true.
               do i = lbound(this%latent,1), ubound(this%latent,1)
                  order(i) = maxloc(this%latent,1,mask)
                  mask(order(i)) = .false.
               end do

               this%latent = this%latent(order)
               this%coeff  = this%coeff(:,order)
            end block
         end select

#if defined(USE_COARRAY)
      end if
#endif

   end subroutine compute_coeff
   !===============================================================================


   !===============================================================================
   !> author: Seyed Ali Ghasemi
#if defined(USE_COARRAY)
   impure subroutine compute_score(this)
#else
   pure subroutine compute_score(this)
#endif
      class(tpca), intent(inout) :: this
      integer                    :: i, j

#if defined(USE_COARRAY)
      if (this_image() == 1) then
#endif
         allocate(this%score(this%nrow,this%ncol))
         do i = 1, this%nrow
            do j = 1, this%npc
               this%score(i, j) = dot_product(this%mean_data(i, :), this%coeff(:, j))
            end do
         end do
#if defined(USE_COARRAY)
      end if
#endif

   end subroutine compute_score
   !===============================================================================


   !===============================================================================
   !> author: Seyed Ali Ghasemi
#if defined(USE_COARRAY)
   impure subroutine reconstruct_data(this)
#else
   pure subroutine reconstruct_data(this)
#endif
      use formatmul, only: matmul

      class(tpca), intent(inout)             :: this
      real(rk), dimension(:, :), allocatable :: X_centered
      real(rk), dimension(:, :), allocatable :: pca_X
      real(rk), dimension(:, :), allocatable :: coeff_T
      integer                                :: i, j

      X_centered = this%matrix - this%mean_data

#if defined(USE_COARRAY)
      call co_broadcast(this%coeff, source_image=1)
      pca_X = matmul(X_centered, this%coeff,method='coarray', option='m2')
      call co_broadcast(pca_X, source_image=1)
      coeff_T = transpose(this%coeff)
      this%matrix_app = matmul(pca_X, coeff_T, method='coarray', option='m2') + this%mean_data
#else
      coeff_T = transpose(this%coeff)
      pca_X = matmul(X_centered, this%coeff, method='default', option='m2')
      this%matrix_app = matmul(pca_X, coeff_T, method='default', option='m2') + this%mean_data
#endif
   end subroutine reconstruct_data
   !===============================================================================


   !===============================================================================
   !> author: Seyed Ali Ghasemi
#if defined(USE_COARRAY)
   impure subroutine cmp_explained_variance(this)
#else
   pure subroutine cmp_explained_variance(this)
#endif
      class(tpca), intent(inout) :: this
      real(rk)                   :: sum_latent

#if defined(USE_COARRAY)
      if (this_image() == 1) then
#endif
         sum_latent = sum(this%latent(1:this%npc))
         this%explained_variance = this%latent / sum_latent
#if defined(USE_COARRAY)
      end if
#endif

   end subroutine cmp_explained_variance
   !===============================================================================


   !===============================================================================
   !> author: Seyed Ali Ghasemi
#if defined(USE_COARRAY)
   impure subroutine pca(this, matrix, npc, method, coeff, score, latent, explained, matrix_app)
#else
   pure subroutine pca(this, matrix, npc, method, coeff, score, latent, explained, matrix_app)
#endif
      class(tpca),                           intent(inout)         :: this
      real(rk), dimension(:,:),              intent(in)            :: matrix
      integer,                               intent(in),  optional :: npc
      character(*),                          intent(in),  optional :: method
      real(rk), dimension(:,:), allocatable, intent(out)           :: coeff
      real(rk), dimension(:,:), allocatable, intent(out), optional :: score
      real(rk), dimension(:),   allocatable, intent(out), optional :: latent
      real(rk), dimension(:),   allocatable, intent(out), optional :: explained
      real(rk), dimension(:,:), allocatable, intent(out), optional :: matrix_app

      call this%initialize(matrix, npc, method)

      call this%compute_coeff()
      coeff = this%coeff

      if(present(score)) then
         call this%compute_score()
         score = this%score
      end if

      if(present(latent)) then
         latent = this%latent
      end if

      if(present(explained)) then
         call this%cmp_explained_variance()
         explained = this%explained_variance*100.0_rk
      end if


      if(present(matrix_app)) then
         call this%reconstruct_data()
         matrix_app = this%matrix_app
      end if

   end subroutine pca
   !===============================================================================


   !===============================================================================
   !> author: Seyed Ali Ghasemi
   elemental pure subroutine deallocate_tpca(this)
      class(tpca), intent(inout) :: this

      if (allocated(this%matrix))     deallocate(this%matrix)
      if (allocated(this%coeff))      deallocate(this%coeff)
      if (allocated(this%mean_data))  deallocate(this%mean_data)
      if (allocated(this%score))      deallocate(this%score)
      if (allocated(this%matrix_app)) deallocate(this%matrix_app)

   end subroutine deallocate_tpca
   !===============================================================================

end module forpca