Octopus
minimizer.F90
Go to the documentation of this file.
1!! Copyright (C) 2002-2006 M. Marques, A. Castro, A. Rubio, G. Bertsch, M. Oliveira
2!!
3!! This program is free software; you can redistribute it and/or modify
4!! it under the terms of the GNU General Public License as published by
5!! the Free Software Foundation; either version 2, or (at your option)
6!! any later version.
7!!
8!! This program is distributed in the hope that it will be useful,
9!! but WITHOUT ANY WARRANTY; without even the implied warranty of
10!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11!! GNU General Public License for more details.
12!!
13!! You should have received a copy of the GNU General Public License
14!! along with this program; if not, write to the Free Software
15!! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16!! 02110-1301, USA.
17!!
18
19#include "global.h"
20
21module minimizer_oct_m
22 use debug_oct_m
23 use global_oct_m
24#ifndef NDEBUG
25 use, intrinsic :: ieee_exceptions
26#endif
27 use iso_c_binding
28 use, intrinsic :: iso_fortran_env
32
33 implicit none
34
35 private
36 public :: &
38 minimize_fire, &
39 minimize_multidim, &
40 minimize_multidim_nograd, &
41 minimize_multidim_nlopt
42
43
44 integer, public, parameter :: &
45 MINMETHOD_STEEPEST_DESCENT = 1, &
46 minmethod_fr_cg = 2, &
47 minmethod_pr_cg = 3, &
48 minmethod_bfgs = 4, &
49 minmethod_bfgs2 = 5, &
53 minmethod_fire = 8, &
55
56 abstract interface
57 subroutine minimizer_function_i(n, x, val)
58 import real64
59 implicit none
60 integer :: n
61 real(real64) :: x(n)
62 real(real64) :: val
63 end subroutine minimizer_function_i
64 subroutine minimizer_with_grad_i(n, x, val, getgrad, grad)
65 import real64
66 implicit none
67 integer, intent(in) :: n
68 real(real64), intent(in) :: x(n)
69 real(real64), intent(inout) :: val
70 integer, intent(in) :: getgrad
71 real(real64), intent(inout) :: grad(n)
72 end subroutine minimizer_with_grad_i
73 subroutine info_i(iter, n, val, maxdr, maxgrad, x)
74 import real64
75 implicit none
76 integer, intent(in) :: iter
77 integer, intent(in) :: n
78 real(real64), intent(in) :: val
79 real(real64), intent(in) :: maxdr
80 real(real64), intent(in) :: maxgrad
81 real(real64), intent(in) :: x(n)
82 end subroutine info_i
83 subroutine info_no_grad_i(iter, n, val, maxdr, x)
84 import real64
85 implicit none
86 integer, intent(in) :: iter
87 integer, intent(in) :: n
88 real(real64), intent(in) :: val
89 real(real64), intent(in) :: maxdr
90 real(real64), intent(in) :: x(n)
91 end subroutine info_no_grad_i
92 end interface
93
94
95 interface loct_1dminimize
96 subroutine oct_1dminimize(a, b, m, f, status)
97 import real64
98 implicit none
99 real(real64), intent(inout) :: a, b, m
100 interface
101 subroutine f(x, fx)
102 import real64
103 implicit none
104 real(real64), intent(in) :: x
105 real(real64), intent(out) :: fx
106 end subroutine f
107 end interface
108 integer, intent(out) :: status
109 end subroutine oct_1dminimize
110 end interface loct_1dminimize
111
112 interface loct_minimize
113 integer function oct_minimize(method, dim, x, step, line_tol, &
114 tolgrad, toldr, maxiter, f, write_iter_info, minimum)
115 import minimizer_with_grad_i, info_i, real64
116 implicit none
117 integer, intent(in) :: method
118 integer, intent(in) :: dim
119 real(real64), intent(inout) :: x
120 real(real64), intent(in) :: step
121 real(real64), intent(in) :: line_tol
122 real(real64), intent(in) :: tolgrad
123 real(real64), intent(in) :: toldr
124 integer, intent(in) :: maxiter
125 procedure(minimizer_with_grad_i) :: f
126 procedure(info_i) :: write_iter_info
127 real(real64), intent(out) :: minimum
128 end function oct_minimize
129 end interface loct_minimize
130
131 interface loct_minimize_direct
132 function oct_minimize_direct(method, dim, x, step, toldr, maxiter, f, write_iter_info, minimum)
134 implicit none
135 integer :: oct_minimize_direct
136 integer, intent(in) :: method
137 integer, intent(in) :: dim
138 real(real64), intent(inout) :: x
139 real(real64), intent(in) :: step
140 real(real64), intent(in) :: toldr
141 integer, intent(in) :: maxiter
142 procedure(minimizer_function_i) :: f
143 procedure(info_no_grad_i) :: write_iter_info
144 real(real64), intent(out) :: minimum
145 end function oct_minimize_direct
146 end interface loct_minimize_direct
147
148contains
149
150 subroutine minimize_multidim_nograd(method, dim, x, step, toldr, maxiter, f, write_iter_info, minimum, ierr)
151 integer, intent(in) :: method
152 integer, intent(in) :: dim
153 real(real64), intent(inout) :: x(:)
154 real(real64), intent(in) :: step
155 real(real64), intent(in) :: toldr
156 integer, intent(in) :: maxiter
157 procedure(minimizer_function_i) :: f
158 procedure(info_no_grad_i) :: write_iter_info
159 real(real64), intent(out) :: minimum
160 integer, intent(out) :: ierr
161
162 push_sub(minimize_multidim_nograd)
163
164 assert(ubound(x, dim = 1) >= dim)
165
166 ierr = 0
167 select case (method)
169 ierr = loct_minimize_direct(method, dim, x(1), step, toldr, maxiter, f, write_iter_info, minimum)
170 end select
171
172 pop_sub(minimize_multidim_nograd)
173
174 end subroutine minimize_multidim_nograd
175
176
177 subroutine minimize_multidim_nlopt(ierr, method, dim, x, step, toldr, maxiter, f, minimum, lb, ub)
178 integer, intent(out) :: ierr
179 integer, intent(in) :: method
180 integer, intent(in) :: dim
181 real(real64), contiguous, intent(inout) :: x(:)
182 real(real64), intent(in) :: step
183 real(real64), intent(in) :: toldr
184 integer, intent(in) :: maxiter
185 interface
186 subroutine f(val, n, x, grad, need_gradient, f_data)
187 use iso_c_binding
188 real(c_double), intent(out) :: val
189 integer(c_int), intent(in) :: n
190 real(c_double), intent(in) :: x(*)
191 real(c_double), intent(out) :: grad(*)
192 integer(c_int), intent(in) :: need_gradient
193 type(c_ptr), intent(in) :: f_data
194 end subroutine f
195 end interface
196 real(real64), intent(out) :: minimum
197 real(real64), intent(in), optional :: lb(:), ub(:)
198#if defined(HAVE_NLOPT)
199
200 interface
201 subroutine nlo_create(opt, alg, n)
202 use iso_c_binding
203 type(c_ptr), intent(out) :: opt
204 integer(c_int), intent(in) :: alg
205 integer(c_int), intent(in) :: n
206 end subroutine nlo_create
208 subroutine nlo_set_lower_bounds(ret, opt, lower_bounds)
209 use iso_c_binding
210 integer(c_int), intent(out) :: ret
211 type(c_ptr), intent(inout) :: opt
212 real(c_double), intent(in) :: lower_bounds(*)
213 end subroutine nlo_set_lower_bounds
214
215 subroutine nlo_set_upper_bounds(ret, opt, upper_bounds)
216 use iso_c_binding
217 integer(c_int), intent(out) :: ret
218 type(c_ptr), intent(inout) :: opt
219 real(c_double), intent(in) :: upper_bounds(*)
220 end subroutine nlo_set_upper_bounds
221
222 subroutine nlo_set_min_objective(ret, opt, f, f_data)
223 use iso_c_binding
224 integer(c_int), intent(out) :: ret
225 type(c_ptr), intent(inout) :: opt
226 interface
227 subroutine f(val, n, x, grad, need_gradient, f_data)
228 use iso_c_binding
229 real(c_double), intent(out) :: val
230 integer(c_int), intent(in) :: n
231 real(c_double), intent(in) :: x(*)
232 real(c_double), intent(out) :: grad(*)
233 integer(c_int), intent(in) :: need_gradient
234 type(c_ptr), intent(in) :: f_data
235 end subroutine f
236 end interface
237 type(c_ptr), intent(in) :: f_data
238 end subroutine nlo_set_min_objective
239
240 subroutine nlo_set_xtol_abs1(ret, opt, xtol_abs)
241 use iso_c_binding
242 integer(c_int), intent(out) :: ret
243 type(c_ptr), intent(inout) :: opt
244 real(c_double), intent(in) :: xtol_abs
245 end subroutine nlo_set_xtol_abs1
246
247 subroutine nlo_set_initial_step1(ret, opt, initial_step1)
248 use iso_c_binding
249 integer(c_int), intent(out) :: ret
250 type(c_ptr), intent(inout) :: opt
251 real(c_double), intent(in) :: initial_step1
252 end subroutine nlo_set_initial_step1
253
254 subroutine nlo_set_maxeval(ret, opt, maxeval)
255 use iso_c_binding
256 integer(c_int), intent(out) :: ret
257 type(c_ptr), intent(inout) :: opt
258 integer(c_int), intent(in) :: maxeval
259 end subroutine nlo_set_maxeval
260
261 subroutine nlo_optimize(ret, opt, x, optf)
262 use iso_c_binding
263 integer(c_int), intent(out) :: ret
264 type(c_ptr), intent(inout) :: opt
265 real(c_double), intent(inout) :: x(*)
266 real(c_double), intent(out) :: optf
267 end subroutine nlo_optimize
268
269 subroutine nlo_destroy(opt)
270 use iso_c_binding
271 type(c_ptr), intent(inout) :: opt
272 end subroutine nlo_destroy
273 end interface
274
275 type(c_ptr) :: opt
276 integer :: ires
277#ifndef NDEBUG
278 logical :: halting_mode(size(ieee_all))
279#endif
280 ! The following values are taken from the ''nlopt.f'' file installed by NLopt.
281 integer, parameter :: NLOPT_LD_LBFGS = 11
282 integer, parameter :: NLOPT_LN_BOBYQA = 34
283
284#ifndef NDEBUG
285 ! NLopt raises benign floating-point exceptions when handling unbounded
286 ! optimizations (BOBYQA), so suspend trapping for builds that enable it
287 call ieee_get_halting_mode(ieee_all, halting_mode)
288 call ieee_set_halting_mode(ieee_all, .false.)
289#endif
290
291 opt = c_null_ptr
292 select case (method)
294 call nlo_create(opt, nlopt_ln_bobyqa, dim)
296 call nlo_create(opt, nlopt_ld_lbfgs, dim)
297 end select
298
299 if (present(lb)) then
300 call nlo_set_lower_bounds(ires, opt, lb)
301 end if
302 if (present(ub)) then
303 call nlo_set_upper_bounds(ires, opt, ub)
304 end if
305
306 call nlo_set_min_objective(ires, opt, f, c_null_ptr)
307 ! This would set an inequality constraint (TODO)
308 ! call nlo_add_inequality_constraint(ires, opt, myconstraint, d1, 1.0e-8_real64)
309
310 call nlo_set_xtol_abs1(ires, opt, toldr)
311 call nlo_set_initial_step1(ires, opt, step)
312 call nlo_set_maxeval(ires, opt, maxiter)
313
314 call nlo_optimize(ires, opt, x, minimum)
315 ierr = ires
316 call nlo_destroy(opt)
317#ifndef NDEBUG
318 call ieee_set_halting_mode(ieee_all, halting_mode)
319#endif
320#else
321 ierr = 0
322 minimum = -m_huge
323#endif
324 end subroutine minimize_multidim_nlopt
325
326
327 !----------------------------------------------
328 subroutine minimize_multidim(method, dim, x, step, line_tol, tolgrad, toldr, maxiter, f, write_iter_info, minimum, ierr)
329 integer, intent(in) :: method
330 integer, intent(in) :: dim
331 real(real64), intent(inout) :: x(:)
332 real(real64), intent(in) :: step
333 real(real64), intent(in) :: line_tol
334 real(real64), intent(in) :: tolgrad
335 real(real64), intent(in) :: toldr
336 integer, intent(in) :: maxiter
337 procedure(minimizer_with_grad_i) :: f
338 procedure(info_i) :: write_iter_info
339 real(real64), intent(out) :: minimum
340 integer, intent(out) :: ierr
341
342 push_sub(minimize_multidim)
343
344 assert(ubound(x, dim = 1) >= dim)
345
346 select case (method)
348 call minimize_sd(dim, x, step, maxiter, f, write_iter_info, minimum, ierr)
349
350 case default
351 ierr = loct_minimize(method, dim, x(1), step, line_tol, tolgrad, toldr, maxiter, f, write_iter_info, minimum)
352
353 end select
354
355 pop_sub(minimize_multidim)
356
357 end subroutine minimize_multidim
358
359 !----------------------------------------------
360
361 subroutine minimize_sd(dim, x, step, maxiter, f, write_iter_info, minimum, ierr)
362 integer, intent(in) :: dim
363 real(real64), intent(inout) :: x(:)
364 real(real64), intent(in) :: step
365 integer, intent(in) :: maxiter
366 procedure(minimizer_with_grad_i) :: f
367 procedure(info_i) :: write_iter_info
368 real(real64), intent(out) :: minimum
369 integer, intent(out) :: ierr
370
371 integer :: iter
372 real(real64), allocatable :: grad(:)
373 real(real64) :: step2, maxgrad
374
375 push_sub(minimize_sd)
376
377 safe_allocate(grad(1:dim))
378
379 step2 = step*10.0_real64
380 do iter = 1, maxiter
381 call f(dim, x, minimum, 1, grad)
382
383 maxgrad = maxval(abs(grad))
384
385 call write_iter_info(iter, dim, minimum, maxgrad*step2, maxgrad, x)
386
387 x(1:dim) = x(1:dim) - step2*grad(1:dim)
388
389 step2 = step2*0.99_real64
390 end do
391 ierr = 0
392
393 pop_sub(minimize_sd)
394 end subroutine minimize_sd
395
396 !----------------------------------------------
410 subroutine minimize_fire(dim, space_dim, x, step, tolgrad, maxiter, gradf, write_iter_info, en, ierr, mass, integrator)
411 integer, intent(in) :: dim
412 integer, intent(in) :: space_dim
413 real(real64), intent(inout) :: x(:)
414 real(real64), intent(in) :: step
415 real(real64), intent(in) :: tolgrad
416 integer, intent(in) :: maxiter
417 procedure(minimizer_with_grad_i) :: gradf
418 procedure(info_i) :: write_iter_info
419 real(real64), intent(out) :: en
420 integer, intent(out) :: ierr
421 real(real64), intent(in) :: mass(:)
422 integer, intent(in) :: integrator
423
424 real(real64), allocatable :: grad(:)
425
426 integer :: p_times, n_times, iter, ia
427 real(real64) :: dt, alpha, p_value, dt_max, dt_min, mix
428 real(real64), allocatable :: grad_atoms(:), vel(:), dr_i(:), x_old(:), dr_atoms(:)
429
430 ! Parameters from the original paper
431 integer, parameter :: n_min = 5, n_decrease_max = 500
432 real(real64), parameter :: f_alpha = 0.99_real64
433 real(real64), parameter :: f_inc = 1.1_real64
434 real(real64), parameter :: f_dec = 0.5_real64
435 real(real64), parameter :: alpha_start = 0.25_real64
436
437 real(real64), parameter :: tol_zero_forces = 1e-10_real64
438
439 push_sub(minimize_fire)
440
441 ! dim must be a multiple of space_dim
442 assert(mod(dim,space_dim) == 0)
443 assert(size(x) == dim)
444
445 safe_allocate(grad_atoms(1:dim/space_dim))
446 safe_allocate(grad(1:dim))
447 safe_allocate(vel(1:dim))
448 safe_allocate(dr_atoms(1:dim/space_dim))
449 safe_allocate(x_old(1:dim))
450 safe_allocate(dr_i(1:dim))
451
452 ! Initial values
453 ierr = 0
454 vel = m_zero
455 dt = step
456 alpha = alpha_start
457 dr_i = m_zero
458 dt_max = 10.0_real64 * dt
459 dt_min = 0.02_real64 * dt
460 p_times = 0
461 n_times = 0
462 call gradf(dim, x, en, 1, grad)
463 grad = -grad
464
465 do iter = 1, maxiter
466
467 ! Perform step F1: compute P = F.v
468 p_value = dot_product(grad, vel)
469
470 ! Perform step F3
471 if (p_value > m_zero) then
472 p_times = p_times + 1
473 n_times = 0
474 if (p_times > n_min) then
475 dt = min(dt * f_inc , dt_max)
476 alpha = alpha * f_alpha
477 end if
478 else ! or step F4
479 p_times = 0
480 n_times = n_times + 1
481
482 if (n_times > n_decrease_max) exit ! If we are stuck, we exit
483
484 if (iter >= n_min) then ! No decrease for the first few steps
485 dt = max(dt * f_dec, dt_min)
486 alpha = alpha_start
487 end if
488 x = x - m_half * vel * dt ! Correct uphill motion
489 vel = m_zero
490 end if
491
492 ! Store old values
493 x_old = x
494
495 ! Perform the MD step: get new gradients from the new positions
496 ! Step F2: v-> (1-\alpha)v + \alpha \hat{F}.|v| is done inside the MD step
497 ! We follow the integration schemes of the FIRE 2.0 paper
498
499 ! Prevent division by zero in the mixing
500 mix = lalg_nrm2(dim,grad)
501 if (mix > tol_zero_forces) then
502 mix = m_one / mix
503 else
504 mix = m_zero
505 end if
506
507 select case (integrator)
508 case (option__gofireintegrator__verlet) ! Velocity Verlet, see Algorithm 6
509 ! Velocity Verlet - update velocities 1
510 vel = vel + m_half * grad * dt / mass
511
512 ! Mixing
513 if (p_value > m_zero) then
514 vel = (m_one - alpha) * vel + alpha * mix * grad * lalg_nrm2(dim, vel)
515 end if
516
517 ! Get the new position
518 dr_i = vel * dt
519 call prevent_large_changes(dim, space_dim, dr_i, dr_atoms)
520 x = x_old + dr_i
521
522 ! Get the new gradient
523 call gradf(dim, x, en, 1, grad)
524 grad = -grad
525
526 ! Velocity Verlet - update velocities 2
527 vel = vel + m_half * grad * dt / mass
528
529 case (option__gofireintegrator__euler) ! Explicit Euler method, see Algorithm 3 in Fire2.0 paper
530 ! Mixing
531 if (p_value > m_zero) then
532 vel = (m_one - alpha) * vel + alpha * mix * grad * lalg_nrm2(dim, vel)
533 end if
534
535 ! Get the new position
536 dr_i = vel * dt
537 call prevent_large_changes(dim, space_dim, dr_i, dr_atoms)
538 x = x_old + dr_i
539
540 ! Get the new gradient
541 call gradf(dim, x, en, 1, grad)
542 grad = -grad
543
544 ! Velocity Verlet - update velocities
545 vel = vel + grad * dt / mass
546
547
548 case (option__gofireintegrator__semi_implicit_euler) ! Semi-implicit Euler method, see Algorithm 4
549 ! Velocity Verlet - update velocities
550 vel = vel + grad * dt / mass
551
552 ! Mixing
553 if (p_value > m_zero) then
554 vel = (m_one - alpha) * vel + alpha * mix * grad * lalg_nrm2(dim, vel)
555 end if
556
557 ! Get the new position
558 dr_i = vel * dt
559 call prevent_large_changes(dim, space_dim, dr_i, dr_atoms)
560 x = x_old + dr_i
561
562 ! Get the new gradient
563 call gradf(dim, x, en, 1, grad)
564 grad = -grad
565 end select
566
567 ! Get the norms of the gradients for each atom
568 do ia = 0, dim/space_dim - 1
569 grad_atoms(ia+1) = norm2(grad(space_dim*ia+1:space_dim*ia+space_dim))
570 end do
571
572 ! Output for each iteration step
573 call write_iter_info(iter, dim, en, maxval(dr_atoms), maxval(abs(grad_atoms)), x)
574
575 ! Check convergence
576 if (maxval(abs(grad_atoms(1:))) < tolgrad) exit
577 end do
578
579 ierr = -iter
580
581 safe_deallocate_a(dr_atoms)
582 safe_deallocate_a(x_old)
583 safe_deallocate_a(dr_i)
584 safe_deallocate_a(vel)
585 safe_deallocate_a(grad)
586 safe_deallocate_a(grad_atoms)
587
588 pop_sub(minimize_fire)
589 end subroutine minimize_fire
590
592 ! TODO: we should instead change the velocities,
593 ! see S. Echeverri Restrepo and P. Andric, Computational Materials Science 218 (2023) 111978
594 subroutine prevent_large_changes(dim, space_dim, dr_i, dr_atoms)
595 integer, intent(in) :: dim
596 integer, intent(in) :: space_dim
597 real(real64), intent(inout) :: dr_i(:), dr_atoms(:)
598
599 integer :: i1, i2, ia
600
601 real(real64), parameter :: maxmove = 0.2_real64 * p_ang
602
603 ! Get the norms of the displaments for each atoms
604 do ia = 0, dim/space_dim - 1
605 i1 = space_dim*ia+1
606 i2 = space_dim*ia+space_dim
607 dr_atoms(ia+1) = norm2(dr_i(i1:i2))
608 ! Rescale the displacement to avoid too large changes
609 if (dr_atoms(ia+1) > maxmove) then
610 dr_i(i1:i2) = maxmove * dr_i(i1:i2) / dr_atoms(ia+1)
611 dr_atoms(ia+1) = maxmove
612 end if
613 end do
614 end subroutine prevent_large_changes
615
616end module minimizer_oct_m
617
618!! Local Variables:
619!! mode: f90
620!! coding: utf-8
621!! End:
Returns the euclidean norm of a vector.
Definition: lalg_basic.F90:200
real(real64), parameter, public m_huge
Definition: global.F90:218
real(real64), parameter, public m_zero
Definition: global.F90:200
real(real64), parameter, public p_ang
Definition: global.F90:238
real(real64), parameter, public m_half
Definition: global.F90:206
real(real64), parameter, public m_one
Definition: global.F90:201
integer, parameter, public minmethod_fr_cg
Definition: minimizer.F90:139
integer, parameter, public minmethod_bfgs
Definition: minimizer.F90:139
integer, parameter, public minmethod_nmsimplex
Definition: minimizer.F90:139
integer, parameter, public minmethod_bfgs2
Definition: minimizer.F90:139
integer, parameter, public minmethod_nlopt_bobyqa
Definition: minimizer.F90:139
integer, parameter, public minmethod_nlopt_lbfgs
Definition: minimizer.F90:139
integer, parameter, public minmethod_pr_cg
Definition: minimizer.F90:139
integer, parameter, public minmethod_fire
Definition: minimizer.F90:139
integer, parameter, public minmethod_sd_native
Definition: minimizer.F90:139
static double f(double w, void *p)