optimizerNelderMead.h

Go to the documentation of this file.
00001 
00014 #ifndef _DLR_OPTIMIZERNELDERMEAD_H_
00015 #define _DLR_OPTIMIZERNELDERMEAD_H_
00016 
00017 #include <functional>
00018 #include <vector>
00019 #include <dlrOptimization/optimizer.h>
00020 #include <dlrCommon/exception.h>
00021 
00022 namespace dlr {
00023 
00024   namespace optimization {
00025 
00040     // template <std::unary_function Functor>
00041     template <class Functor>
00042     class OptimizerNelderMead
00043       : public Optimizer<Functor> {
00044     public:
00045 
00046       // Typedefs for convenience
00047       typedef typename Functor::argument_type argument_type;
00048       typedef typename Functor::result_type result_type;
00049 
00050     
00056       OptimizerNelderMead();
00057 
00058     
00068       explicit OptimizerNelderMead(const Functor& functor);
00069 
00070     
00076       OptimizerNelderMead(const OptimizerNelderMead& source);
00077 
00078     
00082       ~OptimizerNelderMead();
00083 
00084     
00096       virtual std::vector<size_t>
00097       getNumberOfFunctionCalls();
00098 
00099 
00112       void
00113       setDelta(const argument_type& delta);
00114 
00115     
00123       void
00124       setNumberOfRestarts(size_t numberOfRestarts) {
00125         m_numberOfRestarts = numberOfRestarts;
00126       }
00127 
00128     
00175       void
00176       setParameters(argument_type delta,
00177                     size_t functionCallLimit=5000,
00178                     size_t numberOfRestarts=1,
00179                     double alpha=1.0,
00180                     double beta=0.5,
00181                     double gamma=2.0,
00182                     double minimumSimplexValueSpan=0.0001,
00183                     size_t verbosity=0);
00184 
00185     
00193       virtual void
00194       setStartPoint(argument_type startPoint);
00195 
00203       virtual void
00204       setVerbosity(int verbosity) {m_verbosity = verbosity;}
00205 
00212       OptimizerNelderMead&
00213       operator=(const OptimizerNelderMead& source);
00214 
00215     protected:
00216 
00226       void
00227       computeAxisSums(
00228         const std::vector<argument_type>& currentPoints,
00229         argument_type& axisSums);
00230 
00231 
00246       void
00247       doNelderMead(std::vector<argument_type>& currentPoints,
00248                    std::vector<result_type>& currentValues,
00249                    size_t& numberOfFunctionCalls);
00250 
00251 
00272       result_type
00273       evaluateMove(std::vector<argument_type>& currentPoints,
00274                    std::vector<result_type>& currentValues,
00275                    const argument_type& axisSums,
00276                    double factor);
00277 
00278 
00286       std::pair<typename Functor::argument_type, typename Functor::result_type>
00287       run();
00288 
00289       argument_type m_delta;
00290       size_t m_functionCallLimit;
00291       size_t m_numberOfRestarts;
00292       double m_alpha;
00293       double m_beta;
00294       double m_gamma;
00295       double m_minimumSimplexValueSpan;
00296       bool m_deltaValueHack;
00297 
00298       std::vector<size_t> m_functionCallCount;
00299       argument_type m_theta0;
00300       size_t m_verbosity;
00301 
00302     }; // class OptimizerNelderMead
00303 
00304   } // namespace optimization
00305 
00306 } // namespace dlr
00307 
00308 
00309 /* ======= Declarations to maintain compatibility with legacy code. ======= */
00310 
00311 namespace dlr {
00312 
00313   using optimization::OptimizerNelderMead;
00314 
00315 } // namespace dlr
00316 
00317 
00318 /*******************************************************************
00319  * Member function definitions follow.  This would be a .cpp file
00320  * if it weren't templated.
00321  *******************************************************************/
00322 
00323 #include <cmath>
00324 
00325 namespace dlr {
00326 
00327   namespace optimization {
00328 
00330     namespace privateCode {
00331 
00332       template <class Type0, class Type1>
00333       class extractSecond
00334         : public std::unary_function<std::pair<Type0, Type1>, Type1>
00335       {
00336       public:
00337         Type1 operator()(const std::pair<Type0, Type1>& inputPair) {
00338           return inputPair.second;
00339         }
00340       };
00341 
00342       template <class Type>
00343       std::vector<size_t>
00344       argsort(const std::vector<Type>& inputVector)
00345       {
00346         std::vector< std::pair<Type, size_t> >
00347           sortVector(inputVector.size());
00348         std::vector<size_t> resultVector(inputVector.size());
00349 
00350         for(size_t index = 0; index < inputVector.size(); ++index) {
00351           sortVector[index] =
00352             std::pair<Type, size_t>(inputVector[index], index);
00353         }
00354         std::sort(sortVector.begin(), sortVector.end());
00355         std::transform(sortVector.begin(), sortVector.end(), resultVector.begin(),
00356                        extractSecond<Type, size_t>());
00357         return resultVector;    
00358       }
00359 
00360       template <class Type>
00361       std::vector<Type>
00362       take(const std::vector<Type>& inputVector,
00363             const std::vector<size_t>& indexVector)
00364       {
00365         if(inputVector.size() != indexVector.size()) {
00366           std::ostringstream message;
00367           message << "Incorrectly Sized Arguments:\n"
00368                   << " First Argument: " <<inputVector.size()
00369                   << ", Second Argument: " << indexVector.size() << ".";
00370           DLR_THROW(dlr::ValueException, "take()",
00371                     message.str().c_str());
00372         }
00373         std::vector<Type> resultVector(inputVector.size());
00374         for(size_t index = 0; index < resultVector.size(); ++index) {
00375           if(indexVector[index] >= inputVector.size()) {
00376             std::ostringstream message;
00377             message << "Out of Range Index:"
00378                     << indexVector[index] << ".";
00379             DLR_THROW(dlr::ValueException, "take()",
00380                       message.str().c_str());
00381           }
00382           resultVector[index] = inputVector[indexVector[index]];
00383         }
00384         return resultVector;
00385       }
00386 
00387     } // namespace privateCode
00389 
00390   } // namespace optimization
00391   
00392 } // namespace dlr
00393 
00394 
00395 namespace dlr {
00396 
00397   namespace optimization {
00398 
00399     // Sets parameters to reasonable values for functions which take
00400     // values and arguments in the "normal" range of 0 to 100 or so.
00401     // template <std::unary_function Functor>
00402     template <class Functor>
00403     OptimizerNelderMead<Functor>::
00404     OptimizerNelderMead()
00405       : Optimizer<Functor>(),
00406         m_functionCallCount(),
00407         m_verbosity(0)
00408     {
00409       this->setParameters(argument_type());
00410       m_deltaValueHack = true;
00411     }
00412 
00413 
00414     // Constructor which specifies the specific Functor instance to
00415     // use.
00416     // template <std::unary_function Functor>
00417     template <class Functor>
00418     OptimizerNelderMead<Functor>::
00419     OptimizerNelderMead(const Functor& functor)
00420       : Optimizer<Functor>(functor),
00421         m_functionCallCount(),
00422         m_verbosity(0)
00423     {
00424       this->setParameters(argument_type());
00425       m_deltaValueHack = true;
00426     }
00427 
00428     // Copy constructor.
00429     // template <std::unary_function Functor>
00430     template <class Functor>
00431     OptimizerNelderMead<Functor>::
00432     OptimizerNelderMead(const OptimizerNelderMead& source)
00433       : Optimizer<Functor>(source),
00434         m_functionCallLimit(source.m_functionCallLimit),
00435         m_numberOfRestarts(source.m_numberOfRestarts),
00436         m_alpha(source.m_alpha),
00437         m_beta(source.m_beta),
00438         m_gamma(source.m_gamma),
00439         m_minimumSimplexValueSpan(source.m_minimumSimplexValueSpan),
00440         m_deltaValueHack(source.m_deltaValueHack),
00441         m_functionCallCount(source.m_functionCallCount),
00442         m_verbosity(source.m_verbosity)
00443     {
00444       copyArgumentType(source.m_delta, m_delta);
00445     }
00446 
00447     // Destructor.
00448     // template <std::unary_function Functor>
00449     template <class Functor>
00450     OptimizerNelderMead<Functor>::
00451     ~OptimizerNelderMead()
00452     {
00453       // Empty
00454     }
00455 
00456     // Queries the number of functionCalls required to complete the
00457     // previous minimization.
00458     // template <std::unary_function Functor>
00459     template <class Functor>
00460     std::vector<size_t>
00461     OptimizerNelderMead<Functor>::
00462     getNumberOfFunctionCalls()
00463     {
00464       return m_functionCallCount;
00465     }
00466 
00467 
00468     // This method sets the spacing of the initial points used in the
00469     // nonlinear optimization, without affecting any other optimization
00470     // parameters.
00471     template <class Functor>
00472     void
00473     OptimizerNelderMead<Functor>::
00474     setDelta(const typename OptimizerNelderMead<Functor>::argument_type& delta)
00475     {
00476       copyArgumentType(delta, m_delta);
00477     }
00478   
00479   
00480     // Sets minimization parameters.
00481     // template <std::unary_function Functor>
00482     template <class Functor>
00483     void
00484     OptimizerNelderMead<Functor>::
00485     setParameters(typename OptimizerNelderMead<Functor>::argument_type delta,
00486                   size_t functionCallLimit,
00487                   size_t numberOfRestarts,
00488                   double alpha,
00489                   double beta,
00490                   double gamma,
00491                   double minimumSimplexValueSpan,
00492                   size_t verbosity)
00493     {
00494       copyArgumentType(delta, m_delta);
00495       m_functionCallLimit = functionCallLimit;
00496       m_numberOfRestarts = numberOfRestarts;
00497       m_alpha = alpha;
00498       m_beta = beta;
00499       m_gamma = gamma;
00500       m_minimumSimplexValueSpan = minimumSimplexValueSpan;
00501       m_verbosity = verbosity;
00502       m_deltaValueHack = false;
00503 
00504       // Inherited member
00505       Optimizer<Functor>::m_needsOptimization = true;
00506     }
00507 
00508     // Sets the initial conditions for the minimization.
00509     // template <std::unary_function Functor>
00510     template <class Functor>
00511     void
00512     OptimizerNelderMead<Functor>::
00513     setStartPoint(argument_type startPoint)
00514     {
00515       m_theta0 = startPoint;
00516 
00517       // Inherited member
00518       Optimizer<Functor>::m_needsOptimization = true;
00519     }
00520                   
00521     // Assignment operator.
00522     // template <std::unary_function Functor>
00523     template <class Functor>
00524     OptimizerNelderMead<Functor>&
00525     OptimizerNelderMead<Functor>::
00526     operator=(const OptimizerNelderMead<Functor>& source)
00527     {
00528       Optimizer<Functor>::operator=(source);
00529       m_delta = source.m_delta;
00530       m_functionCallLimit = source.m_functionCallLimit;
00531       m_numberOfRestarts = source.m_numberOfRestarts;
00532       m_alpha = source.m_alpha;
00533       m_beta = source.m_beta;
00534       m_gamma = source.m_gamma;
00535       m_minimumSimplexValueSpan = source.m_minimumSimplexValueSpan;
00536       m_deltaValueHack = source.m_deltaValueHack;
00537       m_functionCallCount = source.m_functionCallCount;
00538     }
00539 
00540     // template <std::unary_function Functor>
00541     template <class Functor>
00542     void
00543     OptimizerNelderMead<Functor>::
00544     computeAxisSums(
00545       const std::vector<typename OptimizerNelderMead<Functor>::argument_type>&
00546       currentPoints,
00547       typename OptimizerNelderMead<Functor>::argument_type& axisSums)
00548     {
00549       if(currentPoints.size() == 0) {
00550         DLR_THROW(LogicException, "OptimizerNelderMead::computeAxisSums()",
00551                   "Vector Size of Current Points is 0... "
00552                   "something's not right.");
00553         return;
00554       }
00555       copyArgumentType(currentPoints[0], axisSums);
00556       for(size_t index = 1; index < currentPoints.size(); ++index) {
00557         axisSums += currentPoints[index];
00558       }
00559     }
00560   
00561     // Run the actual simplex search.  Modifies all arguments.
00562     // template <std::unary_function Functor>
00563     template <class Functor>
00564     void
00565     OptimizerNelderMead<Functor>::
00566     doNelderMead(
00567       std::vector<typename OptimizerNelderMead<Functor>::argument_type>&
00568       currentPoints,
00569       std::vector<typename OptimizerNelderMead<Functor>::result_type>&
00570       currentValues,
00571       size_t& numberOfFunctionCalls)
00572     {
00573       size_t dimension = currentValues.size() - 1;
00574       argument_type axisSums;
00575       this->computeAxisSums(currentPoints, axisSums);
00576       while(1) {
00577         std::vector<size_t> indices = privateCode::argsort(currentValues);
00578         currentValues = privateCode::take(currentValues, indices);
00579         currentPoints = privateCode::take(currentPoints, indices);
00580 
00581         if(m_verbosity >= 3) {
00582           std::cout << "\rCurrent best: " << currentValues[0] << std::flush;
00583         }
00584 
00585         double simplexValueSpan = 0;
00586         if((std::fabs(currentValues[dimension])
00587             + std::fabs(currentValues[0])) > 0) {
00588           simplexValueSpan =
00589             (2.0 * std::fabs(currentValues[dimension] - currentValues[0])
00590              / (std::fabs(currentValues[dimension])
00591                 + std::fabs(currentValues[0])));
00592         }
00593         if(m_verbosity >= 3) {
00594           std::cout << "       \t       simplexValueSpan: "
00595                     << simplexValueSpan << "     "
00596                     << std::flush;
00597         }
00598         if((simplexValueSpan < m_minimumSimplexValueSpan)
00599            || (numberOfFunctionCalls >= m_functionCallLimit)) {
00600           if(m_verbosity >= 3) {
00601             std::cout << "\n";
00602           }
00603           if(m_verbosity >= 1) {
00604             std::cout << "Terminating with:\n"
00605                       << "  simplexValueSpan = " << simplexValueSpan
00606                       << " (" << m_minimumSimplexValueSpan << ")\n";
00607             std::cout << "  numberOfFunctionCalls = " << numberOfFunctionCalls
00608                       << " (" << m_functionCallLimit << ")" << std::endl;
00609           }
00610           break;
00611         }
00612         result_type newValue =
00613           this->evaluateMove(currentPoints, currentValues, axisSums, 
00614                              (-1.0 * m_alpha));
00615         numberOfFunctionCalls += 1;
00616         this->computeAxisSums(currentPoints, axisSums);
00617         if(newValue <= currentValues[0]) {
00618           newValue = evaluateMove(currentPoints, currentValues, axisSums, 
00619                                   m_gamma);
00620           numberOfFunctionCalls += 1;
00621           this->computeAxisSums(currentPoints, axisSums);
00622         } else if (newValue >= currentValues[dimension - 1]) {
00623           result_type oldMaxValue = currentValues[dimension];
00624           newValue = evaluateMove(currentPoints, currentValues, axisSums, 
00625                                   m_beta);
00626           numberOfFunctionCalls += 1;
00627           if(newValue >= oldMaxValue) {
00628             for(size_t i = 1; i < dimension + 1; ++i) {
00629               currentPoints[i] = 0.5 * (currentPoints[i] + currentPoints[0]);
00630               currentValues[i] = m_functor(currentPoints[i]);
00631               numberOfFunctionCalls += 1;
00632             }
00633           }
00634           this->computeAxisSums(currentPoints, axisSums);
00635         }
00636       }
00637     }
00638 
00639 
00640     // Evaluate, and maybe accept, a new point.  You'll want to believe
00641     // the following math before reading this code.
00642     // 
00643     // xMean + factor*(xMax - xMean)
00644     // = xMean + factor*xMax - factor*xMean
00645     // = (1 - factor)*xMean + factor*xMax
00646     // = ((1 - factor)/n)*sum(xSubi, xSubi != xMax) + factor*xMax
00647     // = ((1 - factor)/n)*sum(xSubi) - ((1 - factor)/n)*xMax + factor*xMax
00648     // = ((1 - factor)/n)*sum(xSubi) + (factor - ((1 - factor)/n))*xMax
00649     // template <std::unary_function Functor>
00650     template <class Functor>
00651     typename OptimizerNelderMead<Functor>::result_type
00652     OptimizerNelderMead<Functor>::
00653     evaluateMove(std::vector<argument_type>& currentPoints,
00654                  std::vector<result_type>& currentValues,
00655                  const argument_type& axisSums,
00656                  double factor)
00657     {
00658       size_t dimension = currentPoints.size() - 1;
00659       double centroidFactor = (1.0 - factor) / dimension;
00660       double extrapolationFactor = factor - centroidFactor;
00661       argument_type newPoint =
00662         ((centroidFactor * axisSums)
00663          + (extrapolationFactor * currentPoints[dimension]));
00664       result_type newValue = m_functor(newPoint);
00665       if(newValue < currentValues[dimension]) {
00666         currentValues[dimension] = newValue;
00667         currentPoints[dimension] = newPoint;
00668       }
00669       return newValue;
00670     }
00671 
00672     // Perform the minimization (top level).
00673     // template <std::unary_function Functor>
00674     template <class Functor>
00675     std::pair<typename Functor::argument_type, typename Functor::result_type>
00676     OptimizerNelderMead<Functor>::
00677     run()
00678     {
00679       size_t dimension = m_theta0.size();
00680       if(dimension == 0) {
00681         DLR_THROW(ValueException, "OptimizerNelderMead::run()",
00682                   "invalid starting point has zero size.");
00683       }
00684       if(m_deltaValueHack) {
00685         // Initialize delta using only operations that we expect to be
00686         // supported by argtype, and which we don't expect to be
00687         // defined in surprising ways.
00688         m_delta = (m_theta0 * 0.0) + 1.0;
00689         m_deltaValueHack = false;
00690       }
00691     
00692       // The algorithm requires dimension + 1 initial points and values.
00693       std::vector<argument_type> currentPoints(dimension + 1);
00694       std::vector<result_type> currentValues(dimension + 1);
00695       // First point is the one specified by the user.
00696       copyArgumentType(m_theta0, currentPoints[0]);
00697       currentValues[0] = m_functor(currentPoints[0]);
00698 
00699       // We'll repeat the whole minimization several times.
00700       m_functionCallCount.clear();
00701       for(size_t i = 0; i < m_numberOfRestarts + 1; ++i) {
00702         // The remaining points will be built using m_delta
00703         for(size_t j = 0; j < dimension; ++j) {
00704           copyArgumentType(currentPoints[0], currentPoints[j + 1]);
00705           (currentPoints[j + 1])[j] += m_delta[j];
00706           currentValues[j + 1] = m_functor(currentPoints[j + 1]);
00707         }
00708         size_t numberOfFunctionCalls = (i == 0) ? 1 : 0;
00709         numberOfFunctionCalls += dimension;
00710         // Run the minimization algorithm.
00711         this->doNelderMead(currentPoints, currentValues, numberOfFunctionCalls);
00712         // Have to add dimension to numberOfFunctionCalls because of the
00713         // initialization steps above.
00714         m_functionCallCount.push_back(numberOfFunctionCalls + dimension);
00715       }
00716       return std::make_pair(currentPoints[0], currentValues[0]);
00717     }
00718 
00719   } // namespace optimization
00720 
00721 } // namespace dlr
00722 
00723 
00724 #endif // #ifndef _DLR_OPTIMIZERNELDERMEAD_H_

Generated on Mon Jul 9 20:34:03 2007 for dlrLibs Utility Libraries by  doxygen 1.5.2