bpp-core3  3.0.0
OneDimensionOptimizationTools.cpp
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: The Bio++ Development Group
2 //
3 // SPDX-License-Identifier: CECILL-2.1
4 
5 #include "../NumConstants.h"
6 #include "../NumTools.h"
7 #include "BrentOneDimension.h"
10 
11 using namespace bpp;
12 using namespace std;
13 
14 /******************************************************************************
15 * The Point class *
16 ******************************************************************************/
17 inline void BracketPoint::set(double xval, double fval) { this->x = xval; this->f = fval; }
18 
19 /******************************************************************************
20 * The Bracket class *
21 ******************************************************************************/
22 inline void Bracket::setA(double xa, double fa) { a.set(xa, fa); }
23 inline void Bracket::setB(double xb, double fb) { b.set(xb, fb); }
24 inline void Bracket::setC(double xc, double fc) { c.set(xc, fc); }
25 
26 /******************************************************************************/
27 
29  double a,
30  double b,
31  FunctionInterface& function,
32  ParameterList parameters)
33 {
34  Bracket bracket;
35  // Copy the parameter to use.
36  bracket.a.x = a;
37  parameters[0].setValue(bracket.a.x); bracket.a.f = function.f(parameters);
38  bracket.b.x = b;
39  parameters[0].setValue(bracket.b.x); bracket.b.f = function.f(parameters);
40 
41  while (std::isnan(bracket.b.f) || std::isinf(bracket.b.f))
42  {
43  bracket.b.x /= 1.1;
44  parameters[0].setValue(bracket.b.x); bracket.b.f = function.f(parameters);
45  }
46 
47  if (bracket.b.f > bracket.a.f)
48  {
49  // Switch roles of first and second point so that we can go downhill
50  // in the direction from a to b.
51  NumTools::swap<double>(bracket.a.x, bracket.b.x);
52  NumTools::swap<double>(bracket.a.f, bracket.b.f);
53  }
54 
55  // First guess for third point:
56  bracket.c.x = bracket.b.x + NumConstants::GOLDEN_RATIO_PHI() * (bracket.b.x - bracket.a.x);
57  parameters[0].setValue(bracket.c.x); bracket.c.f = function.f(parameters);
58 
59 
60  // Keep returning here until we bracket:
61  while (bracket.b.f > bracket.c.f)
62  {
63  // Compute xu by parabolic extrapolation from a, b, c. TINY is used to prevent
64  // any possible division by 0.
65  double r = (bracket.b.x - bracket.a.x) * (bracket.b.f - bracket.c.f);
66  double q = (bracket.b.x - bracket.c.x) * (bracket.b.f - bracket.a.f);
67 
68  double xu = bracket.b.x - ((bracket.b.x - bracket.c.x) * q - (bracket.b.x - bracket.a.x) * r) /
70  double xulim = (bracket.b.x) + GLIMIT * (bracket.c.x - bracket.b.x);
71  double fu;
72 
73  // We don't go farther than this.
74  // Test various possibilities:
75  if ((bracket.b.x - xu) * (xu - bracket.c.x) > 0.0)
76  {
77  parameters[0].setValue(xu); fu = function.f(parameters);
78  if (fu < bracket.c.f)
79  {
80  bracket.setA(bracket.b.x, bracket.b.f);
81  bracket.setB(xu, fu);
82  return bracket;
83  }
84  else if (fu > bracket.b.f)
85  {
86  bracket.setC(xu, fu);
87  return bracket;
88  }
89  // Parabolic fit was no use.
90  // Use default magnification.
91  xu = bracket.c.x + NumConstants::GOLDEN_RATIO_PHI() * (bracket.c.x - bracket.b.x);
92  parameters[0].setValue(xu); fu = function.f(parameters);
93  }
94  else if ((bracket.c.x - xu) * (xu - xulim) > 0.0)
95  {
96  // Parabolic fit is between point 3 and its allowed limit.
97  parameters[0].setValue(xu); fu = function.f(parameters);
98  if (fu < bracket.c.f)
99  {
100  NumTools::shift<double>(bracket.b.x, bracket.c.x, xu, bracket.c.x + NumConstants::GOLDEN_RATIO_PHI() * (bracket.c.x - bracket.b.x));
101  parameters[0].setValue(xu);
102  NumTools::shift<double>(bracket.b.f, bracket.c.f, fu, function.f(parameters));
103  }
104  }
105  else if ((xu - xulim) * (xulim - bracket.c.x) >= 0.0)
106  {
107  // Limit parabolic xu to maximum allowed value.
108  xu = xulim;
109  parameters[0].setValue(xu); fu = function.f(parameters);
110  }
111  else
112  {
113  // Reject parabolic xu, use default magnification.
114  xu = bracket.c.x + NumConstants::GOLDEN_RATIO_PHI() * (bracket.c.x - bracket.b.x);
115  parameters[0].setValue(xu); fu = function.f(parameters);
116  }
117  // Eliminate oldest point and continue.
118  NumTools::shift<double>(bracket.a.x, bracket.b.x, bracket.c.x, xu);
119  NumTools::shift<double>(bracket.a.f, bracket.b.f, bracket.c.f, fu);
120  }
121  return bracket;
122 }
123 
124 /******************************************************************************/
125 
127  double a,
128  double b,
129  FunctionInterface& function,
130  ParameterList parameters,
131  unsigned int intervalsNum)
132 {
133  Bracket bracket;
134  // Copy the parameter to use.
135  bracket.a.x = a;
136  parameters[0].setValue(bracket.a.x); bracket.a.f = function.f(parameters);
137  bracket.b.x = b;
138  parameters[0].setValue(bracket.b.x); bracket.b.f = function.f(parameters);
139 
140  while (std::isnan(bracket.b.f) || std::isinf(bracket.b.f))
141  {
142  bracket.b.x /= 1.1;
143  parameters[0].setValue(bracket.b.x); bracket.b.f = function.f(parameters);
144  }
145 
146  double bestMiddleX, bestMiddleF;
147  double curr, fcurr, jump;
148 
149  // look for bracket.c.x by scanning n possible assignments and assigining c the best one over the examined values
150  curr = bracket.a.x;
151  fcurr = bracket.a.f;
152  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
153  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
154  jump = (b - a) / static_cast<double>(intervalsNum); // Determine the spacing appropriate to the mesh.
155  for (size_t i = 1; i <= intervalsNum; i++)
156  { // Loop over all intervals
157  curr += jump;
158  parameters[0].setValue(curr); fcurr = function.f(parameters);
159  // If c yields better likelihood than a and b
160  if (fcurr < bestMiddleF)
161  {
162  bestMiddleX = curr;
163  bestMiddleF = fcurr;
164  }
165  }
166  bracket.c.x = bestMiddleX;
167  parameters[0].setValue(bracket.c.x); bracket.c.f = function.f(parameters);
168  return bracket;
169 }
170 
171 /******************************************************************************/
172 
174  std::shared_ptr<DirectionFunction> f1dim,
175  ParameterList& parameters,
176  std::vector<double>& xi,
177  double tolerance,
178  std::shared_ptr<OutputStream> profiler,
179  std::shared_ptr<OutputStream> messenger,
180  unsigned int verbose)
181 {
182  // Initial guess for brackets:
183  double ax = 0.;
184  double xx = 0.01;
185 
186  f1dim->setConstraintPolicy(AutoParameter::CONSTRAINTS_AUTO);
187  f1dim->setMessageHandler(messenger);
188  f1dim->init(parameters, xi);
189  BrentOneDimension bod(f1dim);
190  bod.setMessageHandler(messenger);
191  bod.setProfiler(profiler);
192  bod.setVerbose(verbose >= 1 ? 1 : 0);
194  bod.getStopCondition()->setTolerance(0.01);
195  bod.setInitialInterval(ax, xx);
197  ParameterList singleParameter;
198  singleParameter.addParameter(Parameter("x", 0.0));
199  bod.init(singleParameter);
200  bod.optimize();
201  // Update parameters:
202  // parameters.matchParametersValues(f1dim.getFunction()->getParameters());
203 
204  double xmin = f1dim->getParameters()[0].getValue();
205  for (size_t j = 0; j < parameters.size(); ++j)
206  {
207  xi[j] *= xmin;
208  parameters[j].setValue(parameters[j].getValue() + xi[j]);
209  }
210  return bod.getNumberOfEvaluations();
211 }
212 
213 /******************************************************************************/
214 
216  std::shared_ptr<DirectionFunction> f1dim,
217  ParameterList& parameters,
218  std::vector<double>& xi,
219  std::vector<double>& gradient,
220  std::shared_ptr<OutputStream> profiler,
221  std::shared_ptr<OutputStream> messenger,
222  unsigned int verbose)
223 {
224  size_t size = xi.size();
225 
226  f1dim->setConstraintPolicy(AutoParameter::CONSTRAINTS_AUTO);
227  f1dim->setMessageHandler(messenger);
228  f1dim->init(parameters, xi);
229 
230  double slope = 0;
231  for (size_t i = 0; i < size; i++)
232  {
233  slope += xi[i] * gradient[i];
234  }
235 
236  // if (slope>=0)
237  // throw Exception("Slope problem in OneDimensionOptimizationTools::lineSearch. Slope="+TextTools::toString(slope));
238 
239  double x, temp, test = 0;
240  for (size_t i = 0; i < size; ++i)
241  {
242  x = abs(parameters[i].getValue());
243  temp = abs(xi[i]);
244  if (x > 1.0)
245  temp /= x;
246  if (temp > test)
247  test = temp;
248  }
249 
250  NewtonBacktrackOneDimension nbod(f1dim, slope, test);
251 
252  nbod.setMessageHandler(messenger);
253  nbod.setProfiler(profiler);
254  nbod.setVerbose(verbose >= 1 ? 1 : 0);
256  nbod.getStopCondition()->setTolerance(0.0001);
257  // nbod.setInitialInterval(ax, xx);
259  ParameterList singleParameter;
260  singleParameter.addParameter(Parameter("x", 0.0));
261  nbod.init(singleParameter);
262  nbod.optimize();
263  // Update parameters:
264  // parameters.matchParametersValues(f1dim.getFunction()->getParameters());
265 
266  double xmin = f1dim->getParameters()[0].getValue();
267  for (unsigned int j = 0; j < parameters.size(); ++j)
268  {
269  xi[j] *= xmin;
270  parameters[j].setValue(parameters[j].getValue() + xi[j]);
271  }
272 
273  return nbod.getNumberOfEvaluations();
274 }
275 
276 /******************************************************************************/
277 
279 
280 /******************************************************************************/
double optimize() override
Basic implementation.
static T sign(T a)
Get the sign of a value.
Definition: NumTools.h:42
std::shared_ptr< OptimizationStopCondition > getStopCondition() override
Get the stop condition of the optimization algorithm.
static std::string CONSTRAINTS_KEEP
void setVerbose(unsigned int v) override
Set the verbose level.
static double GOLDEN_RATIO_PHI()
Definition: NumConstants.h:30
size_t size() const
Definition: ParameterList.h:56
void setConstraintPolicy(const std::string &constraintPolicy) override
Set the constraint policy for this optimizer.
STL namespace.
This class is designed to facilitate the manipulation of parameters.
Definition: Parameter.h:97
unsigned int getNumberOfEvaluations() const override
Get the number of function evaluations performed since the call of the init function.
This is the function abstract class.
Definition: Functions.h:52
static double VERY_TINY()
Definition: NumConstants.h:47
void setB(double xb, double fb)
void setA(double xa, double fa)
void setC(double xc, double fc)
The parameter list object.
Definition: ParameterList.h:27
static Bracket bracketMinimum(double a, double b, FunctionInterface &function, ParameterList parameters)
Bracket a minimum.
void setMessageHandler(std::shared_ptr< OutputStream > mh) override
Set the message handler for this optimizer.
void setOptimizationProgressCharacter(const std::string &c)
Set the character to be displayed during optimization.
static T max(T a, T b)
Get the max between 2 values.
Definition: NumTools.h:53
void setProfiler(std::shared_ptr< OutputStream > profiler) override
Set the profiler for this optimizer.
Newton&#39;s backtrack nearly optimization for one parameter.
double optimize()
Initialize optimizer.
virtual void addParameter(const Parameter &param)
Add a new parameter at the end of the list.
Brent&#39;s optimization for one parameter.
static std::string CONSTRAINTS_AUTO
Definition: AutoParameter.h:98
void set(double x, double f)
static Bracket inwardBracketMinimum(double a, double b, FunctionInterface &function, ParameterList parameters, unsigned int intervalsNum=10)
Bracket a minimum by a search within the parameter&#39;s bounds.
static unsigned int lineMinimization(std::shared_ptr< DirectionFunction > f1dim, ParameterList &parameters, std::vector< double > &xi, double tolerance, std::shared_ptr< OutputStream > profiler=nullptr, std::shared_ptr< OutputStream > messenger=nullptr, unsigned int verbose=2)
void setInitialInterval(double inf, double sup)
Set intial search interval.
static unsigned int lineSearch(std::shared_ptr< DirectionFunction > f1dim, ParameterList &parameters, std::vector< double > &xi, std::vector< double > &gradient, std::shared_ptr< OutputStream > profiler=nullptr, std::shared_ptr< OutputStream > messenger=nullptr, unsigned int verbose=2)
Search a &#39;sufficiently low&#39; value for a function in a given direction.
void init(const ParameterList &params) override
Basic implementation.
static double GLIMIT
Maximum magnification allowed for a parabolic-fit step.
static T abs(T a)
Get the magnitude of a value.
Definition: NumTools.h:31