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
274 integer(c_int) function nlopt_algorithm_from_string(name) bind(c)
275 use iso_c_binding
276 character(kind=c_char), intent(in) :: name(*)
277 end function nlopt_algorithm_from_string
278 end interface
279
280 type(c_ptr) :: opt
281 integer :: ires
282 integer(c_int) :: algorithm
283#ifndef NDEBUG
284 logical :: halting_mode(size(ieee_all))
285#endif
286
287#ifndef NDEBUG
288 ! NLopt raises benign floating-point exceptions when handling unbounded
289 ! optimizations (BOBYQA), so suspend trapping for builds that enable it
290 call ieee_get_halting_mode(ieee_all, halting_mode)
291 call ieee_set_halting_mode(ieee_all, .false.)
292#endif
293
294 ! Look up the algorithm by name: the numerical values of the nlopt_algorithm
295 ! enum are not stable across NLopt versions (they changed in NLopt 2.9.0).
296 select case (method)
298 algorithm = nlopt_algorithm_from_string("LN_BOBYQA"//c_null_char)
300 algorithm = nlopt_algorithm_from_string("LD_LBFGS"//c_null_char)
301 case default
302 algorithm = -1_c_int
303 end select
304 if (algorithm < 0_c_int) then
305 message(1) = "The requested algorithm is not available in the linked NLopt library."
306 call messages_fatal(1)
307 end if
308
309 opt = c_null_ptr
310 call nlo_create(opt, algorithm, dim)
311
312 if (present(lb)) then
313 call nlo_set_lower_bounds(ires, opt, lb)
314 end if
315 if (present(ub)) then
316 call nlo_set_upper_bounds(ires, opt, ub)
317 end if
318
319 call nlo_set_min_objective(ires, opt, f, c_null_ptr)
320 ! This would set an inequality constraint (TODO)
321 ! call nlo_add_inequality_constraint(ires, opt, myconstraint, d1, 1.0e-8_real64)
322
323 call nlo_set_xtol_abs1(ires, opt, toldr)
324 call nlo_set_initial_step1(ires, opt, step)
325 call nlo_set_maxeval(ires, opt, maxiter)
326
327 call nlo_optimize(ires, opt, x, minimum)
328 ierr = ires
329 call nlo_destroy(opt)
330#ifndef NDEBUG
331 call ieee_set_halting_mode(ieee_all, halting_mode)
332#endif
333#else
334 ierr = 0
335 minimum = -m_huge
336#endif
337 end subroutine minimize_multidim_nlopt
338
339
340 !----------------------------------------------
341 subroutine minimize_multidim(method, dim, x, step, line_tol, tolgrad, toldr, maxiter, f, write_iter_info, minimum, ierr)
342 integer, intent(in) :: method
343 integer, intent(in) :: dim
344 real(real64), intent(inout) :: x(:)
345 real(real64), intent(in) :: step
346 real(real64), intent(in) :: line_tol
347 real(real64), intent(in) :: tolgrad
348 real(real64), intent(in) :: toldr
349 integer, intent(in) :: maxiter
350 procedure(minimizer_with_grad_i) :: f
351 procedure(info_i) :: write_iter_info
352 real(real64), intent(out) :: minimum
353 integer, intent(out) :: ierr
354
355 push_sub(minimize_multidim)
356
357 assert(ubound(x, dim = 1) >= dim)
358
359 select case (method)
361 call minimize_sd(dim, x, step, maxiter, f, write_iter_info, minimum, ierr)
362
363 case default
364 ierr = loct_minimize(method, dim, x(1), step, line_tol, tolgrad, toldr, maxiter, f, write_iter_info, minimum)
365
366 end select
367
368 pop_sub(minimize_multidim)
369
370 end subroutine minimize_multidim
371
372 !----------------------------------------------
373
374 subroutine minimize_sd(dim, x, step, maxiter, f, write_iter_info, minimum, ierr)
375 integer, intent(in) :: dim
376 real(real64), intent(inout) :: x(:)
377 real(real64), intent(in) :: step
378 integer, intent(in) :: maxiter
379 procedure(minimizer_with_grad_i) :: f
380 procedure(info_i) :: write_iter_info
381 real(real64), intent(out) :: minimum
382 integer, intent(out) :: ierr
384 integer :: iter
385 real(real64), allocatable :: grad(:)
386 real(real64) :: step2, maxgrad
387
388 push_sub(minimize_sd)
389
390 safe_allocate(grad(1:dim))
391
392 step2 = step*10.0_real64
393 do iter = 1, maxiter
394 call f(dim, x, minimum, 1, grad)
395
396 maxgrad = maxval(abs(grad))
397
398 call write_iter_info(iter, dim, minimum, maxgrad*step2, maxgrad, x)
399
400 x(1:dim) = x(1:dim) - step2*grad(1:dim)
401
402 step2 = step2*0.99_real64
403 end do
404 ierr = 0
405
406 pop_sub(minimize_sd)
407 end subroutine minimize_sd
408
409 !----------------------------------------------
423 subroutine minimize_fire(dim, space_dim, x, step, tolgrad, maxiter, gradf, write_iter_info, en, ierr, mass, integrator)
424 integer, intent(in) :: dim
425 integer, intent(in) :: space_dim
426 real(real64), intent(inout) :: x(:)
427 real(real64), intent(in) :: step
428 real(real64), intent(in) :: tolgrad
429 integer, intent(in) :: maxiter
430 procedure(minimizer_with_grad_i) :: gradf
431 procedure(info_i) :: write_iter_info
432 real(real64), intent(out) :: en
433 integer, intent(out) :: ierr
434 real(real64), intent(in) :: mass(:)
435 integer, intent(in) :: integrator
436
437 real(real64), allocatable :: grad(:)
438
439 integer :: p_times, n_times, iter, ia
440 real(real64) :: dt, alpha, p_value, dt_max, dt_min, mix
441 real(real64), allocatable :: grad_atoms(:), vel(:), dr_i(:), x_old(:), dr_atoms(:)
442
443 ! Parameters from the original paper
444 integer, parameter :: n_min = 5, n_decrease_max = 500
445 real(real64), parameter :: f_alpha = 0.99_real64
446 real(real64), parameter :: f_inc = 1.1_real64
447 real(real64), parameter :: f_dec = 0.5_real64
448 real(real64), parameter :: alpha_start = 0.25_real64
449
450 real(real64), parameter :: tol_zero_forces = 1e-10_real64
451
452 push_sub(minimize_fire)
453
454 ! dim must be a multiple of space_dim
455 assert(mod(dim,space_dim) == 0)
456 assert(size(x) == dim)
457
458 safe_allocate(grad_atoms(1:dim/space_dim))
459 safe_allocate(grad(1:dim))
460 safe_allocate(vel(1:dim))
461 safe_allocate(dr_atoms(1:dim/space_dim))
462 safe_allocate(x_old(1:dim))
463 safe_allocate(dr_i(1:dim))
464
465 ! Initial values
466 ierr = 0
467 vel = m_zero
468 dt = step
469 alpha = alpha_start
470 dr_i = m_zero
471 dt_max = 10.0_real64 * dt
472 dt_min = 0.02_real64 * dt
473 p_times = 0
474 n_times = 0
475 call gradf(dim, x, en, 1, grad)
476 grad = -grad
477
478 do iter = 1, maxiter
479
480 ! Perform step F1: compute P = F.v
481 p_value = dot_product(grad, vel)
482
483 ! Perform step F3
484 if (p_value > m_zero) then
485 p_times = p_times + 1
486 n_times = 0
487 if (p_times > n_min) then
488 dt = min(dt * f_inc , dt_max)
489 alpha = alpha * f_alpha
490 end if
491 else ! or step F4
492 p_times = 0
493 n_times = n_times + 1
494
495 if (n_times > n_decrease_max) exit ! If we are stuck, we exit
496
497 if (iter >= n_min) then ! No decrease for the first few steps
498 dt = max(dt * f_dec, dt_min)
499 alpha = alpha_start
500 end if
501 x = x - m_half * vel * dt ! Correct uphill motion
502 vel = m_zero
503 end if
504
505 ! Store old values
506 x_old = x
507
508 ! Perform the MD step: get new gradients from the new positions
509 ! Step F2: v-> (1-\alpha)v + \alpha \hat{F}.|v| is done inside the MD step
510 ! We follow the integration schemes of the FIRE 2.0 paper
511
512 ! Prevent division by zero in the mixing
513 mix = lalg_nrm2(dim,grad)
514 if (mix > tol_zero_forces) then
515 mix = m_one / mix
516 else
517 mix = m_zero
518 end if
519
520 select case (integrator)
521 case (option__gofireintegrator__verlet) ! Velocity Verlet, see Algorithm 6
522 ! Velocity Verlet - update velocities 1
523 vel = vel + m_half * grad * dt / mass
524
525 ! Mixing
526 if (p_value > m_zero) then
527 vel = (m_one - alpha) * vel + alpha * mix * grad * lalg_nrm2(dim, vel)
528 end if
529
530 ! Get the new position
531 dr_i = vel * dt
532 call prevent_large_changes(dim, space_dim, dr_i, dr_atoms)
533 x = x_old + dr_i
534
535 ! Get the new gradient
536 call gradf(dim, x, en, 1, grad)
537 grad = -grad
538
539 ! Velocity Verlet - update velocities 2
540 vel = vel + m_half * grad * dt / mass
541
542 case (option__gofireintegrator__euler) ! Explicit Euler method, see Algorithm 3 in Fire2.0 paper
543 ! Mixing
544 if (p_value > m_zero) then
545 vel = (m_one - alpha) * vel + alpha * mix * grad * lalg_nrm2(dim, vel)
546 end if
547
548 ! Get the new position
549 dr_i = vel * dt
550 call prevent_large_changes(dim, space_dim, dr_i, dr_atoms)
551 x = x_old + dr_i
552
553 ! Get the new gradient
554 call gradf(dim, x, en, 1, grad)
555 grad = -grad
556
557 ! Velocity Verlet - update velocities
558 vel = vel + grad * dt / mass
559
560
561 case (option__gofireintegrator__semi_implicit_euler) ! Semi-implicit Euler method, see Algorithm 4
562 ! Velocity Verlet - update velocities
563 vel = vel + grad * dt / mass
564
565 ! Mixing
566 if (p_value > m_zero) then
567 vel = (m_one - alpha) * vel + alpha * mix * grad * lalg_nrm2(dim, vel)
568 end if
569
570 ! Get the new position
571 dr_i = vel * dt
572 call prevent_large_changes(dim, space_dim, dr_i, dr_atoms)
573 x = x_old + dr_i
574
575 ! Get the new gradient
576 call gradf(dim, x, en, 1, grad)
577 grad = -grad
578 end select
579
580 ! Get the norms of the gradients for each atom
581 do ia = 0, dim/space_dim - 1
582 grad_atoms(ia+1) = norm2(grad(space_dim*ia+1:space_dim*ia+space_dim))
583 end do
584
585 ! Output for each iteration step
586 call write_iter_info(iter, dim, en, maxval(dr_atoms), maxval(abs(grad_atoms)), x)
587
588 ! Check convergence
589 if (maxval(abs(grad_atoms(1:))) < tolgrad) exit
590 end do
591
592 ierr = -iter
593
594 safe_deallocate_a(dr_atoms)
595 safe_deallocate_a(x_old)
596 safe_deallocate_a(dr_i)
597 safe_deallocate_a(vel)
598 safe_deallocate_a(grad)
599 safe_deallocate_a(grad_atoms)
600
601 pop_sub(minimize_fire)
602 end subroutine minimize_fire
603
605 ! TODO: we should instead change the velocities,
606 ! see S. Echeverri Restrepo and P. Andric, Computational Materials Science 218 (2023) 111978
607 subroutine prevent_large_changes(dim, space_dim, dr_i, dr_atoms)
608 integer, intent(in) :: dim
609 integer, intent(in) :: space_dim
610 real(real64), intent(inout) :: dr_i(:), dr_atoms(:)
611
612 integer :: i1, i2, ia
613
614 real(real64), parameter :: maxmove = 0.2_real64 * p_ang
615
616 ! Get the norms of the displaments for each atoms
617 do ia = 0, dim/space_dim - 1
618 i1 = space_dim*ia+1
619 i2 = space_dim*ia+space_dim
620 dr_atoms(ia+1) = norm2(dr_i(i1:i2))
621 ! Rescale the displacement to avoid too large changes
622 if (dr_atoms(ia+1) > maxmove) then
623 dr_i(i1:i2) = maxmove * dr_i(i1:i2) / dr_atoms(ia+1)
624 dr_atoms(ia+1) = maxmove
625 end if
626 end do
627 end subroutine prevent_large_changes
628
629end module minimizer_oct_m
630
631!! Local Variables:
632!! mode: f90
633!! coding: utf-8
634!! 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
character(len=256), dimension(max_lines), public message
to be output by fatal, warning
Definition: messages.F90:162
subroutine, public messages_fatal(no_lines, only_root_writes, namespace)
Definition: messages.F90:410
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)