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

Generated by: LCOV version 2.0-1