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 Optimizers used by pao_main.F
10 : !> \author Ole Schuett
11 : ! **************************************************************************************************
12 : MODULE pao_optimizer
13 : USE arnoldi_api, ONLY: arnoldi_extremal
14 : USE cp_dbcsr_api, ONLY: &
15 : dbcsr_add, dbcsr_add_on_diag, dbcsr_copy, dbcsr_create, dbcsr_dot, dbcsr_frobenius_norm, &
16 : dbcsr_get_info, dbcsr_multiply, dbcsr_release, dbcsr_reserve_diag_blocks, dbcsr_scale, &
17 : dbcsr_set, dbcsr_type
18 : USE kinds, ONLY: dp
19 : USE pao_input, ONLY: pao_opt_bfgs,&
20 : pao_opt_cg
21 : USE pao_types, ONLY: pao_env_type
22 : #include "./base/base_uses.f90"
23 :
24 : IMPLICIT NONE
25 :
26 : PRIVATE
27 :
28 : PUBLIC :: pao_opt_init, pao_opt_finalize, pao_opt_new_dir
29 :
30 : CONTAINS
31 :
32 : ! **************************************************************************************************
33 : !> \brief Initialize the optimizer
34 : !> \param pao ...
35 : ! **************************************************************************************************
36 246 : SUBROUTINE pao_opt_init(pao)
37 : TYPE(pao_env_type), POINTER :: pao
38 :
39 246 : CALL dbcsr_copy(pao%matrix_D, pao%matrix_G)
40 246 : CALL dbcsr_set(pao%matrix_D, 0.0_dp)
41 :
42 246 : CALL dbcsr_copy(pao%matrix_G_prev, pao%matrix_D)
43 :
44 246 : IF (pao%precondition) THEN
45 82 : CALL dbcsr_copy(pao%matrix_D_preconed, pao%matrix_D)
46 : END IF
47 :
48 246 : IF (pao%optimizer == pao_opt_bfgs) &
49 12 : CALL pao_opt_init_bfgs(pao)
50 :
51 246 : END SUBROUTINE pao_opt_init
52 :
53 : ! **************************************************************************************************
54 : !> \brief Initialize the BFGS optimizer
55 : !> \param pao ...
56 : ! **************************************************************************************************
57 12 : SUBROUTINE pao_opt_init_bfgs(pao)
58 : TYPE(pao_env_type), POINTER :: pao
59 :
60 12 : INTEGER, DIMENSION(:), POINTER :: nparams
61 :
62 12 : CALL dbcsr_get_info(pao%matrix_X, row_blk_size=nparams)
63 :
64 : CALL dbcsr_create(pao%matrix_BFGS, &
65 : template=pao%matrix_X, &
66 : row_blk_size=nparams, &
67 : col_blk_size=nparams, &
68 12 : name="PAO matrix_BFGS")
69 :
70 12 : CALL dbcsr_reserve_diag_blocks(pao%matrix_BFGS)
71 12 : CALL dbcsr_set(pao%matrix_BFGS, 0.0_dp)
72 12 : CALL dbcsr_add_on_diag(pao%matrix_BFGS, 1.0_dp)
73 :
74 12 : END SUBROUTINE pao_opt_init_bfgs
75 :
76 : ! **************************************************************************************************
77 : !> \brief Finalize the optimizer
78 : !> \param pao ...
79 : ! **************************************************************************************************
80 246 : SUBROUTINE pao_opt_finalize(pao)
81 : TYPE(pao_env_type), POINTER :: pao
82 :
83 246 : CALL dbcsr_release(pao%matrix_D)
84 246 : CALL dbcsr_release(pao%matrix_G_prev)
85 246 : IF (pao%precondition) &
86 82 : CALL dbcsr_release(pao%matrix_D_preconed)
87 :
88 246 : IF (pao%optimizer == pao_opt_bfgs) &
89 12 : CALL dbcsr_release(pao%matrix_BFGS)
90 :
91 246 : END SUBROUTINE pao_opt_finalize
92 :
93 : ! **************************************************************************************************
94 : !> \brief Calculates the new search direction.
95 : !> \param pao ...
96 : !> \param icycle ...
97 : ! **************************************************************************************************
98 2620 : SUBROUTINE pao_opt_new_dir(pao, icycle)
99 : TYPE(pao_env_type), POINTER :: pao
100 : INTEGER, INTENT(IN) :: icycle
101 :
102 : CHARACTER(len=*), PARAMETER :: routineN = 'pao_opt_new_dir'
103 :
104 : INTEGER :: handle
105 : TYPE(dbcsr_type) :: matrix_G_preconed
106 :
107 2620 : CALL timeset(routineN, handle)
108 :
109 2620 : IF (pao%precondition) THEN
110 : ! We can't convert matrix_D for and back every time, the numeric noise would disturb the CG,
111 : ! hence we keep matrix_D_preconed around.
112 1290 : CALL dbcsr_copy(matrix_G_preconed, pao%matrix_G)
113 : CALL dbcsr_multiply("N", "N", 1.0_dp, pao%matrix_precon, pao%matrix_G, &
114 1290 : 0.0_dp, matrix_G_preconed, retain_sparsity=.TRUE.)
115 1290 : CALL pao_opt_new_dir_low(pao, icycle, matrix_G_preconed, pao%matrix_G_prev, pao%matrix_D_preconed)
116 : CALL dbcsr_multiply("N", "N", 1.0_dp, pao%matrix_precon, pao%matrix_D_preconed, &
117 1290 : 0.0_dp, pao%matrix_D, retain_sparsity=.TRUE.)
118 :
119 : ! store preconditioned gradient for next iteration
120 1290 : CALL dbcsr_copy(pao%matrix_G_prev, matrix_G_preconed)
121 :
122 1290 : pao%norm_G = dbcsr_frobenius_norm(matrix_G_preconed)
123 1290 : IF (pao%iw > 0) WRITE (pao%iw, *) "PAO| norm of preconditioned gradient:", pao%norm_G
124 1290 : CALL dbcsr_release(matrix_G_preconed)
125 :
126 : ELSE
127 1330 : CALL pao_opt_new_dir_low(pao, icycle, pao%matrix_G, pao%matrix_G_prev, pao%matrix_D)
128 1330 : CALL dbcsr_copy(pao%matrix_G_prev, pao%matrix_G) ! store gradient for next iteration
129 1330 : pao%norm_G = dbcsr_frobenius_norm(pao%matrix_G)
130 1330 : IF (pao%iw > 0) WRITE (pao%iw, *) "PAO| norm of gradient:", pao%norm_G
131 : END IF
132 :
133 2620 : CALL timestop(handle)
134 :
135 2620 : END SUBROUTINE pao_opt_new_dir
136 :
137 : ! **************************************************************************************************
138 : !> \brief Calculates the new search direction.
139 : !> \param pao ...
140 : !> \param icycle ...
141 : !> \param matrix_G ...
142 : !> \param matrix_G_prev ...
143 : !> \param matrix_D ...
144 : ! **************************************************************************************************
145 2620 : SUBROUTINE pao_opt_new_dir_low(pao, icycle, matrix_G, matrix_G_prev, matrix_D)
146 : TYPE(pao_env_type), POINTER :: pao
147 : INTEGER, INTENT(IN) :: icycle
148 : TYPE(dbcsr_type) :: matrix_G, matrix_G_prev, matrix_D
149 :
150 4952 : SELECT CASE (pao%optimizer)
151 : CASE (pao_opt_cg)
152 2332 : CALL pao_opt_newdir_cg(pao, icycle, matrix_G, matrix_G_prev, matrix_D)
153 : CASE (pao_opt_bfgs)
154 288 : CALL pao_opt_newdir_bfgs(pao, icycle, matrix_G, matrix_G_prev, matrix_D)
155 : CASE DEFAULT
156 2620 : CPABORT("PAO: unknown optimizer")
157 : END SELECT
158 :
159 2620 : END SUBROUTINE pao_opt_new_dir_low
160 :
161 : ! **************************************************************************************************
162 : !> \brief Conjugate Gradient algorithm
163 : !> \param pao ...
164 : !> \param icycle ...
165 : !> \param matrix_G ...
166 : !> \param matrix_G_prev ...
167 : !> \param matrix_D ...
168 : ! **************************************************************************************************
169 2332 : SUBROUTINE pao_opt_newdir_cg(pao, icycle, matrix_G, matrix_G_prev, matrix_D)
170 : TYPE(pao_env_type), POINTER :: pao
171 : INTEGER, INTENT(IN) :: icycle
172 : TYPE(dbcsr_type) :: matrix_G, matrix_G_prev, matrix_D
173 :
174 : REAL(KIND=dp) :: beta, change, trace_D, trace_D_Gnew, &
175 : trace_G_mix, trace_G_new, trace_G_prev
176 :
177 : ! determine CG mixing factor
178 2332 : IF (icycle <= pao%cg_init_steps) THEN
179 444 : IF (pao%iw_opt > 0) WRITE (pao%iw_opt, *) "PAO|CG| warming up with steepest descent"
180 444 : beta = 0.0_dp
181 : ELSE
182 1888 : CALL dbcsr_dot(matrix_G, matrix_G, trace_G_new)
183 1888 : CALL dbcsr_dot(matrix_G_prev, matrix_G_prev, trace_G_prev)
184 1888 : CALL dbcsr_dot(matrix_G, matrix_G_prev, trace_G_mix)
185 1888 : CALL dbcsr_dot(matrix_D, matrix_G, trace_D_Gnew)
186 1888 : CALL dbcsr_dot(matrix_D, matrix_D, trace_D)
187 1888 : IF (pao%iw_opt > 0) WRITE (pao%iw_opt, *) "PAO|CG| trace_G_new ", trace_G_new
188 1888 : IF (pao%iw_opt > 0) WRITE (pao%iw_opt, *) "PAO|CG| trace_G_prev ", trace_G_prev
189 1888 : IF (pao%iw_opt > 0) WRITE (pao%iw_opt, *) "PAO|CG| trace_G_mix ", trace_G_mix
190 1888 : IF (pao%iw_opt > 0) WRITE (pao%iw_opt, *) "PAO|CG| trace_D ", trace_D
191 1888 : IF (pao%iw_opt > 0) WRITE (pao%iw_opt, *) "PAO|CG| trace_D_Gnew", trace_D_Gnew
192 :
193 1888 : IF (trace_G_prev /= 0.0_dp) THEN
194 1888 : beta = (trace_G_new - trace_G_mix)/trace_G_prev !Polak-Ribiere
195 : END IF
196 :
197 1888 : IF (beta < 0.0_dp) THEN
198 78 : IF (pao%iw_opt > 0) WRITE (pao%iw_opt, *) "PAO|CG| resetting because beta < 0"
199 78 : beta = 0.0_dp
200 : END IF
201 :
202 1888 : change = trace_D_Gnew**2/trace_D*trace_G_new
203 1888 : IF (change > pao%cg_reset_limit) THEN
204 0 : IF (pao%iw_opt > 0) WRITE (pao%iw_opt, *) "PAO|CG| resetting because change > CG_RESET_LIMIT"
205 0 : beta = 0.0_dp
206 : END IF
207 :
208 : END IF
209 :
210 2332 : IF (pao%iw_opt > 0) WRITE (pao%iw_opt, *) "PAO|CG| beta: ", beta
211 :
212 : ! calculate new CG direction matrix_D
213 2332 : CALL dbcsr_add(matrix_D, matrix_G, beta, -1.0_dp)
214 :
215 2332 : END SUBROUTINE pao_opt_newdir_cg
216 :
217 : ! **************************************************************************************************
218 : !> \brief Broyden-Fletcher-Goldfarb-Shanno algorithm
219 : !> \param pao ...
220 : !> \param icycle ...
221 : !> \param matrix_G ...
222 : !> \param matrix_G_prev ...
223 : !> \param matrix_D ...
224 : ! **************************************************************************************************
225 288 : SUBROUTINE pao_opt_newdir_bfgs(pao, icycle, matrix_G, matrix_G_prev, matrix_D)
226 : TYPE(pao_env_type), POINTER :: pao
227 : INTEGER, INTENT(IN) :: icycle
228 : TYPE(dbcsr_type) :: matrix_G, matrix_G_prev, matrix_D
229 :
230 : CHARACTER(len=*), PARAMETER :: routineN = 'pao_opt_newdir_bfgs'
231 :
232 : INTEGER :: handle
233 : LOGICAL :: arnoldi_converged
234 : REAL(dp) :: eval_max, eval_min, theta, trace_ry, &
235 : trace_sy, trace_yHy, trace_yy
236 : TYPE(dbcsr_type) :: matrix_Hy, matrix_Hyr, matrix_r, &
237 : matrix_rr, matrix_ryH, matrix_ryHyr, &
238 : matrix_s, matrix_y, matrix_yr
239 :
240 288 : CALL timeset(routineN, handle)
241 :
242 : !TODO add filtering?
243 :
244 : ! Notation according to the book from Nocedal and Wright, see chapter 6.
245 288 : IF (icycle > 1) THEN
246 : ! y = G - G_prev
247 276 : CALL dbcsr_copy(matrix_y, matrix_G)
248 276 : CALL dbcsr_add(matrix_y, matrix_G_prev, 1.0_dp, -1.0_dp) ! dG
249 :
250 : ! s = X - X_prev
251 276 : CALL dbcsr_copy(matrix_s, matrix_D)
252 276 : CALL dbcsr_scale(matrix_s, pao%linesearch%step_size) ! dX
253 :
254 : ! sy = MATMUL(TRANPOSE(s), y)
255 276 : CALL dbcsr_dot(matrix_s, matrix_y, trace_sy)
256 :
257 : ! heuristic initialization
258 276 : IF (icycle == 2) THEN
259 10 : CALL dbcsr_dot(matrix_Y, matrix_Y, trace_yy)
260 10 : CALL dbcsr_scale(pao%matrix_BFGS, trace_sy/trace_yy)
261 10 : IF (pao%iw_opt > 0) WRITE (pao%iw_opt, *) "PAO|BFGS| Initializing with:", trace_sy/trace_yy
262 : END IF
263 :
264 : ! Hy = MATMUL(H, y)
265 276 : CALL dbcsr_create(matrix_Hy, template=matrix_G, matrix_type="N")
266 276 : CALL dbcsr_multiply("N", "N", 1.0_dp, pao%matrix_BFGS, matrix_y, 0.0_dp, matrix_Hy)
267 :
268 : ! yHy = MATMUL(TRANPOSE(y), Hy)
269 276 : CALL dbcsr_dot(matrix_y, matrix_Hy, trace_yHy)
270 :
271 : ! Use damped BFGS algorithm to ensure H remains positive definite.
272 : ! See chapter 18 in Nocedal and Wright's book for details.
273 : ! The formulas were adopted to inverse Hessian algorithm.
274 276 : IF (trace_sy < 0.2_dp*trace_yHy) THEN
275 0 : theta = 0.8_dp*trace_yHy/(trace_yHy - trace_sy)
276 0 : IF (pao%iw_opt > 0) WRITE (pao%iw_opt, *) "PAO|BFGS| Dampening theta:", theta
277 : ELSE
278 276 : theta = 1.0
279 : END IF
280 :
281 : ! r = theta*s + (1-theta)*Hy
282 276 : CALL dbcsr_copy(matrix_r, matrix_s)
283 276 : CALL dbcsr_add(matrix_r, matrix_Hy, theta, (1.0_dp - theta))
284 :
285 : ! use t instead of y to update B matrix
286 276 : CALL dbcsr_dot(matrix_r, matrix_y, trace_ry)
287 276 : CPASSERT(trace_RY > 0.0_dp)
288 :
289 : ! yr = MATMUL(y, TRANSPOSE(r))
290 276 : CALL dbcsr_create(matrix_yr, template=pao%matrix_BFGS, matrix_type="N")
291 276 : CALL dbcsr_multiply("N", "T", 1.0_dp, matrix_y, matrix_r, 0.0_dp, matrix_yr)
292 :
293 : ! Hyr = MATMUL(H, yr)
294 276 : CALL dbcsr_create(matrix_Hyr, template=pao%matrix_BFGS, matrix_type="N")
295 276 : CALL dbcsr_multiply("N", "N", 1.0_dp, pao%matrix_BFGS, matrix_yr, 0.0_dp, matrix_Hyr)
296 :
297 : ! ryH = MATMUL(TRANSPOSE(yr), H)
298 276 : CALL dbcsr_create(matrix_ryH, template=pao%matrix_BFGS, matrix_type="N")
299 276 : CALL dbcsr_multiply("T", "N", 1.0_dp, matrix_yr, pao%matrix_BFGS, 0.0_dp, matrix_ryH)
300 :
301 : ! ryHry = MATMUL(ryH,yr)
302 276 : CALL dbcsr_create(matrix_ryHyr, template=pao%matrix_BFGS, matrix_type="N")
303 276 : CALL dbcsr_multiply("N", "N", 1.0_dp, matrix_ryH, matrix_yr, 0.0_dp, matrix_ryHyr)
304 :
305 : ! rr = MATMUL(r,TRANSPOSE(r))
306 276 : CALL dbcsr_create(matrix_rr, template=pao%matrix_BFGS, matrix_type="N")
307 276 : CALL dbcsr_multiply("N", "T", 1.0_dp, matrix_r, matrix_r, 0.0_dp, matrix_rr)
308 :
309 : ! H = H - Hyr/ry - ryH/ry + ryHyr/(ry**2) + rr/ry
310 276 : CALL dbcsr_add(pao%matrix_BFGS, matrix_HYR, 1.0_dp, -1.0_dp/trace_ry)
311 276 : CALL dbcsr_add(pao%matrix_BFGS, matrix_ryH, 1.0_dp, -1.0_dp/trace_ry)
312 276 : CALL dbcsr_add(pao%matrix_BFGS, matrix_ryHyr, 1.0_dp, +1.0_dp/(trace_ry**2))
313 276 : CALL dbcsr_add(pao%matrix_BFGS, matrix_rr, 1.0_dp, +1.0_dp/trace_ry)
314 :
315 : ! clean up
316 276 : CALL dbcsr_release(matrix_y)
317 276 : CALL dbcsr_release(matrix_s)
318 276 : CALL dbcsr_release(matrix_r)
319 276 : CALL dbcsr_release(matrix_Hy)
320 276 : CALL dbcsr_release(matrix_yr)
321 276 : CALL dbcsr_release(matrix_Hyr)
322 276 : CALL dbcsr_release(matrix_ryH)
323 276 : CALL dbcsr_release(matrix_ryHyr)
324 276 : CALL dbcsr_release(matrix_rr)
325 : END IF
326 :
327 : ! approximate condition of Hessian
328 : !TODO: good setting for arnoldi?
329 : CALL arnoldi_extremal(pao%matrix_BFGS, eval_max, eval_min, max_iter=100, &
330 288 : threshold=1e-2_dp, converged=arnoldi_converged)
331 288 : IF (arnoldi_converged) THEN
332 432 : IF (pao%iw_opt > 0) WRITE (pao%iw_opt, *) "PAO|BFGS| evals of inv. Hessian: min, max, max/min", &
333 288 : eval_min, eval_max, eval_max/eval_min
334 : ELSE
335 0 : IF (pao%iw_opt > 0) WRITE (pao%iw_opt, *) "PAO|BFGS| arnoldi of inv. Hessian did not converged."
336 : END IF
337 :
338 : ! calculate new direction
339 : ! d = MATMUL(H, -g)
340 : CALL dbcsr_multiply("N", "N", -1.0_dp, pao%matrix_BFGS, matrix_G, &
341 288 : 0.0_dp, matrix_D, retain_sparsity=.TRUE.)
342 :
343 288 : CALL timestop(handle)
344 288 : END SUBROUTINE pao_opt_newdir_bfgs
345 :
346 : END MODULE pao_optimizer
|