0.6.2-dev0
FORCES
FORtran lib for Comp. Env. Sys.
Loading...
Searching...
No Matches
mo_anneal.f90
Go to the documentation of this file.
1!> \file mo_anneal.f90
2!> \brief \copybrief mo_anneal
3!> \details \copydetails mo_anneal
4
5!> \brief Anneal optimization of cost function.
6!> \details Minimization of cost function and temperature finding of minima.
7!> \changelog
8!! - Juliane Mai, Mar 2012
9!! - module implementation
10!! - Juliane Mai, May 2012
11!! - anneal: sp version
12!! - Juliane Mai, May 2012
13!! - anneal: documentation
14!! - Juliane Mai, May 2012
15!! - GetTemperature: sp and dp version
16!! - Juliane Mai, Jun 2012
17!! - weighted parameter selection
18!! - Juliane Mai, Aug 2012
19!! - function anneal instead of subroutine
20!! - using new module get_timeseed as default for seeding
21!! - new optional for minimization or maximization
22!! - fixed parameter ranges possible instead of interface range
23!! - Juliane Mai, Nov 2012
24!! - history of achieved objective function values as optional out only in anneal but not anneal_valid
25!! - Juliane Mai, Jan 2013
26!! - including DDS features in anneal, i.e. reflection at parameter boundaries,
27!! different parameter selection modes (one, all, neighborhood), and
28!! different parameter pertubation modes (flexible r=dR (anneal version) or
29!! constant r=0.2 (dds version))
30!! - remove sp versions
31!! - fixed and flexible parameter ranges are now in one function
32!! using optional arguments
33!! - undef_funcval instead of anneal_valid function
34!! - Juliane Mai, Feb 2013
35!! - xor4096 optionals combined in save_state
36!! - Arya Prasetya, Dec 2021
37!! - doxygen documentation anneal and get_temperature
38!> \author Juliane Mai
39!> \date Mar 2012
40!> \copyright Copyright 2005-\today, the CHS Developers, Sabine Attinger: All rights reserved.
41!! FORCES is released under the LGPLv3+ license \license_note
43
44 USE mo_kind, ONLY : i4, i8, dp
45 USE mo_utils, ONLY : le, ge
48
49 IMPLICIT NONE
50
51 PUBLIC :: anneal ! Minimization of a cost function via Simaulated Annealing
52 PUBLIC :: gettemperature ! Function which returns the optimal initial temperature for
53 ! ! a given acceptance ratio and initial parameter set
54
55 ! ------------------------------------------------------------------
56
57 !> \brief Optimize cost function with simulated annealing.
58
59 !> \details
60 !! Optimizes a user provided cost function using the Simulated Annealing strategy.
61 !!
62 !! \b Example
63 !!
64 !! User defined function `cost_dp` which calculates the cost function value for a
65 !! parameter set (the interface given below has to be used for this function!).
66 !!
67 !! \code{.f90}
68 !! para = (/ 1.0_dp , 2.0_dp /)
69 !! prange(1,:) = (/ 0.0_dp, 10.0_dp /)
70 !! prange(2,:) = (/ 0.0_dp, 50.0_dp /)
71 !!
72 !! parabest = anneal(cost_dp, para, prange)
73 !! \endcode
74 !!
75 !! See also test folder for a detailed example, "test/test_mo_anneal".
76 !!
77 !! \b Literature
78 !! 1. S. Kirkpatrick, C. D. Gelatt, and M. P. Vecchi.
79 !! _Optimization by simulated annealing_.
80 !! Science, 220:671-680, 1983.
81 !! 2. N. Metropolis, A. W. Rosenbluth, M. N. Rosenbluth, A. H. Teller, and E. Teller.
82 !! _Equation of state calculations by fast computing machines_.
83 !! J. Chem. Phys., 21:1087-1092, June 1953.
84 !! 3. B. A. Tolson, and C. A. Shoemaker.
85 !! _Dynamically dimensioned search algorithm for computationally efficient watershed model calibration_.
86 !! WRR, 43(1), W01413, 2007.
87 !!
88 !> \param[in] "INTERFACE :: eval" Interface calculating the eval function at a given point.
89 !> \param[in] "INTERFACE :: cost" Interface calculating the cost function at a given point.
90 !> \param[in] "real(dp), dimension(:) :: para" Initial parameter set.
91 !> \param[in] "real(dp), dimension(size(para),2), optional :: prange"
92 !! Lower and upper bound per parameter.
93 !> \param[in] "interface, optional :: prange_func" Interface calculating the
94 !! feasible range for a parameter
95 !! at a certain point, if ranges are variable.
96 !> \param[in] "real(dp), optional :: temp" Initial temperature. \n
97 !! DEFAULT: Get_Temperature.
98 !> \param[in] "real(dp), optional :: DT" Geometrical decreement of temperature. \n
99 !! 0.7<DT<0.999. \n
100 !! DEFAULT: 0.9_dp.
101 !> \param[in] "integer(i4), optional :: nITERmax" Maximal number of iterations. \n
102 !! Will be increased by 10% if stopping criteria of
103 !! acceptance ratio or epsilon decreement of cost
104 !! function is not fullfilled. \n
105 !! DEFAULT: 1000_i4.
106 !> \param[in] "integer(i4), optional :: LEN" Length of Markov Chain. \n
107 !! DEFAULT: MAX(250_i4, size(para,1)).
108 !> \param[in] "integer(i4), optional :: nST" Number of consecutive LEN steps. \n
109 !! DEFAULT: 5_i4.
110 !> \param[in] "real(dp), optional :: eps" Stopping criteria of epsilon decreement of
111 !! cost function \n
112 !! DEFAULT: 0.0001_dp
113 !> \param[in] "real(dp), optional :: acc" Stopping criteria for Acceptance Ratio \n
114 !! acc <= 0.1_dp \n
115 !! DEFAULT: 0.1_dp
116 !> \param[in] "integer(i4/i8), dimension(3), optional :: seeds"
117 !! Seeds of random numbers used for random parameter
118 !! set generation \n
119 !! DEFAULT: dependent on current time
120 !> \param[in] "logical, optional :: printflag"
121 !! If .true. detailed command line output is written \n
122 !! DEFAULT: .false.
123 !> \param[in] "logical, dimension(size(para)), optional :: maskpara"
124 !! maskpara(i) = .true. --> parameter is optimized \n
125 !! maskpara(i) = .false. --> parameter is discarded
126 !! from optimiztaion \n
127 !! DEFAULT: .true.
128 !> \param[in] "real(dp), dimension(size(para)), optional :: weight"
129 !! vector of weights per parameter: \n
130 !! gives the frequency of parameter to be chosen for
131 !! optimization (will be scaled to a CDF internally) \n
132 !! eg. [1,2,1] --> parameter 2 is chosen twice as
133 !! often as parameter 1 and 2 \n
134 !! DEFAULT: weight = 1.0_dp
135 !> \param[in] "integer(i4), optional :: changeParaMode" which and how many param.are changed in one step \n
136 !! 1 = one parameter \n
137 !! 2 = all parameter \n
138 !! 3 = neighborhood parameter \n
139 !! DEFAULT: 1_i4
140 !> \param[in] "logical, optional :: reflectionFlag" if new parameter values are Gaussian
141 !! distributed and reflected (.true.) or
142 !! uniform in range (.false.) \n
143 !! DEFAULT: .false.
144 !> \param[in] "logical, optional :: pertubFlexFlag" if pertubation of Gaussian distributed
145 !! parameter values is constant
146 !! at 0.2 (.false.) or
147 !! depends on dR (.true.) \n
148 !! DEFAULT: .true.
149 !> \param[in] "logical, optional :: maxit" maximizing (.true.) or minimizing (.false.)
150 !! a function \n
151 !! DEFAULT: .false. (minimization)
152 !> \param[in] "real(dp), optional :: undef_funcval" objective function value defining invalid
153 !! model output, e.g. -999.0_dp \n
154 !> \param[in] "character(len=*) , optional :: tmp_file" file with temporal output
155 !> \param[out] "real(dp), optional :: funcbest" minimized value of cost function
156 !> \param[out] "real(dp), dimension(:,:), allocatable, optional :: history"
157 !! returns a vector of achieved objective
158 !! after ith model evaluation
159 !> \retval "real(dp) :: parabest(size(para))" Parameter set minimizing the cost function.
160
161 !> \note
162 !! - Either fixed parameter range (`prange`) OR flexible parameter range (function interface `prange_func`)
163 !! has to be given in calling sequence.
164 !! - Only double precision version available.
165 !! - If single precision is needed not only dp has to be replaced by sp
166 !! but also i8 of `save_state` (random number variables) has to be replaced by i4.
167 !! - ParaChangeMode > 1 is not applied in GetTemperature.
168 !! - For Temperature estimation always only one single parameter is changed (ParaChangeMode=1)
169 !! which should give theoretically always the best estimate.
170 !! - `cost_func` and `prange_func` are user defined functions. See interface definition.
171
172 !> \author Luis Samaniego
173 !> \date Jan 2000
174 !> \date Mar 2003
175 !! - Re-heating
176
177 !> \author Juliane Mai
178 !> \date Mar 2012
179 !! - Modular version
180 !> \date May 2012
181 !! - sp version
182 !! - documentation
183
184 ! ------------------------------------------------------------------
185
186 INTERFACE anneal
187 MODULE PROCEDURE anneal_dp
188 END INTERFACE anneal
189
190 ! ------------------------------------------------------------------
191
192 !> \brief Find initial temperature for simulated annealing.
193
194 !> \details Determines an initial temperature for Simulated Annealing achieving
195 !! certain acceptance ratio.
196 !!
197 !! \b Example
198 !!
199 !! User defined function 'cost_dp' which calculates the cost function value for a
200 !! parameter set (the interface given below has to be used for this function!).
201 !!
202 !! \code{.f90}
203 !! para = (/ 1.0_dp , 2.0_dp /)
204 !! acc_goal = 0.95_dp
205 !! prange(1,:) = (/ 0.0_dp, 10.0_dp /)
206 !! prange(2,:) = (/ 0.0_dp, 50.0_dp /)
207 !!
208 !! temp = GetTemperature(para, cost_dp, acc_goal, prange)
209 !! \endcode
210 !!
211 !! See also test folder for a detailed example, "pf_tests/test_mo_anneal".
212 !!
213 !! \b Literature
214 !! 1. Walid Ben-Ameur.
215 !! _Compututing the Initial Temperature of Simulated Annealing_.
216 !! Comput. Opt. and App. (2004).
217 !!
218 !> \param[in] "real(dp), dimension(:) :: paraset" Initial (valid) parameter set.
219 !> \param[in] "INTERFACE :: eval" Interface calculating the eval function at a given point.
220 !> \param[in] "INTERFACE :: cost" Interface calculating the cost function at a given point.
221 !> \param[in] "INTERFACE :: prange_func" Interface for functional ranges.
222 !> \param[in] "real(dp) :: acc_goal" Acceptance Ratio which has to be achieved.
223 !> \param[in] "real(dp), dimension(size(para),2), optional :: prange"
224 !! Lower and upper bound per parameter.
225 !> \param[in] "INTERFACE, optional :: prange_func" Interface calculating the
226 !! feasible range for a parameter
227 !! at a certain point, if ranges are variable.
228 !> \param[in] "integer(i4), optional :: samplesize" Number of iterations the estimation of temperature
229 !! is based on. \n
230 !! DEFAULT: Max(20_i4*n,250_i4)
231 !> \param[in] "logical, dimension(size(para)), optional :: maskpara"
232 !! maskpara(i) = .true. --> parameter is optimized. \n
233 !! maskpara(i) = .false. --> parameter is discarded
234 !! from optimiztaion. \n
235 !! DEFAULT: .true. .
236 !> \param[in] "INTEGER(I4/I8), dimension(2), optional :: seeds"
237 !! Seeds of random numbers used for random parameter
238 !! set generation. \n
239 !! DEFAULT: dependent on current time.
240 !> \param[in] "logical, optional :: printflag"
241 !! If .true. detailed command line output is written.
242 !! DEFAULT: .false. .
243 !> \param[in] "real(dp), dimension(size(para,1)), optional :: weight"
244 !! Vector of weights per parameter. \n
245 !! Gives the frequency of parameter to be chosen for
246 !! optimization (will be scaled to a CDF internally). \n
247 !! eg. [1,2,1] --> parameter 2 is chosen twice as
248 !! often as parameter 1 and 2. \n
249 !! DEFAULT: weight = 1.0_dp.
250 !> \param[in] "logical, optional :: maxit" Minimizing (.false.) or maximizing (.true.)
251 !! a function. \n
252 !! DEFAULT: .false. (minimization).
253 !> \param[in] "real(dp), optional :: undef_funcval" Objective function value defining invalid
254 !! model output, e.g. -999.0_dp.
255
256 !> \retval "real(dp) :: temperature" Temperature achieving a certain
257 !! acceptance ratio in Simulated Annealing.
258
259 !> \note
260 !! - Either fixed parameter range (`prange`) OR flexible parameter range (function interface `prange_func`)
261 !! has to be given in calling sequence.
262 !! - Only double precision version available.
263 !! - If single precision is needed not only dp has to be replaced by sp
264 !! but also i8 of `save_state` (random number variables) has to be replaced by i4.
265 !! - ParaChangeMode > 1 is not applied in GetTemperature.
266 !! - For Temperature estimation always only one single parameter is changed (ParaChangeMode=1)
267 !! which should give theoretically always the best estimate.
268 !! - `cost_dp` and `prange_func` are user defined functions. See interface definition.
269
270 !> \author Juliane Mai
271 !> \date May 2012
272
273 ! ------------------------------------------------------------------
274
276 MODULE PROCEDURE gettemperature_dp
277 END INTERFACE gettemperature
278
279 PRIVATE
280
282 MODULE PROCEDURE generate_neighborhood_weight_dp
283 END INTERFACE generate_neighborhood_weight
284
285 ! ------------------------------------------------------------------
286
287CONTAINS
288
289 FUNCTION anneal_dp(eval, cost, para, & ! obligatory
290 prange, prange_func, & ! optional IN: exactly one of both
291 temp, Dt, nITERmax, Len, nST, eps, acc, & ! optional IN
292 seeds, printflag, maskpara, weight, & ! optional IN
293 changeParaMode, reflectionFlag, & ! optional IN
294 pertubFlexFlag, maxit, undef_funcval, & ! optional IN
295 tmp_file, & ! optional IN
296 funcbest, history & ! optional OUT
297 ) &
298 result(parabest)
299
300 IMPLICIT NONE
301
302 procedure(eval_interface), INTENT(IN), POINTER :: eval
303 procedure(objective_interface), intent(in), pointer :: cost
304
305 INTERFACE
306 SUBROUTINE prange_func(paraset, iPar, rangePar)
307 ! gives the range (min,max) of the parameter iPar at a certain parameter set paraset
308 use mo_kind
309 real(dp), dimension(:), INTENT(IN) :: paraset
310 integer(i4), INTENT(IN) :: ipar
311 real(dp), dimension(2), INTENT(OUT) :: rangepar
312 END SUBROUTINE prange_func
313 END INTERFACE
314 optional :: prange_func
315
316 real(dp), dimension(:), intent(in) :: para !< initial parameter
317
318 real(dp), optional, dimension(size(para, 1), 2), intent(in) :: prange !< lower and upper limit per parameter
319 real(dp), optional, intent(in) :: temp !< starting temperature (DEFAULT: Get_Temperature)
320 real(dp), optional, intent(in) :: dt !< geometrical decreement, 0.7<DT<0.999 (DEFAULT: 0.9)
321 integer(i4), optional, intent(in) :: nitermax !< maximal number of iterations (DEFAULT: 1000)
322 integer(i4), optional, intent(in) :: len !< Length of Markov Chain,
323 !! DEFAULT: max(250, size(para,1))
324 integer(i4), optional, intent(in) :: nst !< Number of consecutive LEN steps! (DEFAULT: 5)
325 real(dp), optional, intent(in) :: eps !< epsilon decreement of cost function (DEFAULT: 0.01)
326 real(dp), optional, intent(in) :: acc !< Acceptance Ratio, <0.1 stopping criteria
327 !! (DEFAULT: 0.1)
328 INTEGER(I8), optional, intent(in) :: seeds(3) !< Seeds of random numbers (DEFAULT: Get_timeseed)
329 logical, optional, intent(in) :: printflag !< If command line output is written (.true.)
330 !! (DEFAULT: .false.)
331 logical, optional, dimension(size(para, 1)), intent(in) :: maskpara !< true if parameter will be optimized
332 !! false if parameter is discarded in optimization
333 !! (DEFAULT: .true.)
334 real(dp), optional, dimension(size(para, 1)), intent(in) :: weight !< vector of weights per parameter
335 !! gives the frequency of parameter to be
336 !! chosen for optimization (DEFAULT: uniform)
337 integer(i4), optional, intent(in) :: changeparamode !< which and how many param. are changed in a step
338 !! 1 = one parameter 2 = all parameter
339 !! 3 = neighborhood parameter (DEFAULT: 1_i4)
340 logical, optional, intent(in) :: reflectionflag !< if new parameter values are selected normal
341 !! distributed and reflected (.true.) or
342 !! uniform in range (.false.) (DEFAULT: .false.)
343 logical, optional, intent(in) :: pertubflexflag !< if pertubation of normal distributed parameter
344 !! values is constant 0.2 (.false.) or
345 !! depends on dR (.true.) (DEFAULT: .true.)
346 logical, optional, intent(in) :: maxit !< Maximization or minimization of function
347 !! maximization = .true., minimization = .false.
348 !! (DEFAULT: .false.)
349 real(dp), optional, intent(in) :: undef_funcval !< objective function value occuring if
350 !! parameter set leads to invalid model results,
351 !! e.g. -9999.0_dp! (DEFAULT: not present)
352 CHARACTER(LEN = *), optional, intent(in) :: tmp_file !< file for temporal output
353 real(dp), optional, intent(out) :: funcbest !< minimized value of cost function
354 !! (DEFAULT: not present)
355 real(dp), optional, dimension(:, :), allocatable, intent(out) :: history !< returns a vector of achieved objective!
356 !! after ith model evaluation (DEFAULT: not present)
357
358 real(dp), dimension(size(para, 1)) :: parabest !< parameter set minimizing objective
359
360 integer(i4) :: n ! Number of parameters
361 integer(i4) :: ipar, par ! counter for parameter
362 real(dp), dimension(2) :: iparrange ! parameter's range
363 real(dp) :: t_in ! Temperature
364 real(dp) :: dt_in ! Temperature decreement
365 integer(i4) :: nitermax_in ! maximal number of iterations
366 integer(i4) :: len_in ! Length of Markov Chain
367 integer(i4) :: nst_in ! Number of consecutive LEN steps
368 real(dp) :: eps_in ! epsilon decreement of cost function
369 real(dp) :: acc_in ! Acceptance Ratio, <0.1 stopping criteria
370 INTEGER(I8) :: seeds_in(3) ! Seeds of random numbers
371 logical :: printflag_in ! If command line output is written
372 logical :: coststatus ! Checks status of cost function value,
373 ! ! i.e. is parameter set is feasible
374 logical, dimension(size(para, 1)) :: maskpara_in ! true if parameter will be optimized
375 integer(i4), dimension(:), allocatable :: truepara ! indexes of parameters to be optimized
376 real(dp), dimension(size(para, 1)) :: weight_in ! CDF of parameter chosen for optimization
377 real(dp), dimension(size(para, 1)) :: weightuni ! uniform CDF of parameter chosen for optimization
378 real(dp), dimension(size(para, 1)) :: weightgrad ! linear combination of weight and weightUni
379 integer(i4) :: changeparamode_inin ! 1=one, 2=all, 3=neighborhood
380 logical :: reflectionflag_inin ! true=gaussian distributed and reflected,
381 ! ! false=uniform distributed in range
382 logical :: pertubflexflag_inin ! variance of normal distributed parameter values
383 ! ! .true. = depends on dR, .false. = constant 0.2
384 real(dp) :: maxit_in ! maximization = -1._dp, minimization = 1._dp
385 real(dp) :: costbest ! minimized value of cost function
386 real(dp), dimension(:, :), allocatable :: history_out ! best cost function value after k iterations
387 real(dp), dimension(:, :), allocatable :: history_out_tmp ! helper vector
388
389 type paramlim
390 real(dp) :: min ! minimum value
391 real(dp) :: max ! maximum value
392 real(dp) :: new ! new state value
393 real(dp) :: old ! old state value
394 real(dp) :: best ! best value found
395 real(dp) :: dmult ! sensitivity multiplier for parameter search
396 end type paramlim
397 type (paramlim), dimension (size(para, 1)), target :: gamma ! Parameter
398
399 ! for random numbers
400 real(dp) :: rn1, rn2, rn3 ! Random numbers
401 integer(I8), dimension(n_save_state) :: save_state_1 ! optional arguments for restarting RN stream 1
402 integer(I8), dimension(n_save_state) :: save_state_2 ! optional arguments for restarting RN stream 2
403 integer(I8), dimension(n_save_state) :: save_state_3 ! optional arguments for restarting RN stream 3
404 ! for dds parameter selection
405 logical, dimension(size(para, 1)) :: neighborhood ! selected parameter in neighborhood
406 real(dp) :: pertubationr ! neighborhood pertubation size parameter
407
408 ! for SA
409 integer(i4) :: idummy, i
410 real(dp) :: normphi
411 real(dp) :: ac_ratio, pa
412 integer(i4), dimension(:, :), allocatable :: ipos_ineg_history ! stores iPos and iNeg of nST last Markov Chains
413 real(dp) :: fo, fn, df, fbest
414 real(dp) :: rho, finc, fbb, dr
415 real(dp) :: t0, dt0
416 real(dp), parameter :: small = -700._dp
417 integer(i4) :: j, iter, kk
418 integer(i4) :: ipos, ineg
419 integer(i4) :: iconl, iconr, iconf
420 integer(i4) :: itotalcounter ! includes reheating for final conditions
421 integer(i4) :: itotalcounterr ! counter of interations in one reheating
422 logical :: istop
423 logical :: ldummy
424
425 ! CHECKING OPTIONALS
426
427 n = size(para, 1)
428
429 ! either range or rangfunc has to be given
430 if (present(prange) .eqv. present(prange_func)) then
431 stop 'anneal: Either range or prange_func has to be given'
432 end if
433
434 if (present(dt)) then
435 if ((dt .lt. 0.7_dp) .or. (dt .gt. 0.999_dp)) then
436 stop 'Input argument DT must lie between 0.7 and 0.999'
437 else
438 dt_in = dt
439 end if
440 else
441 dt_in = 0.9_dp
442 end if
443
444 if (present(nitermax)) then
445 if (nitermax .lt. 1_i4) then
446 stop 'Input argument nITERmax must be greater 0'
447 else
448 nitermax_in = nitermax
449 end if
450 else
451 nitermax_in = 1000_i4
452 end if
453
454 if (present(len)) then
455 if (len .lt. max(20_i4 * n, 250_i4)) then
456 print*, 'WARNING: Input argument LEN should be greater than Max(250,20*N), N=number of parameters'
457 len_in = len
458 else
459 len_in = len
460 end if
461 else
462 len_in = max(20_i4 * n, 250_i4)
463 end if
464
465 idummy = nitermax_in / len_in + 1_i4
466 allocate(history_out(idummy, 2))
467
468 if (present(nst)) then
469 if (nst .lt. 0_i4) then
470 stop 'Input argument nST must be greater than 0'
471 else
472 nst_in = nst
473 end if
474 else
475 nst_in = 5_i4
476 end if
477
478 allocate(ipos_ineg_history(nst_in, 2))
479 ipos_ineg_history = 0_i4
480
481 if (present(eps)) then
482 if (le(eps, 0.0_dp)) then
483 stop 'Input argument eps must be greater than 0'
484 else
485 eps_in = eps
486 end if
487 else
488 eps_in = 0.0001_dp
489 end if
490
491 if (present(acc)) then
492 if (le(acc, 0.0_dp) .or. ge(acc, 1.0_dp)) then
493 stop 'Input argument acc must lie between 0.0 and 1.0'
494 else
495 acc_in = acc
496 end if
497 else
498 acc_in = 0.1_dp
499 end if
500
501 if (present(seeds)) then
502 seeds_in = seeds
503 else
504 ! Seeds depend on actual time
505 call get_timeseed(seeds_in)
506 end if
507
508 if (present(printflag)) then
509 printflag_in = printflag
510 else
511 printflag_in = .false.
512 end if
513
514 if (present(maskpara)) then
515 if (count(maskpara) .eq. 0_i4) then
516 stop 'Input argument maskpara: At least one element has to be true'
517 else
518 maskpara_in = maskpara
519 end if
520 else
521 maskpara_in = .true.
522 end if
523
524 if (present(maxit)) then
525 ldummy = maxit
526 if (maxit) then
527 maxit_in = -1._dp
528 else
529 maxit_in = 1._dp
530 end if
531 else
532 ldummy = .false.
533 maxit_in = 1._dp
534 end if
535
536 if (present(temp)) then
537 if ((temp .lt. 0.0_dp)) then
538 stop 'Input argument temp must be greater then zero'
539 else
540 t_in = temp
541 end if
542 else
543 if (present(prange_func)) then
544 if (present(undef_funcval)) then
545 t_in = gettemperature(eval, cost, para, 0.95_dp, prange_func = prange_func, &
546 maskpara = maskpara_in, samplesize = 2_i4 * len_in, &
547 seeds = seeds_in(1 : 2), printflag = printflag_in, &
548 maxit = ldummy, undef_funcval = undef_funcval)
549 else
550 t_in = gettemperature(eval, cost, para, 0.95_dp, prange_func = prange_func, &
551 maskpara = maskpara_in, samplesize = 2_i4 * len_in, &
552 seeds = seeds_in(1 : 2), printflag = printflag_in, &
553 maxit = ldummy)
554 end if
555 else
556 if (present(undef_funcval)) then
557 t_in = gettemperature(eval, cost, para, 0.95_dp, prange = prange, &
558 maskpara = maskpara_in, samplesize = 2_i4 * len_in, &
559 seeds = seeds_in(1 : 2), printflag = printflag_in, &
560 maxit = ldummy, undef_funcval = undef_funcval)
561 else
562 t_in = gettemperature(eval, cost, para, 0.95_dp, prange = prange, &
563 maskpara = maskpara_in, samplesize = 2_i4 * len_in, &
564 seeds = seeds_in(1 : 2), printflag = printflag_in, &
565 maxit = ldummy)
566 end if
567 end if
568 end if
569
570 if (present(changeparamode)) then
571 changeparamode_inin = changeparamode
572 else
573 changeparamode_inin = 1_i4
574 end if
575
576 if (present(reflectionflag)) then
577 reflectionflag_inin = reflectionflag
578 else
579 reflectionflag_inin = .false.
580 end if
581
582 if (present(pertubflexflag)) then
583 pertubflexflag_inin = pertubflexflag
584 else
585 pertubflexflag_inin = .true.
586 end if
587
588 ! Temporal file writing
589 if(present(tmp_file)) then
590 open(unit = 999, file = trim(adjustl(tmp_file)), action = 'write', status = 'unknown')
591 write(999, *) '# settings :: general'
592 write(999, *) '# nIterations iseed'
593 write(999, *) nitermax_in, seeds_in
594 write(999, *) '# settings :: anneal specific'
595 write(999, *) '# sa_tmp'
596 write(999, *) t_in
597 write(999, *) '# iter bestf (bestx(j),j=1,nopt)'
598 close(999)
599 end if
600
601 ! INITIALIZATION
602
603 allocate (truepara(count(maskpara_in)))
604 idummy = 0_i4
605 do i = 1, n
606 if (maskpara_in(i)) then
607 idummy = idummy + 1_i4
608 truepara(idummy) = i
609 end if
610 end do
611
612 if (printflag_in) then
613 print*, 'Following parameters will be optimized: ', truepara
614 end if
615
616 weight_in = 0.0_dp
617 weightuni = 0.0_dp
618 if (present(weight)) then
619 where (maskpara_in(:))
620 weight_in(:) = weight(:)
621 weightuni(:) = 1.0_dp
622 end where
623 else
624 where (maskpara_in(:))
625 weight_in(:) = 1.0_dp
626 weightuni(:) = 1.0_dp
627 end where
628 end if
629 ! scaling the weights
630 weight_in = weight_in / sum(weight_in)
631 weightuni = weightuni / sum(weightuni)
632 ! cummulating the weights
633 do i = 2, n
634 weight_in(i) = weight_in(i) + weight_in(i - 1)
635 weightuni(i) = weightuni(i) + weightuni(i - 1)
636 end do
637
638 call xor4096 (seeds_in(1), rn1, save_state = save_state_1)
639 call xor4096 (seeds_in(2), rn2, save_state = save_state_2)
640 call xor4096g(seeds_in(3), rn3, save_state = save_state_3)
641 seeds_in = 0_i8
642
643 ! Start Simulated Annealing routine
644 gamma(:)%dmult = 1.0_dp
645 gamma(:)%new = para(:)
646 gamma(:)%old = para(:)
647 gamma(:)%best = para(:)
648 normphi = -9999.9_dp
649 t0 = t_in
650 dt0 = dt_in
651
652 ! Generate and evaluate the initial solution state
653 fo = cost(gamma(:)%old, eval) * maxit_in
654 if (abs(fo) .lt. tiny(0.0_dp)) fo = 0.0000001_dp * maxit_in
655
656 file_write : if (present(tmp_file)) then
657 open(unit = 999, file = trim(adjustl(tmp_file)), action = 'write', position = 'append', recl = (n + 2) * 30)
658 if (.not. ldummy) then
659 write(999, *) '0', fo, gamma(:)%old
660 else
661 write(999, *) '0', -fo, gamma(:)%old
662 end if
663 close(999)
664 end if file_write
665
666 ! initialize counters /var for new SA
667 iconl = 0_i4
668 iconr = 1_i4
669 iconf = 0_i4
670 itotalcounter = 0_i4
671 itotalcounterr = 0_i4
672 fn = 0.0_dp
673 ac_ratio = 1.0_dp
674 istop = .true.
675 iter = 0_i4
676 ! restoring initial T param.
677 dt_in = dt0
678 ! Storing the best solution so far
679 normphi = fo * maxit_in
680 fo = fo / normphi
681 fbest = 1.0_dp * maxit_in
682
683 if (printflag_in) then
684 print '(A15,E15.7,A4,E15.7,A4)', ' start NSe = ', fbest * maxit_in, ' ( ', fbest * normphi * maxit_in, ' ) '
685 print '(I8, 2I5, 4E15.7)', 1_i4, 0_i4, 0_i4, 1._dp, t_in, fo * normphi * maxit_in, fbest * normphi * maxit_in
686 end if
687
688 ! ****************** Stop Criterium *******************
689 ! Repeat until the % reduction of nST consecutive Markov
690 ! chains (LEN) of the objective function (f) <= epsilon
691 ! ******************************************************
692 looptest : do while (istop)
693 iter = iter + 1_i4
694 ipos = 0_i4
695 ineg = 0_i4
696 fbb = fbest
697 !LEN_IN = int( real(iter,dp)*sqrt(real(iter,dp)) / nITER + 1.5*real(N,dp),i4)
698 ! Repeat LEN times with feasible solution
699 j = 1
700 looplen : do while (j .le. len_in)
701
702 itotalcounterr = itotalcounterr + 1_i4
703 itotalcounter = itotalcounter + 1_i4
704
705 ! Generate a random subsequent state and evaluate its objective function
706 ! (1) Generate new parameter set
707 dr = (1.0_dp - real(itotalcounterr, dp) / real(nitermax_in, dp))**2.0_dp
708 if (.not. (ge(dr, 0.05_dp)) .and. &
709 (itotalcounterr <= int(real(nitermax_in, dp) / 3._dp * 4_dp, i4))) then
710 dr = 0.05_dp
711 end if
712
713 if (pertubflexflag_inin) then
714 pertubationr = max(dr / 5.0_dp, 0.05_dp)
715 else
716 pertubationr = 0.2_dp
717 end if
718
719 ! gradual weights:
720 ! acc_ratio close to 1 --> weight
721 ! acc_ratio close to 0 --> weight uniform
722 ! gradual weighting is linear combination of these two weights
723 weightgrad(:) = weightuni(:) + (ac_ratio) * (weight_in(:) - weightuni(:))
724
725 select case(changeparamode_inin)
726 case(1_i4) ! only one parameter is changed
727 ! (1a) Change one parameter traditionally
728 call xor4096(seeds_in(2), rn2, save_state = save_state_2)
729 ipar = 1_i4
730 !
731 do while (weightgrad(ipar) .lt. rn2)
732 ipar = ipar + 1_i4
733 end do
734 if (present(prange_func)) then
735 call prange_func(gamma(:)%old, ipar, iparrange)
736 else
737 iparrange(1) = prange(ipar, 1)
738 iparrange(2) = prange(ipar, 2)
739 end if
740 gamma(ipar)%min = iparrange(1)
741 gamma(ipar)%max = iparrange(2)
742 if (reflectionflag_inin) then
743 call xor4096g(seeds_in(3), rn3, save_state = save_state_3)
744 gamma(ipar)%new = pargen_dds_dp(gamma(ipar)%old, pertubationr, &
745 gamma(ipar)%min, gamma(ipar)%max, rn3)
746 else
747 call xor4096(seeds_in(2), rn2, save_state = save_state_2)
748 gamma(ipar)%new = pargen_anneal_dp(gamma(ipar)%old, dr, &
749 gamma(ipar)%min, gamma(ipar)%max, rn2)
750 end if
751 case(2_i4) ! all parameter are changed
752 do par = 1, size(truepara)
753 ipar = truepara(par)
754 if (present(prange_func)) then
755 call prange_func(gamma(:)%old, ipar, iparrange)
756 else
757 iparrange(1) = prange(ipar, 1)
758 iparrange(2) = prange(ipar, 2)
759 end if
760 gamma(ipar)%min = iparrange(1)
761 gamma(ipar)%max = iparrange(2)
762 if (reflectionflag_inin) then
763 call xor4096g(seeds_in(3), rn3, save_state = save_state_3)
764 gamma(ipar)%new = pargen_dds_dp(gamma(ipar)%old, pertubationr, &
765 gamma(ipar)%min, gamma(ipar)%max, rn3)
766 else
767 call xor4096(seeds_in(2), rn2, save_state = save_state_2)
768 gamma(ipar)%new = pargen_anneal_dp(gamma(ipar)%old, dr, &
769 gamma(ipar)%min, gamma(ipar)%max, rn2)
770 end if
771 end do
772 case(3_i4) ! parameter in neighborhood are changed
773 ! Generate new neighborhood
774 call generate_neighborhood_weight_dp(truepara, weightgrad, save_state_1, &
775 itotalcounter, nitermax_in, neighborhood)
776 !
777 ! change parameter in neighborhood
778 do ipar = 1, n
779 if (neighborhood(ipar)) then
780 ! find range of parameter
781 if (present(prange_func)) then
782 call prange_func(gamma(:)%old, ipar, iparrange)
783 else
784 iparrange(1) = prange(ipar, 1)
785 iparrange(2) = prange(ipar, 2)
786 end if
787 gamma(ipar)%min = iparrange(1)
788 gamma(ipar)%max = iparrange(2)
789 !
790 if (reflectionflag_inin) then
791 ! generate gaussian distributed new parameter value which is reflected if out of bound
792 call xor4096g(seeds_in(3), rn3, save_state = save_state_3)
793 gamma(ipar)%new = pargen_dds_dp(gamma(ipar)%old, pertubationr, &
794 gamma(ipar)%min, gamma(ipar)%max, rn3)
795 else
796 ! generate new parameter value uniform distributed in range (no reflection)
797 call xor4096(seeds_in(2), rn2, save_state = save_state_2)
798 gamma(ipar)%new = pargen_anneal_dp(gamma(ipar)%old, dr, &
799 gamma(ipar)%min, gamma(ipar)%max, rn2)
800 end if
801 end if
802 end do
803 end select
804
805 ! (2) Calculate new objective function value
806 fn = cost(gamma(:)%new, eval) * maxit_in
807 coststatus = .true.
808 if (present(undef_funcval)) then
809 if (abs(fn * maxit_in - undef_funcval) .lt. tiny(1.0_dp)) then
810 coststatus = .false.
811 end if
812 end if
813
814 feasible : if (coststatus) then ! feasible parameter set
815 fn = fn / normphi
816
817 ! Change in cost function value
818 df = fn - fo
819
820 ! analyze change in the objective function: df
821 if (df < 0.0_dp) then
822
823 ! accept the new state
824 ipos = ipos + 1_i4
825 fo = fn
826 gamma(:)%old = gamma(:)%new
827
828 ! keep best solution
829 if (fo < fbest) then
830 fbest = fo
831 gamma(:)%best = gamma(:)%new
832 end if
833 else
834 if (df > eps_in) then
835 rho = -df / t_in
836 if (rho < small) then
837 pa = 0.0_dp
838 else
839 pa = exp(rho)
840 end if
841 !
842 call xor4096(seeds_in(1), rn1, save_state = save_state_1)
843 !
844 if (pa > rn1) then
845 ! accept new state with certain probability
846 ineg = ineg + 1_i4
847 fo = fn
848 ! save old state
849 gamma(:)%old = gamma(:)%new
850 end if
851 end if
852 end if
853 j = j + 1
854 else
855 ! function value was not valid
856 itotalcounterr = itotalcounterr - 1_i4
857 itotalcounter = itotalcounter - 1_i4
858 end if feasible !valid parameter set
859 end do looplen
860
861 ! estimate acceptance ratio
862 ac_ratio = (real(ipos, dp) + real(ineg, dp)) / real(len_in, dp)
863 if (modulo(itotalcounter, len_in * nst_in) / len_in .gt. 0_i4) then
864 idummy = modulo(itotalcounter, len_in * nst_in) / len_in
865 else
866 idummy = nst_in
867 end if
868 ipos_ineg_history(idummy, :) = (/ ipos, ineg /)
869
870 ! store current best in history vector
871 history_out(itotalcounter / len_in, 1) = real(itotalcounter, dp)
872 history_out(itotalcounter / len_in, 2) = maxit_in * fbest * normphi
873
874 file_write2 : if (present(tmp_file)) then
875 open(unit = 999, file = trim(adjustl(tmp_file)), action = 'write', position = 'append', recl = (n + 2) * 30)
876 write(999, *) itotalcounter, maxit_in * fbest * normphi, gamma(:)%best
877 close(999)
878 end if file_write2
879
880 if (printflag_in) then
881 print '(I8, 2I5, E15.7, 3E15.7)', itotalcounter, ipos, ineg, ac_ratio, t_in, &
882 fo * normphi * maxit_in, fbest * normphi * maxit_in
883 end if
884
885 ! Cooling schedule
886 if (fbb .gt. tiny(1.0_dp)) then
887 finc = (fbb - fbest) / fbb
888 else
889 finc = 1.0_dp
890 end if
891 if (finc < 0.00000001_dp) then
892 iconf = iconf + 1_i4
893 else
894 iconf = 0_i4
895 end if
896
897 if ((ac_ratio < 0.15_dp) .and. (iconf > 5_i4) .and. (iconr <= -3_i4)) then ! - iConR no reheating
898 ! Re-heating
899 if (printflag_in) then
900 print *, 'Re-heating: ', iconr
901 end if
902 iconr = iconr + 1_i4
903 t_in = t0 / 2._dp
904 iter = 0_i4 ! for LEN
905 itotalcounterr = 0_i4 ! for dR
906 ! start from current best
907 gamma(:)%old = gamma(:)%best
908 else
909 ! Update Temperature (geometrical decrement)
910 if (ac_ratio < 0.4_dp) then
911 dt_in = 0.995_dp
912 else
913 dt_in = dt0
914 end if
915 t_in = t_in * dt_in
916 end if
917
918 ! Stop Criteria: consecutive MC with marginal decrements and acceptance ratio
919 if (finc < eps_in) then
920 iconl = iconl + 1_i4
921 else
922 iconl = 0_i4
923 end if
924 if ((iconl > nst_in) .and. (ac_ratio < acc_in) .and. (iconr > 2_i4) .and. &
925 (sum(ipos_ineg_history(:, :)) .lt. 1_i4)) then
926 istop = .false.
927 end if
928 ! a way out maximum
929 if ((itotalcounter > nitermax_in) .and. & !(ac_ratio > acc_in) .and. &
930 (sum(ipos_ineg_history(:, :)) .ge. 1_i4)) then
931
932 ! store achieved history so far
933 allocate(history_out_tmp(size(history_out, 1), size(history_out, 2)))
934 history_out_tmp = history_out
935 deallocate(history_out)
936
937 nitermax_in = max(nitermax_in + len_in, int(real(nitermax_in, dp) * 1.10_dp, i4))
938 if (printflag_in) then
939 print *, 'nITERmax changed to =', nitermax_in
940 end if
941
942 ! increase lenght of history vecor
943 idummy = nitermax_in / len_in + 1_i4
944 allocate(history_out(idummy, 2))
945
946 do j = 1, size(history_out_tmp, 1)
947 history_out(j, :) = history_out_tmp(j, :)
948 end do
949
950 deallocate(history_out_tmp)
951
952 end if
953 ! STOP condition
954 if (itotalcounter > nitermax_in) then
955 istop = .false.
956 end if
957
958 end do looptest
959
960 ! calculate cost function again (only for check and return values)
961 parabest = gamma(:)%best
962 costbest = cost(parabest, eval) * maxit_in
963 if (present (funcbest)) then
964 funcbest = costbest * maxit_in
965 end if
966 fo = costbest / normphi
967
968 if (printflag_in) then
969 print *, ' '
970 print '(A15,E15.7)', ' end cost = ', maxit_in * fbest * normphi
971 print *, 'end parameter: '
972 do kk = 1, n
973 print '(A10,I3,A3, E15.7)', ' para #', kk, ' = ', gamma(kk)%best
974 end do
975
976 print *, 'Final check: ', (fo - fbest)
977 end if
978
979 if (present(history)) then
980 allocate(history(size(history_out, 1) - 1, size(history_out, 2)))
981 history(:, :) = history_out(1 : size(history_out, 1) - 1, :)
982 end if
983
984 deallocate(truepara)
985 deallocate(history_out)
986
987 return
988
989 END FUNCTION anneal_dp
990
991 real(dp) function gettemperature_dp(eval, cost, paraset, acc_goal, & ! obligatory
992 prange, prange_func, & ! optional IN: exactly one of both
993 samplesize, maskpara, seeds, printflag, & ! optional IN
994 weight, maxit, undef_funcval & ! optional IN
995 )
996 use mo_kind, only : dp, i4, i8
997 implicit none
998
999 procedure(eval_interface), INTENT(IN), POINTER :: eval
1000 procedure(objective_interface), intent(in), pointer :: cost
1001 real(dp), dimension(:), intent(in) :: paraset !< a valid parameter set of the model
1002 real(dp), intent(in) :: acc_goal !< acceptance ratio to achieve
1003 real(dp), optional, dimension(size(paraset, 1), 2), intent(in) :: prange !< lower and upper limit per parameter
1004 integer(i4), optional, intent(in) :: samplesize !< size of random set the acc_estimate is based on.
1005 !! DEFAULT: Max(250, 20*Number paras)
1006 logical, optional, dimension(size(paraset, 1)), intent(in) :: maskpara !< true if parameter will be optimized.
1007 !! false if parameter is discarded in optimization.
1008 !! DEFAULT: .true.
1009 integer(i8), optional, dimension(2), intent(in) :: seeds !< Seeds of random numbers. DEFAULT: time dependent.
1010 logical, optional, intent(in) :: printflag !< .true. if detailed temperature estimation is
1011 !! printed. DEFAULT: .false.
1012 real(dp), OPTIONAL, dimension(size(paraset, 1)), INTENT(IN) :: weight !< vector of weights per parameter gives the
1013 !! frequency of parameter to be chosen for
1014 !! optimization. DEFAULT: equal weighting
1015 logical, OPTIONAL, INTENT(IN) :: maxit !< Maxim. or minim. of function
1016 !! maximization = .true., minimization = .false. .
1017 !! DEFAULT: .false.
1018 real(dp), OPTIONAL, INTENT(IN) :: undef_funcval !< objective function value occuring if
1019 !! parameter set leads to invalid model results,
1020 !! e.g. -999.0_dp. DEFAULT: not present
1021
1022 INTERFACE
1023 SUBROUTINE prange_func(paraset, iPar, rangePar)
1024 ! gives the range (min,max) of the parameter iPar at a certain parameter set paraset
1025 use mo_kind
1026 real(dp), dimension(:), INTENT(IN) :: paraset
1027 integer(i4), INTENT(IN) :: ipar
1028 real(dp), dimension(2), INTENT(OUT) :: rangepar
1029 END SUBROUTINE prange_func
1030 END INTERFACE
1031 OPTIONAL :: prange_func
1032 !
1033 ! Local variables
1034
1035 integer(i4) :: n
1036 integer(i4) :: samplesize_in
1037 integer(i4) :: idummy, i, j
1038 integer(i4) :: ipar
1039 real(dp), dimension(2) :: iparrange
1040 real(dp) :: normphi
1041 real(dp) :: fo, fn, dr
1042 real(dp) :: t
1043
1044 logical :: coststatus ! true if model output is valid
1045 logical, dimension(size(paraset, 1)) :: maskpara_in ! true if parameter will be optimized
1046 integer(i4), dimension(:), ALLOCATABLE :: truepara ! indexes of parameters to be optimized
1047 logical :: printflag_in ! if detailed estimation of temperature is printed
1048 real(dp), dimension(size(paraset, 1)) :: weight_in ! CDF of parameter to chose for optimization
1049 real(dp) :: maxit_in ! Maximization or minimization of function:
1050 ! ! -1 = maxim, 1 = minim
1051
1052 type paramlim
1053 real(dp) :: min ! minimum value
1054 real(dp) :: max ! maximum value
1055 real(dp) :: new ! new state value
1056 real(dp) :: old ! old state value
1057 real(dp) :: best ! best value found
1058 real(dp) :: dmult ! sensitivity multiplier
1059 ! ! for parameter search
1060 end type paramlim
1061 type (paramlim), dimension (size(paraset, 1)), target :: gamma ! Parameter
1062
1063 ! for random numbers
1064 INTEGER(I8) :: seeds_in(2)
1065 real(dp) :: rn1, rn2 ! Random numbers
1066 integer(I8), dimension(n_save_state) :: save_state_1 ! optional arguments for restarting RN stream 1
1067 integer(I8), dimension(n_save_state) :: save_state_2 ! optional arguments for restarting RN stream 2
1068
1069 ! for initial temperature estimate
1070 real(dp) :: acc_estim ! estimate of acceptance probability
1071 ! depends on temperature
1072 ! goal: find a temperature such that acc_estim ~ 0.9
1073 real(dp), dimension(:, :), allocatable :: energy ! dim(LEN,2) cost function values before (:,1) and
1074 ! ! after (:,2) transition
1075
1076 n = size(paraset, 1)
1077
1078 ! either range or rangfunc has to be given
1079 if (present(prange) .eqv. present(prange_func)) then
1080 stop 'anneal: Either range or prange_func has to be given'
1081 end if
1082
1083 if (present(samplesize)) then
1084 if (samplesize .lt. max(20_i4 * n, 250_i4)) then
1085 !stop 'Input argument LEN must be greater than Max(250,20*N), N=number of parameters'
1086 print*, 'WARNING (GetTemperature): '
1087 print*, 'Input argument samplesize should be greater than Max(250,20*N), N=number of parameters'
1088 samplesize_in = samplesize
1089 else
1090 samplesize_in = samplesize
1091 end if
1092 else
1093 samplesize_in = max(20_i4 * n, 250_i4)
1094 end if
1095
1096 if (present(maskpara)) then
1097 if (count(maskpara) .eq. 0_i4) then
1098 stop 'Input argument maskpara: At least one element has to be true'
1099 else
1100 maskpara_in = maskpara
1101 end if
1102 else
1103 maskpara_in = .true.
1104 end if
1105
1106 if (present(seeds)) then
1107 seeds_in = seeds
1108 else
1109 ! Seeds depend on actual time
1110 call get_timeseed(seeds_in)
1111 print*, 'temp: seeds(1)=', seeds_in(1)
1112 end if
1113
1114 if (present(printflag)) then
1115 printflag_in = printflag
1116 else
1117 printflag_in = .false.
1118 end if
1119
1120 if (present(maxit)) then
1121 if (maxit) then
1122 maxit_in = -1._dp
1123 else
1124 maxit_in = 1._dp
1125 end if
1126 else
1127 maxit_in = 1._dp
1128 end if
1129
1130 allocate(energy(samplesize_in, 2))
1131
1132 allocate (truepara(count(maskpara_in)))
1133 idummy = 0_i4
1134 do i = 1, n
1135 if (maskpara_in(i)) then
1136 idummy = idummy + 1_i4
1137 truepara(idummy) = i
1138 end if
1139 end do
1140
1141 weight_in = 0.0_dp
1142 if (present(weight)) then
1143 where (maskpara_in(:))
1144 weight_in(:) = weight(:)
1145 end where
1146 else
1147 where (maskpara_in(:))
1148 weight_in(:) = 1.0_dp
1149 end where
1150 end if
1151 ! scaling the weights
1152 weight_in = weight_in / sum(weight_in)
1153 ! cummulating the weights
1154 do i = 2, n
1155 weight_in(i) = weight_in(i) + weight_in(i - 1)
1156 end do
1157
1158 ! Setting up the RNG
1159 ! (1) Seeds depend on actual time or on input seeds
1160 ! (2) Initialize the streams
1161 call xor4096(seeds_in(1), rn1, save_state = save_state_1)
1162 call xor4096(seeds_in(2), rn2, save_state = save_state_2)
1163 seeds_in = 0_i8
1164 ! (3) Now ready for calling
1165
1166 gamma(:)%dmult = 1.0_dp
1167 gamma(:)%new = paraset(:)
1168 gamma(:)%old = paraset(:)
1169 gamma(:)%best = paraset(:)
1170 normphi = -9999.9_dp
1171
1172 fo = cost(paraset, eval) * maxit_in
1173 if (abs(fo) .lt. tiny(0.0_dp)) fo = 0.0000001_dp * maxit_in
1174 normphi = fo
1175 fo = fo / normphi * maxit_in
1176
1177 j = 1
1178 loopsamplesize : do while (j .le. samplesize_in)
1179 ! Generate a random subsequent state and evaluate its objective function
1180 ! (1) Generate new parameter set
1181 dr = 1.0_dp
1182
1183 ! (1a) Select parameter to be changed
1184 call xor4096(seeds_in(1), rn1, save_state = save_state_1)
1185 ipar = 1_i4
1186 do while (weight_in(ipar) .lt. rn1)
1187 ipar = ipar + 1_i4
1188 end do
1189
1190 ! (1b) Generate new value of selected parameter
1191 if (present(prange_func)) then
1192 call prange_func(gamma(:)%old, ipar, iparrange)
1193 else
1194 iparrange(1) = prange(ipar, 1)
1195 iparrange(2) = prange(ipar, 2)
1196 end if
1197 gamma(ipar)%min = iparrange(1)
1198 gamma(ipar)%max = iparrange(2)
1199 call xor4096(seeds_in(2), rn2, save_state = save_state_2)
1200 gamma(ipar)%new = pargen_anneal_dp(gamma(ipar)%old, gamma(ipar)%dMult * dr, &
1201 gamma(ipar)%min, gamma(ipar)%max, rn2)
1202 !
1203 ! (2) Calculate new objective function value and normalize it
1204 fn = cost(gamma(:)%new, eval) * maxit_in
1205 coststatus = .true.
1206 if (present(undef_funcval)) then
1207 if (abs(fn * maxit_in - undef_funcval) .lt. tiny(1.0_dp)) then
1208 coststatus = .false.
1209 end if
1210 end if
1211
1212 feasible : if (coststatus) then ! feasible parameter set
1213 fn = fn / normphi
1214 ! Save Energy states of feasible transitions
1215 ! for adaption of optimal initial temperature
1216 ! Walid Ben-Ameur: "Comput. the Initial Temperature of Sim. Annealing"
1217 ! Comput. Opt. and App. 2004
1218 energy(j, 2) = fn ! E_max_t
1219 energy(j, 1) = fo ! E_min_t
1220 j = j + 1
1221 end if feasible !valid parameter set
1222 end do loopsamplesize
1223
1224 ! estimation of the acceptance probability based on the random set ||<Samplesize>||
1225 ! only if actual temperature (T) equals initial temperature (temp)
1226 t = maxval(energy)
1227
1228 acc_estim = sum(exp(-(energy(:, 2) / t))) / sum(exp(-(energy(:, 1) / t)))
1229 if (printflag_in) then
1230 print*, "acc_estimate = ", acc_estim, " ( T = ", t, " )"
1231 end if
1232 Do While ((acc_estim .lt. 1.0_dp) .and. (abs(acc_estim - acc_goal) .gt. 0.0001_dp))
1233 t = t * (log(acc_estim) / log(acc_goal))**(0.5_dp) ! **(1.0/p) with p=1.0
1234 if (all(t .gt. energy(:, 1) / 709._dp) .and. all(t .gt. energy(:, 2) / 709._dp)) then
1235 acc_estim = sum(exp(-(energy(:, 2) / t))) / sum(exp(-(energy(:, 1) / t)))
1236 if (printflag_in) then
1237 print*, "acc_estimate = ", acc_estim, " ( T = ", t, " )"
1238 end if
1239 else
1240 t = t / (log(acc_estim) / log(acc_goal))**(0.5_dp)
1241 exit
1242 end if
1243 end do
1245
1246 end function gettemperature_dp
1247
1248 !***************************************************
1249 !* PRIVATE FUNCTIONS *
1250 !***************************************************
1251
1252 real(dp) function pargen_anneal_dp(old, dmax, omin, omax, rn)
1253 use mo_kind, only : dp, i4
1254 implicit none
1255
1256 real(dp), intent(IN) :: old, dmax, omin, omax
1257 real(dp), intent(IN) :: rn
1258
1259 ! local variables
1260 real(dp) :: oi, ox, delta, dmaxscal
1261 integer(i4) :: idigit, iszero ! were intent IN before
1262 !
1263 iszero = 1_i4
1264 idigit = 8_i4
1265
1266 oi = omin
1267 ox = omax
1268 ! scaling (new)
1269 dmaxscal = dmax * abs(omax - omin)
1270
1271 if(ge(oi, ox)) then
1272 pargen_anneal_dp = (oi + ox) / 2.0_dp
1273 else
1274 if ((old - dmaxscal) > oi) oi = old - dmaxscal
1275 if ((old + dmaxscal) < ox) ox = old + dmaxscal
1276 delta = (oi + rn * (ox - oi)) - old
1277 if (delta > dmaxscal) delta = dmaxscal
1278 if (delta <-dmaxscal) delta = -dmaxscal
1279 pargen_anneal_dp = old + dchange_dp(delta, idigit, iszero)
1280 end if
1281
1282 ! Parameter is bounded between Max and Min.
1283 ! Correction from Kumar and Luis
1284
1285 if (pargen_anneal_dp < omin) then
1286 pargen_anneal_dp = omin
1287 elseif(pargen_anneal_dp > omax)then
1288 pargen_anneal_dp = omax
1289 end if
1290 end function pargen_anneal_dp
1291
1292 real(dp) function pargen_dds_dp(old, perturb, omin, omax, rn)
1293 ! PURPOSE:
1294 ! Perturb variable using a standard normal random number RN
1295 ! and reflecting at variable bounds if necessary
1296 ! Tolson et al. (2007): STEP 4
1297 !
1298 implicit none
1299
1300 real(dp), intent(IN) :: old, perturb, omin, omax
1301 real(dp), intent(IN) :: rn
1302
1303 ! generating new value
1304 pargen_dds_dp = old + perturb * (omax - omin) * rn
1305
1306 ! reflect one time and set to boundary value
1307 if (pargen_dds_dp .lt. omin) then
1308 pargen_dds_dp = omin + (omin - pargen_dds_dp)
1309 if (pargen_dds_dp .gt. omax) pargen_dds_dp = omin
1310 end if
1311
1312 if (pargen_dds_dp .gt. omax) then
1313 pargen_dds_dp = omax - (pargen_dds_dp - omax)
1314 if (pargen_dds_dp .lt. omin) pargen_dds_dp = omax
1315 end if
1316
1317 end function pargen_dds_dp
1318
1319 real(dp) function dchange_dp(delta, idigit, iszero)
1320 use mo_kind, only : i4, i8, dp
1321 implicit none
1322
1323 integer(i4), intent(IN) :: idigit, iszero
1324 real(dp), intent(IN) :: delta
1325
1326 ! local variables
1327 integer(i4) :: ioszt
1328 integer(I8) :: idelta
1329
1330 ioszt = 10**idigit
1331 idelta = int(delta * real(ioszt, dp), i8)
1332 if(iszero == 1_i4) then
1333 if(idelta == 0_i8) then
1334 if(delta < 0_i4) then
1335 idelta = -1_i8
1336 else
1337 idelta = 1_i8
1338 end if
1339 end if
1340 end if
1341 dchange_dp = real(idelta, dp) / real(ioszt, dp)
1342 end function dchange_dp
1343
1344 subroutine generate_neighborhood_weight_dp(truepara, cum_weight, save_state_xor, iTotalCounter, &
1345 nITERmax, neighborhood)
1346 ! PURPOSE:
1347 ! generates a new neighborhood
1348 ! Tolson et al. (2007): STEP 3
1349 !
1350 integer(i4), dimension(:), intent(in) :: truepara
1351 real(dp), dimension(:), intent(in) :: cum_weight
1352 integer(i8), dimension(n_save_state), intent(inout) :: save_state_xor
1353 integer(i4), intent(in) :: itotalcounter
1354 integer(i4), intent(in) :: nitermax
1355 logical, dimension(size(cum_weight)), intent(out) :: neighborhood
1356
1357 ! local variables
1358 integer(i4) :: ipar, npar
1359 integer(i4) :: isize, size_neighbor
1360 real(dp) :: prob
1361 real(dp) :: rn
1362 real(dp) :: weight, norm
1363 real(dp), dimension(size(cum_weight)) :: local_cum_weight
1364
1365 prob = 1.0_dp - log(real(itotalcounter, dp)) / log(real(nitermax, dp))
1366 neighborhood = .false.
1367
1368 npar = size(cum_weight)
1369 local_cum_weight = cum_weight
1370
1371 ! How many parameters will be selected for neighborhood?
1372 size_neighbor = 0_i4
1373 do ipar = 1, size(truepara)
1374 call xor4096(0_i8, rn, save_state = save_state_xor)
1375 if (rn < prob) then
1376 size_neighbor = size_neighbor + 1_i4
1377 end if
1378 end do
1379 ! at least one...
1380 size_neighbor = max(1_i4, size_neighbor)
1381
1382 ! Which parameter will be used for neighborhood?
1383 do isize = 1, size_neighbor
1384 ! (1) generate RN
1385 call xor4096(0_i8, rn, save_state = save_state_xor)
1386 !
1387 ! (2) find location <iPar> in cummulative distribution function
1388 ipar = 1_i4
1389 do while (local_cum_weight(ipar) .lt. rn)
1390 ipar = ipar + 1_i4
1391 end do
1392 !
1393 ! (3) add parameter to neighborhood
1394 neighborhood(ipar) = .true.
1395 !
1396 ! (4) recalculate cummulative distribution function
1397 ! (4.1) Which weight had iPar?
1398 if (ipar .gt. 1_i4) then
1399 weight = local_cum_weight(ipar) - local_cum_weight(ipar - 1)
1400 else
1401 weight = local_cum_weight(1)
1402 end if
1403 ! (4.2) Substract this weight from cummulative array starting at iPar
1404 local_cum_weight(ipar : npar) = local_cum_weight(ipar : npar) - weight
1405 ! (4.3) Renormalize cummulative weight to one
1406 if (count(neighborhood) .lt. size(truepara)) then
1407 norm = 1.0_dp / local_cum_weight(npar)
1408 else
1409 norm = 1.0_dp
1410 end if
1411 local_cum_weight(:) = local_cum_weight(:) * norm
1412 !
1413 end do
1414
1415 end subroutine generate_neighborhood_weight_dp
1416
1417END MODULE mo_anneal
Optimize cost function with simulated annealing.
Find initial temperature for simulated annealing.
Interface for evaluation routine.
Comparison of real values: a >= b.
Definition mo_utils.F90:294
Comparison of real values: a <= b.
Definition mo_utils.F90:299
Generate seed for xor4096.
Uniform XOR4096-based RNG.
Gaussian XOR4096-based RNG.
Anneal optimization of cost function.
Definition mo_anneal.f90:42
real(dp) function gettemperature_dp(eval, cost, paraset, acc_goal, prange, prange_func, samplesize, maskpara, seeds, printflag, weight, maxit, undef_funcval)
real(dp) function, dimension(size(para, 1)) anneal_dp(eval, cost, para, prange, prange_func, temp, dt, nitermax, len, nst, eps, acc, seeds, printflag, maskpara, weight, changeparamode, reflectionflag, pertubflexflag, maxit, undef_funcval, tmp_file, funcbest, history)
Define number representations.
Definition mo_kind.F90:17
integer, parameter i4
4 Byte Integer Kind
Definition mo_kind.F90:40
integer, parameter i8
8 Byte Integer Kind
Definition mo_kind.F90:42
integer, parameter dp
Double Precision Real Kind.
Definition mo_kind.F90:46
Utility functions, such as interface definitions, for optimization routines.
General utilities for the CHS library.
Definition mo_utils.F90:20
XOR4096-based random number generator.
integer(i4), parameter, public n_save_state
Dimension of vector saving the state of a stream.