00001 /* 00002 ------------------------------------------------------------------- 00003 00004 Copyright (C) 2006, 2007, 2008, Andrew W. Steiner 00005 00006 This file is part of O2scl. 00007 00008 O2scl is free software; you can redistribute it and/or modify 00009 it under the terms of the GNU General Public License as published by 00010 the Free Software Foundation; either version 3 of the License, or 00011 (at your option) any later version. 00012 00013 O2scl is distributed in the hope that it will be useful, 00014 but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00016 GNU General Public License for more details. 00017 00018 You should have received a copy of the GNU General Public License 00019 along with O2scl. If not, see <http://www.gnu.org/licenses/>. 00020 00021 ------------------------------------------------------------------- 00022 */ 00023 #ifndef O2SCL_GSL_MMIN_SIMP_H 00024 #define O2SCL_GSL_MMIN_SIMP_H 00025 00026 #include <gsl/gsl_multimin.h> 00027 #include <o2scl/multi_min.h> 00028 #include <o2scl/cblas.h> 00029 #include <o2scl/vec_stats.h> 00030 00031 #ifndef DOXYGENP 00032 namespace o2scl { 00033 #endif 00034 00035 /** 00036 \brief Multidimensional minimization by the Simplex method (GSL) 00037 00038 This class minimizes a function using Nelder and Mead's Simplex 00039 algorithm. A simplex in a N-dimensional space is definted as a 00040 set of N+1 points which describe an N-dimensional volume 00041 surrounding the minimum. The algorithm proceeds by shifting 00042 the simplex points until the simplex is sufficiently small and 00043 thus the minimum is known with sufficient accuracy. 00044 00045 This class has a high-level interface using mmin(), 00046 mmin_twovec() or mmin_simplex() which automatically performs the 00047 memory allocation and minimization, or a GSL-like interface 00048 using allocate(), free(), interate() and set() or set_simplex(). 00049 00050 The simplex can be completely specified by the user (see 00051 mmin_simplex() and set_simplex()). Alternatively, the simplex is 00052 automatically specified given initial guess \f$ x_j \f$ and a 00053 step size vector \f$ s_k \f$ for \f$ 0\leq k<n_s \f$. The 00054 simplex \f$ p_{ij} \f$ with \f$ 0\leq i\leq n \f$ and \f$ 0\leq 00055 j<n \f$ is chosen with \f$ p_{0j} = x_j \f$ and 00056 \f{eqnarray*} 00057 p_{i+1,j} &=& x_j\quad\mathrm{for}\quad i\neq j \\ 00058 p_{i+1,j} &=& x_j+s_{j~\mathrm{mod}~n_s}\quad\mathrm{for}\quad i=j 00059 \f} 00060 for \f$ 0<i<n \f$. The step size vector \f$ s \f$ is set by the 00061 set_step() member function. The prsence of \f$ \mathrm{mod} \f$ 00062 in the recipe above just indicates that elements of the step 00063 size vector are automatically re-used if there are less step 00064 sizes than dimensions in the minimization problem. 00065 */ 00066 template<class param_t,class func_t=multi_funct<param_t>, 00067 class vec_t=ovector_view, class alloc_vec_t=ovector, 00068 class alloc_t=ovector_alloc> class gsl_mmin_simp : 00069 public multi_min<param_t,func_t,func_t,vec_t> { 00070 00071 #ifndef DOXYGEN_INTERNAL 00072 00073 protected: 00074 00075 /// An array of n+1 vectors containing the simplex 00076 alloc_vec_t *x1; 00077 /** 00078 \brief The n+1 function values at the simplex points 00079 00080 \comment 00081 This can't be the template type because it has to be 00082 a different size (n+1) rather than n. 00083 \endcomment 00084 */ 00085 ovector y1; 00086 /// Workspace vector 1 00087 alloc_vec_t ws1; 00088 /// Workspace vector 2 00089 alloc_vec_t ws2; 00090 /// Workspace vector 3 00091 alloc_vec_t ws3; 00092 00093 /// Compute the center of the simplex and store in \c mp 00094 int nmsimplex_calc_center(vec_t &mp) { 00095 size_t i, j; 00096 double val; 00097 00098 for (j = 0; j < dim; j++) { 00099 val = 0.0; 00100 for (i = 0; i < dim+1; i++) { 00101 val += x1[i][j]; 00102 } 00103 val /= dim+1; 00104 mp[j]=val; 00105 } 00106 00107 return 0; 00108 } 00109 00110 /** 00111 \brief Compute the size of the simplex 00112 00113 Calculates simplex size as average sum of length of vectors 00114 from simplex center to corner points: 00115 00116 \f$ (1/n) \sum || y - y_{\mathrm{middlepoint}} || \f$ 00117 */ 00118 double nmsimplex_size() { 00119 00120 double ss=0.0; 00121 nmsimplex_calc_center(ws2); 00122 00123 for(size_t i=0;i<dim+1;i++) { 00124 for(size_t j=0;j<dim;j++) ws1[j]=x1[i][j]; 00125 o2scl_cblas::daxpy(dim,-1.0,ws2,ws1); 00126 ss += o2scl_cblas::dnrm2(dim,ws1); 00127 } 00128 00129 return ss/(double)(dim+1); 00130 } 00131 00132 /** 00133 \brief Move a corner of a simplex 00134 00135 Moves a simplex corner scaled by coeff (negative value 00136 represents mirroring by the middle point of the "other" corner 00137 points) and gives new corner in xc and function value at xc 00138 in \c newval. 00139 */ 00140 virtual int move_corner_err(const double coeff, size_t corner, 00141 vec_t &xc, func_t &f, 00142 size_t nvar, param_t &pa, double &newval) { 00143 00144 size_t i,j; 00145 double mp; 00146 00147 for (j=0;j<dim;j++) { 00148 mp=0.0; 00149 for (i=0;i<dim+1;i++) { 00150 if (i!=corner) { 00151 mp+=x1[i][j]; 00152 } 00153 } 00154 mp/=(double)dim; 00155 xc[j]=mp-coeff*(mp-x1[corner][j]); 00156 } 00157 00158 return f(nvar,xc,newval,pa); 00159 } 00160 00161 /** 00162 \brief Contract the simplex towards the best point 00163 00164 Function contracts the simplex in respect to best valued 00165 corner. All corners besides the best corner are moved. 00166 00167 The vector, \c xc, is used as work space. 00168 */ 00169 virtual int contract_by_best(size_t best, vec_t &xc, 00170 func_t &f, size_t nvar, param_t &pa) { 00171 00172 size_t i,j, it; 00173 double newval; 00174 bool failed; 00175 00176 for (i=0;i<dim+1;i++) { 00177 00178 if (i!=best) { 00179 00180 for (j=0;j<dim;j++) { 00181 x1[i][j]=0.5*(x1[i][j]+x1[best][j]); 00182 } 00183 int ret=f(nvar,x1[i],y1[i],pa); 00184 00185 if (avoid_nonzero==true && ret!=0) { 00186 std::cout << "Found problem in contract." << std::endl; 00187 00188 // copy the old point to ws3 00189 for (j=0;j<dim;j++) { 00190 ws3[j]=2.0*x1[i][j]-x1[best][j]; 00191 } 00192 00193 // Try (21*best+20*xold)/41, (22*best+19*xold)/41, ..., 00194 // (40*best+xold)/41 to see if we can find a contraction 00195 // that works 00196 for(it=0;ret!=0 && it<20;it++) { 00197 for (j=0;j<dim;j++) { 00198 x1[i][j]=((20-it)*ws3[j]+(it+21)*x1[best][j])/41.0; 00199 } 00200 ret=f(nvar,x1[i],y1[i],pa); 00201 std::cout << "it, ret, x: " << it << " " << ret << " " 00202 << x << std::endl; 00203 } 00204 if (ret!=0) { 00205 set_err2_ret("Failed to find suitable contraction ", 00206 "in gsl_mmin_simp::contract_by_best().", 00207 gsl_efailed); 00208 } 00209 } 00210 } 00211 } 00212 00213 return gsl_success; 00214 } 00215 00216 /// Number of variables to be minimized over 00217 size_t dim; 00218 00219 /// Function 00220 func_t *func; 00221 00222 /// Parameters 00223 param_t *params; 00224 00225 /// True if set() has been called 00226 bool set_called; 00227 00228 /// Vector of step sizes 00229 ovector step_vec; 00230 00231 /// Vector allocator 00232 alloc_t ao; 00233 00234 /** 00235 \brief If true, try to automatically avoid regions where the 00236 function returns a non-zero value (default false) 00237 00238 \note This option doesn't work yet, so I've made the variable 00239 protected to prevent the user from changing it. 00240 */ 00241 bool avoid_nonzero; 00242 00243 #endif 00244 00245 public: 00246 00247 gsl_mmin_simp() { 00248 dim=0; 00249 print_simplex=0; 00250 step_vec.allocate(1); 00251 step_vec[0]=1.0; 00252 avoid_nonzero=false; 00253 } 00254 00255 virtual ~gsl_mmin_simp() { 00256 free(); 00257 step_vec.free(); 00258 } 00259 00260 /// Set the step sizes for each independent variable 00261 template<class vec2_t> int set_step(size_t nv, vec2_t &step) { 00262 if (nv>0) { 00263 step_vec.free(); 00264 step_vec.allocate(nv); 00265 for(size_t i=0;i<nv;i++) step_vec[i]=step[i]; 00266 } 00267 return 0; 00268 } 00269 00270 /// Size of current simplex computed by iterate() 00271 double size; 00272 00273 /// Present minimum vector computed by iterate() 00274 alloc_vec_t x; 00275 00276 /// Function value at minimum computed by iterate() 00277 double fval; 00278 00279 /** 00280 \brief Print simplex information in print_iter() (default 0) 00281 00282 If this is 1 and \ref verbose is greater than 0, 00283 then print_iter() will print the function values at all 00284 the simplex points. If this is 2 and \ref verbose is 00285 greater than 0, then print_iter() will print the 00286 simplex coordinates in addition to the function values. 00287 */ 00288 int print_simplex; 00289 00290 /** \brief Calculate the minimum \c min of \c func w.r.t the 00291 array \c x of size \c nvar. 00292 */ 00293 virtual int mmin(size_t nn, vec_t &xx, double &fmin, param_t &pa, 00294 func_t &ufunc) { 00295 00296 int ret=0,status,iter=0; 00297 00298 allocate(nn); 00299 00300 alloc_vec_t ss; 00301 ao.allocate(ss,nn); 00302 for (size_t is=0;is<nn;is++) ss[is]=step_vec[is % step_vec.size()]; 00303 ret=set(ufunc,pa,nn,xx,ss); 00304 ao.free(ss); 00305 00306 if(ret!=0) { 00307 free(); 00308 return ret; 00309 } 00310 00311 do { 00312 iter++; 00313 00314 status=iterate(); 00315 if(status) break; 00316 00317 if(this->verbose>0) { 00318 print_iter(nn,x,x1,fval,iter,size,this->tolx, 00319 "gsl_mmin_simp"); 00320 } 00321 00322 status=gsl_multimin_test_size(size,this->tolx); 00323 00324 } while(status == GSL_CONTINUE && iter<this->ntrial); 00325 00326 for (size_t i=0;i<nn;i++) xx[i]=x[i]; 00327 fmin=fval; 00328 00329 free(); 00330 this->last_ntrial=iter; 00331 00332 if(iter>=this->ntrial) { 00333 set_err_ret("Exceeded max # of iterations in gsl_mmin_simp::mmin().", 00334 gsl_emaxiter); 00335 } 00336 00337 return status; 00338 } 00339 00340 /** \brief Calculate the minimum \c min of \c func w.r.t the 00341 array \c x of size \c nvar, using \c xx and \c xx2 to specify 00342 the simplex 00343 */ 00344 virtual int mmin_twovec(size_t nn, vec_t &xx, vec_t &xx2, double &fmin, 00345 param_t &pa, func_t &ufunc) { 00346 00347 int ret=0,i,status,iter=0; 00348 00349 allocate(nn); 00350 00351 alloc_vec_t ss; 00352 ao.allocate(ss,nn); 00353 00354 for (size_t is=0;is<nn;is++) ss[is]=xx2[is]-xx[is]; 00355 ret=set(ufunc,pa,nn,xx,ss); 00356 ao.free(ss); 00357 00358 if(ret!=0) { 00359 free(); 00360 return ret; 00361 } 00362 00363 do { 00364 iter++; 00365 00366 status=iterate(); 00367 if(status) break; 00368 00369 if(this->verbose>0) { 00370 print_iter(nn,x,x1,fval,iter,size,this->tolx, 00371 "gsl_mmin_simp"); 00372 } 00373 00374 status=gsl_multimin_test_size(size,this->tolx); 00375 00376 } while(status == GSL_CONTINUE && iter<this->ntrial); 00377 00378 for (i=0;i<((int)nn);i++) xx[i]=x[i]; 00379 fmin=fval; 00380 00381 free(); 00382 this->last_ntrial=iter; 00383 00384 if(iter>=this->ntrial) { 00385 set_err_ret 00386 ("Exceeded max # of iterations in gsl_mmin_simp::mmin_twovec().", 00387 gsl_emaxiter); 00388 } 00389 00390 return status; 00391 } 00392 00393 /** \brief Calculate the minimum \c min of \c func w.r.t the 00394 array \c x of size \c nvar, given an initial simplex 00395 */ 00396 template<class mat_t> 00397 int mmin_simplex(size_t nn, mat_t &sx, double &fmin, 00398 param_t &pa, func_t &ufunc) { 00399 00400 int ret=0,i,status,iter=0; 00401 00402 allocate(nn); 00403 00404 ret=set_simplex(ufunc,pa,sx); 00405 if(ret!=0) { 00406 free(); 00407 return ret; 00408 } 00409 00410 do { 00411 iter++; 00412 00413 status=iterate(); 00414 if(status) break; 00415 00416 if(this->verbose>0) { 00417 print_iter(nn,x,x1,fval,iter,size,this->tolx, 00418 "gsl_mmin_simp"); 00419 } 00420 00421 status=gsl_multimin_test_size(size,this->tolx); 00422 00423 } while(status == GSL_CONTINUE && iter<this->ntrial); 00424 00425 for (i=0;i<((int)nn);i++) sx[0][i]=x[i]; 00426 fmin=fval; 00427 00428 free(); 00429 this->last_ntrial=iter; 00430 00431 if(iter>=this->ntrial) { 00432 set_err_ret 00433 ("Exceeded max # of iterations in gsl_mmin_simp::mmin_twovec().", 00434 gsl_emaxiter); 00435 } 00436 00437 return status; 00438 } 00439 00440 /// Allocate the memory 00441 virtual int allocate(size_t n) { 00442 int status; 00443 if(dim!=0) free(); 00444 set_called=false; 00445 dim=n; 00446 00447 ao.allocate(x,n); 00448 x1=new alloc_vec_t[n+1]; 00449 for(size_t i=0;i<n+1;i++) ao.allocate(x1[i],n); 00450 y1.allocate(n+1); 00451 ao.allocate(ws1,n); 00452 ao.allocate(ws2,n); 00453 ao.allocate(ws3,n); 00454 00455 return gsl_success; 00456 } 00457 00458 /// Free the allocated memory 00459 virtual int free() { 00460 00461 if (dim>0) { 00462 for(size_t i=0;i<dim+1;i++) ao.free(x1[i]); 00463 delete[] x1; 00464 y1.free(); 00465 ao.free(ws1); 00466 ao.free(ws2); 00467 ao.free(ws3); 00468 ao.free(x); 00469 } 00470 00471 dim=0; 00472 00473 return 0; 00474 } 00475 00476 /// Set the function and initial guess 00477 virtual int set(func_t &ufunc, param_t &pa, size_t n, vec_t &ax, 00478 vec_t &step_size) { 00479 size_t i; 00480 00481 if(dim!=n) allocate(n); 00482 params=&pa; 00483 func=&ufunc; 00484 00485 // Copy initial guess to x 00486 for (i=0;i<dim;i++) x[i]=ax[i]; 00487 00488 // first point is the original x0 00489 00490 int ret=ufunc(dim,ax,y1[0],pa); 00491 for(i=0;i<dim;i++) x1[0][i]=ax[i]; 00492 00493 /* following points are initialized to x0+step_size */ 00494 00495 for (i=1;i<dim+1;i++) { 00496 for(size_t j=0;j<dim;j++) x1[i][j]=x[j]; 00497 x1[i][i-1]=x1[i][i-1]+step_size[i-1]; 00498 ufunc(dim,x1[i],y1[i],pa); 00499 } 00500 00501 /* Initialize simplex size */ 00502 00503 size=nmsimplex_size(); 00504 00505 set_called=true; 00506 00507 return gsl_success; 00508 } 00509 00510 /// Set the function and initial simplex 00511 template<class mat_t> 00512 int set_simplex(func_t &ufunc, param_t &pa, mat_t &sx) { 00513 00514 if(dim==0) { 00515 set_err_ret("Memory not allocated in gsl_mmin_simp::set().", 00516 gsl_ebadlen); 00517 } 00518 00519 params=&pa; 00520 func=&ufunc; 00521 00522 for(size_t i=0;i<dim+1;i++) { 00523 for(size_t j=0;j<dim;j++) { 00524 x1[i][j]=sx[i][j]; 00525 } 00526 ufunc(dim,x1[i],y1[i],pa); 00527 } 00528 00529 /* Initialize simplex size */ 00530 00531 size=nmsimplex_size(); 00532 00533 set_called=true; 00534 00535 return gsl_success; 00536 } 00537 00538 /// Perform an iteration 00539 virtual int iterate() { 00540 00541 size_t n=dim+1; 00542 size_t i; 00543 size_t hi=0,s_hi=0,lo=0; 00544 double dhi,ds_hi,dlo; 00545 int status; 00546 double val,val2; 00547 00548 /* get index of highest,second highest and lowest point */ 00549 00550 dhi=ds_hi=dlo=y1[0]; 00551 00552 for (i=1;i<n;i++) { 00553 val=y1[i]; 00554 if(val<dlo) { 00555 dlo=val; 00556 lo=i; 00557 } else if (val > dhi) { 00558 ds_hi=dhi; 00559 s_hi=hi; 00560 dhi=val; 00561 hi=i; 00562 } else if(val > ds_hi) { 00563 ds_hi=val; 00564 s_hi=i; 00565 } 00566 } 00567 00568 /* reflect the highest value */ 00569 00570 int ret1=move_corner_err(-1.0,hi,ws1,*func,dim,*params,val); 00571 if (avoid_nonzero && ret1!=0) { 00572 std::cout << "Found problem move1: " << std::endl; 00573 for (size_t it=0;it<20 && ret1!=0;it++) { 00574 ret1=move_corner_err(-1.0+((double)it)/10.0,hi,ws1, 00575 *func,dim,*params,val); 00576 std::cout << "it,ret: " << it << " " << ret1 << std::endl; 00577 } 00578 if (ret1!=0) { 00579 set_err2_ret("Failed to move corner (1) in ", 00580 "gsl_mmin_simp::iterate().",gsl_efailed); 00581 } 00582 } 00583 00584 if (val<y1[lo]) { 00585 00586 /* reflected point becomes lowest point,try expansion */ 00587 00588 int ret2=move_corner_err(-2.0,hi,ws2,*func,dim,*params,val2); 00589 if (avoid_nonzero && ret2!=0) { 00590 std::cout << "Found problem move2: " << std::endl; 00591 for (size_t it=0;it<20 && ret2!=0;it++) { 00592 ret2=move_corner_err(-2.0+((double)it)/6.7,hi,ws2, 00593 *func,dim,*params,val2); 00594 std::cout << "it,ret: " << it << " " << ret2 << std::endl; 00595 } 00596 if (ret2!=0) { 00597 set_err2_ret("Failed to move corner (2) in ", 00598 "gsl_mmin_simp::iterate().",gsl_efailed); 00599 } 00600 } 00601 00602 if (val2<y1[lo]) { 00603 for(i=0;i<dim;i++) x1[hi][i]=ws2[i]; 00604 y1[hi]=val2; 00605 } else { 00606 for(i=0;i<dim;i++) x1[hi][i]=ws1[i]; 00607 y1[hi]=val; 00608 } 00609 00610 } else if (val > y1[s_hi]) { 00611 00612 /* reflection does not improve things enough */ 00613 00614 if (val <= y1[hi]) { 00615 00616 /* if trial point is better than highest point,replace 00617 highest point */ 00618 00619 for(i=0;i<dim;i++) x1[hi][i]=ws1[i]; 00620 y1[hi]=val; 00621 } 00622 00623 /* try one dimensional contraction */ 00624 00625 int ret3=move_corner_err(0.5,hi,ws2,*func,dim,*params,val2); 00626 if (avoid_nonzero && ret3!=0) { 00627 std::cout << "Found problem move3: " << std::endl; 00628 for (size_t it=0;it<20 && ret3!=0;it++) { 00629 ret3=move_corner_err(0.025*((double)(19-it)),hi,ws2, 00630 *func,dim,*params,val2); 00631 std::cout << "it,ret: " << it << " " << ret3 << std::endl; 00632 } 00633 if (ret3!=0) { 00634 set_err2_ret("Failed to move corner (2) in ", 00635 "gsl_mmin_simp::iterate().",gsl_efailed); 00636 } 00637 } 00638 00639 if (val2 <= y1[hi]) { 00640 00641 for(i=0;i<dim;i++) x1[hi][i]=ws2[i]; 00642 y1[hi]=val2; 00643 00644 } else { 00645 00646 /* contract the whole simplex in respect to the best point */ 00647 status=contract_by_best(lo,ws1,*func,dim,*params); 00648 if(status != 0) { 00649 set_err("Function contract_by_best() failed in iterate().", 00650 gsl_efailed); 00651 } 00652 00653 } 00654 00655 } else { 00656 00657 /* trial point is better than second highest point. 00658 Replace highest point by it */ 00659 00660 for(i=0;i<dim;i++) x1[hi][i]=ws1[i]; 00661 y1[hi]=val; 00662 } 00663 00664 /* return lowest point of simplex as x */ 00665 00666 lo=vector_min_index(dim+1,y1,val); 00667 for(i=0;i<dim;i++) x[i]=x1[lo][i]; 00668 fval=y1[lo]; 00669 00670 /* Update simplex size */ 00671 00672 size=nmsimplex_size(); 00673 00674 return gsl_success; 00675 } 00676 00677 /** 00678 \brief Print out iteration information. 00679 00680 Depending on the value of the variable verbose, this prints out 00681 the iteration information. If verbose=0, then no information is 00682 printed, while if verbose>1, then after each iteration, the 00683 present values of x and y are output to std::cout along with the 00684 iteration number. If verbose>=2 then each iteration waits for a 00685 character. 00686 */ 00687 virtual int print_iter(size_t nv, vec_t &xx, alloc_vec_t *simp, 00688 double y, int iter, 00689 double value, double limit, 00690 std::string comment) { 00691 if (this->verbose<=0) return 0; 00692 00693 size_t i; 00694 char ch; 00695 00696 std::cout << comment << " Iteration: " << iter << std::endl; 00697 text_out_file outs(&std::cout,79); 00698 outs.word_out("x:"); 00699 for(i=0;i<nv;i++) outs.double_out(xx[i]); 00700 outs.end_line(); 00701 if (print_simplex>0) { 00702 if (print_simplex==2) { 00703 std::cout << "Simplex Values:" << std::endl; 00704 for(i=0;i<nv+1;i++) { 00705 outs.int_out(i); 00706 outs.char_out(':'); 00707 for(size_t j=0;j<nv;j++) { 00708 outs.double_out(simp[i][j]); 00709 } 00710 outs.char_out(':'); 00711 outs.double_out(y1[i]); 00712 outs.end_line(); 00713 } 00714 } else { 00715 outs.word_out("Simplex Values:"); 00716 for(i=0;i<nv+1;i++) { 00717 outs.double_out(y1[i]); 00718 } 00719 outs.end_line(); 00720 } 00721 } 00722 std::cout << "y: " << y << " Val: " << value << " Lim: " 00723 << limit << std::endl; 00724 if (this->verbose>1) { 00725 std::cout << "Press a key and type enter to continue. "; 00726 std::cin >> ch; 00727 } 00728 00729 return 0; 00730 } 00731 00732 /// Return string denoting type("gsl_mmin_simp") 00733 virtual const char *type() { return "gsl_mmin_simp";} 00734 00735 }; 00736 00737 #ifndef DOXYGENP 00738 } 00739 #endif 00740 00741 #endif
Documentation generated with Doxygen and provided under the GNU Free Documentation License. See License Information for details.
Project hosting provided by
,
O2scl Sourceforge Project Page