DownhillSimplexOptimizer.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  1. /**
  2. *
  3. * @file: DownhillSimplexOptimizer.cpp: implementation of the downhill Simplex Optimier
  4. *
  5. * @author: Matthias Wacker, Esther Platzer
  6. *
  7. */
  8. #include "optimization/DownhillSimplexOptimizer.h"
  9. #include "optimization/Opt_Namespace.h"
  10. #include "mateigen.h" // for SVD
  11. #include <iostream>
  12. using namespace optimization;
  13. DownhillSimplexOptimizer::DownhillSimplexOptimizer(OptLogBase *loger): SuperClass(loger)
  14. {
  15. m_abort = false;
  16. m_simplexInitialized = false;
  17. //note that we use the negative value of alpha in amoeba
  18. m_alpha = 1.0;
  19. m_beta = 0.5;
  20. // m_gamma was previously 1.0, but this actually deactivates extrapolation. We use 2.0 as suggested in Num. Recipes
  21. m_gamma = 2.0;
  22. m_rankdeficiencythresh = 0.01;
  23. m_rankcheckenabled = false;
  24. }
  25. DownhillSimplexOptimizer::DownhillSimplexOptimizer(const DownhillSimplexOptimizer &opt) : SuperClass(opt)
  26. {
  27. m_abort = opt.m_abort;
  28. m_simplexInitialized = opt.m_simplexInitialized;
  29. m_vertices = opt.m_vertices;
  30. m_y = opt.m_y;
  31. m_alpha = opt.m_alpha;
  32. m_beta = opt.m_beta;
  33. m_gamma = opt.m_gamma;
  34. m_rankdeficiencythresh= 0.01;
  35. m_rankcheckenabled= false;
  36. }
  37. DownhillSimplexOptimizer::~DownhillSimplexOptimizer()
  38. {
  39. }
  40. bool DownhillSimplexOptimizer::setWholeSimplex(const matrix_type &simplex)
  41. {
  42. if(simplex.rows() == static_cast<int>(m_numberOfParameters) && simplex.cols() == static_cast<int>(m_numberOfParameters + 1))
  43. {
  44. /*
  45. * Check if simplex has rank(simplex)=numberOfParameters
  46. * otherwise return false because of bad posed simplex
  47. */
  48. //#ifdef OPTIMIZATION_DOWNHILLSIMPLEX_ENABLE_RANK_CHECK
  49. if(m_rankcheckenabled)
  50. {
  51. int rank = getRank(simplex, m_rankdeficiencythresh);
  52. if(rank < static_cast<int>(m_numberOfParameters))
  53. {
  54. m_simplexInitialized = false;
  55. return false;
  56. }
  57. }
  58. //#endif
  59. m_vertices = simplex;
  60. m_simplexInitialized = true;
  61. for (int k = 0; k < static_cast<int>(m_numberOfParameters +1); k++)
  62. {
  63. m_y[k][0] = evaluateCostFunction(m_vertices(0,k,m_numberOfParameters-1,k));
  64. }
  65. return true;
  66. }
  67. else
  68. {
  69. m_simplexInitialized = false;
  70. return false;
  71. }
  72. }
  73. void DownhillSimplexOptimizer::init()
  74. {
  75. SuperClass::init();
  76. // allocate matrices
  77. m_vertices = matrix_type(m_numberOfParameters, m_numberOfParameters + 1);
  78. m_y = matrix_type(m_numberOfParameters + 1,1);
  79. m_abort=false;
  80. m_simplexInitialized == false;// force a random initialization
  81. /*
  82. compute the function values corresponding to the simplex
  83. */
  84. /* do a random initialization */
  85. if (m_simplexInitialized == false)
  86. {
  87. bool abort = false;
  88. while(abort == false)
  89. {
  90. matrix_type simplex(m_numberOfParameters,m_numberOfParameters+1);
  91. // random initialization of the simplex
  92. for(int i = 0; i < static_cast<int>(m_numberOfParameters); i++)
  93. {
  94. for(int j = 0; j < static_cast<int>(m_numberOfParameters+1); j++)
  95. {
  96. simplex[i][j] = m_parameters[i][0];
  97. if( j == i+1 )
  98. {
  99. double tmpRand = m_scales[i][0];// * ((double)rand()) / RAND_MAX;
  100. simplex[i][j] += tmpRand;
  101. }
  102. }
  103. }
  104. if(this->setWholeSimplex(simplex) == true)
  105. {
  106. /* simplexInitializen is only to indicate manual initialization .. */
  107. m_simplexInitialized = false;
  108. abort =true;
  109. }
  110. }
  111. }
  112. for (int k = 0; k < static_cast<int>(m_numberOfParameters +1); k++)
  113. {
  114. m_y[k][0] = evaluateCostFunction(m_vertices(0,k,m_numberOfParameters-1,k));
  115. }
  116. }
  117. int DownhillSimplexOptimizer::optimize()
  118. {
  119. //if(m_simplexInitialized == false)
  120. //{
  121. this->init();
  122. //}
  123. if(m_loger)
  124. m_loger->logTrace("Starting Downhill Simplex Optimization\n");
  125. int tmp=amoeba();
  126. m_parameters = m_vertices(0,tmp,m_numberOfParameters-1,tmp);
  127. m_currentCostFunctionValue = evaluateCostFunction(m_parameters);
  128. return m_returnReason;
  129. }
  130. /*
  131. * Numerical Recipies in C
  132. */
  133. int DownhillSimplexOptimizer::amoeba()
  134. {
  135. double tol =m_funcTol;
  136. const int ndim=m_numberOfParameters; // dimension
  137. //int max_not_improve=10*ndim; // maximum number of not
  138. // successful steps
  139. //int count_not_improve=0;
  140. //double last_best_value=1e99;
  141. if (m_verbose)
  142. {
  143. std::cerr << "ndim: " << ndim << std::endl;
  144. }
  145. const int spts=ndim+1; // number of vertices
  146. int i,j, max_val;
  147. int ilo; // index of lowest vertex-point
  148. int ihi; // index of hightest (best)
  149. // vertex-point
  150. int inhi; // index of next hightest
  151. // vertex-point
  152. double rtol,ytry;
  153. double ysave; // save function value
  154. matrix_type psum(1,ndim);
  155. /*
  156. start time criteria
  157. */
  158. m_startTime = clock();
  159. for (j=0;j<ndim;j++)
  160. { // get sum of vertex-coordinates
  161. double sum=0.0;
  162. for (i=0;i<spts;i++)
  163. sum += m_vertices[j][i];
  164. psum[0][j]=sum;
  165. std::cerr << sum << " " ;
  166. }
  167. std::cerr << std::endl;
  168. if (m_verbose)
  169. {
  170. std::cerr << "initial psum: ";
  171. for (j=0;j<ndim;j++)
  172. {
  173. std::cerr << psum[0][j] << " " ;
  174. }
  175. std::cerr << std::endl;
  176. }
  177. for (;;)
  178. { // loop until terminating
  179. if(m_verbose)
  180. {
  181. for(int u = 0; u < ndim+1; u++)
  182. {
  183. for(int v = 0; v < ndim ; v++)
  184. {
  185. std::cerr << m_vertices[v][u] << " ";
  186. }
  187. std::cerr<< " " << m_y[u][0] << std::endl;
  188. }
  189. std::cerr << std::endl;
  190. }
  191. ilo = 0;
  192. // first we must determine
  193. // which point is highest,
  194. // next-highest
  195. // and lowest
  196. // by looping over the simplex
  197. /*
  198. this works only, if m_numberOfParameters=ndim >= 2
  199. */
  200. if(ndim >= 2)
  201. {
  202. ihi = m_y[1][0]>m_y[2][0] ? (inhi=1,2) : (inhi=2,1);
  203. for (i=0;i<spts;i++)
  204. {
  205. if (m_y[i][0] <= m_y[ilo][0]) ilo=i;
  206. if (m_y[i][0] > m_y[ihi][0]) {
  207. inhi=ihi;
  208. ihi=i;
  209. }
  210. else
  211. if (m_y[i][0] > m_y[inhi][0])
  212. if (i != ihi)
  213. inhi=i;
  214. }
  215. }
  216. else
  217. {
  218. if(m_y[0][0]>m_y[1][0])
  219. {
  220. ilo = 1;
  221. inhi = 1;
  222. ihi = 0;
  223. }
  224. else
  225. {
  226. ilo = 0;
  227. inhi = 0;
  228. ihi = 1;
  229. }
  230. }
  231. // log parameters if parameter logger is given (does nothing for other loggers!)
  232. if(m_loger)
  233. {
  234. // extract optimal parameters from simplex
  235. matrix_type optparams(m_vertices.rows(),1,0);
  236. for(int i= 0; i< m_vertices.rows(); ++i)
  237. {
  238. optparams[i][0]= m_vertices[i][ilo];
  239. }
  240. matrix_type fullparams= m_costFunction->getFullParamsFromSubParams(optparams);
  241. m_loger->writeParamsToFile(fullparams);
  242. }
  243. //#define DEBUGESTHER
  244. //#ifdef DEBUGESTHER
  245. // cout<<"Iteration: "<<m_numIter<<" ";
  246. // for(unsigned int paramnum= 0; paramnum< m_numberOfParameters; paramnum++)
  247. // {
  248. // cout<<m_vertices[paramnum][ilo]<<" ";
  249. // }
  250. // cout<<endl;
  251. // //std::cout<<"---------- ihi inhi ilo ---------- " <<ihi << " "<<inhi << " "<<ilo << " " << std::endl;
  252. //#endif
  253. /*
  254. Check m_abort .. outofbounds may have occurred in amotry() last iteration
  255. */
  256. if(m_abort == true)
  257. {
  258. break;
  259. }
  260. /*
  261. Check time criterion
  262. */
  263. if(m_maxSecondsActive)
  264. {
  265. m_currentTime = clock();
  266. /* time limit exceeded ? */
  267. if(((float)(m_currentTime - m_startTime )/CLOCKS_PER_SEC) >= m_maxSeconds )
  268. {
  269. /* set according return status and end optimization */
  270. m_returnReason = SUCCESS_TIMELIMIT;
  271. break;
  272. }
  273. }
  274. /*
  275. check functol criterion
  276. */
  277. if(m_funcTolActive == true)
  278. {
  279. rtol=2.0*fabs(m_y[ihi][0]-m_y[ilo][0])/(fabs(m_y[ihi][0])+fabs(m_y[ilo][0])+1e-10);
  280. // compute the fractional
  281. // range from highest to lowest
  282. #ifdef OPT_DEBUG
  283. std::cout<<"rtol"<<" "<<rtol<< std::endl;
  284. #endif
  285. if (rtol<tol)
  286. { // if satisfactory (terminating
  287. // criterion)
  288. max_val=(int)m_y[ilo][0]; // save lowest value
  289. m_returnReason = SUCCESS_FUNCTOL;
  290. break; // return
  291. }
  292. }
  293. /*
  294. check param tol criterion
  295. */
  296. if (m_paramTolActive == true)
  297. {
  298. // get norm of
  299. //matrix_type tmp = m_vertices(0,ihi,m_numberOfParameters-1,ihi) - m_vertices(0,ilo,m_numberOfParameters,ilo);
  300. //double norm)
  301. if ( (m_vertices(0,ihi,m_numberOfParameters-1,ihi) -
  302. m_vertices(0,ilo,m_numberOfParameters-1,ilo)).Norm(0) < m_paramTol)
  303. {
  304. /*
  305. set according return reason and end optimization
  306. */
  307. m_returnReason = SUCCESS_PARAMTOL;
  308. break;
  309. }
  310. }
  311. m_numIter++;
  312. /*
  313. check max num iter criterion
  314. */
  315. if(m_maxNumIterActive == true)
  316. {
  317. if (m_numIter >= m_maxNumIter)
  318. {
  319. //max_val=(int)m_y[ilo][0];
  320. m_returnReason = SUCCESS_MAXITER;
  321. break;
  322. }
  323. }
  324. //informative output
  325. if (m_verbose)
  326. {
  327. std::cerr << "start new iteration with amotry -alpha, i.e., reflect worst point through simplex" << std::endl;
  328. }
  329. // Begin a new iteration.
  330. // First extrapolate by the
  331. // factor alpha through the
  332. // face of the simplex across
  333. // from the high point, i.e.
  334. // reflect the simplex from the
  335. // high point:
  336. ytry=amotry(psum,ihi,-m_alpha);
  337. if (ytry < m_y[ilo][0])
  338. {
  339. //informative output
  340. if (m_verbose)
  341. {
  342. std::cerr << "reflected point is better than best point, perform further extrapolation with gamma" << std::endl;
  343. }
  344. // result is better than best
  345. // point, try additional
  346. // extrapolation
  347. ytry=amotry(psum,ihi,m_gamma);
  348. #ifdef OPT_DEBUG
  349. std::cout<<"Case one .. reflected highest through simplex" << std::endl;
  350. #endif
  351. }
  352. else
  353. {
  354. if (ytry >= m_y[inhi][0])
  355. {
  356. //informative output
  357. if (m_verbose)
  358. {
  359. std::cerr << "reflected point is worse then second worst, looking for intermediate point with beta" << std::endl;
  360. }
  361. // The reflected point is worse
  362. // than the second
  363. ysave=m_y[ihi][0]; // highest, so look for
  364. // intermediate lower point.
  365. ytry=amotry(psum,ihi,m_beta);
  366. #ifdef OPT_DEBUG
  367. std::cout<<"Case two .. looking for intermediate point" << std::endl;
  368. #endif
  369. if (ytry >= ysave)
  370. { // Can't seem to get rid
  371. // of that high point. Better
  372. #ifdef OPT_DEBUG
  373. std::cout<<"Case three .. contract around lowest point" << std::endl;
  374. #endif
  375. //informative output
  376. if (m_verbose)
  377. {
  378. std::cerr << "Intermediate point is also worse, contract around current best point with factor 0.5." << std::endl;
  379. }
  380. for (i=0;i<spts;i++)
  381. { // contract around lowest
  382. // (best) point
  383. if (i!=ilo)
  384. {
  385. for (j=0;j<ndim;j++)
  386. {
  387. psum[0][j]=0.5*(m_vertices[j][i]+m_vertices[j][ilo]);
  388. #ifdef OPT_DEBUG
  389. printf("psum(%d)=%f\n",j,psum[0][j]);
  390. #endif
  391. m_vertices[j][i]=psum[0][j];
  392. }
  393. if (checkParameters(!psum))
  394. {
  395. m_y[i][0]= evaluateCostFunction(!psum);
  396. //eval_count++; // count function evaluations
  397. }
  398. else
  399. {
  400. m_returnReason = ERROR_XOUTOFBOUNDS; // out of domain !!!
  401. break;
  402. }
  403. }
  404. for (j=0;j<ndim;j++)
  405. { // get sum of vertex-coordinates
  406. double sum=0.0;
  407. for (int ii=0;ii<spts;ii++)
  408. sum += m_vertices[j][ii];
  409. psum[0][j]=sum;
  410. }//for
  411. }//for
  412. }//if (ytry >= ysave)
  413. }//if (ytry >= m_y(inhi))
  414. }//else
  415. } // next iteration
  416. return ilo; // return index of best point
  417. }
  418. double DownhillSimplexOptimizer::amotry(matrix_type & psum, int ihi, double fac)
  419. {
  420. // extrapolates by a factor fac through the face of the simplex across
  421. // from the low point, tries it, and replaces the low point if
  422. // the new point is better
  423. const double maxreal= (m_maximize == true)? -1.0e+300 : 1.0e+300;
  424. double fac1,fac2,ytry;
  425. int ndim=m_numberOfParameters;
  426. matrix_type ptry(1,ndim);
  427. fac1=(1.0-fac)/ndim;
  428. fac2=fac1-fac;
  429. for (int j=0;j<ndim;j++)
  430. {
  431. ptry[0][j]=psum[0][j]*fac1-m_vertices[j][ihi]*fac2;
  432. }
  433. //informative output
  434. if (m_verbose)
  435. {
  436. std::cerr << "amotry fac: " << fac << std::endl;
  437. std::cerr << "fac1: " << fac1 << " fac2: " << fac2 << std::endl;
  438. std::cerr << "ptry: ";
  439. for (int j=0;j<ndim;j++)
  440. {
  441. std::cerr << ptry[0][j] << " ";
  442. }
  443. std::cerr << std::endl;
  444. }
  445. if (checkParameters(!ptry))
  446. {
  447. ytry=evaluateCostFunction(!ptry); // evaluate function at the
  448. // trial point
  449. // eval_count++; // count function evaluations
  450. if (ytry<m_y[ihi][0]) { // if trial point is better
  451. // than lowest point,
  452. m_y[ihi][0]=ytry; // then replace the lowest
  453. for (int j=0; j<ndim;j++) {
  454. psum[0][j] = psum[0][j] + ptry[0][j]-m_vertices[j][ihi]; // update psum
  455. m_vertices[j][ihi]=ptry[0][j]; // replace lowest point
  456. }
  457. }
  458. }
  459. else
  460. {
  461. ytry=maxreal; // out of domain
  462. m_abort = true;
  463. m_returnReason = ERROR_XOUTOFBOUNDS;
  464. }
  465. return ytry;
  466. }
  467. void DownhillSimplexOptimizer::setDownhillParams(const double alpha, const double beta, const double gamma)
  468. {
  469. m_alpha = alpha;
  470. m_beta = beta;
  471. m_gamma = gamma;
  472. }
  473. void DownhillSimplexOptimizer::setRankDeficiencyThresh(float rankdeficiencythresh)
  474. {
  475. m_rankdeficiencythresh= rankdeficiencythresh;
  476. }
  477. void DownhillSimplexOptimizer::setRankCheckStatus(bool status)
  478. {
  479. m_rankcheckenabled= status;
  480. }
  481. double DownhillSimplexOptimizer::getDownhillParameterAlpha() const
  482. {
  483. return m_alpha;
  484. }
  485. double DownhillSimplexOptimizer::getDownhillParameterBeta() const
  486. {
  487. return m_beta;
  488. }
  489. double DownhillSimplexOptimizer::getDownhillParameterGamma() const
  490. {
  491. return m_gamma;
  492. }
  493. bool DownhillSimplexOptimizer::getRankCheckStatus()
  494. {
  495. return m_rankcheckenabled;
  496. }
  497. unsigned int getRank(const matrix_type &A,double numZero)
  498. {
  499. unsigned int tmpCount = 0;
  500. matrix_type U,s,Vt;
  501. //std::cout << "A.rows " << A.rows() << "A.cols() " << A.cols() << std::endl;
  502. if(A.rows() < A.cols())
  503. {
  504. SingularValueDcmp(!A, U, s, Vt); // call of the singular value decomp.
  505. }
  506. else
  507. {
  508. SingularValueDcmp(A, U, s, Vt); // call of the singular value decomp.
  509. }
  510. for(int i= 0; i < s.rows();i++) // count singular values > numZero
  511. {
  512. if( s[i][i] > numZero )
  513. {
  514. tmpCount++;
  515. }
  516. }
  517. return tmpCount;
  518. }