LCOV - code coverage report
Current view: top level - src - pao_ml.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:71c3ab0) Lines: 97.5 % 236 230
Test Date: 2026-07-25 06:35:44 Functions: 75.0 % 16 12

            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 Main module for PAO Machine Learning
      10              : !> \author Ole Schuett
      11              : ! **************************************************************************************************
      12              : MODULE pao_ml
      13              :    USE atomic_kind_types,               ONLY: atomic_kind_type,&
      14              :                                               get_atomic_kind
      15              :    USE basis_set_types,                 ONLY: gto_basis_set_type
      16              :    USE cell_methods,                    ONLY: cell_create
      17              :    USE cell_types,                      ONLY: cell_type
      18              :    USE cp_dbcsr_api,                    ONLY: dbcsr_iterator_blocks_left,&
      19              :                                               dbcsr_iterator_next_block,&
      20              :                                               dbcsr_iterator_start,&
      21              :                                               dbcsr_iterator_stop,&
      22              :                                               dbcsr_iterator_type,&
      23              :                                               dbcsr_type
      24              :    USE kinds,                           ONLY: default_path_length,&
      25              :                                               default_string_length,&
      26              :                                               dp
      27              :    USE machine,                         ONLY: m_flush
      28              :    USE message_passing,                 ONLY: mp_para_env_type
      29              :    USE pao_input,                       ONLY: id2str,&
      30              :                                               pao_ml_gp,&
      31              :                                               pao_ml_lazy,&
      32              :                                               pao_ml_nn,&
      33              :                                               pao_ml_prior_mean,&
      34              :                                               pao_ml_prior_zero,&
      35              :                                               pao_rotinv_param
      36              :    USE pao_io,                          ONLY: pao_ioblock_type,&
      37              :                                               pao_iokind_type,&
      38              :                                               pao_kinds_ensure_equal,&
      39              :                                               pao_read_raw
      40              :    USE pao_ml_descriptor,               ONLY: pao_ml_calc_descriptor
      41              :    USE pao_ml_gaussprocess,             ONLY: pao_ml_gp_gradient,&
      42              :                                               pao_ml_gp_predict,&
      43              :                                               pao_ml_gp_train
      44              :    USE pao_ml_neuralnet,                ONLY: pao_ml_nn_gradient,&
      45              :                                               pao_ml_nn_predict,&
      46              :                                               pao_ml_nn_train
      47              :    USE pao_types,                       ONLY: pao_env_type,&
      48              :                                               training_matrix_type
      49              :    USE particle_types,                  ONLY: particle_type
      50              :    USE qs_environment_types,            ONLY: get_qs_env,&
      51              :                                               qs_environment_type
      52              :    USE qs_kind_types,                   ONLY: get_qs_kind,&
      53              :                                               qs_kind_type
      54              : #include "./base/base_uses.f90"
      55              : 
      56              :    IMPLICIT NONE
      57              : 
      58              :    PRIVATE
      59              : 
      60              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'pao_ml'
      61              : 
      62              :    PUBLIC :: pao_ml_init, pao_ml_predict, pao_ml_forces
      63              : 
      64              :    ! linked list used to group training points by kind
      65              :    TYPE training_point_type
      66              :       TYPE(training_point_type), POINTER       :: next => Null()
      67              :       REAL(dp), DIMENSION(:), ALLOCATABLE      :: input
      68              :       REAL(dp), DIMENSION(:), ALLOCATABLE      :: output
      69              :    END TYPE training_point_type
      70              : 
      71              :    TYPE training_list_type
      72              :       CHARACTER(LEN=default_string_length)     :: kindname = ""
      73              :       TYPE(training_point_type), POINTER       :: head => Null()
      74              :       INTEGER                                  :: npoints = 0
      75              :    END TYPE training_list_type
      76              : 
      77              : CONTAINS
      78              : 
      79              : ! **************************************************************************************************
      80              : !> \brief Initializes the learning machinery
      81              : !> \param pao ...
      82              : !> \param qs_env ...
      83              : ! **************************************************************************************************
      84           98 :    SUBROUTINE pao_ml_init(pao, qs_env)
      85              :       TYPE(pao_env_type), POINTER                        :: pao
      86              :       TYPE(qs_environment_type), POINTER                 :: qs_env
      87              : 
      88              :       INTEGER                                            :: i
      89           98 :       TYPE(atomic_kind_type), DIMENSION(:), POINTER      :: atomic_kind_set
      90              :       TYPE(mp_para_env_type), POINTER                    :: para_env
      91              :       TYPE(training_list_type), ALLOCATABLE, &
      92           98 :          DIMENSION(:)                                    :: training_lists
      93              : 
      94           98 :       IF (SIZE(pao%ml_training_set) == 0) RETURN
      95              : 
      96           18 :       IF (pao%iw > 0) WRITE (pao%iw, *) 'PAO|ML| Initializing maschine learning...'
      97              : 
      98           18 :       IF (pao%parameterization /= pao_rotinv_param) THEN
      99            0 :          CPABORT("PAO maschine learning requires ROTINV parametrization")
     100              :       END IF
     101              : 
     102           18 :       CALL get_qs_env(qs_env, para_env=para_env, atomic_kind_set=atomic_kind_set)
     103              : 
     104              :       ! create training-set data-structure
     105           74 :       ALLOCATE (training_lists(SIZE(atomic_kind_set)))
     106           38 :       DO i = 1, SIZE(training_lists)
     107           38 :          CALL get_atomic_kind(atomic_kind_set(i), name=training_lists(i)%kindname)
     108              :       END DO
     109              : 
     110              :       ! parses training files, calculates descriptors and stores all training-points as linked lists
     111           52 :       DO i = 1, SIZE(pao%ml_training_set)
     112           52 :          CALL add_to_training_list(pao, qs_env, training_lists, filename=pao%ml_training_set(i)%fn)
     113              :       END DO
     114              : 
     115              :       ! ensure there there are training points for all kinds that use pao
     116           18 :       CALL sanity_check(qs_env, training_lists)
     117              : 
     118              :       ! turns linked lists into matrices and syncs them across ranks
     119           18 :       CALL training_list2matrix(training_lists, pao%ml_training_matrices, para_env)
     120              : 
     121              :       ! calculate and subtract prior
     122           18 :       CALL pao_ml_substract_prior(pao%ml_prior, pao%ml_training_matrices)
     123              : 
     124              :       ! print some statistics about the training set and dump it upon request
     125           18 :       CALL pao_ml_print(pao, pao%ml_training_matrices)
     126              : 
     127              :       ! use training-set to train model
     128           18 :       CALL pao_ml_train(pao)
     129              : 
     130          116 :    END SUBROUTINE pao_ml_init
     131              : 
     132              : ! **************************************************************************************************
     133              : !> \brief Reads the given file and adds its training points to linked lists.
     134              : !> \param pao ...
     135              : !> \param qs_env ...
     136              : !> \param training_lists ...
     137              : !> \param filename ...
     138              : ! **************************************************************************************************
     139           34 :    SUBROUTINE add_to_training_list(pao, qs_env, training_lists, filename)
     140              :       TYPE(pao_env_type), POINTER                        :: pao
     141              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     142              :       TYPE(training_list_type), DIMENSION(:)             :: training_lists
     143              :       CHARACTER(LEN=default_path_length)                 :: filename
     144              : 
     145              :       CHARACTER(LEN=default_string_length)               :: param
     146              :       INTEGER                                            :: iatom, ikind, natoms, nkinds, nparams
     147           34 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: atom2kind, kindsmap
     148              :       INTEGER, DIMENSION(2)                              :: ml_range
     149           34 :       REAL(dp), ALLOCATABLE, DIMENSION(:, :)             :: hmat, positions
     150           34 :       TYPE(atomic_kind_type), DIMENSION(:), POINTER      :: atomic_kind_set
     151              :       TYPE(cell_type), POINTER                           :: cell
     152              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     153           34 :       TYPE(pao_ioblock_type), ALLOCATABLE, DIMENSION(:)  :: xblocks
     154           34 :       TYPE(pao_iokind_type), ALLOCATABLE, DIMENSION(:)   :: kinds
     155           34 :       TYPE(particle_type), DIMENSION(:), POINTER         :: my_particle_set
     156           34 :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     157              :       TYPE(training_point_type), POINTER                 :: new_point
     158              : 
     159           34 :       NULLIFY (new_point, cell)
     160              : 
     161           17 :       IF (pao%iw > 0) WRITE (pao%iw, '(A,A)') " PAO|ML| Reading training frame from file: ", TRIM(filename)
     162              : 
     163           34 :       CALL get_qs_env(qs_env, para_env=para_env)
     164              : 
     165              :       ! parse training data on first rank
     166           34 :       IF (para_env%is_source()) THEN
     167           17 :          CALL pao_read_raw(filename, param, hmat, kinds, atom2kind, positions, xblocks, ml_range)
     168              : 
     169              :          ! check parametrization
     170           17 :          IF (TRIM(param) /= TRIM(ADJUSTL(id2str(pao%parameterization)))) THEN
     171           17 :             CPABORT("Restart PAO parametrization does not match")
     172              :          END IF
     173              : 
     174              :          ! map read-in kinds onto kinds of this run
     175           17 :          CALL match_kinds(pao, qs_env, kinds, kindsmap)
     176           17 :          nkinds = SIZE(kindsmap)
     177           17 :          natoms = SIZE(positions, 1)
     178              :       END IF
     179              : 
     180              :       ! broadcast parsed raw training data
     181           34 :       CALL para_env%bcast(nkinds)
     182           34 :       CALL para_env%bcast(natoms)
     183           34 :       IF (.NOT. para_env%is_source()) THEN
     184           17 :          ALLOCATE (hmat(3, 3))
     185           51 :          ALLOCATE (kindsmap(nkinds))
     186           51 :          ALLOCATE (positions(natoms, 3))
     187           51 :          ALLOCATE (atom2kind(natoms))
     188              :       END IF
     189           34 :       CALL para_env%bcast(hmat)
     190           34 :       CALL para_env%bcast(kindsmap)
     191           34 :       CALL para_env%bcast(atom2kind)
     192           34 :       CALL para_env%bcast(positions)
     193           34 :       CALL para_env%bcast(ml_range)
     194              : 
     195           34 :       IF (ml_range(1) /= 1 .OR. ml_range(2) /= natoms) THEN
     196            0 :          CPWARN("Skipping some atoms for PAO-ML training.")
     197              :       END IF
     198              : 
     199              :       ! create cell from read-in h-matrix
     200           34 :       CALL cell_create(cell, hmat)
     201              : 
     202              :       ! create a particle_set based on read-in positions and refere to kinds of this run
     203           34 :       CALL get_qs_env(qs_env, atomic_kind_set=atomic_kind_set, qs_kind_set=qs_kind_set)
     204          476 :       ALLOCATE (my_particle_set(natoms))
     205          102 :       DO iatom = 1, natoms
     206           68 :          ikind = kindsmap(atom2kind(iatom))
     207           68 :          my_particle_set(iatom)%atomic_kind => atomic_kind_set(ikind)
     208          306 :          my_particle_set(iatom)%r = positions(iatom, :)
     209              :       END DO
     210              : 
     211              :       ! fill linked list with training points
     212              :       ! Afterwards all ranks will have lists with the same number of entries,
     213              :       ! however the input and output arrays will only be allocated on one rank per entry.
     214              :       ! We farm out the expensive calculation of the descriptor across ranks.
     215          102 :       DO iatom = 1, natoms
     216           68 :          IF (iatom < ml_range(1) .OR. ml_range(2) < iatom) CYCLE
     217           68 :          ALLOCATE (new_point)
     218              : 
     219              :          ! training-point input, calculate descriptor only on one rank
     220           68 :          IF (MOD(iatom - 1, para_env%num_pe) == para_env%mepos) THEN
     221              :             CALL pao_ml_calc_descriptor(pao, &
     222              :                                         my_particle_set, &
     223              :                                         qs_kind_set, &
     224              :                                         cell, &
     225              :                                         iatom=iatom, &
     226           34 :                                         descriptor=new_point%input)
     227              :          END IF
     228              : 
     229              :          ! copy training-point output on first rank
     230           68 :          IF (para_env%is_source()) THEN
     231           34 :             nparams = SIZE(xblocks(iatom)%p, 1)
     232          102 :             ALLOCATE (new_point%output(nparams))
     233          272 :             new_point%output(:) = xblocks(iatom)%p(:, 1)
     234              :          END IF
     235              : 
     236              :          ! add to linked list
     237           68 :          ikind = kindsmap(atom2kind(iatom))
     238           68 :          training_lists(ikind)%npoints = training_lists(ikind)%npoints + 1
     239           68 :          new_point%next => training_lists(ikind)%head
     240          102 :          training_lists(ikind)%head => new_point
     241              :       END DO
     242              : 
     243           34 :       DEALLOCATE (cell, my_particle_set, hmat, kindsmap, positions, atom2kind)
     244              : 
     245          119 :    END SUBROUTINE add_to_training_list
     246              : 
     247              : ! **************************************************************************************************
     248              : !> \brief Make read-in kinds on to atomic-kinds of this run
     249              : !> \param pao ...
     250              : !> \param qs_env ...
     251              : !> \param kinds ...
     252              : !> \param kindsmap ...
     253              : ! **************************************************************************************************
     254           17 :    SUBROUTINE match_kinds(pao, qs_env, kinds, kindsmap)
     255              :       TYPE(pao_env_type), POINTER                        :: pao
     256              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     257              :       TYPE(pao_iokind_type), DIMENSION(:)                :: kinds
     258              :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: kindsmap
     259              : 
     260              :       CHARACTER(LEN=default_string_length)               :: name
     261              :       INTEGER                                            :: ikind, jkind
     262           17 :       TYPE(atomic_kind_type), DIMENSION(:), POINTER      :: atomic_kind_set
     263              : 
     264           17 :       CALL get_qs_env(qs_env, atomic_kind_set=atomic_kind_set)
     265              : 
     266           17 :       CPASSERT(.NOT. ALLOCATED(kindsmap))
     267           51 :       ALLOCATE (kindsmap(SIZE(kinds)))
     268           34 :       kindsmap(:) = -1
     269              : 
     270           34 :       DO ikind = 1, SIZE(kinds)
     271           35 :          DO jkind = 1, SIZE(atomic_kind_set)
     272           18 :             CALL get_atomic_kind(atomic_kind_set(jkind), name=name)
     273              :             ! match kinds via their name
     274           18 :             IF (TRIM(kinds(ikind)%name) == TRIM(name)) THEN
     275           17 :                CALL pao_kinds_ensure_equal(pao, qs_env, jkind, kinds(ikind))
     276           17 :                kindsmap(ikind) = jkind
     277           17 :                EXIT
     278              :             END IF
     279              :          END DO
     280              :       END DO
     281              : 
     282           34 :       IF (ANY(kindsmap < 1)) THEN
     283            0 :          CPABORT("PAO: Could not match all kinds from training set")
     284              :       END IF
     285           17 :    END SUBROUTINE match_kinds
     286              : 
     287              : ! **************************************************************************************************
     288              : !> \brief Checks that there is at least one training point per pao-enabled kind
     289              : !> \param qs_env ...
     290              : !> \param training_lists ...
     291              : ! **************************************************************************************************
     292           18 :    SUBROUTINE sanity_check(qs_env, training_lists)
     293              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     294              :       TYPE(training_list_type), DIMENSION(:), TARGET     :: training_lists
     295              : 
     296              :       INTEGER                                            :: ikind, pao_basis_size
     297              :       TYPE(gto_basis_set_type), POINTER                  :: basis_set
     298           18 :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     299              :       TYPE(training_list_type), POINTER                  :: training_list
     300              : 
     301           18 :       CALL get_qs_env(qs_env, qs_kind_set=qs_kind_set)
     302              : 
     303           38 :       DO ikind = 1, SIZE(training_lists)
     304           20 :          training_list => training_lists(ikind)
     305           20 :          IF (training_list%npoints > 0) CYCLE ! it's ok
     306            2 :          CALL get_qs_kind(qs_kind_set(ikind), basis_set=basis_set, pao_basis_size=pao_basis_size)
     307           20 :          IF (pao_basis_size /= basis_set%nsgf) THEN
     308              :             ! if this kind has pao enabled...
     309            0 :             CPABORT("Found no training-points for kind: "//TRIM(training_list%kindname))
     310              :          END IF
     311              :       END DO
     312              : 
     313           18 :    END SUBROUTINE sanity_check
     314              : 
     315              : ! **************************************************************************************************
     316              : !> \brief Turns the linked lists of training points into matrices
     317              : !> \param training_lists ...
     318              : !> \param training_matrices ...
     319              : !> \param para_env ...
     320              : ! **************************************************************************************************
     321           18 :    SUBROUTINE training_list2matrix(training_lists, training_matrices, para_env)
     322              :       TYPE(training_list_type), ALLOCATABLE, &
     323              :          DIMENSION(:), TARGET                            :: training_lists
     324              :       TYPE(training_matrix_type), ALLOCATABLE, &
     325              :          DIMENSION(:), TARGET                            :: training_matrices
     326              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     327              : 
     328              :       INTEGER                                            :: i, ikind, inp_size, ninputs, noutputs, &
     329              :                                                             npoints, out_size
     330              :       TYPE(training_list_type), POINTER                  :: training_list
     331              :       TYPE(training_matrix_type), POINTER                :: training_matrix
     332              :       TYPE(training_point_type), POINTER                 :: cur_point, prev_point
     333              : 
     334           18 :       CPASSERT(ALLOCATED(training_lists) .AND. .NOT. ALLOCATED(training_matrices))
     335              : 
     336           74 :       ALLOCATE (training_matrices(SIZE(training_lists)))
     337              : 
     338           38 :       DO ikind = 1, SIZE(training_lists)
     339           20 :          training_list => training_lists(ikind)
     340           20 :          training_matrix => training_matrices(ikind)
     341           20 :          training_matrix%kindname = training_list%kindname ! copy kindname
     342           20 :          npoints = training_list%npoints ! number of points
     343           20 :          IF (npoints == 0) THEN
     344            2 :             ALLOCATE (training_matrix%inputs(0, 0))
     345            2 :             ALLOCATE (training_matrix%outputs(0, 0))
     346            2 :             CYCLE
     347              :          END IF
     348              : 
     349              :          ! figure out size of input and output
     350           18 :          inp_size = 0; out_size = 0
     351           18 :          IF (ALLOCATED(training_list%head%input)) THEN
     352            9 :             inp_size = SIZE(training_list%head%input)
     353              :          END IF
     354           18 :          IF (ALLOCATED(training_list%head%output)) THEN
     355            9 :             out_size = SIZE(training_list%head%output)
     356              :          END IF
     357           18 :          CALL para_env%sum(inp_size)
     358           18 :          CALL para_env%sum(out_size)
     359              : 
     360              :          ! allocate matices to hold all training points
     361           72 :          ALLOCATE (training_matrix%inputs(inp_size, npoints))
     362           72 :          ALLOCATE (training_matrix%outputs(out_size, npoints))
     363         3258 :          training_matrix%inputs(:, :) = 0.0_dp
     364          562 :          training_matrix%outputs(:, :) = 0.0_dp
     365              : 
     366              :          ! loop over all training points, consume linked-list in the process
     367           18 :          ninputs = 0; noutputs = 0
     368           18 :          cur_point => training_list%head
     369           18 :          NULLIFY (training_list%head)
     370           86 :          DO i = 1, npoints
     371           68 :             IF (ALLOCATED(cur_point%input)) THEN
     372         3240 :                training_matrix%inputs(:, i) = cur_point%input(:)
     373           34 :                ninputs = ninputs + 1
     374              :             END IF
     375           68 :             IF (ALLOCATED(cur_point%output)) THEN
     376          544 :                training_matrix%outputs(:, i) = cur_point%output(:)
     377           34 :                noutputs = noutputs + 1
     378              :             END IF
     379              :             ! advance to next entry and deallocate the current one
     380           68 :             prev_point => cur_point
     381           68 :             cur_point => cur_point%next
     382           86 :             DEALLOCATE (prev_point)
     383              :          END DO
     384           18 :          training_list%npoints = 0 ! list is now empty
     385              : 
     386              :          ! sync training_matrix across ranks
     387           18 :          CALL para_env%sum(training_matrix%inputs)
     388           18 :          CALL para_env%sum(training_matrix%outputs)
     389              : 
     390              :          ! sanity check
     391           18 :          CALL para_env%sum(noutputs)
     392           18 :          CALL para_env%sum(ninputs)
     393           36 :          CPASSERT(noutputs == npoints .AND. ninputs == npoints)
     394              :       END DO
     395              : 
     396           18 :    END SUBROUTINE training_list2matrix
     397              : 
     398              : ! **************************************************************************************************
     399              : !> \brief TODO
     400              : !> \param ml_prior ...
     401              : !> \param training_matrices ...
     402              : ! **************************************************************************************************
     403           18 :    SUBROUTINE pao_ml_substract_prior(ml_prior, training_matrices)
     404              :       INTEGER, INTENT(IN)                                :: ml_prior
     405              :       TYPE(training_matrix_type), DIMENSION(:), TARGET   :: training_matrices
     406              : 
     407              :       INTEGER                                            :: i, ikind, npoints, out_size
     408              :       TYPE(training_matrix_type), POINTER                :: training_matrix
     409              : 
     410           38 :       DO ikind = 1, SIZE(training_matrices)
     411           20 :          training_matrix => training_matrices(ikind)
     412           20 :          out_size = SIZE(training_matrix%outputs, 1)
     413           20 :          npoints = SIZE(training_matrix%outputs, 2)
     414           20 :          IF (npoints == 0) CYCLE
     415           54 :          ALLOCATE (training_matrix%prior(out_size))
     416              : 
     417              :          ! calculate prior
     418           18 :          SELECT CASE (ml_prior)
     419              :          CASE (pao_ml_prior_zero)
     420           96 :             training_matrix%prior(:) = 0.0_dp
     421              :          CASE (pao_ml_prior_mean)
     422          188 :             training_matrix%prior(:) = SUM(training_matrix%outputs, 2)/REAL(npoints, dp)
     423              :          CASE DEFAULT
     424           18 :             CPABORT("PAO: unknown prior")
     425              :          END SELECT
     426              : 
     427              :          ! subtract prior from all training points
     428          104 :          DO i = 1, npoints
     429          564 :             training_matrix%outputs(:, i) = training_matrix%outputs(:, i) - training_matrix%prior
     430              :          END DO
     431              :       END DO
     432              : 
     433           18 :    END SUBROUTINE pao_ml_substract_prior
     434              : 
     435              : ! **************************************************************************************************
     436              : !> \brief Print some statistics about the training set and dump it upon request
     437              : !> \param pao ...
     438              : !> \param training_matrices ...
     439              : ! **************************************************************************************************
     440           18 :    SUBROUTINE pao_ml_print(pao, training_matrices)
     441              :       TYPE(pao_env_type), POINTER                        :: pao
     442              :       TYPE(training_matrix_type), DIMENSION(:), TARGET   :: training_matrices
     443              : 
     444              :       INTEGER                                            :: i, ikind, N, npoints
     445              :       TYPE(training_matrix_type), POINTER                :: training_matrix
     446              : 
     447              :       ! dump training data
     448           18 :       IF (pao%iw_mldata > 0) THEN
     449            2 :          DO ikind = 1, SIZE(training_matrices)
     450            1 :             training_matrix => training_matrices(ikind)
     451            1 :             npoints = SIZE(training_matrix%outputs, 2)
     452            6 :             DO i = 1, npoints
     453            4 :                WRITE (pao%iw_mldata, *) "PAO|ML| training-point kind: ", TRIM(training_matrix%kindname), &
     454            8 :                   " point:", i, " in:", training_matrix%inputs(:, i), &
     455           37 :                   " out:", training_matrix%outputs(:, i)
     456              :             END DO
     457              :          END DO
     458            1 :          CALL m_flush(pao%iw_mldata)
     459              :       END IF
     460              : 
     461              :       ! print stats
     462           18 :       IF (pao%iw > 0) THEN
     463           19 :          DO ikind = 1, SIZE(training_matrices)
     464           10 :             training_matrix => training_matrices(ikind)
     465           30 :             N = SIZE(training_matrix%inputs)
     466           10 :             IF (N == 0) CYCLE
     467              :             WRITE (pao%iw, "(A,I3,A,E10.1,1X,E10.1,1X,E10.1)") " PAO|ML| Descriptor for kind: "// &
     468            9 :                TRIM(training_matrix%kindname)//" size: ", &
     469            9 :                SIZE(training_matrix%inputs, 1), " min/mean/max: ", &
     470         1629 :                MINVAL(training_matrix%inputs), &
     471         1629 :                SUM(training_matrix%inputs)/REAL(N, dp), &
     472         1648 :                MAXVAL(training_matrix%inputs)
     473              :          END DO
     474              :       END IF
     475              : 
     476           18 :    END SUBROUTINE pao_ml_print
     477              : 
     478              : ! **************************************************************************************************
     479              : !> \brief Calls the actual learning algorthim to traing on the given matrices
     480              : !> \param pao ...
     481              : ! **************************************************************************************************
     482           18 :    SUBROUTINE pao_ml_train(pao)
     483              :       TYPE(pao_env_type), POINTER                        :: pao
     484              : 
     485              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'pao_ml_train'
     486              : 
     487              :       INTEGER                                            :: handle
     488              : 
     489           18 :       CALL timeset(routineN, handle)
     490              : 
     491           28 :       SELECT CASE (pao%ml_method)
     492              :       CASE (pao_ml_gp)
     493           10 :          CALL pao_ml_gp_train(pao)
     494              :       CASE (pao_ml_nn)
     495            4 :          CALL pao_ml_nn_train(pao)
     496              :       CASE (pao_ml_lazy)
     497              :          ! nothing to do
     498              :       CASE DEFAULT
     499           18 :          CPABORT("PAO: unknown machine learning scheme")
     500              :       END SELECT
     501              : 
     502           18 :       CALL timestop(handle)
     503              : 
     504           18 :    END SUBROUTINE pao_ml_train
     505              : 
     506              : ! **************************************************************************************************
     507              : !> \brief Fills pao%matrix_X based on machine learning predictions
     508              : !> \param pao ...
     509              : !> \param qs_env ...
     510              : ! **************************************************************************************************
     511          138 :    SUBROUTINE pao_ml_predict(pao, qs_env)
     512              :       TYPE(pao_env_type), POINTER                        :: pao
     513              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     514              : 
     515              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'pao_ml_predict'
     516              : 
     517              :       INTEGER                                            :: acol, arow, handle, iatom, ikind, natoms
     518          138 :       REAL(dp), ALLOCATABLE, DIMENSION(:)                :: descriptor, variances
     519          138 :       REAL(dp), DIMENSION(:, :), POINTER                 :: block_X
     520          138 :       TYPE(atomic_kind_type), DIMENSION(:), POINTER      :: atomic_kind_set
     521              :       TYPE(cell_type), POINTER                           :: cell
     522              :       TYPE(dbcsr_iterator_type)                          :: iter
     523              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     524          138 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     525          138 :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     526              : 
     527          138 :       CALL timeset(routineN, handle)
     528              : 
     529              :       CALL get_qs_env(qs_env, &
     530              :                       para_env=para_env, &
     531              :                       cell=cell, &
     532              :                       particle_set=particle_set, &
     533              :                       atomic_kind_set=atomic_kind_set, &
     534              :                       qs_kind_set=qs_kind_set, &
     535          138 :                       natom=natoms)
     536              : 
     537              :       ! fill matrix_X
     538          414 :       ALLOCATE (variances(natoms))
     539          138 :       variances(:) = 0.0_dp
     540              : !$OMP PARALLEL DEFAULT(NONE) SHARED(pao,qs_env,particle_set,qs_kind_set,cell,variances) &
     541          138 : !$OMP PRIVATE(iter,arow,acol,iatom,ikind,descriptor,block_X)
     542              :       CALL dbcsr_iterator_start(iter, pao%matrix_X)
     543              :       DO WHILE (dbcsr_iterator_blocks_left(iter))
     544              :          CALL dbcsr_iterator_next_block(iter, arow, acol, block_X)
     545              :          iatom = arow; CPASSERT(arow == acol)
     546              :          CALL get_atomic_kind(particle_set(iatom)%atomic_kind, kind_number=ikind)
     547              :          IF (SIZE(block_X) == 0) CYCLE ! pao disabled for iatom
     548              : 
     549              :          ! calculate descriptor
     550              :          CALL pao_ml_calc_descriptor(pao, &
     551              :                                      particle_set, &
     552              :                                      qs_kind_set, &
     553              :                                      cell, &
     554              :                                      iatom, &
     555              :                                      descriptor)
     556              : 
     557              :          ! call actual machine learning for prediction
     558              :          CALL pao_ml_predict_low(pao, ikind=ikind, &
     559              :                                  descriptor=descriptor, &
     560              :                                  output=block_X(:, 1), &
     561              :                                  variance=variances(iatom))
     562              : 
     563              :          DEALLOCATE (descriptor)
     564              : 
     565              :          !add prior
     566              :          block_X(:, 1) = block_X(:, 1) + pao%ml_training_matrices(ikind)%prior
     567              :       END DO
     568              :       CALL dbcsr_iterator_stop(iter)
     569              : !$OMP END PARALLEL
     570              : 
     571              :       ! print variances
     572          138 :       CALL para_env%sum(variances)
     573          138 :       IF (pao%iw_mlvar > 0) THEN
     574            3 :          DO iatom = 1, natoms
     575            3 :             WRITE (pao%iw_mlvar, *) "PAO|ML| atom:", iatom, " prediction variance:", variances(iatom)
     576              :          END DO
     577            1 :          CALL m_flush(pao%iw_mlvar)
     578              :       END IF
     579              : 
     580              :       ! one-line summary
     581          207 :       IF (pao%iw > 0) WRITE (pao%iw, "(A,E20.10,A,T71,I10)") " PAO|ML| max prediction variance:", &
     582          485 :          MAXVAL(variances), " for atom:", MAXLOC(variances)
     583              : 
     584          416 :       IF (MAXVAL(variances) > pao%ml_tolerance) THEN
     585            0 :          CPABORT("Variance of prediction above ML_TOLERANCE.")
     586              :       END IF
     587              : 
     588          138 :       DEALLOCATE (variances)
     589              : 
     590          138 :       CALL timestop(handle)
     591              : 
     592          276 :    END SUBROUTINE pao_ml_predict
     593              : 
     594              : ! **************************************************************************************************
     595              : !> \brief Queries the actual learning algorthim to make a prediction
     596              : !> \param pao ...
     597              : !> \param ikind ...
     598              : !> \param descriptor ...
     599              : !> \param output ...
     600              : !> \param variance ...
     601              : ! **************************************************************************************************
     602          138 :    SUBROUTINE pao_ml_predict_low(pao, ikind, descriptor, output, variance)
     603              :       TYPE(pao_env_type), POINTER                        :: pao
     604              :       INTEGER, INTENT(IN)                                :: ikind
     605              :       REAL(dp), DIMENSION(:), INTENT(IN)                 :: descriptor
     606              :       REAL(dp), DIMENSION(:), INTENT(OUT)                :: output
     607              :       REAL(dp), INTENT(OUT)                              :: variance
     608              : 
     609          220 :       SELECT CASE (pao%ml_method)
     610              :       CASE (pao_ml_gp)
     611           82 :          CALL pao_ml_gp_predict(pao, ikind, descriptor, output, variance)
     612              :       CASE (pao_ml_nn)
     613           28 :          CALL pao_ml_nn_predict(pao, ikind, descriptor, output, variance)
     614              :       CASE (pao_ml_lazy)
     615          224 :          output = 0.0_dp ! let's be really lazy and just rely on the prior
     616           28 :          variance = 0
     617              :       CASE DEFAULT
     618          138 :          CPABORT("PAO: unknown machine learning scheme")
     619              :       END SELECT
     620              : 
     621          138 :    END SUBROUTINE pao_ml_predict_low
     622              : 
     623              : ! **************************************************************************************************
     624              : !> \brief Calculate forces contributed by machine learning
     625              : !> \param pao ...
     626              : !> \param qs_env ...
     627              : !> \param matrix_G ...
     628              : !> \param forces ...
     629              : ! **************************************************************************************************
     630           18 :    SUBROUTINE pao_ml_forces(pao, qs_env, matrix_G, forces)
     631              :       TYPE(pao_env_type), POINTER                        :: pao
     632              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     633              :       TYPE(dbcsr_type)                                   :: matrix_G
     634              :       REAL(dp), DIMENSION(:, :), INTENT(INOUT)           :: forces
     635              : 
     636              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'pao_ml_forces'
     637              : 
     638              :       INTEGER                                            :: acol, arow, handle, iatom, ikind
     639           18 :       REAL(dp), ALLOCATABLE, DIMENSION(:)                :: descr_grad, descriptor
     640           18 :       REAL(dp), DIMENSION(:, :), POINTER                 :: block_G
     641           18 :       TYPE(atomic_kind_type), DIMENSION(:), POINTER      :: atomic_kind_set
     642              :       TYPE(cell_type), POINTER                           :: cell
     643              :       TYPE(dbcsr_iterator_type)                          :: iter
     644              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     645           18 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     646           18 :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     647              : 
     648           18 :       CALL timeset(routineN, handle)
     649              : 
     650              :       CALL get_qs_env(qs_env, &
     651              :                       para_env=para_env, &
     652              :                       cell=cell, &
     653              :                       particle_set=particle_set, &
     654              :                       atomic_kind_set=atomic_kind_set, &
     655           18 :                       qs_kind_set=qs_kind_set)
     656              : 
     657              : !$OMP PARALLEL DEFAULT(NONE) SHARED(pao,matrix_G,particle_set,qs_kind_set,cell) &
     658              : !$OMP REDUCTION(+:forces) &
     659           18 : !$OMP PRIVATE(iter,arow,acol,iatom,ikind,block_G,descriptor,descr_grad)
     660              :       CALL dbcsr_iterator_start(iter, matrix_G)
     661              :       DO WHILE (dbcsr_iterator_blocks_left(iter))
     662              :          CALL dbcsr_iterator_next_block(iter, arow, acol, block_G)
     663              :          iatom = arow; CPASSERT(arow == acol)
     664              :          CALL get_atomic_kind(particle_set(iatom)%atomic_kind, kind_number=ikind)
     665              :          IF (SIZE(block_G) == 0) CYCLE ! pao disabled for iatom
     666              : 
     667              :          ! calculate descriptor
     668              :          CALL pao_ml_calc_descriptor(pao, &
     669              :                                      particle_set, &
     670              :                                      qs_kind_set, &
     671              :                                      cell, &
     672              :                                      iatom=iatom, &
     673              :                                      descriptor=descriptor)
     674              : 
     675              :          ! calcaulte derivate of machine learning prediction
     676              :          CALL pao_ml_gradient_low(pao, ikind=ikind, &
     677              :                                   descriptor=descriptor, &
     678              :                                   outer_deriv=block_G(:, 1), &
     679              :                                   gradient=descr_grad)
     680              : 
     681              :          ! calculate force contributions from descriptor
     682              :          CALL pao_ml_calc_descriptor(pao, &
     683              :                                      particle_set, &
     684              :                                      qs_kind_set, &
     685              :                                      cell, &
     686              :                                      iatom=iatom, &
     687              :                                      descr_grad=descr_grad, &
     688              :                                      forces=forces)
     689              : 
     690              :          DEALLOCATE (descriptor, descr_grad)
     691              :       END DO
     692              :       CALL dbcsr_iterator_stop(iter)
     693              : !$OMP END PARALLEL
     694              : 
     695           18 :       CALL timestop(handle)
     696              : 
     697           36 :    END SUBROUTINE pao_ml_forces
     698              : 
     699              : ! **************************************************************************************************
     700              : !> \brief Calculate gradient of machine learning algorithm
     701              : !> \param pao ...
     702              : !> \param ikind ...
     703              : !> \param descriptor ...
     704              : !> \param outer_deriv ...
     705              : !> \param gradient ...
     706              : ! **************************************************************************************************
     707           18 :    SUBROUTINE pao_ml_gradient_low(pao, ikind, descriptor, outer_deriv, gradient)
     708              :       TYPE(pao_env_type), POINTER                        :: pao
     709              :       INTEGER, INTENT(IN)                                :: ikind
     710              :       REAL(dp), DIMENSION(:), INTENT(IN)                 :: descriptor, outer_deriv
     711              :       REAL(dp), ALLOCATABLE, DIMENSION(:)                :: gradient
     712              : 
     713           54 :       ALLOCATE (gradient(SIZE(descriptor)))
     714              : 
     715           28 :       SELECT CASE (pao%ml_method)
     716              :       CASE (pao_ml_gp)
     717           10 :          CALL pao_ml_gp_gradient(pao, ikind, descriptor, outer_deriv, gradient)
     718              :       CASE (pao_ml_nn)
     719            4 :          CALL pao_ml_nn_gradient(pao, ikind, descriptor, outer_deriv, gradient)
     720              :       CASE (pao_ml_lazy)
     721            4 :          gradient = 0.0_dp
     722              :       CASE DEFAULT
     723           18 :          CPABORT("PAO: unknown machine learning scheme")
     724              :       END SELECT
     725              : 
     726           18 :    END SUBROUTINE pao_ml_gradient_low
     727              : 
     728            0 : END MODULE pao_ml
        

Generated by: LCOV version 2.0-1