DownhillSimplexOptimizer.cpp 13 KB

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