@@ -108,6 +108,15 @@ module nlesolver_module
108108 character (len= :),allocatable :: message ! ! latest status message
109109 integer :: istat = - 999 ! ! latest status message
110110
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+
111120 procedure (func_func),pointer :: func = > null () ! ! user-supplied routine to compute the function
112121 procedure (export_func),pointer :: export_iteration = > null () ! ! user-supplied routine to export iterations
113122 procedure (wait_func),pointer :: user_input_check = > null () ! ! user-supplied routine to enable user to stop iterations
@@ -167,6 +176,7 @@ module nlesolver_module
167176 procedure ,public :: status = > get_status
168177
169178 procedure :: set_status
179+ procedure :: adjust_x_for_bounds
170180
171181 end type nlesolver_type
172182 ! *********************************************************
@@ -361,7 +371,8 @@ subroutine initialize_nlesolver_variables(me,&
361371 verbose ,iunit ,n_uphill_max ,n_intervals ,&
362372 sparsity_mode ,irow ,icol ,&
363373 atol ,btol ,conlim ,damp ,itnlim ,nout ,&
364- lusol_method ,localSize ,custom_solver_sparse )
374+ lusol_method ,localSize ,custom_solver_sparse ,&
375+ bounds_mode ,xlow ,xupp )
365376
366377 implicit none
367378
@@ -437,6 +448,14 @@ subroutine initialize_nlesolver_variables(me,&
437448 ! ! At most `min(m,n)` vectors will be allocated.
438449 procedure (sparse_solver_func),optional :: custom_solver_sparse ! ! for `sparsity_mode=5`, this is the
439450 ! ! user-provided linear solver.
451+ logical ,intent (in ),optional :: bounds_mode ! ! how to handle the `x` variable bounds:
452+ ! !
453+ ! ! * 0 = ignore bounds
454+ ! ! * 1 = use bounds (if specified) by adjusting the `x` vector
455+ ! ! at each step so that each individual `x` component is within
456+ ! ! the bounds
457+ real (wp),dimension (n),intent (in ),optional :: xlow ! ! lower bounds for `x` (size is `n`). only used if `bounds_mode>0`.
458+ real (wp),dimension (n),intent (in ),optional :: xupp ! ! upper bounds for `x` (size is `n`). only used if `bounds_mode>0`.
440459
441460 logical :: status_ok ! ! true if there were no errors
442461
@@ -453,6 +472,14 @@ subroutine initialize_nlesolver_variables(me,&
453472
454473 ! optional:
455474
475+ if (present (bounds_mode) .and. present (xlow) .and. present (xupp)) then
476+ me% bounds_mode = bounds_mode
477+ me% xupp = xupp
478+ me% xlow = xlow
479+ else
480+ me% bounds_mode = 0 ! default
481+ end if
482+
456483 if (present (step_mode)) then
457484 select case (step_mode)
458485 case (1 ) ! = use the specified `alpha` (0,1]
@@ -672,6 +699,7 @@ subroutine nlesolver_solver(me,x)
672699 end if
673700
674701 ! evaluate the function:
702+ call me% adjust_x_for_bounds(x) ! if the guess is out of bounds it may also be adjusted first.
675703 call me% func(x,fvec)
676704 f = norm2(fvec)
677705
@@ -923,6 +951,22 @@ subroutine nlesolver_solver(me,x)
923951 end subroutine nlesolver_solver
924952! *****************************************************************************************
925953
954+ ! *****************************************************************************************
955+ ! >
956+ ! if necessary, adjust the `x` vector to be within the bounds.
957+
958+ subroutine adjust_x_for_bounds (me ,x )
959+
960+ implicit none
961+
962+ class(nlesolver_type),intent (inout ) :: me
963+ real (wp),dimension (me% n),intent (inout ) :: x ! ! the `x` vector to adjust
964+
965+ if (me% bounds_mode== 1 ) x = min (max (x,me% xlow),me% xupp)
966+
967+ end subroutine adjust_x_for_bounds
968+ ! *****************************************************************************************
969+
926970! *****************************************************************************************
927971! >
928972! Destructor
@@ -1042,6 +1086,7 @@ subroutine simple_step(me,xold,p,x,f,fvec,fjac,fjac_sparse)
10421086 real (wp),dimension (:),intent (in ),optional :: fjac_sparse ! ! jacobian matrix [sparse]
10431087
10441088 x = xold + p * me% alpha
1089+ call me% adjust_x_for_bounds(x)
10451090
10461091 ! evaluate the function at the new point:
10471092 call me% func(x,fvec)
@@ -1115,6 +1160,7 @@ subroutine backtracking_linesearch(me,xold,p,x,f,fvec,fjac,fjac_sparse)
11151160 do
11161161
11171162 xtmp = xold + p * alpha
1163+ call me% adjust_x_for_bounds(xtmp)
11181164 call me% func(xtmp,fvectmp)
11191165 ftmp = norm2(fvectmp)
11201166
@@ -1183,6 +1229,7 @@ subroutine exact_linesearch(me,xold,p,x,f,fvec,fjac,fjac_sparse)
11831229 if (me% verbose) write (me% iunit,' (1P,*(A,1X,E16.6))' ) ' alpha_min = ' , alpha_min
11841230
11851231 x = xold + p * alpha_min
1232+ call me% adjust_x_for_bounds(x)
11861233 if (all (x== xnew)) then
11871234 ! already computed in the func
11881235 else
@@ -1198,6 +1245,7 @@ real(wp) function func_for_fmin(alpha)
11981245 real (wp),intent (in ) :: alpha ! ! indep variable
11991246
12001247 xnew = xold + p * alpha
1248+ call me% adjust_x_for_bounds(xnew)
12011249 call me% func(xnew,fvec)
12021250 func_for_fmin = norm2(fvec) ! return result
12031251
@@ -1260,6 +1308,7 @@ subroutine fixed_point_linesearch(me,xold,p,x,f,fvec,fjac,fjac_sparse)
12601308 do i = 1 , n_points
12611309
12621310 x_tmp = xold + p * alphas_to_try(i)
1311+ call me% adjust_x_for_bounds(x_tmp)
12631312
12641313 ! evaluate the function at tthis point:
12651314 call me% func(x_tmp,fvec_tmp)
0 commit comments