7 module subroutine qns_solve(this, fcn, x, fvec, ib, err)
9 class(quasi_newton_solver),
intent(inout) :: this
10 class(vecfcn_helper),
intent(in) :: fcn
11 real(real64),
intent(inout),
dimension(:) :: x
12 real(real64),
intent(out),
dimension(:) :: fvec
13 type(iteration_behavior),
optional :: ib
14 class(errors),
intent(inout),
optional,
target :: err
17 real(real64),
parameter :: zero = 0.0d0
18 real(real64),
parameter :: half = 0.5d0
19 real(real64),
parameter :: one = 1.0d0
20 real(real64),
parameter :: factor = 1.0d2
23 logical :: restart, xcnvrg, fcnvrg, gcnvrg, check
24 integer(int32) :: i, neqn, nvar, flag, lw1, lw2, lw3, neval, iter, &
26 real(real64),
allocatable,
dimension(:) :: work, tau, dx, df, fvold, &
28 real(real64),
allocatable,
dimension(:,:) :: q, r, b
29 real(real64) :: test, f, fold, temp, ftol, xtol, gtol, &
30 stpmax, x2, xnorm, fnorm
31 type(iteration_behavior) :: lib
32 class(errors),
pointer :: errmgr
33 type(errors),
target :: deferr
34 character(len = 256) :: errmsg
35 class(line_search),
allocatable :: ls
42 neqn = fcn%get_equation_count()
43 nvar = fcn%get_variable_count()
47 ftol = this%get_fcn_tolerance()
48 xtol = this%get_var_tolerance()
49 gtol = this%get_gradient_tolerance()
50 maxeval = this%get_max_fcn_evals()
54 ib%jacobian_count = njac
56 ib%converge_on_fcn = fcnvrg
57 ib%converge_on_chng = xcnvrg
58 ib%converge_on_zero_diff = gcnvrg
60 if (
present(err))
then
65 if (this%get_use_line_search())
then
66 if (.not.this%is_line_search_defined()) &
67 call this%set_default_line_search()
68 call this%get_line_search(ls)
72 if (.not.fcn%is_fcn_defined())
then
74 call errmgr%report_error(
"qns_solve", &
75 "No function has been defined.", &
76 nl_invalid_operation_error)
79 if (nvar /= neqn)
then
81 write(errmsg, 100)
"The number of equations (", neqn, &
82 ") does not match the number of unknowns (", nvar,
")."
83 call errmgr%report_error(
"qns_solve", trim(errmsg), &
84 nl_invalid_input_error)
88 if (
size(x) /= nvar)
then
90 else if (
size(fvec) /= neqn)
then
95 write(errmsg, 101)
"Input number ", flag, &
96 " is not sized correctly."
97 call errmgr%report_error(
"qns_solve", trim(errmsg), &
103 allocate(q(neqn, neqn), stat = flag)
104 if (flag == 0)
allocate(r(neqn, nvar), stat = flag)
105 if (flag == 0)
allocate(tau(min(neqn, nvar)), stat = flag)
106 if (flag == 0)
allocate(b(neqn, nvar), stat = flag)
107 if (flag == 0)
allocate(df(neqn), stat = flag)
108 if (flag == 0)
allocate(fvold(neqn), stat = flag)
109 if (flag == 0)
allocate(xold(nvar), stat = flag)
110 if (flag == 0)
allocate(dx(nvar), stat = flag)
111 if (flag == 0)
allocate(s(neqn), stat = flag)
114 call errmgr%report_error(
"qns_solve", &
115 "Insufficient memory available.", nl_out_of_memory_error)
118 call qr_factor(r, tau, work, lw1)
119 call form_qr(r, tau, q, work, lw2)
120 call fcn%jacobian(x, b, fv = fvec, olwork = lw3)
121 allocate(work(max(lw1, lw2, lw3)), stat = flag)
124 call errmgr%report_error(
"qns_solve", &
125 "Insufficient memory available.", nl_out_of_memory_error)
130 call fcn%fcn(x, fvec)
131 f = half * dot_product(fvec, fvec)
135 test = max(abs(fvec(i)), test)
137 if (test < ftol)
then
143 if (.not.fcnvrg)
then
145 stpmax = factor * max(norm2(x), real(nvar, real64))
155 call fcn%jacobian(x, b, fvec, work)
160 call qr_factor(r, tau, work)
161 call form_qr(r, tau, q, work)
169 x2 = dot_product(dx, dx)
172 s = (df - matmul(b, dx))
173 call recip_mult_array(x2, s)
177 call rank1_update(one, s, dx, b)
178 call qr_rank1_update(q, r, s, dx, work)
186 call mtx_mult(.true., one, b, fvec, zero, dx)
195 call mtx_mult(.true., -one, q, fvec, zero, df)
200 call solve_triangular_system(.true., .false., .true., r, &
205 temp = dot_product(dx, df(1:nvar))
206 if (temp >= zero)
then
208 if (this%get_print_status())
then
209 call print_status(iter, neval, njac, xnorm, fnorm)
215 if (this%get_use_line_search())
then
217 temp = dot_product(df(1:nvar), df(1:nvar))
218 if (temp > stpmax) df(1:nvar) = df(1:nvar) * (stpmax / temp)
221 call limit_search_vector(df(1:nvar), stpmax)
222 call ls%search(fcn, xold, dx, df(1:nvar), x, fvec, fold, &
224 neval = neval + lib%fcn_count
228 call fcn%fcn(x, fvec)
229 f = half * dot_product(fvec, fvec)
234 if (lib%converge_on_zero_diff .and. &
235 this%get_use_line_search())
then
236 call test_convergence(x, xold, fvec, dx, .true., xtol, &
237 ftol, gtol, check, xcnvrg, fcnvrg, gcnvrg, xnorm, fnorm)
239 call test_convergence(x, xold, fvec, dx, .false., xtol, &
240 ftol, gtol, check, xcnvrg, fcnvrg, gcnvrg, xnorm, fnorm)
251 "It appears the solution has settled to " // &
252 "a point where the slope of the gradient " // &
253 "is effectively zero. " // new_line(
'c') // &
254 "Function evaluations performed: ", neval, &
255 "." // new_line(
'c') // &
256 "Change in Variable: ", xnorm, &
257 new_line(
'c') //
"Residual: ", fnorm
258 call errmgr%report_warning(
"nqs_solve", &
259 trim(errmsg), nl_spurious_convergence_error)
271 if (jcount >= this%m_jDelta)
then
283 if (this%get_print_status())
then
284 call print_status(iter, neval, njac, xnorm, fnorm)
288 if (neval >= maxeval)
then
296 if (
present(ib))
then
299 ib%jacobian_count = njac
300 ib%gradient_count = 0
301 ib%converge_on_fcn = fcnvrg
302 ib%converge_on_chng = xcnvrg
303 ib%converge_on_zero_diff = gcnvrg
308 write(errmsg, 102)
"The algorithm failed to " // &
309 "converge. Function evaluations performed: ", neval, &
310 "." // new_line(
'c') //
"Change in Variable: ", xnorm, &
311 new_line(
'c') //
"Residual: ", fnorm
312 call errmgr%report_error(
"qns_solve", trim(errmsg), &
313 nl_convergence_error)
317100
format(a, i0, a, i0, a)
319102
format(a, i0, a, e10.3, a, e10.3)
323 pure module function qns_get_jac_interval(this) result(n)
324 class(quasi_newton_solver),
intent(in) :: this
330 module subroutine qns_set_jac_interval(this, n)
331 class(quasi_newton_solver),
intent(inout) :: this
332 integer(int32),
intent(in) :: n