bpp-core3  3.0.0
OneDimensionOptimizationTools.cpp
Go to the documentation of this file.
1 //
2 // File: OneDimensionOptimizationTools.cpp
3 // Authors:
4 // Julien Dutheil
5 // Created: 2003-11-17 11:15:22
6 //
7 
8 /*
9  Copyright or © or Copr. Bio++ Development Team, (November 17, 2004)
10 
11  This software is a computer program whose purpose is to provide classes
12  for numerical calculus. This file is part of the Bio++ project.
13 
14  This software is governed by the CeCILL license under French law and
15  abiding by the rules of distribution of free software. You can use,
16  modify and/ or redistribute the software under the terms of the CeCILL
17  license as circulated by CEA, CNRS and INRIA at the following URL
18  "http://www.cecill.info".
19 
20  As a counterpart to the access to the source code and rights to copy,
21  modify and redistribute granted by the license, users are provided only
22  with a limited warranty and the software's author, the holder of the
23  economic rights, and the successive licensors have only limited
24  liability.
25 
26  In this respect, the user's attention is drawn to the risks associated
27  with loading, using, modifying and/or developing or reproducing the
28  software by the user in light of its specific status of free software,
29  that may mean that it is complicated to manipulate, and that also
30  therefore means that it is reserved for developers and experienced
31  professionals having in-depth computer knowledge. Users are therefore
32  encouraged to load and test the software's suitability as regards their
33  requirements in conditions enabling the security of their systems and/or
34  data to be ensured and, more generally, to use and operate it in the
35  same conditions as regards security.
36 
37  The fact that you are presently reading this means that you have had
38  knowledge of the CeCILL license and that you accept its terms.
39 */
40 
41 
42 #include "../NumConstants.h"
43 #include "../NumTools.h"
44 #include "BrentOneDimension.h"
47 
48 using namespace bpp;
49 using namespace std;
50 
51 /******************************************************************************
52 * The Point class *
53 ******************************************************************************/
54 inline void BracketPoint::set(double xval, double fval) { this->x = xval; this->f = fval; }
55 
56 /******************************************************************************
57 * The Bracket class *
58 ******************************************************************************/
59 inline void Bracket::setA(double xa, double fa) { a.set(xa, fa); }
60 inline void Bracket::setB(double xb, double fb) { b.set(xb, fb); }
61 inline void Bracket::setC(double xc, double fc) { c.set(xc, fc); }
62 
63 /******************************************************************************/
64 
66  double a,
67  double b,
68  Function* function,
69  ParameterList parameters)
70 {
71  Bracket bracket;
72  // Copy the parameter to use.
73  bracket.a.x = a;
74  parameters[0].setValue(bracket.a.x); bracket.a.f = function->f(parameters);
75  bracket.b.x = b;
76  parameters[0].setValue(bracket.b.x); bracket.b.f = function->f(parameters);
77 
78  while (std::isnan(bracket.b.f) || std::isinf(bracket.b.f))
79  {
80  bracket.b.x /= 1.1;
81  parameters[0].setValue(bracket.b.x); bracket.b.f = function->f(parameters);
82  }
83 
84  if (bracket.b.f > bracket.a.f)
85  {
86  // Switch roles of first and second point so that we can go downhill
87  // in the direction from a to b.
88  NumTools::swap<double>(bracket.a.x, bracket.b.x);
89  NumTools::swap<double>(bracket.a.f, bracket.b.f);
90  }
91 
92  // First guess for third point:
93  bracket.c.x = bracket.b.x + NumConstants::GOLDEN_RATIO_PHI() * (bracket.b.x - bracket.a.x);
94  parameters[0].setValue(bracket.c.x); bracket.c.f = function->f(parameters);
95 
96 
97  // Keep returning here until we bracket:
98  while (bracket.b.f > bracket.c.f)
99  {
100  // Compute xu by parabolic extrapolation from a, b, c. TINY is used to prevent
101  // any possible division by 0.
102  double r = (bracket.b.x - bracket.a.x) * (bracket.b.f - bracket.c.f);
103  double q = (bracket.b.x - bracket.c.x) * (bracket.b.f - bracket.a.f);
104 
105  double xu = bracket.b.x - ((bracket.b.x - bracket.c.x) * q - (bracket.b.x - bracket.a.x) * r) /
107  double xulim = (bracket.b.x) + GLIMIT * (bracket.c.x - bracket.b.x);
108  double fu;
109 
110  // We don't go farther than this.
111  // Test various possibilities:
112  if ((bracket.b.x - xu) * (xu - bracket.c.x) > 0.0)
113  {
114  parameters[0].setValue(xu); fu = function->f(parameters);
115  if (fu < bracket.c.f)
116  {
117  bracket.setA(bracket.b.x, bracket.b.f);
118  bracket.setB(xu, fu);
119  return bracket;
120  }
121  else if (fu > bracket.b.f)
122  {
123  bracket.setC(xu, fu);
124  return bracket;
125  }
126  // Parabolic fit was no use.
127  // Use default magnification.
128  xu = bracket.c.x + NumConstants::GOLDEN_RATIO_PHI() * (bracket.c.x - bracket.b.x);
129  parameters[0].setValue(xu); fu = function->f(parameters);
130  }
131  else if ((bracket.c.x - xu) * (xu - xulim) > 0.0)
132  {
133  // Parabolic fit is between point 3 and its allowed limit.
134  parameters[0].setValue(xu); fu = function->f(parameters);
135  if (fu < bracket.c.f)
136  {
137  NumTools::shift<double>(bracket.b.x, bracket.c.x, xu, bracket.c.x + NumConstants::GOLDEN_RATIO_PHI() * (bracket.c.x - bracket.b.x));
138  parameters[0].setValue(xu);
139  NumTools::shift<double>(bracket.b.f, bracket.c.f, fu, function->f(parameters));
140  }
141  }
142  else if ((xu - xulim) * (xulim - bracket.c.x) >= 0.0)
143  {
144  // Limit parabolic xu to maximum allowed value.
145  xu = xulim;
146  parameters[0].setValue(xu); fu = function->f(parameters);
147  }
148  else
149  {
150  // Reject parabolic xu, use default magnification.
151  xu = bracket.c.x + NumConstants::GOLDEN_RATIO_PHI() * (bracket.c.x - bracket.b.x);
152  parameters[0].setValue(xu); fu = function->f(parameters);
153  }
154  // Eliminate oldest point and continue.
155  NumTools::shift<double>(bracket.a.x, bracket.b.x, bracket.c.x, xu);
156  NumTools::shift<double>(bracket.a.f, bracket.b.f, bracket.c.f, fu);
157  }
158  return bracket;
159 }
160 
161 /******************************************************************************/
162 
164  double a,
165  double b,
166  Function* function,
167  ParameterList parameters,
168  uint intervalsNum)
169 {
170  Bracket bracket;
171  // Copy the parameter to use.
172  bracket.a.x = a;
173  parameters[0].setValue(bracket.a.x); bracket.a.f = function->f(parameters);
174  bracket.b.x = b;
175  parameters[0].setValue(bracket.b.x); bracket.b.f = function->f(parameters);
176 
177  while (std::isnan(bracket.b.f) || std::isinf(bracket.b.f))
178  {
179  bracket.b.x /= 1.1;
180  parameters[0].setValue(bracket.b.x); bracket.b.f = function->f(parameters);
181  }
182 
183  double bestMiddleX, bestMiddleF;
184  double curr, fcurr, jump;
185 
186  // look for bracket.c.x by scanning n possible assignments and assigining c the best one over the examined values
187  curr = bracket.a.x;
188  fcurr = bracket.a.f;
189  bestMiddleX = (bracket.a.f < bracket.b.f ? bracket.a.x : bracket.b.x); // determine the currently optimal point with respect to f out of a and b
190  bestMiddleF = (bracket.a.f < bracket.b.f ? bracket.a.f : bracket.b.f); // determine the currently optimal point with respect to f out of a and b
191  jump = (b - a) / static_cast<double>(intervalsNum); // Determine the spacing appropriate to the mesh.
192  for (size_t i = 1; i <= intervalsNum; i++)
193  { // Loop over all intervals
194  curr += jump;
195  parameters[0].setValue(curr); fcurr = function->f(parameters);
196  // If c yields better likelihood than a and b
197  if (fcurr < bestMiddleF)
198  {
199  bestMiddleX = curr;
200  bestMiddleF = fcurr;
201  }
202  }
203  bracket.c.x = bestMiddleX;
204  parameters[0].setValue(bracket.c.x); bracket.c.f = function->f(parameters);
205  return bracket;
206 }
207 
208 /******************************************************************************/
209 
211  DirectionFunction& f1dim,
212  ParameterList& parameters,
213  std::vector<double>& xi,
214  double tolerance,
215  OutputStream* profiler,
216  OutputStream* messenger,
217  unsigned int verbose)
218 {
219  // Initial guess for brackets:
220  double ax = 0.;
221  double xx = 0.01;
222 
224  f1dim.setMessageHandler(messenger);
225  f1dim.init(parameters, xi);
226  BrentOneDimension bod(&f1dim);
227  bod.setMessageHandler(messenger);
228  bod.setProfiler(profiler);
229  bod.setVerbose(verbose >= 1 ? 1 : 0);
231  bod.getStopCondition()->setTolerance(0.01);
232  bod.setInitialInterval(ax, xx);
234  ParameterList singleParameter;
235  singleParameter.addParameter(Parameter("x", 0.0));
236  bod.init(singleParameter);
237  bod.optimize();
238  // Update parameters:
239  // parameters.matchParametersValues(f1dim.getFunction()->getParameters());
240 
241  double xmin = f1dim.getParameters()[0].getValue();
242  for (size_t j = 0; j < parameters.size(); j++)
243  {
244  xi[j] *= xmin;
245  parameters[j].setValue(parameters[j].getValue() + xi[j]);
246  }
247  return bod.getNumberOfEvaluations();
248 }
249 
250 /******************************************************************************/
251 
253  ParameterList& parameters,
254  std::vector<double>& xi,
255  std::vector<double>& gradient,
256  OutputStream* profiler,
257  OutputStream* messenger,
258  unsigned int verbose)
259 {
260  size_t size = xi.size();
261 
263  f1dim.setMessageHandler(messenger);
264  f1dim.init(parameters, xi);
265 
266  double slope = 0;
267  for (size_t i = 0; i < size; i++)
268  {
269  slope += xi[i] * gradient[i];
270  }
271 
272  // if (slope>=0)
273  // throw Exception("Slope problem in OneDimensionOptimizationTools::lineSearch. Slope="+TextTools::toString(slope));
274 
275  double x, temp, test = 0;
276  for (size_t i = 0; i < size; ++i)
277  {
278  x = abs(parameters[i].getValue());
279  temp = abs(xi[i]);
280  if (x > 1.0)
281  temp /= x;
282  if (temp > test)
283  test = temp;
284  }
285 
286  NewtonBacktrackOneDimension nbod(&f1dim, slope, test);
287 
288  nbod.setMessageHandler(messenger);
289  nbod.setProfiler(profiler);
290  nbod.setVerbose(verbose >= 1 ? 1 : 0);
292  nbod.getStopCondition()->setTolerance(0.0001);
293  // nbod.setInitialInterval(ax, xx);
295  ParameterList singleParameter;
296  singleParameter.addParameter(Parameter("x", 0.0));
297  nbod.init(singleParameter);
298  nbod.optimize();
299  // Update parameters:
300  // parameters.matchParametersValues(f1dim.getFunction()->getParameters());
301 
302  double xmin = f1dim.getParameters()[0].getValue();
303  for (unsigned int j = 0; j < parameters.size(); j++)
304  {
305  xi[j] *= xmin;
306  parameters[j].setValue(parameters[j].getValue() + xi[j]);
307  }
308 
309  return nbod.getNumberOfEvaluations();
310 }
311 
312 /******************************************************************************/
313 
315 
316 /******************************************************************************/
void setOptimizationProgressCharacter(const std::string &c)
Set the character to be displayed during optimization.
void setProfiler(OutputStream *profiler)
Set the profiler for this optimizer.
void setConstraintPolicy(const std::string &constraintPolicy)
Set the constraint policy for this optimizer.
void setMessageHandler(OutputStream *mh)
Set the message handler for this optimizer.
double optimize()
Basic implementation.
void init(const ParameterList &params)
Basic implementation.
void setVerbose(unsigned int v)
Set the verbose level.
unsigned int getNumberOfEvaluations() const
Get the number of function evaluations performed since the call of the init function.
OptimizationStopCondition * getStopCondition()
Get the stop condition of the optimization algorithm.
static std::string CONSTRAINTS_AUTO
static std::string CONSTRAINTS_KEEP
void set(double x, double f)
void setA(double xa, double fa)
void setB(double xb, double fb)
void setC(double xc, double fc)
Brent's optimization for one parameter.
double optimize()
Initialize optimizer.
void setInitialInterval(double inf, double sup)
Set intial search interval.
void setConstraintPolicy(const std::string &constraintPolicy)
void init(const ParameterList &p, const std::vector< double > &xi)
void setMessageHandler(OutputStream *messenger)
const ParameterList & getParameters() const
Get all parameters available.
This is the function abstract class.
Definition: Functions.h:89
Newton's backtrack nearly optimization for one parameter.
static double VERY_TINY()
Definition: NumConstants.h:83
static double GOLDEN_RATIO_PHI()
Definition: NumConstants.h:66
static T abs(T a)
Get the magnitude of a value.
Definition: NumTools.h:67
static T sign(T a)
Get the sign of a value.
Definition: NumTools.h:78
static T max(T a, T b)
Get the max between 2 values.
Definition: NumTools.h:89
static Bracket inwardBracketMinimum(double a, double b, Function *function, ParameterList parameters, uint intervalsNum=10)
Bracket a minimum by a search within thw parameter's bounds.
static Bracket bracketMinimum(double a, double b, Function *function, ParameterList parameters)
Bracket a minimum.
static unsigned int lineSearch(DirectionFunction &f1dim, ParameterList &parameters, std::vector< double > &xi, std::vector< double > &gradient, OutputStream *profiler=0, OutputStream *messenger=0, unsigned int verbose=2)
Search a 'sufficiently low' value for a function in a given direction.
static unsigned int lineMinimization(DirectionFunction &f1dim, ParameterList &parameters, std::vector< double > &xi, double tolerance, OutputStream *profiler=0, OutputStream *messenger=0, unsigned int verbose=2)
static double GLIMIT
Maximum magnification allowed for a parabolic-fit step.
virtual void setTolerance(double tolerance)=0
Set the tolerance parameter.
OutputStream interface.
Definition: OutputStream.h:67
The parameter list object.
Definition: ParameterList.h:65
size_t size() const
Definition: ParameterList.h:92
virtual void addParameter(const Parameter &param)
Add a new parameter at the end of the list.
This class is designed to facilitate the manipulation of parameters.
Definition: Parameter.h:135