LCOV - code coverage report
Current view: top level - src/fm - cp_fm_cholesky.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:42dac4a) Lines: 87.0 % 100 87
Test Date: 2025-07-25 12:55:17 Functions: 100.0 % 4 4

            Line data    Source code
       1              : !--------------------------------------------------------------------------------------------------!
       2              : !   CP2K: A general program to perform molecular dynamics simulations                              !
       3              : !   Copyright 2000-2025 CP2K developers group <https://cp2k.org>                                   !
       4              : !                                                                                                  !
       5              : !   SPDX-License-Identifier: GPL-2.0-or-later                                                      !
       6              : !--------------------------------------------------------------------------------------------------!
       7              : 
       8              : ! **************************************************************************************************
       9              : !> \brief various cholesky decomposition related routines
      10              : !> \par History
      11              : !>      09.2002 created [fawzi]
      12              : !> \author Fawzi Mohamed
      13              : ! **************************************************************************************************
      14              : MODULE cp_fm_cholesky
      15              :    USE cp_blacs_env,                    ONLY: cp_blacs_env_type
      16              :    USE cp_dlaf_utils_api,               ONLY: cp_dlaf_create_grid,&
      17              :                                               cp_dlaf_initialize
      18              :    USE cp_fm_dlaf_api,                  ONLY: cp_pdpotrf_dlaf,&
      19              :                                               cp_pdpotri_dlaf,&
      20              :                                               cp_pspotrf_dlaf,&
      21              :                                               cp_pspotri_dlaf
      22              :    USE cp_fm_types,                     ONLY: cp_fm_type
      23              :    USE cp_log_handling,                 ONLY: cp_to_string
      24              :    USE kinds,                           ONLY: dp,&
      25              :                                               sp
      26              : #include "../base/base_uses.f90"
      27              : 
      28              :    IMPLICIT NONE
      29              :    PRIVATE
      30              : 
      31              :    LOGICAL, PRIVATE, PARAMETER :: debug_this_module = .TRUE.
      32              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'cp_fm_cholesky'
      33              : 
      34              :    PUBLIC :: cp_fm_cholesky_decompose, cp_fm_cholesky_invert, &
      35              :              cp_fm_cholesky_reduce, cp_fm_cholesky_restore
      36              : 
      37              :    ! The following saved variables are Cholesky decomposition global
      38              :    ! Stores the default library for Cholesky decomposition
      39              :    INTEGER, SAVE, PUBLIC   :: cholesky_type = 0
      40              :    ! Minimum matrix size for the use of the DLAF Cholesky decomposition.
      41              :    ! ScaLAPACK is used as fallback for all smaller cases.
      42              :    INTEGER, SAVE, PUBLIC    :: dlaf_cholesky_n_min = 0
      43              :    ! Constants for the diag_type above
      44              :    INTEGER, PARAMETER, PUBLIC  :: FM_CHOLESKY_TYPE_SCALAPACK = 101, &
      45              :                                   FM_CHOLESKY_TYPE_DLAF = 104
      46              :    INTEGER, PARAMETER, PUBLIC :: FM_CHOLESKY_TYPE_DEFAULT = FM_CHOLESKY_TYPE_SCALAPACK
      47              : 
      48              : !***
      49              : CONTAINS
      50              : 
      51              : ! **************************************************************************************************
      52              : !> \brief used to replace a symmetric positive def. matrix M with its cholesky
      53              : !>      decomposition U: M = U^T * U, with U upper triangular
      54              : !> \param matrix the matrix to replace with its cholesky decomposition
      55              : !> \param n the number of row (and columns) of the matrix &
      56              : !>        (defaults to the min(size(matrix)))
      57              : !> \param info_out ...
      58              : !> \par History
      59              : !>      05.2002 created [JVdV]
      60              : !>      12.2002 updated, added n optional parm [fawzi]
      61              : !> \author Joost
      62              : ! **************************************************************************************************
      63        57335 :    SUBROUTINE cp_fm_cholesky_decompose(matrix, n, info_out)
      64              :       TYPE(cp_fm_type), INTENT(IN)             :: matrix
      65              :       INTEGER, INTENT(in), OPTIONAL            :: n
      66              :       INTEGER, INTENT(out), OPTIONAL           :: info_out
      67              : 
      68              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_cholesky_decompose'
      69              : 
      70              :       INTEGER                                  :: handle, info, my_n
      71        57335 :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: a
      72        57335 :       REAL(KIND=sp), DIMENSION(:, :), POINTER  :: a_sp
      73              : #if defined(__parallel)
      74              :       INTEGER, DIMENSION(9)                    :: desca
      75              : #endif
      76              : 
      77        57335 :       CALL timeset(routineN, handle)
      78              : 
      79              :       my_n = MIN(matrix%matrix_struct%nrow_global, &
      80        57335 :                  matrix%matrix_struct%ncol_global)
      81        57335 :       IF (PRESENT(n)) THEN
      82        15496 :          CPASSERT(n <= my_n)
      83        15496 :          my_n = n
      84              :       END IF
      85              : 
      86        57335 :       a => matrix%local_data
      87        57335 :       a_sp => matrix%local_data_sp
      88              : 
      89              : #if defined(__parallel)
      90       573350 :       desca(:) = matrix%matrix_struct%descriptor(:)
      91              : #if defined(__DLAF)
      92              :       IF (cholesky_type == FM_CHOLESKY_TYPE_DLAF .AND. matrix%matrix_struct%nrow_global >= dlaf_cholesky_n_min) THEN
      93              :          ! Initialize DLA-Future on-demand; if already initialized, does nothing
      94              :          CALL cp_dlaf_initialize()
      95              : 
      96              :          ! Create DLAF grid from BLACS context; if already present, does nothing
      97              :          CALL cp_dlaf_create_grid(matrix%matrix_struct%context%get_handle())
      98              : 
      99              :          IF (matrix%use_sp) THEN
     100              :             CALL cp_pspotrf_dlaf('U', my_n, a_sp(:, :), 1, 1, desca, info)
     101              :          ELSE
     102              :             CALL cp_pdpotrf_dlaf('U', my_n, a(:, :), 1, 1, desca, info)
     103              :          END IF
     104              :       ELSE
     105              : #endif
     106        57335 :          IF (matrix%use_sp) THEN
     107            0 :             CALL pspotrf('U', my_n, a_sp(1, 1), 1, 1, desca, info)
     108              :          ELSE
     109        57335 :             CALL pdpotrf('U', my_n, a(1, 1), 1, 1, desca, info)
     110              :          END IF
     111              : #if defined(__DLAF)
     112              :       END IF
     113              : #endif
     114              : #else
     115              :       IF (matrix%use_sp) THEN
     116              :          CALL spotrf('U', my_n, a_sp(1, 1), SIZE(a_sp, 1), info)
     117              :       ELSE
     118              :          CALL dpotrf('U', my_n, a(1, 1), SIZE(a, 1), info)
     119              :       END IF
     120              : #endif
     121              : 
     122        57335 :       IF (PRESENT(info_out)) THEN
     123        20512 :          info_out = info
     124        36823 :       ELSE IF (info /= 0) THEN
     125              :          CALL cp_abort(__LOCATION__, &
     126            0 :                        "Cholesky decompose failed: the matrix is not positive definite or ill-conditioned.")
     127              :       END IF
     128              : 
     129        57335 :       CALL timestop(handle)
     130              : 
     131        57335 :    END SUBROUTINE cp_fm_cholesky_decompose
     132              : 
     133              : ! **************************************************************************************************
     134              : !> \brief used to replace the cholesky decomposition by the inverse
     135              : !> \param matrix the matrix to invert (must be an upper triangular matrix)
     136              : !> \param n size of the matrix to invert (defaults to the min(size(matrix)))
     137              : !> \param info_out ...
     138              : !> \par History
     139              : !>      05.2002 created [JVdV]
     140              : !> \author Joost VandeVondele
     141              : ! **************************************************************************************************
     142        17519 :    SUBROUTINE cp_fm_cholesky_invert(matrix, n, info_out)
     143              :       TYPE(cp_fm_type), INTENT(IN)           :: matrix
     144              :       INTEGER, INTENT(in), OPTIONAL             :: n
     145              :       INTEGER, INTENT(OUT), OPTIONAL            :: info_out
     146              : 
     147              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_cholesky_invert'
     148        17519 :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: a
     149        17519 :       REAL(KIND=sp), DIMENSION(:, :), POINTER  :: a_sp
     150              :       INTEGER                                   :: info, handle
     151              :       INTEGER                                   :: my_n
     152              : #if defined(__parallel)
     153              :       INTEGER, DIMENSION(9)                     :: desca
     154              : #endif
     155              : 
     156        17519 :       CALL timeset(routineN, handle)
     157              : 
     158              :       my_n = MIN(matrix%matrix_struct%nrow_global, &
     159        17519 :                  matrix%matrix_struct%ncol_global)
     160        17519 :       IF (PRESENT(n)) THEN
     161            0 :          CPASSERT(n <= my_n)
     162            0 :          my_n = n
     163              :       END IF
     164              : 
     165        17519 :       a => matrix%local_data
     166        17519 :       a_sp => matrix%local_data_sp
     167              : 
     168              : #if defined(__parallel)
     169              : 
     170       175190 :       desca(:) = matrix%matrix_struct%descriptor(:)
     171              : 
     172              : #if defined(__DLAF)
     173              :       IF (cholesky_type == FM_CHOLESKY_TYPE_DLAF .AND. matrix%matrix_struct%nrow_global >= dlaf_cholesky_n_min) THEN
     174              :          ! Initialize DLA-Future on-demand; if already initialized, does nothing
     175              :          CALL cp_dlaf_initialize()
     176              : 
     177              :          ! Create DLAF grid from BLACS context; if already present, does nothing
     178              :          CALL cp_dlaf_create_grid(matrix%matrix_struct%context%get_handle())
     179              : 
     180              :          IF (matrix%use_sp) THEN
     181              :             CALL cp_pspotri_dlaf('U', my_n, a_sp(:, :), 1, 1, desca, info)
     182              :          ELSE
     183              :             CALL cp_pdpotri_dlaf('U', my_n, a(:, :), 1, 1, desca, info)
     184              :          END IF
     185              :       ELSE
     186              : #endif
     187        17519 :          IF (matrix%use_sp) THEN
     188            0 :             CALL pspotri('U', my_n, a_sp(1, 1), 1, 1, desca, info)
     189              :          ELSE
     190        17519 :             CALL pdpotri('U', my_n, a(1, 1), 1, 1, desca, info)
     191              :          END IF
     192              : #if defined(__DLAF)
     193              :       END IF
     194              : #endif
     195              : 
     196              : #else
     197              : 
     198              :       IF (matrix%use_sp) THEN
     199              :          CALL spotri('U', my_n, a_sp(1, 1), SIZE(a_sp, 1), info)
     200              :       ELSE
     201              :          CALL dpotri('U', my_n, a(1, 1), SIZE(a, 1), info)
     202              :       END IF
     203              : 
     204              : #endif
     205              : 
     206        17519 :       IF (PRESENT(info_out)) THEN
     207            0 :          info_out = info
     208              :       ELSE
     209        17519 :          IF (info /= 0) &
     210            0 :             CPABORT("Cholesky invert failed: the matrix is not positive definite or ill-conditioned.")
     211              :       END IF
     212              : 
     213        17519 :       CALL timestop(handle)
     214              : 
     215        17519 :    END SUBROUTINE cp_fm_cholesky_invert
     216              : 
     217              : ! **************************************************************************************************
     218              : !> \brief reduce a matrix pencil A,B to normal form
     219              : !>      B has to be cholesky decomposed with  cp_fm_cholesky_decompose
     220              : !>      before calling this routine
     221              : !>      A,B -> inv(U^T)*A*inv(U),1
     222              : !>      (AX=BX -> inv(U^T)*A*inv(U)*U*X=U*X hence evecs U*X)
     223              : !> \param matrix the symmetric matrix A
     224              : !> \param matrixb the cholesky decomposition of matrix B
     225              : !> \param itype ...
     226              : !> \par History
     227              : !>      05.2002 created [JVdV]
     228              : !> \author Joost VandeVondele
     229              : ! **************************************************************************************************
     230         4078 :    SUBROUTINE cp_fm_cholesky_reduce(matrix, matrixb, itype)
     231              :       TYPE(cp_fm_type), INTENT(IN)     :: matrix, matrixb
     232              :       INTEGER, OPTIONAL                   :: itype
     233              : 
     234              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_cholesky_reduce'
     235         4078 :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: a, b
     236              :       INTEGER                                   :: info, handle
     237              :       INTEGER                                   :: n, my_itype
     238              : #if defined(__parallel)
     239              :       REAL(KIND=dp)                             :: scale
     240              :       INTEGER, DIMENSION(9)                     :: desca, descb
     241              : #endif
     242              : 
     243         4078 :       CALL timeset(routineN, handle)
     244              : 
     245         4078 :       n = matrix%matrix_struct%nrow_global
     246              : 
     247         4078 :       my_itype = 1
     248         4078 :       IF (PRESENT(itype)) my_itype = itype
     249              : 
     250         4078 :       a => matrix%local_data
     251         4078 :       b => matrixb%local_data
     252              : 
     253              : #if defined(__parallel)
     254              : 
     255        40780 :       desca(:) = matrix%matrix_struct%descriptor(:)
     256        40780 :       descb(:) = matrixb%matrix_struct%descriptor(:)
     257              : 
     258         4078 :       CALL pdsygst(my_itype, 'U', n, a(1, 1), 1, 1, desca, b(1, 1), 1, 1, descb, scale, info)
     259              : 
     260              :       ! this is supposed to be one in current version of lapack
     261              :       ! if not, eigenvalues have to be scaled by this number
     262         4078 :       IF (scale /= 1.0_dp) &
     263            0 :          CPABORT("scale not equal 1 (scale="//cp_to_string(scale)//")")
     264              : #else
     265              : 
     266              :       CALL dsygst(my_itype, 'U', n, a(1, 1), n, b(1, 1), n, info)
     267              : 
     268              : #endif
     269              : 
     270         4078 :       CPASSERT(info == 0)
     271              : 
     272         4078 :       CALL timestop(handle)
     273              : 
     274         4078 :    END SUBROUTINE cp_fm_cholesky_reduce
     275              : 
     276              : !
     277              : ! op can be "SOLVE" (out = U^-1 * in ) or "MULTIPLY"   (out = U * in )
     278              : ! pos can be "LEFT" or "RIGHT" (U at the left or at the right)
     279              : !
     280              : ! DEPRECATED, see cp_fm_basic_linalg:cp_fm_triangular_multiply
     281              : !
     282              : ! **************************************************************************************************
     283              : !> \brief ...
     284              : !> \param matrix ...
     285              : !> \param neig ...
     286              : !> \param matrixb ...
     287              : !> \param matrixout ...
     288              : !> \param op ...
     289              : !> \param pos ...
     290              : !> \param transa ...
     291              : ! **************************************************************************************************
     292       256623 :    SUBROUTINE cp_fm_cholesky_restore(matrix, neig, matrixb, matrixout, op, pos, transa)
     293              :       TYPE(cp_fm_type), INTENT(IN)         :: matrix, matrixb, matrixout
     294              :       INTEGER, INTENT(IN)                     :: neig
     295              :       CHARACTER(LEN=*), INTENT(IN)            :: op
     296              :       CHARACTER(LEN=*), INTENT(IN), OPTIONAL  :: pos
     297              :       CHARACTER(LEN=*), INTENT(IN), OPTIONAL  :: transa
     298              : 
     299              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_cholesky_restore'
     300       256623 :       REAL(KIND=dp), DIMENSION(:, :), POINTER         :: a, b, out
     301       256623 :       REAL(KIND=sp), DIMENSION(:, :), POINTER         :: a_sp, b_sp, out_sp
     302              :       INTEGER                                   :: itype, handle
     303              :       INTEGER                                   :: n
     304              :       REAL(KIND=dp)                           :: alpha
     305              :       INTEGER                                   :: myprow, mypcol
     306              :       TYPE(cp_blacs_env_type), POINTER          :: context
     307              :       CHARACTER                                 :: chol_pos, chol_transa
     308              : #if defined(__parallel)
     309              :       INTEGER                                   :: i
     310              :       INTEGER, DIMENSION(9)                     :: desca, descb, descout
     311              : #endif
     312              : 
     313       256623 :       CALL timeset(routineN, handle)
     314              : 
     315       256623 :       context => matrix%matrix_struct%context
     316       256623 :       myprow = context%mepos(1)
     317       256623 :       mypcol = context%mepos(2)
     318       256623 :       n = matrix%matrix_struct%nrow_global
     319       256623 :       itype = 1
     320       256623 :       IF (op /= "SOLVE" .AND. op /= "MULTIPLY") &
     321            0 :          CPABORT("wrong argument op")
     322              : 
     323       256623 :       IF (PRESENT(pos)) THEN
     324        84155 :          SELECT CASE (pos)
     325              :          CASE ("LEFT")
     326        84155 :             chol_pos = 'L'
     327              :          CASE ("RIGHT")
     328        83187 :             chol_pos = 'R'
     329              :          CASE DEFAULT
     330       167342 :             CPABORT("wrong argument pos")
     331              :          END SELECT
     332              :       ELSE
     333        89281 :          chol_pos = 'L'
     334              :       END IF
     335              : 
     336       256623 :       chol_transa = 'N'
     337       256623 :       IF (PRESENT(transa)) chol_transa = transa
     338              : 
     339       256623 :       IF ((matrix%use_sp .NEQV. matrixb%use_sp) .OR. (matrix%use_sp .NEQV. matrixout%use_sp)) &
     340            0 :          CPABORT("not the same precision")
     341              : 
     342              :       ! notice b is the cholesky guy
     343       256623 :       a => matrix%local_data
     344       256623 :       b => matrixb%local_data
     345       256623 :       out => matrixout%local_data
     346       256623 :       a_sp => matrix%local_data_sp
     347       256623 :       b_sp => matrixb%local_data_sp
     348       256623 :       out_sp => matrixout%local_data_sp
     349              : 
     350              : #if defined(__parallel)
     351              : 
     352      2566230 :       desca(:) = matrix%matrix_struct%descriptor(:)
     353      2566230 :       descb(:) = matrixb%matrix_struct%descriptor(:)
     354      2566230 :       descout(:) = matrixout%matrix_struct%descriptor(:)
     355       256623 :       alpha = 1.0_dp
     356      4846219 :       DO i = 1, neig
     357      4846219 :          IF (matrix%use_sp) THEN
     358            0 :             CALL pscopy(n, a_sp(1, 1), 1, i, desca, 1, out_sp(1, 1), 1, i, descout, 1)
     359              :          ELSE
     360      4589596 :             CALL pdcopy(n, a(1, 1), 1, i, desca, 1, out(1, 1), 1, i, descout, 1)
     361              :          END IF
     362              :       END DO
     363       256623 :       IF (op .EQ. "SOLVE") THEN
     364       255193 :          IF (matrix%use_sp) THEN
     365              :             CALL pstrsm(chol_pos, 'U', chol_transa, 'N', n, neig, REAL(alpha, sp), b_sp(1, 1), 1, 1, descb, &
     366            0 :                         out_sp(1, 1), 1, 1, descout)
     367              :          ELSE
     368       255193 :             CALL pdtrsm(chol_pos, 'U', chol_transa, 'N', n, neig, alpha, b(1, 1), 1, 1, descb, out(1, 1), 1, 1, descout)
     369              :          END IF
     370              :       ELSE
     371         1430 :          IF (matrix%use_sp) THEN
     372              :             CALL pstrmm(chol_pos, 'U', chol_transa, 'N', n, neig, REAL(alpha, sp), b_sp(1, 1), 1, 1, descb, &
     373            0 :                         out_sp(1, 1), 1, 1, descout)
     374              :          ELSE
     375         1430 :             CALL pdtrmm(chol_pos, 'U', chol_transa, 'N', n, neig, alpha, b(1, 1), 1, 1, descb, out(1, 1), 1, 1, descout)
     376              :          END IF
     377              :       END IF
     378              : #else
     379              : 
     380              :       alpha = 1.0_dp
     381              :       IF (matrix%use_sp) THEN
     382              :          CALL scopy(neig*n, a_sp(1, 1), 1, out_sp(1, 1), 1)
     383              :       ELSE
     384              :          CALL dcopy(neig*n, a(1, 1), 1, out(1, 1), 1)
     385              :       END IF
     386              :       IF (op .EQ. "SOLVE") THEN
     387              :          IF (matrix%use_sp) THEN
     388              :             CALL strsm(chol_pos, 'U', chol_transa, 'N', n, neig, REAL(alpha, sp), b_sp(1, 1), SIZE(b_sp, 1), out_sp(1, 1), n)
     389              :          ELSE
     390              :             CALL dtrsm(chol_pos, 'U', chol_transa, 'N', n, neig, alpha, b(1, 1), SIZE(b, 1), out(1, 1), n)
     391              :          END IF
     392              :       ELSE
     393              :          IF (matrix%use_sp) THEN
     394              :             CALL strmm(chol_pos, 'U', chol_transa, 'N', n, neig, REAL(alpha, sp), b_sp(1, 1), n, out_sp(1, 1), n)
     395              :          ELSE
     396              :             CALL dtrmm(chol_pos, 'U', chol_transa, 'N', n, neig, alpha, b(1, 1), n, out(1, 1), n)
     397              :          END IF
     398              :       END IF
     399              : 
     400              : #endif
     401              : 
     402       256623 :       CALL timestop(handle)
     403              : 
     404       256623 :    END SUBROUTINE cp_fm_cholesky_restore
     405              : 
     406              : END MODULE cp_fm_cholesky
        

Generated by: LCOV version 2.0-1