LCOV - code coverage report
Current view: top level - src - pao_ml.F (source / functions) Hit Total Coverage
Test: CP2K Regtests (git:e7e05ae) Lines: 231 236 97.9 %
Date: 2024-04-18 06:59:28 Functions: 12 16 75.0 %

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

Generated by: LCOV version 1.15