LCOV - code coverage report
Current view: top level - src/fm - cp_fm_cholesky.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:40fb615) Lines: 72.5 % 91 66
Test Date: 2026-07-05 06:50:58 Functions: 80.0 % 5 4

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

Generated by: LCOV version 2.0-1