bpp-phyl  2.2.0
PseudoNewtonOptimizer.cpp
Go to the documentation of this file.
1 //
2 // File: PseudoNewtonOptimizer.cpp
3 // Created by: Julien Dutheil
4 // Created on: Tue Nov 16 12:33 2004
5 //
6 
7 /*
8 Copyright or © or Copr. Bio++ Development Team, (November 16, 2004)
9 
10 This software is a computer program whose purpose is to provide classes
11 for phylogenetic data analysis.
12 
13 This software is governed by the CeCILL license under French law and
14 abiding by the rules of distribution of free software. You can use,
15 modify and/ or redistribute the software under the terms of the CeCILL
16 license as circulated by CEA, CNRS and INRIA at the following URL
17 "http://www.cecill.info".
18 
19 As a counterpart to the access to the source code and rights to copy,
20 modify and redistribute granted by the license, users are provided only
21 with a limited warranty and the software's author, the holder of the
22 economic rights, and the successive licensors have only limited
23 liability.
24 
25 In this respect, the user's attention is drawn to the risks associated
26 with loading, using, modifying and/or developing or reproducing the
27 software by the user in light of its specific status of free software,
28 that may mean that it is complicated to manipulate, and that also
29 therefore means that it is reserved for developers and experienced
30 professionals having in-depth computer knowledge. Users are therefore
31 encouraged to load and test the software's suitability as regards their
32 requirements in conditions enabling the security of their systems and/or
33 data to be ensured and, more generally, to use and operate it in the
34 same conditions as regards security.
35 
36 The fact that you are presently reading this means that you have had
37 knowledge of the CeCILL license and that you accept its terms.
38 */
39 
40 /**************************************************************************/
41 
42 #include "../TreeTemplateTools.h"
43 #include "PseudoNewtonOptimizer.h"
45 
46 #include <Bpp/Numeric/VectorTools.h>
47 #include <Bpp/Numeric/Function/ConjugateGradientMultiDimensions.h>
48 #include <Bpp/Text/TextTools.h>
49 #include <Bpp/App/ApplicationTools.h>
50 
51 using namespace bpp;
52 
53 /**************************************************************************/
54 
56 {
57  return NumTools::abs<double>(
58  dynamic_cast<const PseudoNewtonOptimizer*>(optimizer_)->currentValue_ -
59  dynamic_cast<const PseudoNewtonOptimizer*>(optimizer_)->previousValue_);
60 }
61 
62 /**************************************************************************/
63 
65 {
66  return getCurrentTolerance() < tolerance_;
67 }
68 
69 /**************************************************************************/
70 
71 PseudoNewtonOptimizer::PseudoNewtonOptimizer(DerivableSecondOrder* function) :
72  AbstractOptimizer(function),
74  previousValue_(0),
75  n_(0),
76  params_(),
77  maxCorrection_(10),
78  useCG_(true)
79 {
80  setDefaultStopCondition_(new FunctionStopCondition(this));
81  setStopCondition(*getDefaultStopCondition());
82 }
83 
84 /**************************************************************************/
85 
86 void PseudoNewtonOptimizer::doInit(const ParameterList& params) throw (Exception)
87 {
88  n_ = getParameters().size();
89  params_ = getParameters().getParameterNames();
90  getFunction()->enableSecondOrderDerivatives(true);
91  getFunction()->setParameters(getParameters());
92 }
93 
94 /**************************************************************************/
95 
96 double PseudoNewtonOptimizer::doStep() throw (Exception)
97 {
98  ParameterList* bckPoint = 0;
99  if (updateParameters()) bckPoint = new ParameterList(getFunction()->getParameters());
100  double newValue = 0;
101  // Compute derivative at current point:
102  std::vector<double> movements(n_);
103  ParameterList newPoint = getParameters();
104  for (size_t i = 0; i < n_; i++)
105  {
106  double firstOrderDerivative = getFunction()->getFirstOrderDerivative(params_[i]);
107  double secondOrderDerivative = getFunction()->getSecondOrderDerivative(params_[i]);
108  if (secondOrderDerivative == 0)
109  {
110  movements[i] = 0;
111  }
112  else if (secondOrderDerivative < 0)
113  {
114  printMessage("!!! Second order derivative is negative for parameter " + params_[i] + "(" + TextTools::toString(getParameters()[i].getValue()) + "). Moving in the other direction.");
115  //movements[i] = 0; // We want to reach a minimum, not a maximum!
116  // My personnal improvement:
117  movements[i] = -firstOrderDerivative / secondOrderDerivative;
118  }
119  else movements[i] = firstOrderDerivative / secondOrderDerivative;
120  if (std::isnan(movements[i]))
121  {
122  printMessage("!!! Non derivable point at " + params_[i] + ". No move performed. (f=" + TextTools::toString(currentValue_) + ", d1=" + TextTools::toString(firstOrderDerivative) + ", d2=" + TextTools::toString(secondOrderDerivative) + ").");
123  movements[i] = 0; // Either first or second order derivative is infinity. This may happen when the function == inf at this point.
124  }
125  //DEBUG:
126  //cerr << "PN[" << params_[i] << "]=" << getParameters().getParameter(params_[i]).getValue() << "\t" << movements[i] << "\t " << firstOrderDerivative << "\t" << secondOrderDerivative << endl;
127  newPoint[i].setValue(getParameters()[i].getValue() - movements[i]);
128  //Correct the movement in case of constraint (this is used in case of Felsenstein-Churchill correction:
129  movements[i] = getParameters()[i].getValue() - newPoint[i].getValue();
130  }
131  newValue = getFunction()->f(newPoint);
132 
133  // Check newValue:
134  unsigned int count = 0;
135  while ((count < maxCorrection_) && ((newValue > currentValue_ + getStopCondition()->getTolerance()) || std::isnan(newValue))) {
136  //Restore previous point (all parameters in case of global constraint):
137  if ((count==0) && updateParameters()) getFunction()->setParameters(*bckPoint);
138 
139  if (!(useCG_ && (count==3))){
140  printMessage("!!! Function at new point is greater than at current point: " + TextTools::toString(newValue) + ">" + TextTools::toString(currentValue_) + ". Applying Felsenstein-Churchill correction: " + TextTools::toString(count));
141 
142  for (size_t i = 0; i < movements.size(); i++) {
143  movements[i] = movements[i] / 2;
144  newPoint[i].setValue(getParameters()[i].getValue() - movements[i]);
145  }
146  newValue = getFunction()->f(newPoint);
147  }
148  else {
149  printMessage("!!! Felsenstein-Churchill correction applied too many times.");
150  printMessage("Use conjugate gradients optimization.");
151  getFunction()->enableSecondOrderDerivatives(false);
152  ConjugateGradientMultiDimensions opt(getFunction());
153  opt.setConstraintPolicy(getConstraintPolicy());
154  opt.setProfiler(getProfiler());
155  opt.setMessageHandler(getMessageHandler());
156  opt.setVerbose(0);
157  double tol = std::max(getStopCondition()->getCurrentTolerance() / 2., getStopCondition()->getTolerance());
158  opt.getStopCondition()->setTolerance(tol);
159  opt.setMaximumNumberOfEvaluations(nbEvalMax_);
160  getFunction()->setParameters(getParameters());
161  opt.init(getParameters());
162  opt.optimize();
163  newPoint = opt.getParameters();
164  newValue = opt.getFunctionValue();
165 
166  if (newValue > currentValue_ + tol) {
167  printMessage("!!! Conjugate gradient method failed to improve likelihood.");
168  printMessage("Back to Felsenstein-Churchill method.");
169  }
170  }
171  count++;
172  }
173 
174  if (newValue > currentValue_ + getStopCondition()->getTolerance()){
175  printMessage("PseudoNewtonOptimizer::doStep. Value could not be ameliorated!");
176  newValue = currentValue_;
177  }
178  else {
179  //getFunction()->enableFirstOrderDerivatives(true);
180  getFunction()->enableSecondOrderDerivatives(true);
181  getFunction()->setParameters(newPoint); //Compute derivatives for this point
182 
183  previousPoint_ = getParameters();
184  previousValue_ = currentValue_;
185  getParameters_() = newPoint;
186  }
187 
188  if (updateParameters()) delete bckPoint;
189  return newValue;
190 
191 }
192 
193 /**************************************************************************/
194 
PseudoNewtonOptimizer(DerivableSecondOrder *function)
std::vector< std::string > params_
void doInit(const ParameterList &params)
This Optimizer implements Newton&#39;s algorithm for finding a minimum of a function. This is in fact a m...
const DerivableSecondOrder * getFunction() const