b2api
B2000++ API Reference Manual, VERSION 4.6
 
Loading...
Searching...
No Matches
b2line_search.H
1// Copyright (C) 2008 Davis E. King (davis@dlib.net)
2// License: Boost Software License.
3//
4// 2013 SMR: Remove the dependency to the dlib library
5
6#ifndef __B2LINE_SEARCH_H__
7#define __B2LINE_SEARCH_H__
8
9#include <algorithm>
10#include <cassert>
11#include <limits>
12
13namespace b2000 {
14
15inline double put_in_range(double min, double max, double v) {
16 if (v < min) { return min; }
17 if (v > max) { return max; }
18 return v;
19}
20
21inline double poly_min_extrap(double f0, double d0, double f1) {
22 const double temp = 2 * (f1 - f0 - d0);
23 if (std::abs(temp) <= d0 * std::numeric_limits<double>::epsilon()) { return 0.5; }
24
25 const double alpha = -d0 / temp;
26
27 // now make sure the minimum is within the allowed range of (0,1)
28 return put_in_range(0, 1, alpha);
29}
30
31inline double poly_min_extrap(double f0, double d0, double f1, double d1) {
32 const double n = 3 * (f1 - f0) - 2 * d0 - d1;
33 const double e = d0 + d1 - 2 * (f1 - f0);
34
35 // find the minimum of the derivative of the polynomial
36
37 double temp = std::max(n * n - 3 * e * d0, 0.0);
38
39 if (temp < 0) { return 0.5; }
40
41 temp = std::sqrt(temp);
42
43 if (std::abs(e) <= std::numeric_limits<double>::epsilon()) { return 0.5; }
44
45 // figure out the two possible min values
46 double x1 = (temp - n) / (3 * e);
47 double x2 = -(temp + n) / (3 * e);
48
49 // compute the value of the interpolating polynomial at these two points
50 double y1 = f0 + d0 * x1 + n * x1 * x1 + e * x1 * x1 * x1;
51 double y2 = f0 + d0 * x2 + n * x2 * x2 + e * x2 * x2 * x2;
52
53 // pick the best point
54 double x;
55 if (y1 < y2) {
56 x = x1;
57 } else {
58 x = x2;
59 }
60
61 // now make sure the minimum is within the allowed range of (0,1)
62 return put_in_range(0, 1, x);
63}
64
65inline double poly_min_extrap(
66 double f0, double d0, double x1, double f_x1, double x2, double f_x2) {
67 assert(0 < x1 && x1 < x2);
68
69 // The contents of this function follow the equations described on page 58 of the
70 // book Numerical Optimization by Nocedal and Wright, second edition.
71 const double aa2 = x2 * x2;
72 const double aa1 = x1 * x1;
73 const double m[2][2] = {{aa2, -aa2 * x2}, {-aa1, aa1 * x1}};
74 const double v[2] = {f_x1 - f0 - d0 * x1, f_x2 - f0 - d0 * x2};
75 double temp = aa2 * aa1 * (x1 - x2);
76
77 // just take a guess if this happens
78 if (temp == 0) { return x1 / 2.0; }
79
80 const double inv_temp = 1 / temp;
81 const double a = inv_temp * (m[0][0] * v[0] + m[1][0] * v[1]);
82 const double b = inv_temp * (m[0][1] * v[0] + m[1][1] * v[1]);
83
84 temp = b * b - 3 * a * d0;
85 if (temp < 0 || a == 0) {
86 // This is probably a line so just pick the lowest point
87 if (f0 < f_x2) {
88 return 0;
89 } else {
90 return x2;
91 }
92 }
93 temp = (-b + std::sqrt(temp)) / (3 * a);
94 return put_in_range(0, x2, temp);
95}
96
97template <typename funct>
98double line_search(
99 const funct& f, const double f0, const double d0, double rho, double sigma, double min_f,
100 unsigned long max_iter) {
101 assert(0 < rho && rho < sigma && sigma < 1 && max_iter > 0);
102
103 // The bracketing phase of this function is implemented according to block 2.6.2 from
104 // the book Practical Methods of Optimization by R. Fletcher. The sectioning
105 // phase is an implementation of 2.6.4 from the same book.
106
107 // tau1 > 1. Controls the alpha jump size during the search
108 const double tau1 = 9;
109
110 // it must be the case that 0 < tau2 < tau3 <= 1/2 for the algorithm to function
111 // correctly but the specific values of tau2 and tau3 aren't super important.
112 const double tau2 = 1.0 / 10.0;
113 const double tau3 = 1.0 / 2.0;
114
115 // Stop right away and return a step size of 0 if the gradient is 0 at the starting point
116 if (std::abs(d0) < std::numeric_limits<double>::epsilon()) { return 0; }
117
118 // Stop right away if the current value is good enough according to min_f
119 if (f0 <= min_f) { return 0; }
120
121 // Figure out a reasonable upper bound on how large alpha can get.
122 const double mu = (min_f - f0) / (rho * d0);
123
124 double alpha = 1;
125 if (mu < 0) { alpha = -alpha; }
126 alpha = put_in_range(0, 0.65 * mu, alpha);
127
128 double last_alpha = 0;
129 double last_val = f0;
130 double last_val_der = d0;
131
132 // The bracketing stage will find a range of points [a,b]
133 // that contains a reasonable solution to the line search
134 double a, b;
135
136 // These variables will hold the values and derivatives of f(a) and f(b)
137 double a_val, b_val, a_val_der, b_val_der;
138
139 // This thresh value represents the Wolfe curvature condition
140 const double thresh = std::abs(sigma * d0);
141
142 unsigned long itr = 0;
143 // do the bracketing stage to find the bracket range [a,b]
144 while (true) {
145 ++itr;
146 double val_der;
147 const double val = f(alpha, val_der);
148
149 // we are done with the line search since we found a value smaller
150 // than the minimum f value
151 if (val <= min_f) { return alpha; }
152
153 if (val > f0 + rho * alpha * d0 || val >= last_val) {
154 a_val = last_val;
155 a_val_der = last_val_der;
156 b_val = val;
157 b_val_der = val_der;
158
159 a = last_alpha;
160 b = alpha;
161 break;
162 }
163
164 if (std::abs(val_der) <= thresh) { return alpha; }
165
166 // if we are stuck not making progress then quit with the current alpha
167 if (last_alpha == alpha || itr >= max_iter) { return alpha; }
168
169 if (val_der >= 0) {
170 a_val = val;
171 a_val_der = val_der;
172 b_val = last_val;
173 b_val_der = last_val_der;
174
175 a = alpha;
176 b = last_alpha;
177 break;
178 }
179
180 if (mu <= 2 * alpha - last_alpha) {
181 last_alpha = alpha;
182 alpha = mu;
183 } else {
184 const double temp = alpha;
185
186 double first = 2 * alpha - last_alpha;
187 double last;
188 if (mu > 0) {
189 last = std::min(mu, alpha + tau1 * (alpha - last_alpha));
190 } else {
191 last = std::max(mu, alpha + tau1 * (alpha - last_alpha));
192 }
193
194 // pick a point between first and last by doing some kind of interpolation
195 if (last_alpha < alpha) {
196 alpha = last_alpha
197 + (alpha - last_alpha)
198 * poly_min_extrap(last_val, last_val_der, val, val_der);
199 } else {
200 alpha = alpha
201 + (last_alpha - alpha)
202 * poly_min_extrap(val, val_der, last_val, last_val_der);
203 }
204
205 alpha = put_in_range(first, last, alpha);
206
207 last_alpha = temp;
208 }
209
210 last_val = val;
211 last_val_der = val_der;
212 }
213
214 // Now do the sectioning phase from 2.6.4
215 while (true) {
216 ++itr;
217 double first = a + tau2 * (b - a);
218 double last = b - tau3 * (b - a);
219
220 // use interpolation to pick alpha between first and last
221 alpha = a + (b - a) * poly_min_extrap(a_val, a_val_der, b_val, b_val_der);
222 alpha = put_in_range(first, last, alpha);
223
224 double val_der;
225 const double val = f(alpha, val_der);
226
227 // we are done with the line search since we found a value smaller
228 // than the minimum f value or we ran out of iterations.
229 if (val <= min_f || itr >= max_iter) { return alpha; }
230
231 // stop if the interval gets so small that it isn't shrinking any more due to rounding error
232 if (a == first || b == last) { return b; }
233
234 if (val > f0 + rho * alpha * d0 || val >= a_val) {
235 b = alpha;
236 b_val = val;
237 b_val_der = val_der;
238 } else {
239 if (std::abs(val_der) <= thresh) { return alpha; }
240
241 if ((b - a) * val_der >= 0) {
242 b = a;
243 b_val = a_val;
244 b_val_der = a_val_der;
245 }
246
247 a = alpha;
248 a_val = val;
249 a_val_der = val_der;
250 }
251 }
252}
253
254template <typename funct>
255double backtracking_line_search(
256 const funct& f, double f0, double d0, double alpha, double rho, unsigned long max_iter) {
257 assert(0 < rho && rho < 1 && max_iter > 0);
258
259 // make sure alpha is going in the right direction. That is, it should be opposite
260 // the direction of the gradient.
261 if ((d0 > 0 && alpha > 0) || (d0 < 0 && alpha < 0)) { alpha *= -1; }
262
263 bool have_prev_alpha = false;
264 double prev_alpha = 0;
265 double prev_val = 0;
266 unsigned long iter = 0;
267 while (true) {
268 ++max_iter;
269 const double val = f(alpha);
270 if (val <= f0 + alpha * rho * d0 || iter >= max_iter) {
271 return alpha;
272 } else {
273 // Interpolate a new alpha. We also make sure the step by which we
274 // reduce alpha is not super small.
275 double step;
276 if (!have_prev_alpha) {
277 if (d0 < 0) {
278 step = alpha * put_in_range(0.1, 0.9, poly_min_extrap(f0, d0, val));
279 } else {
280 step = alpha * put_in_range(0.1, 0.9, poly_min_extrap(f0, -d0, val));
281 }
282 have_prev_alpha = true;
283 } else {
284 if (d0 < 0) {
285 step = put_in_range(
286 0.1 * alpha, 0.9 * alpha,
287 poly_min_extrap(f0, d0, alpha, val, prev_alpha, prev_val));
288 } else {
289 step = put_in_range(
290 0.1 * alpha, 0.9 * alpha,
291 -poly_min_extrap(f0, -d0, -alpha, val, -prev_alpha, prev_val));
292 }
293 }
294
295 prev_alpha = alpha;
296 prev_val = val;
297
298 alpha = step;
299 }
300 }
301}
302
303} // namespace b2000
304
305#endif
Contains the base classes for implementing Finite Elements.
Definition b2boundary_condition.H:32