OpenLB 1.7
Loading...
Searching...
No Matches
optimizerLineSearch.h
Go to the documentation of this file.
1/* This file is part of the OpenLB library
2 *
3 * Copyright (C) 2012-2016 Mathias J. Krause, Benjamin Förster
4 * E-mail contact: info@openlb.net
5 * The most recent release of OpenLB can be downloaded at
6 * <http://www.openlb.net/>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public
19 * License along with this program; if not, write to the Free
20 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22*/
23
29#ifndef OPTIMIZER_LINE_SEARCH_H
30#define OPTIMIZER_LINE_SEARCH_H
31
32#include "optimizer.h"
33
34// All OpenLB code is contained in this namespace.
35namespace olb {
36
38namespace opti {
39
41
50template<typename S, typename C>
51class OptimizerLineSearch : public Optimizer<S,C> {
52
53private:
54 mutable OstreamManager clout;
55
56protected:
61 // Upper/Lower bound
66
69
70 // Step condition
71 std::string _stepCondition;
74
75public:
77 OptimizerLineSearch(int dimCtrl, S eps, int maxIt, S lambda, int maxStepAttempts, std::string stepCondition,
78 bool verboseOn=true, const std::string fname="", const std::string logFileName="",
79 bool withUpperBound=false, S upperBound=S(),
80 bool withLowerBound=false, S lowerBound=S(), bool vectorBounds=false,
81 S controlEps=S(std::numeric_limits<double>::epsilon() ), bool failOnMaxIter = true,
82 std::vector<OptimizerLogType> gplotAnalysis = {})
83 : Optimizer<S,C>(dimCtrl, eps, maxIt, verboseOn, fname, logFileName, withUpperBound, upperBound, withLowerBound,
84 lowerBound, vectorBounds, controlEps, failOnMaxIter, gplotAnalysis),
85 clout(std::cout,"OptimizerLineSearch")
86 {
87 _lambda = lambda;
90 _maxStepAttempts = maxStepAttempts;
91 _stepCondition = stepCondition;
92 _lowerBoundFlag = false;
93 _upperBoundFlag = false;
94 _nextDerivFlag = false;
95
97 // No Condition
98 if (_stepCondition == "None") {
100 _stepLengthFunction = nullptr;
101 // Smaller objective function
102 }
103 else if (_stepCondition == "Smaller") {
106 // Armijo, Wolfe and Strong Wolfe conditions
107 }
108 else if (_stepCondition == "Armijo" || _stepCondition == "Wolfe" || _stepCondition == "StrongWolfe") {
111 }
112 else {
113 clout << "No step condition chosen! Exiting..." << std::endl;
114 exit(1);
115 }
116
117 };
118
119 virtual ~OptimizerLineSearch() { };
120
121 virtual void computeDirection() = 0;
122
124 {
125 if (this->_withUpperBound) {
126 bool atUpper = true;
127 for (int iDim=0; iDim<this->_dimCtrl; ++iDim) {
128 const int i = (this->_vectorBounds) ? iDim : 0;
129 atUpper &= (util::nearZero(this->_boundedControl[iDim] - this->_upperBound[i]));
130 }
131 if (atUpper) {
132 if (_upperBoundFlag) {
133 clout << "Control stuck at upper bound: " << std::string(this->_upperBound.begin(), this->_upperBound.end()) << std::endl;
134 exit(1);
135 }
136 _upperBoundFlag = true;
137 }
138 else {
139 _upperBoundFlag = false;
140 }
141 }
142 if (this->_withLowerBound) {
143 bool atLower = true;
144 for (int iDim=0; iDim<this->_dimCtrl; ++iDim) {
145 const int i = (this->_vectorBounds) ? iDim : 0;
146 atLower &= (util::nearZero(this->_boundedControl[iDim] - this->_lowerBound[i]));
147 }
148 if (atLower) {
149 if (_lowerBoundFlag) {
150 clout << "Control stuck at lower bound: " << std::string(this->_lowerBound.begin(), this->_lowerBound.end()) << std::endl;
151 exit(1);
152 }
153 _lowerBoundFlag = true;
154 }
155 else {
156 _lowerBoundFlag = false;
157 }
158 }
159 };
160
162 {
163 for (int iDim=0; iDim<this->_dimCtrl; iDim++) {
164 this->_boundedControl[iDim] = this->_control[iDim];
165 const int i = (this->_vectorBounds) ? iDim : 0;
166 if (this->_withUpperBound) {
167 const S tmp = this->_upperBound[i];
168 if (this->_control[iDim] > tmp ||
169 (std::isnan(this->_control[iDim])
170 && !std::signbit(this->_control[iDim])) ) {
171 this->_boundedControl[iDim] = tmp;
172 }
173 }
174 if (this->_withLowerBound) {
175 const S tmp = this->_lowerBound[i];
176 if (this->_control[iDim] < tmp ||
177 (std::isnan(this->_control[iDim])
178 && std::signbit(this->_control[iDim])) ) {
179 this->_boundedControl[iDim] = tmp;
180 }
181 }
182 }
183 };
184
185 bool smallerValue(const S& tempValue)
186 {
187 if (!(tempValue > this->_value || std::isnan(tempValue))) {
188 return true;
189 }
190 else {
191 (this->*_stepLengthFunction)(tempValue);
192 return false;
193 }
194 }
195
196 bool noCondition(const S& tempValue)
197 {
198 return true;
199 }
200
201 // Combined Armijo, Wolfe and Strong Wolfe condition
202 // Depends on _stepCondition
203 bool armijoWolfeConditions(const S& tempValue)
204 {
205 S c1 = 1e-4;
206 S c2 = 0.9; // 0.9 for (quasi)Newton; else 0.1
207 S dir = 0.;
208 S dirNext = 0.;
209
210 // \nabla f_k \dot p_k
211 for (int i=0; i<this->_dimCtrl; i++) {
212 dir += this->_direction[i] * this->_derivative[i];
213 }
214
215 // Armijo rule
216 if (!(tempValue <= this->_value + c1*this->_lambda*dir)) {
217 if (this->_verboseOn) {
218 clout << "Armijo failed!" << std::endl;
219 }
220 // Decrease step length
221 (this->*_stepLengthFunction)(tempValue);
222 //this->_lambda *= .5;
223 return false;
224 }
225 // (strong) Wolfe conditions (curvature condition)
226 if ( _stepCondition == "Wolfe" || _stepCondition == "StrongWolfe") {
227 // Compute derivative for the changed control and store it in nextDerivative
228 // Set nextDerivFlag to true to prevent the optiCase to redo this process
229 this->computeDerivatives(this->_control, _nextDerivative);
230 _nextDerivFlag = true;
231 // \nabla f_{k+1} \dot p_k
232 for (int i=0; i<this->_dimCtrl; i++) {
233 dirNext += this->_direction[i]*_nextDerivative[i];
234 }
235 bool curvature = ( dirNext <= c2*dir );
236 if (_stepCondition == "StrongWolfe") {
237 curvature = ( util::abs(dirNext) <= c2*util::abs(dir) );
238 }
239
240 if (!curvature) {
241 if (this->_verboseOn) {
242 clout << "Curvature failed!" << std::endl;
243 }
244 // Increase step length
245 this->_lambda *= 2.1;
246 return false;
247 }
248 }
249 return true;
250 }
251
252 // Quadratic Interpolation [Nocedal; p.58]
253 void quadraticInterpolationStep(const S& tempValue)
254 {
255 S newLambda = S();
256 S dir = S();
257 if (std::isnan(tempValue) ) {
258 newLambda = this->_lambda/2.;
259 }
260 else {
261 for (int iDim=0; iDim<this->_dimCtrl; iDim++) {
262 dir += this->_derivative[iDim]*this->_direction[iDim];
263 }
264 newLambda = dir*this->_lambda*this->_lambda / (2.*(tempValue - this->_value + dir*this->_lambda));
265 // Step size control
266 if (newLambda < this->_lambda*.1 ) {
267 newLambda = this->_lambda*.1;
268 }
269 if (newLambda > this->_lambda*.5 ) {
270 newLambda = this->_lambda*.5;
271 }
272 }
273 this->_lambda = newLambda;
274 }
275
276 void backtrackingLineSearch(S& tempValue, S lambda, bool(OptimizerLineSearch::*condition)(const S&))
277 {
278
279 // Save this->_lambda so that it is unchanged after the line search
280 S startLambda = this->_lambda;
281 int refinementStep = 0;
282 // Do line search until the condition is fullfilled
283 // the step size (this->_lambda) will be changed in the condition
284 while ( !(this->*condition)(tempValue) ) {
285 if ( util::abs(lambda/this->_value) < std::numeric_limits<double>::epsilon() ) {
286 clout << "Excessive refinement steps (too small step size).\nProgram terminated." <<std::endl;
287 exit(1);
288 }
289 refinementStep++;
290
291 // Leave program if maximum number of step attempts is exceeded.
292 if ( refinementStep >= _maxStepAttempts ) {
293 clout << "Excessive refinement steps (maxStepAttempts exeeded).\nProgram terminated." <<std::endl;
294 exit(1);
295 }
296
297 S newLambda = this->_lambda;
298 bool notSensible = true;
299 for (int iDim=0; iDim<this->_dimCtrl; iDim++) {
300 // Go back (newLambda-lambda) from previous control(=control-lambda*direction),
301 // therefore this produces a step of newLambda*direction
302 S tmp = (newLambda-lambda)*this->_direction[iDim];
303 this->_control[iDim] -= tmp;
304 // is this move larger than machine precision wrt control
305 if (util::abs(this->_control[iDim]) > 0 && util::abs(tmp) > 0) {
306 notSensible &= ( util::nearZero(tmp/this->_control[iDim]) );
307 }
308 }
309 // stop excessive refinement steps when values become no longer sensible
310 if (notSensible) {
311 clout << "Excessive refinement steps (not sensible).\nProgram terminated." <<std::endl;
312 exit(1);
313 }
314
315 if (this->_withUpperBound||this->_withLowerBound) {
316 boundControl();
317 }
318
319 if (this->_verboseOn) {
320 clout << "[Step " << this->_it << "][Ref " << refinementStep << "] <<<<<<<<<< lambda=" << newLambda << " <<<<<<<<<<" << std::endl;
321 }
322
323 if (this->_withUpperBound||this->_withLowerBound) {
324 this->evaluateObjective(this->_boundedControl, tempValue);
325 }
326 else {
327 this->evaluateObjective(this->_control, tempValue);
328 }
329 lambda = newLambda;
330 }
331 this->_lambda = startLambda;
332 };
333
334
336 virtual void optimizationStep()
337 {
338 // Compute search direction
339 if (this->_verboseOn) {
340 clout << "Computing directions..." << std::endl;
341 }
342
343 // Use given optimization algorithm to get direction:
344 // Steepest Descent, LBFGS or Barzilai-Borwein
346
347 _upperBoundFlag = false;
348 _lowerBoundFlag = false;
349 checkBound();
350
351 // save initial control
352 const C initialControl = this->_control;
353
354 // Search along line to find new control
355 for (int iDim=0; iDim<this->_dimCtrl; iDim++) {
356 this->_control[iDim] -= this->_lambda*_direction[iDim];
357 }
358
359 if (this->_withUpperBound||this->_withLowerBound) {
360 boundControl();
361 checkBound();
362 }
363
364 if (this->_verboseOn) {
365 clout << "[Step " << this->_it << "] <<<<<<<<<< lambda=" << this->_lambda << " <<<<<<<<<<" << std::endl;
366 }
367
368 S tempValue = S();
369 if (this->_withUpperBound||this->_withLowerBound) {
370 this->evaluateObjective(this->_boundedControl, tempValue);
371 }
372 else {
373 this->evaluateObjective(this->_control, tempValue);
374 }
375
376 // Backtracking line search with step condition
377 backtrackingLineSearch(tempValue, this->_lambda, _stepConditionFunction);
378
379 if (this->_withUpperBound||this->_withLowerBound) {
380 if (this->_verboseOn) {
381 clout << "Bounding control" << std::endl;
382 }
383 for (int iDim=0; iDim<this->_dimCtrl; iDim++) {
384 this->_control[iDim] = this->_boundedControl[iDim];
385 }
386 }
387
388 // Update value of the objective functional
389 this->_value = tempValue;
390 // Update step no.
391 this->_it++;
392
393 // check if control have changed sufficiently or break from optimisation
394 this->_controlsConverged = true;
395 for (int iDim=0; iDim<this->_dimCtrl; ++iDim) {
396 S ave = 0.5 * (initialControl[iDim] + this->_control[iDim]);
397 if (util::abs(ave) > 0) {
398 if (util::abs( (initialControl[iDim] - this->_control[iDim]) / ave) >= this->_controlEps) {
399 this->_controlsConverged = false;
400 break;
401 }
402 } // else control diff is zero anyway
403 }
404 if (this->_verboseOn) {
405 clout << "Controls converged within " << this->_controlEps << ": " << ((this->_controlsConverged) ? "true" : "false") << std::endl;
406 }
407 };
408};
409
410} // namespace opti
411
412} // namespace olb
413
414#endif
class for marking output with some text
Optimization algorithm: LineSearch.
virtual void computeDirection()=0
OptimizerLineSearch(int dimCtrl, S eps, int maxIt, S lambda, int maxStepAttempts, std::string stepCondition, bool verboseOn=true, const std::string fname="", const std::string logFileName="", bool withUpperBound=false, S upperBound=S(), bool withLowerBound=false, S lowerBound=S(), bool vectorBounds=false, S controlEps=S(std::numeric_limits< double >::epsilon()), bool failOnMaxIter=true, std::vector< OptimizerLogType > gplotAnalysis={})
Construction of an OptimizerLineSearch.
void backtrackingLineSearch(S &tempValue, S lambda, bool(OptimizerLineSearch::*condition)(const S &))
bool(OptimizerLineSearch::* _stepConditionFunction)(const S &)
bool armijoWolfeConditions(const S &tempValue)
int _maxStepAttempts
Maximal number of step attempts for conditioned line search.
bool noCondition(const S &tempValue)
void quadraticInterpolationStep(const S &tempValue)
void(OptimizerLineSearch::* _stepLengthFunction)(const S &)
virtual void optimizationStep()
Optimization step: line search.
bool smallerValue(const S &tempValue)
Interface for the use of various optimization algorithms.
Definition optimizer.h:56
bool _withUpperBound
Bounded versions.
Definition optimizer.h:84
int _it
Current iteration no.
Definition optimizer.h:73
C _derivative
Vector of derivatives of the object functional with respect to the controlled variables.
Definition optimizer.h:71
int _dimCtrl
Number of controlled variables.
Definition optimizer.h:63
bool _verboseOn
Verbose.
Definition optimizer.h:81
C _control
Vector of controlled variables (size _dimCtrl)
Definition optimizer.h:65
S _value
Value of the objective functional evaluated for controlled variables saved in _control.
Definition optimizer.h:68
bool _controlsConverged
For setting tolerance of controls.
Definition optimizer.h:92
void computeDerivatives(const C &control, C &derivatives)
Definition optimizer.h:144
void evaluateObjective(const C &control, S &result)
Definition optimizer.h:140
void exit(int exitcode)
Definition singleton.h:165
ADf< T, DIM > abs(const ADf< T, DIM > &a)
Definition aDiff.h:1019
bool nearZero(const ADf< T, DIM > &a)
Definition aDiff.h:1087
Top level namespace for all of OpenLB.
Optimization Code.
The description of optimization algorithms – header file.
Creates a container of type C.