@@ -108,6 +108,15 @@ module nlesolver_module
108
108
character (len= :),allocatable :: message ! ! latest status message
109
109
integer :: istat = - 999 ! ! latest status message
110
110
111
+ integer :: bounds_mode = 0 ! ! how to handle the `x` variable bounds:
112
+ ! !
113
+ ! ! * 0 = ignore bounds.
114
+ ! ! * 1 = use bounds (if specified) by adjusting the `x` vector
115
+ ! ! at each function evaluation so that each individual `x`
116
+ ! ! component is within its bounds.
117
+ real (wp),dimension (:),allocatable :: xlow ! ! lower bounds for `x` (size is `n`). only used if `bounds_mode>0`.
118
+ real (wp),dimension (:),allocatable :: xupp ! ! upper bounds for `x` (size is `n`). only used if `bounds_mode>0`.
119
+
111
120
procedure (func_func),pointer :: func = > null () ! ! user-supplied routine to compute the function
112
121
procedure (export_func),pointer :: export_iteration = > null () ! ! user-supplied routine to export iterations
113
122
procedure (wait_func),pointer :: user_input_check = > null () ! ! user-supplied routine to enable user to stop iterations
@@ -167,6 +176,7 @@ module nlesolver_module
167
176
procedure ,public :: status = > get_status
168
177
169
178
procedure :: set_status
179
+ procedure :: adjust_x_for_bounds
170
180
171
181
end type nlesolver_type
172
182
! *********************************************************
@@ -270,27 +280,14 @@ subroutine set_status(me,istat,string,i,r)
270
280
integer ,intent (in ),optional :: i ! ! an integer value to append
271
281
real (wp),intent (in ),optional :: r ! ! a real value to append
272
282
273
- character (len= 256 ) :: numstr ! ! for number fo string conversion
274
283
character (len= :),allocatable :: message ! ! the full message to log
275
284
integer :: iostat ! ! write `iostat` code
276
285
277
286
message = trim (string)
278
- if (present (i)) then
279
- numstr = ' '
280
- write (numstr,fmt=* ,iostat= iostat) i
281
- if (iostat/= 0 ) numstr = ' ****'
282
- message = message// ' ' // trim (adjustl (numstr))
283
- end if
284
- if (present (r)) then
285
- numstr = ' '
286
- write (numstr,fmt=* ,iostat= iostat) r
287
- if (iostat/= 0 ) numstr = ' ****'
288
- message = message// ' ' // trim (adjustl (numstr))
289
- end if
287
+ if (present (i)) message = message// ' ' // int2str(i)
288
+ if (present (r)) message = message// ' ' // real2str(r)
290
289
291
- if (me% verbose) then
292
- write (me% iunit,' (A)' ,iostat= iostat) message
293
- end if
290
+ if (me% verbose) write (me% iunit,' (A)' ,iostat= iostat) message
294
291
295
292
! store in the class:
296
293
me% istat = istat
@@ -299,6 +296,42 @@ subroutine set_status(me,istat,string,i,r)
299
296
end subroutine set_status
300
297
! *****************************************************************************************
301
298
299
+ ! *****************************************************************************************
300
+ ! >
301
+ ! Convert an integer to a string. Works for up to 256 digits.
302
+
303
+ function int2str (i ) result(s)
304
+ integer , intent (in ) :: i ! ! integer to convert
305
+ character (len= :),allocatable :: s ! ! string result
306
+ character (len= 256 ) :: tmp ! ! temp string
307
+ integer :: iostat ! ! write `iostat` code
308
+ write (tmp,fmt=* ,iostat= iostat) i
309
+ if (iostat/= 0 ) then
310
+ s = ' ****'
311
+ else
312
+ s = trim (adjustl (tmp))
313
+ end if
314
+ end function int2str
315
+ ! *****************************************************************************************
316
+
317
+ ! *****************************************************************************************
318
+ ! >
319
+ ! Convert a real to a string. Works for up to 256 digits.
320
+
321
+ function real2str (r ) result(s)
322
+ real (wp), intent (in ) :: r ! ! real to convert
323
+ character (len= :),allocatable :: s ! ! string result
324
+ character (len= 256 ) :: tmp ! ! temp string
325
+ integer :: iostat ! ! write `iostat` code
326
+ write (tmp,fmt=* ,iostat= iostat) r
327
+ if (iostat/= 0 ) then
328
+ s = ' ****'
329
+ else
330
+ s = trim (adjustl (tmp))
331
+ end if
332
+ end function real2str
333
+ ! *****************************************************************************************
334
+
302
335
! *****************************************************************************************
303
336
! >
304
337
! Return the status code and message from the [[nlesolver_type]] class.
@@ -320,6 +353,7 @@ end subroutine set_status
320
353
! * -13 -- Error: backtracking linesearch tau must be in range (0, 1)
321
354
! * -14 -- Error: must specify grad_sparse, irow, and icol for sparsity_mode > 1
322
355
! * -15 -- Error: irow and icol must be the same length
356
+ ! * -16 -- Error: xlow > xupp
323
357
! * -999 -- Error: class has not been initialized
324
358
! * 0 -- Class successfully initialized in [[nlesolver_type:initialize]]
325
359
! * 1 -- Required accuracy achieved
@@ -361,7 +395,8 @@ subroutine initialize_nlesolver_variables(me,&
361
395
verbose ,iunit ,n_uphill_max ,n_intervals ,&
362
396
sparsity_mode ,irow ,icol ,&
363
397
atol ,btol ,conlim ,damp ,itnlim ,nout ,&
364
- lusol_method ,localSize ,custom_solver_sparse )
398
+ lusol_method ,localSize ,custom_solver_sparse ,&
399
+ bounds_mode ,xlow ,xupp )
365
400
366
401
implicit none
367
402
@@ -437,6 +472,16 @@ subroutine initialize_nlesolver_variables(me,&
437
472
! ! At most `min(m,n)` vectors will be allocated.
438
473
procedure (sparse_solver_func),optional :: custom_solver_sparse ! ! for `sparsity_mode=5`, this is the
439
474
! ! user-provided linear solver.
475
+ integer ,intent (in ),optional :: bounds_mode ! ! how to handle the `x` variable bounds:
476
+ ! !
477
+ ! ! * 0 = ignore bounds
478
+ ! ! * 1 = use bounds (if specified) by adjusting the `x` vector
479
+ ! ! at each step so that each individual `x` component is within
480
+ ! ! the bounds
481
+ real (wp),dimension (n),intent (in ),optional :: xlow ! ! lower bounds for `x` (size is `n`). only used if `bounds_mode>0` and
482
+ ! ! both `xlow` and `xupp` are specified.
483
+ real (wp),dimension (n),intent (in ),optional :: xupp ! ! upper bounds for `x` (size is `n`). only used if `bounds_mode>0` and
484
+ ! ! both `xlow` and `xupp` are specified.
440
485
441
486
logical :: status_ok ! ! true if there were no errors
442
487
@@ -453,6 +498,19 @@ subroutine initialize_nlesolver_variables(me,&
453
498
454
499
! optional:
455
500
501
+ if (present (bounds_mode) .and. present (xlow) .and. present (xupp)) then
502
+ if (any (xlow> xupp)) then ! check for consistency
503
+ status_ok = .false.
504
+ call me% set_status(istat = - 16 , string = ' Error: xlow > xupp' )
505
+ return
506
+ end if
507
+ me% bounds_mode = bounds_mode
508
+ me% xupp = xupp
509
+ me% xlow = xlow
510
+ else
511
+ me% bounds_mode = 0 ! default
512
+ end if
513
+
456
514
if (present (step_mode)) then
457
515
select case (step_mode)
458
516
case (1 ) ! = use the specified `alpha` (0,1]
@@ -672,6 +730,7 @@ subroutine nlesolver_solver(me,x)
672
730
end if
673
731
674
732
! evaluate the function:
733
+ call me% adjust_x_for_bounds(x) ! if the guess is out of bounds it may also be adjusted first.
675
734
call me% func(x,fvec)
676
735
f = norm2(fvec)
677
736
@@ -923,6 +982,35 @@ subroutine nlesolver_solver(me,x)
923
982
end subroutine nlesolver_solver
924
983
! *****************************************************************************************
925
984
985
+ ! *****************************************************************************************
986
+ ! >
987
+ ! if necessary, adjust the `x` vector to be within the bounds.
988
+
989
+ subroutine adjust_x_for_bounds (me ,x )
990
+
991
+ implicit none
992
+
993
+ class(nlesolver_type),intent (inout ) :: me
994
+ real (wp),dimension (me% n),intent (inout ) :: x ! ! the `x` vector to adjust
995
+
996
+ integer :: i ! ! counter
997
+
998
+ if (me% bounds_mode== 1 ) then
999
+ ! x = min(max(x,me%xlow),me%xupp)
1000
+ do i = 1 , me% n
1001
+ if (x(i)<me% xlow(i)) then
1002
+ x(i) = me% xlow(i)
1003
+ if (me% verbose) write (me% iunit, ' (A)' ) ' x(' // int2str(i)// ' ) < xlow(i) : adjusting to lower bound'
1004
+ else if (x(i)>me% xupp(i)) then
1005
+ x(i) = me% xupp(i)
1006
+ if (me% verbose) write (me% iunit, ' (A)' ) ' x(' // int2str(i)// ' ) > xupp(i) : adjusting to upper bound'
1007
+ end if
1008
+ end do
1009
+ end if
1010
+
1011
+ end subroutine adjust_x_for_bounds
1012
+ ! *****************************************************************************************
1013
+
926
1014
! *****************************************************************************************
927
1015
! >
928
1016
! Destructor
@@ -1042,6 +1130,7 @@ subroutine simple_step(me,xold,p,x,f,fvec,fjac,fjac_sparse)
1042
1130
real (wp),dimension (:),intent (in ),optional :: fjac_sparse ! ! jacobian matrix [sparse]
1043
1131
1044
1132
x = xold + p * me% alpha
1133
+ call me% adjust_x_for_bounds(x)
1045
1134
1046
1135
! evaluate the function at the new point:
1047
1136
call me% func(x,fvec)
@@ -1115,6 +1204,7 @@ subroutine backtracking_linesearch(me,xold,p,x,f,fvec,fjac,fjac_sparse)
1115
1204
do
1116
1205
1117
1206
xtmp = xold + p * alpha
1207
+ call me% adjust_x_for_bounds(xtmp)
1118
1208
call me% func(xtmp,fvectmp)
1119
1209
ftmp = norm2(fvectmp)
1120
1210
@@ -1183,6 +1273,7 @@ subroutine exact_linesearch(me,xold,p,x,f,fvec,fjac,fjac_sparse)
1183
1273
if (me% verbose) write (me% iunit,' (1P,*(A,1X,E16.6))' ) ' alpha_min = ' , alpha_min
1184
1274
1185
1275
x = xold + p * alpha_min
1276
+ call me% adjust_x_for_bounds(x)
1186
1277
if (all (x== xnew)) then
1187
1278
! already computed in the func
1188
1279
else
@@ -1198,6 +1289,7 @@ real(wp) function func_for_fmin(alpha)
1198
1289
real (wp),intent (in ) :: alpha ! ! indep variable
1199
1290
1200
1291
xnew = xold + p * alpha
1292
+ call me% adjust_x_for_bounds(xnew)
1201
1293
call me% func(xnew,fvec)
1202
1294
func_for_fmin = norm2(fvec) ! return result
1203
1295
@@ -1260,6 +1352,7 @@ subroutine fixed_point_linesearch(me,xold,p,x,f,fvec,fjac,fjac_sparse)
1260
1352
do i = 1 , n_points
1261
1353
1262
1354
x_tmp = xold + p * alphas_to_try(i)
1355
+ call me% adjust_x_for_bounds(x_tmp)
1263
1356
1264
1357
! evaluate the function at tthis point:
1265
1358
call me% func(x_tmp,fvec_tmp)
0 commit comments