44 integer(int32) :: m_maxeval = 100
51 real(real64) :: m_alpha = 1.0d-4
59 real(real64) :: m_factor = 0.1d0
63 procedure,
public :: get_max_fcn_evals => ls_get_max_eval
66 procedure,
public :: set_max_fcn_evals => ls_set_max_eval
69 procedure,
public :: get_scaling_factor => ls_get_scale
72 procedure,
public :: set_scaling_factor => ls_set_scale
75 procedure,
public :: get_distance_factor => ls_get_dist
78 procedure,
public :: set_distance_factor => ls_set_dist
83 generic,
public :: search => ls_search_mimo, ls_search_miso
85 procedure :: ls_search_mimo
86 procedure :: ls_search_miso
213 subroutine ls_search_mimo(this, fcn, xold, grad, dir, x, fvec, fold, fx, &
218 real(real64),
intent(in),
dimension(:) :: xold, dir, grad
219 real(real64),
intent(out),
dimension(:) :: x, fvec
220 real(real64),
intent(in),
optional :: fold
221 real(real64),
intent(out),
optional :: fx
223 class(errors),
intent(inout),
optional,
target :: err
226 real(real64),
parameter :: zero = 0.0d0
227 real(real64),
parameter :: p5 = 0.5d0
228 real(real64),
parameter :: one = 1.0d0
229 real(real64),
parameter :: two = 2.0d0
230 real(real64),
parameter :: three = 3.0d0
233 logical :: xcnvrg, fcnvrg
234 integer(int32) :: i, m, n, neval, niter, flag, maxeval
235 real(real64) :: alam, alam1, alamin, f1, slope, temp, test, tmplam, &
236 alpha, tolx, lambdamin, f, fo
237 class(errors),
pointer :: errmgr
238 type(errors),
target :: deferr
239 character(len = 128) :: errmsg
246 m = fcn%get_equation_count()
247 n = fcn%get_variable_count()
248 tolx = two * epsilon(tolx)
250 lambdamin = this%m_factor
251 maxeval = this%m_maxEval
252 if (
present(fx)) fx = zero
253 if (
present(ib))
then
254 ib%iter_count = niter
256 ib%converge_on_fcn = fcnvrg
257 ib%converge_on_chng = xcnvrg
258 ib%converge_on_zero_diff = .false.
260 if (
present(err))
then
267 if (.not.fcn%is_fcn_defined())
then
269 call errmgr%report_error(
"ls_search_mimo", &
270 "No function has been defined.", &
271 nl_invalid_operation_error)
275 if (
size(xold) /= n)
then
277 else if (
size(grad) /= n)
then
279 else if (
size(dir) /= n)
then
281 else if (
size(x) /= n)
then
283 else if (
size(fvec) /= m)
then
288 write(errmsg, 100)
"Input number ", flag, &
289 " is not sized correctly."
290 call errmgr%report_error(
"ls_search_mimo", trim(errmsg), &
296 if (
present(fold))
then
300 call fcn%fcn(xold, fvec)
301 fo = p5 * dot_product(fvec, fvec)
306 slope = dot_product(grad, dir)
307 if (slope >= zero)
then
309 call errmgr%report_error(
"ls_search_mimo", &
310 "The search direction vector appears to be pointing in " // &
311 "an uphill direction - away from a minimum.", &
312 nl_divergent_behavior_error)
319 temp = abs(dir(i)) / max(abs(xold(i)), one)
320 if (temp > test) test = temp
329 x = xold + alam * dir
330 call fcn%fcn(x, fvec)
331 f = p5 * dot_product(fvec, fvec)
336 if (alam < alamin)
then
342 if (norm2(x - xold) == zero)
then
344 call errmgr%report_warning(
"ls_search_mimo", &
345 "The line search appears to have fully " // &
346 "backtracked. As such, check results carefully, " // &
347 "and/or consider attempting the solve without " // &
348 "the line search.", &
349 nl_convergence_error)
354 else if (f <= fo + alpha * alam * slope)
then
360 tmplam = min_backtrack_search(niter, fo, f, f1, alam, &
368 alam = max(tmplam, lambdamin * alam)
371 if (neval >= maxeval)
then
377 if (
present(fx)) fx = f
380 if (
present(ib))
then
381 ib%iter_count = niter
383 ib%converge_on_fcn = fcnvrg
384 ib%converge_on_chng = xcnvrg
385 ib%converge_on_zero_diff = .false.
390 write(errmsg, 100)
"The line search failed to " // &
391 "converge. Function evaluations performed: ", neval,
"."
392 call errmgr%report_error(
"ls_search_mimo", trim(errmsg), &
393 nl_convergence_error)
431 subroutine ls_search_miso(this, fcn, xold, grad, dir, x, fold, fx, &
436 real(real64),
intent(in),
dimension(:) :: xold, dir, grad
437 real(real64),
intent(out),
dimension(:) :: x
438 real(real64),
intent(in),
optional :: fold
439 real(real64),
intent(out),
optional :: fx
441 class(errors),
intent(inout),
optional,
target :: err
444 real(real64),
parameter :: zero = 0.0d0
445 real(real64),
parameter :: p5 = 0.5d0
446 real(real64),
parameter :: one = 1.0d0
447 real(real64),
parameter :: two = 2.0d0
448 real(real64),
parameter :: three = 3.0d0
451 logical :: xcnvrg, fcnvrg
452 integer(int32) :: i, n, neval, niter, flag, maxeval
453 real(real64) :: alam, alam1, alamin, f1, slope, temp, test, tmplam, &
454 alpha, tolx, lambdamin, f, fo
455 class(errors),
pointer :: errmgr
456 type(errors),
target :: deferr
457 character(len = 128) :: errmsg
464 n = fcn%get_variable_count()
465 tolx = two * epsilon(tolx)
467 lambdamin = this%m_factor
468 maxeval = this%m_maxEval
469 if (
present(fx)) fx = zero
470 if (
present(ib))
then
471 ib%iter_count = niter
473 ib%converge_on_fcn = fcnvrg
474 ib%converge_on_chng = xcnvrg
475 ib%converge_on_zero_diff = .false.
477 if (
present(err))
then
484 if (.not.fcn%is_fcn_defined())
then
486 call errmgr%report_error(
"ls_search_miso", &
487 "No function has been defined.", &
488 nl_invalid_operation_error)
492 if (
size(xold) /= n)
then
494 else if (
size(grad) /= n)
then
496 else if (
size(dir) /= n)
then
498 else if (
size(x) /= n)
then
503 write(errmsg, 100)
"Input number ", flag, &
504 " is not sized correctly."
505 call errmgr%report_error(
"ls_search_miso", trim(errmsg), &
511 if (
present(fold))
then
520 slope = dot_product(grad, dir)
521 if (slope >= zero)
then
523 call errmgr%report_error(
"ls_search_miso", &
524 "The search direction vector appears to be pointing in " // &
525 "an uphill direction - away from a minimum.", &
526 nl_divergent_behavior_error)
533 temp = abs(dir(i)) / max(abs(xold(i)), one)
534 if (temp > test) test = temp
543 x = xold + alam * dir
549 if (alam < alamin)
then
555 if (norm2(x - xold) == zero)
then
557 call errmgr%report_warning(
"ls_search_miso", &
558 "The line search appears to have fully " // &
559 "backtracked. As such, check results carefully, " // &
560 "and/or consider attempting the solve without " // &
561 "the line search.", &
562 nl_convergence_error)
567 else if (f <= fo + alpha * alam * slope)
then
573 tmplam = min_backtrack_search(niter, fo, f, f1, alam, &
581 alam = max(tmplam, lambdamin * alam)
584 if (neval >= maxeval)
then
590 if (
present(fx)) fx = f
593 if (
present(ib))
then
594 ib%iter_count = niter
596 ib%converge_on_fcn = fcnvrg
597 ib%converge_on_chng = xcnvrg
598 ib%converge_on_zero_diff = .false.
603 write(errmsg, 100)
"The line search failed to " // &
604 "converge. Function evaluations performed: ", neval,
"."
605 call errmgr%report_error(
"ls_search_miso", trim(errmsg), &
606 nl_convergence_error)
626 pure function min_backtrack_search(mode, f0, f, f1, alam, alam1, &
629 integer(int32),
intent(in) :: mode
630 real(real64),
intent(in) :: f0, f, f1, alam, alam1, slope
634 real(real64),
parameter :: zero = 0.0d0
635 real(real64),
parameter :: p5 = 0.5d0
636 real(real64),
parameter :: two = 2.0d0
637 real(real64),
parameter :: three = 3.0d0
640 real(real64) :: rhs1, rhs2, a, b, disc
645 lam = -slope / (two * (f - f0 - slope))
648 rhs1 = f - f0 - alam * slope
649 rhs2 = f1 - f0 - alam1 * slope
650 a = (rhs1 / alam**2 - rhs2 / alam1**2) / (alam - alam1)
651 b = (-alam1 * rhs1 / alam**2 + alam * rhs2 / alam1**2) / &
654 lam = -slope / (two * b)
656 disc = b**2 - three * a * slope
657 if (disc < zero)
then
659 else if (b <= zero)
then
660 lam = (-b + sqrt(disc)) / (three * a)
662 lam = -slope / (b + sqrt(disc))
665 if (lam > p5 * alam) lam = p5 * alam