nonlin 1.5.2
A library that provides routines to compute the solutions to systems of nonlinear equations.
Loading...
Searching...
No Matches
nonlin_optimize_bfgs.f90
1! nonlin_optimize_bfgs.f90
2
3submodule(nonlin_optimize) nonlin_optimize_bfgs
4 use lapack
5 implicit none
6
7 interface
8 subroutine dsymv(uplo, n, alpha, a, lda, x, incx, beta, y, incy)
9 use iso_fortran_env, only : int32, real64
10 character, intent(in) :: uplo
11 integer(int32), intent(in) :: n, lda, incx, incy
12 real(real64), intent(in) :: alpha, beta, a(lda,*), x(*)
13 real(real64), intent(inout) :: y(*)
14 end subroutine
15 end interface
16
17contains
18 module subroutine bfgs_solve(this, fcn, x, fout, ib, err)
19 ! Arguments
20 class(bfgs), intent(inout) :: this
21 class(fcnnvar_helper), intent(in) :: fcn
22 real(real64), intent(inout), dimension(:) :: x
23 real(real64), intent(out), optional :: fout
24 type(iteration_behavior), optional :: ib
25 class(errors), intent(inout), optional, target :: err
26
27 ! Parameters
28 real(real64), parameter :: zero = 0.0d0
29 real(real64), parameter :: one = 1.0d0
30 real(real64), parameter :: negone = -1.0d0
31 real(real64), parameter :: factor = 1.0d2
32 real(real64), parameter :: small = 1.0d-10
33
34 ! Local Variables
35 logical :: xcnvrg, gcnvrg
36 integer(int32) :: i, n, maxeval, neval, ngrad, flag, iter
37 real(real64) :: xtol, gtol, fp, stpmax, fret, xtest, gtest, temp, ydx
38 real(real64), allocatable, dimension(:) :: g, dx, u, v, y, gold, xnew, bdx
39 real(real64), allocatable, dimension(:,:) :: b, r
40 class(errors), pointer :: errmgr
41 type(errors), target :: deferr
42 character(len = 256) :: errmsg
43 type(iteration_behavior) :: lib
44 class(line_search), allocatable :: ls
45
46 ! Initialization
47 n = fcn%get_variable_count()
48 maxeval = this%get_max_fcn_evals()
49 gtol = this%get_tolerance()
50 xtol = this%get_var_tolerance()
51 iter = 0
52 neval = 0
53 ngrad = 0
54 xcnvrg = .false.
55 gcnvrg = .false.
56 if (present(ib)) then
57 ib%iter_count = iter
58 ib%fcn_count = neval
59 ib%jacobian_count = 0
60 ib%gradient_count = ngrad
61 ib%converge_on_fcn = .false.
62 ib%converge_on_chng = xcnvrg
63 ib%converge_on_zero_diff = gcnvrg
64 end if
65 if (present(err)) then
66 errmgr => err
67 else
68 errmgr => deferr
69 end if
70 if (this%get_use_line_search()) then
71 if (.not.this%is_line_search_defined()) &
72 call this%set_default_line_search()
73 call this%get_line_search(ls)
74 end if
75
76 ! Input Check
77 if (.not.fcn%is_fcn_defined()) then
78 ! ERROR: No function is defined
79 call errmgr%report_error("bfgs_solve", &
80 "No function has been defined.", &
81 nl_invalid_operation_error)
82 return
83 end if
84 if (size(x) /= n) then
85 write(errmsg, 100) &
86 "It was expected to receive a coordinate vector of length ", &
87 n, " , but a vector of length ", size(x), " was received."
88 call errmgr%report_error("bfgs_solve", trim(errmsg), &
89 nl_invalid_input_error)
90 return
91 end if
92
93 ! Local Memory Allocation
94 allocate(g(n), stat = flag)
95 if (flag == 0) allocate(dx(n), stat = flag)
96 if (flag == 0) allocate(u(n), stat = flag)
97 if (flag == 0) allocate(v(n), stat = flag)
98 if (flag == 0) allocate(y(n), stat = flag)
99 if (flag == 0) allocate(bdx(n), stat = flag)
100 if (flag == 0) allocate(gold(n), stat = flag)
101 if (flag == 0) allocate(xnew(n), stat = flag)
102 if (flag == 0) allocate(b(n,n), stat = flag)
103 if (flag == 0) allocate(r(n,n), stat = flag)
104 if (flag /= 0) then
105 ! ERROR: Memory Error
106 call errmgr%report_error("bfgs_solve", &
107 "Insufficient memory available.", nl_out_of_memory_error)
108 return
109 end if
110
111 ! Process
112 fp = fcn%fcn(x)
113 call fcn%gradient(x, g, fp)
114 neval = 1
115 ngrad = 1
116
117 ! Check for a "zero" gradient at the initial point
118 gtest = norm2(g)
119 if (gtest < gtol) then
120 gcnvrg = .true.
121 end if
122
123 ! Main Loop
124 flag = 0
125 if (.not.gcnvrg) then
126 do
127 ! Update the iteration counter
128 iter = iter + 1
129
130 ! Define the initial direction, and a limit on the line search
131 ! step
132 if (iter == 1) then
133 dx = -g
134 stpmax = factor * max(norm2(x), real(n, real64))
135 end if
136
137 ! Perform the line search
138 if (this%get_use_line_search()) then
139 call limit_search_vector(dx, stpmax)
140 call ls%search(fcn, x, g, dx, xnew, fp, fret, lib, errmgr)
141 neval = neval + lib%fcn_count
142 fp = fret
143 else
144 xnew = x + dx
145 fp = fcn%fcn(xnew)
146 neval = neval + 1
147 end if
148
149 ! Update the gradient and line direction
150 do i = 1, n
151 dx(i) = xnew(i) - x(i)
152 x(i) = xnew(i)
153 gold(i) = g(i)
154 end do
155 call fcn%gradient(x, g, fp)
156 ngrad = ngrad + 1
157
158 ! Test for convergence on the change in X
159 xtest = zero
160 do i = 1, n
161 temp = abs(dx(i)) / max(abs(x(i)), one)
162 xtest = max(temp, xtest)
163 end do
164 if (xtest < xtol) then
165 xcnvrg = .true.
166 exit
167 end if
168
169 ! Test for convergence on the gradient
170 gtest = norm2(g)
171 if (gtest < gtol) then
172 gcnvrg = .true.
173 exit
174 end if
175
176 ! Perform the BFGS update
177 y = g - gold
178 ydx = dot_product(y, dx)
179
180 ! Establish an initial approximation to the Hessian matrix
181 if (iter == 1) then
182 temp = sqrt(dot_product(y, y) / ydx)
183 call dlaset('A', n, n, zero, temp, r, n)
184 end if
185
186 ! Compute: B = R**T * R
187 call tri_mtx_mult(.true., one, r, zero, b)
188
189 ! Compute bdx = B * dX (B is symmetric)
190 call dsymv('u', n, one, b, n, dx, 1, zero, bdx, 1)
191
192 ! Perform the actual update
193 if (ydx > small) then
194 ! Compute the rank 1 update and downdate
195 u = y / sqrt(ydx)
196 v = bdx / sqrt(dot_product(dx, bdx))
197 call cholesky_rank1_update(r, u)
198 call cholesky_rank1_downdate(r, v)
199 end if ! Else just skip the update
200
201 ! Compute the solution to: B * dx = -g = (R**T * R) * dx
202 dx = -g
203 call solve_cholesky(.true., r, dx)
204
205 ! Print iteration status
206 if (this%get_print_status()) then
207 print *, ""
208 print 101, "Iteration: ", iter
209 print 101, "Function Evaluations: ", neval
210 print 102, "Function Value: ", fp
211 print 102, "Change in Variable: ", xtest
212 print 102, "Gradient: ", gtest
213 end if
214
215 ! Ensure we haven't made too many function evaluations
216 if (neval >= maxeval) then
217 flag = 1
218 exit
219 end if
220 end do
221 end if
222
223 ! Report out iteration statistics
224 if (present(ib)) then
225 ib%iter_count = iter
226 ib%fcn_count = neval
227 ib%jacobian_count = 0
228 ib%gradient_count = ngrad
229 ib%converge_on_fcn = .false.
230 ib%converge_on_chng = xcnvrg
231 ib%converge_on_zero_diff = gcnvrg
232 end if
233
234 ! Get the function value at the computed minimum
235 if (present(fout)) fout = fp
236
237 ! Check for convergence issues
238 if (flag /= 0) then
239 write(errmsg, 103) &
240 "The algorithm failed to converge." // new_line('c') // &
241 "Function evaluations performed: ", neval, new_line('c') // &
242 "Function Value: ", fp, new_line('c') // &
243 "Change in Variable: ", xtest, new_line('c') // &
244 "Gradient: ", gtest
245 call errmgr%report_error("bfgs_solve", trim(errmsg), &
246 nl_convergence_error)
247 end if
248
249 ! Formatting
250100 format(a, i0, a, i0, a)
251101 format(a, i0)
252102 format(a, e10.3)
253103 format(a, i0, a, e10.3, a, e10.3, a, e10.3)
254 end subroutine
255end submodule
nonlin_optimize