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
00041 template <class Functor>
00042 class OptimizerNelderMead
00043 : public Optimizer<Functor> {
00044 public:
00045
00046
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 };
00303
00304 }
00305
00306 }
00307
00308
00309
00310
00311 namespace dlr {
00312
00313 using optimization::OptimizerNelderMead;
00314
00315 }
00316
00317
00318
00319
00320
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 }
00389
00390 }
00391
00392 }
00393
00394
00395 namespace dlr {
00396
00397 namespace optimization {
00398
00399
00400
00401
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
00415
00416
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
00429
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
00448
00449 template <class Functor>
00450 OptimizerNelderMead<Functor>::
00451 ~OptimizerNelderMead()
00452 {
00453
00454 }
00455
00456
00457
00458
00459 template <class Functor>
00460 std::vector<size_t>
00461 OptimizerNelderMead<Functor>::
00462 getNumberOfFunctionCalls()
00463 {
00464 return m_functionCallCount;
00465 }
00466
00467
00468
00469
00470
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
00481
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
00505 Optimizer<Functor>::m_needsOptimization = true;
00506 }
00507
00508
00509
00510 template <class Functor>
00511 void
00512 OptimizerNelderMead<Functor>::
00513 setStartPoint(argument_type startPoint)
00514 {
00515 m_theta0 = startPoint;
00516
00517
00518 Optimizer<Functor>::m_needsOptimization = true;
00519 }
00520
00521
00522
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
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
00562
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
00641
00642
00643
00644
00645
00646
00647
00648
00649
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
00673
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
00686
00687
00688 m_delta = (m_theta0 * 0.0) + 1.0;
00689 m_deltaValueHack = false;
00690 }
00691
00692
00693 std::vector<argument_type> currentPoints(dimension + 1);
00694 std::vector<result_type> currentValues(dimension + 1);
00695
00696 copyArgumentType(m_theta0, currentPoints[0]);
00697 currentValues[0] = m_functor(currentPoints[0]);
00698
00699
00700 m_functionCallCount.clear();
00701 for(size_t i = 0; i < m_numberOfRestarts + 1; ++i) {
00702
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
00711 this->doNelderMead(currentPoints, currentValues, numberOfFunctionCalls);
00712
00713
00714 m_functionCallCount.push_back(numberOfFunctionCalls + dimension);
00715 }
00716 return std::make_pair(currentPoints[0], currentValues[0]);
00717 }
00718
00719 }
00720
00721 }
00722
00723
00724 #endif // #ifndef _DLR_OPTIMIZERNELDERMEAD_H_