![]() |
Object-oriented Scientific Computing Library: Version 0.910
|
00001 /* 00002 ------------------------------------------------------------------- 00003 00004 Copyright (C) 2006-2012, 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_ANNEAL_MT_H 00024 #define O2SCL_ANNEAL_MT_H 00025 00026 #include <o2scl/gsl_rnga.h> 00027 #include <o2scl/ovector_tlate.h> 00028 #include <o2scl/multi_funct.h> 00029 00030 // for boost::bind() 00031 #include <boost/bind.hpp> 00032 // for boost::thread 00033 #include <boost/thread/thread.hpp> 00034 00035 #ifndef DOXYGENP 00036 namespace o2scl { 00037 #endif 00038 00039 /** \brief Multidimensional minimization by simulated annealing 00040 (Boost multi-threaded version) 00041 00042 This header-only class additionally requires the Boost 00043 libraries. It performs simulated annealing using an arbitrary 00044 number of processors using <tt>boost::thread</tt>, which is 00045 closely related to the standard Unix pthread library. It works 00046 very similarly to \ref gsl_anneal, it performs \ref ntrial 00047 evaluations over each processor, then applying the metropolis 00048 algorithm to the results from all of the processors at the end. 00049 00050 Because <tt>np</tt> function calls are happening simultaneously, 00051 where <tt>np</tt> is the number of processors, <tt>np</tt> 00052 copies of the function parameters of type <tt>param_t</tt> must 00053 also be specified. The user-defined function to minimize must 00054 also be thread-safe, allowing multiple threads to call the 00055 function at once (albeit given different parameters). The 00056 default type for these <tt>np</tt> copies of the parameters of 00057 type <tt>param_t</tt> is <tt>std::vector<param_t></tt>. 00058 00059 This works particularly well for functions which are not trivial 00060 to evaluate, i.e. functions where the execution time is more 00061 longer than the bookkeeping that \ref anneal_mt performs between 00062 trials. For functions which satisfy this requirement, this 00063 algorithm scales nearly linearly with the number of processors. 00064 00065 Verbose I/O for this class happens only outside the theads 00066 unless the user places I/O in the streams in the function that 00067 is specified. 00068 00069 \future There may be a good way to remove the function indirection 00070 here to make this class a bit faster. 00071 */ 00072 template<class param_t, class param_vec_t=std::vector<param_t>, 00073 class func_t=multi_funct<param_t>, class vec_t=ovector_base, 00074 class alloc_vec_t=ovector, class alloc_t=ovector_alloc, 00075 class rng_t=gsl_rnga> class anneal_mt { 00076 00077 public: 00078 00079 anneal_mt() { 00080 ntrial=100; 00081 nproc=1; 00082 outs=&std::cout; 00083 ins=&std::cin; 00084 tolx=1.0e-6; 00085 T_start=1.0; 00086 T_dec=1.5; 00087 step_dec=1.5; 00088 min_step_ratio=100.0; 00089 step_vec.allocate(1); 00090 step_vec[0]=1.0; 00091 out_best=false; 00092 out_step_changes=false; 00093 } 00094 00095 virtual ~anneal_mt() { 00096 step_vec.free(); 00097 } 00098 00099 /// \name Basic usage 00100 //@{ 00101 /** \brief Calculate the minimum \c fmin of \c func w.r.t the 00102 array \c x0 of size \c nv using \c np threads. 00103 */ 00104 virtual int mmin(size_t nv, vec_t &x0, double &fmin, 00105 func_t &func, size_t np, param_vec_t &pars) { 00106 00107 if (nv==0) { 00108 O2SCL_ERR2_RET("Tried to minimize over zero variables ", 00109 " in anneal_mt::mmin().",gsl_einval); 00110 } 00111 if (np==0) { 00112 O2SCL_ERR2_RET("Tried to use zero threads in ", 00113 "anneal_mt::mmin().",gsl_einval); 00114 } 00115 00116 allocate(nv,np); 00117 params=&pars; 00118 f=&func; 00119 fmin=0.0; 00120 00121 double E, best_E, T, old_E; 00122 int i, iter=0; 00123 size_t j; 00124 00125 for(j=0;j<nv;j++) { 00126 x[j]=x0[j]; 00127 best_x[j]=x0[j]; 00128 } 00129 00130 E=func(nv,x,pars[0]); 00131 best_E=E; 00132 00133 // Setup initial temperature and step sizes 00134 start(nv,T); 00135 00136 bool done=false; 00137 00138 boost::thread **thrd; 00139 00140 while (!done) { 00141 00142 // Copy old value of x for next() function 00143 00144 for(j=0;j<nv;j++) old_x[j]=x[j]; 00145 old_E=E; 00146 00147 size_t nmoves=0; 00148 00149 for (i=0;i<ntrial;++i) { 00150 00151 // Determine the stepsize, create and execute the new threads 00152 thrd=new boost::thread *[np]; 00153 for(size_t ip=0;ip<np;ip++) { 00154 new_x[ip]=x; 00155 thrd[ip]=new boost::thread 00156 (boost::bind(&anneal_mt::func_wrapper,this,ip)); 00157 } 00158 // Wait until all the threads are done 00159 for(size_t ip=0;ip<np;ip++) { 00160 thrd[ip]->join(); 00161 } 00162 // Delete the threads and continue 00163 for(size_t ip=0;ip<np;ip++) { 00164 delete thrd[ip]; 00165 } 00166 delete[] thrd; 00167 // Process the results from each thread 00168 for(size_t ip=0;ip<np;ip++) { 00169 00170 // Store best value obtained so far 00171 if(new_E[ip]<=best_E){ 00172 for(j=0;j<nv;j++) best_x[j]=new_x[ip][j]; 00173 best_E=new_E[ip]; 00174 if (this->verbose>0 && out_best) { 00175 std::cout << "Best: " << best_x << " " << best_E << std::endl; 00176 } 00177 } 00178 00179 // Take the crucial step: see if the new point is accepted 00180 // or not, as determined by the boltzmann probability 00181 if (new_E[ip]<E) { 00182 for(j=0;j<nv;j++) x[j]=new_x[ip][j]; 00183 E=new_E[ip]; 00184 nmoves++; 00185 } else if (this->def_rng.random() < exp(-(new_E[ip]-E)/(boltz*T)) ) { 00186 for(j=0;j<nv;j++) x[j]=new_x[ip][j]; 00187 E=new_E[ip]; 00188 nmoves++; 00189 } 00190 } 00191 00192 } 00193 00194 if (this->verbose>0) { 00195 print_iter(nv,best_x,best_E,iter,T,"anneal_mt"); 00196 iter++; 00197 } 00198 00199 // See if we're finished and proceed to the next step 00200 next(nv,old_x,old_E,x,E,T,nmoves,done); 00201 00202 } 00203 00204 for(j=0;j<nv;j++) x0[j]=best_x[j]; 00205 fmin=best_E; 00206 00207 free(np); 00208 00209 return 0; 00210 } 00211 //@} 00212 00213 /// \name Iteration control 00214 //@{ 00215 /// Determine how to change the minimization for the next iteration 00216 virtual int next(size_t nv, vec_t &x_old, double min_old, vec_t &x_new, 00217 double min_new, double &T, size_t n_moves, 00218 bool &finished) { 00219 00220 if (T/T_dec<this->tolx) { 00221 finished=true; 00222 return gsl_success; 00223 } 00224 if (n_moves==0) { 00225 bool changed=false; 00226 for(size_t i=0;i<nv;i++) { 00227 if (i<step_vec.size() && step_vec[i]>this->tolx*min_step_ratio) { 00228 step_vec[i]/=step_dec; 00229 changed=true; 00230 } 00231 } 00232 if (changed && verbose>0 && out_step_changes) { 00233 std::cout << "Step sizes changed: " << step_vec << std::endl; 00234 } 00235 } 00236 T/=T_dec; 00237 return gsl_success; 00238 } 00239 00240 /// Setup initial temperature and stepsize 00241 virtual int start(size_t nv, double &T) { 00242 T=T_start; 00243 return gsl_success; 00244 } 00245 //@} 00246 00247 /// \name Parameters 00248 //@{ 00249 /// Boltzmann factor (default 1.0). 00250 double boltz; 00251 00252 /// Number of iterations 00253 int ntrial; 00254 00255 /// Output control 00256 int verbose; 00257 00258 /// Output step size changes (default false) 00259 bool out_step_changes; 00260 00261 /// Output best point (default false) 00262 bool out_best; 00263 00264 /// The independent variable tolerance (default \f$ 10^{-6} \f$ ) 00265 double tolx; 00266 00267 /// Initial temperature (default 1.0) 00268 double T_start; 00269 00270 /// Factor to decrease temperature by (default 1.5) 00271 double T_dec; 00272 00273 /// Factor to decrease step size by (default 1.5) 00274 double step_dec; 00275 00276 /// Ratio between minimum step size and \ref tolx (default 100.0) 00277 double min_step_ratio; 00278 //@} 00279 00280 /// The default random number generator 00281 rng_t def_rng; 00282 00283 /// Return string denoting type ("anneal_mt") 00284 virtual const char *type() { return "anneal_mt"; } 00285 00286 /// Set streams for verbose I/O 00287 int set_verbose_stream(std::ostream &out, std::istream &in) { 00288 outs=&out; 00289 ins=∈ 00290 return 0; 00291 } 00292 00293 /** \brief Print out iteration information. 00294 00295 Depending on the value of the variable verbose, this prints out 00296 the iteration information. If verbose=0, then no information is 00297 printed. If verbose>0, then after each iteration, the present 00298 values of x and y are output to std::cout along with the 00299 iteration number. Also, if verbose>0, every time a new smallest 00300 function value is found, the location and the function value is 00301 output. If verbose>=2 then each iteration waits for a character 00302 between each trial. 00303 */ 00304 virtual int print_iter(size_t nv, vec_t &xx, double y, int iter, 00305 double tptr, std::string comment) 00306 { 00307 if (this->verbose<=0) return 0; 00308 00309 size_t i; 00310 char ch; 00311 00312 (*this->outs) << comment << " Iteration: " << iter << std::endl; 00313 text_out_file outsf(this->outs,79); 00314 outsf.word_out("x:"); 00315 for(i=0;i<nv;i++) outsf.double_out(xx[i]); 00316 outsf.end_line(); 00317 (*this->outs) << "y: " << y << " Tptr: " << tptr << std::endl; 00318 if (this->verbose>1) { 00319 (*this->outs) << "Press a key and type enter to continue. "; 00320 (*this->ins) >> ch; 00321 } 00322 00323 return 0; 00324 } 00325 00326 /// Set the step sizes 00327 template<class vec2_t> int set_step(size_t nv, vec2_t &stepv) { 00328 if (nv>0) { 00329 step_vec.free(); 00330 step_vec.allocate(nv); 00331 for(size_t i=0;i<nv;i++) step_vec[i]=stepv[i]; 00332 } 00333 return 0; 00334 } 00335 00336 #ifndef DOXYGEN_INTERNAL 00337 00338 protected: 00339 00340 /// The function wrapper executed by thread with index \c ip 00341 void func_wrapper(size_t ip) { 00342 step(nvar,new_x[ip]); 00343 (*f)(nvar,new_x[ip],new_E[ip],(*params)[ip]); 00344 } 00345 00346 /// Stream for verbose output 00347 std::ostream *outs; 00348 00349 /// Stream for verbose input 00350 std::istream *ins; 00351 00352 /// The number of threads to run 00353 size_t nproc; 00354 00355 /// The user-specified parameters 00356 param_vec_t *params; 00357 00358 /// The number of variables over which we minimize 00359 size_t nvar; 00360 00361 /// The function to minimize 00362 func_t *f; 00363 00364 /// \name Storage for present, next, and best vectors 00365 //@{ 00366 alloc_vec_t x, *new_x, best_x, new_E, old_x; 00367 //@} 00368 00369 /// Allocation object 00370 alloc_t ao; 00371 00372 /// Vector of step sizes 00373 ovector step_vec; 00374 00375 /** \brief Allocate memory for a minimizer over \c n dimensions 00376 with stepsize \c step 00377 */ 00378 virtual int allocate(size_t nv, size_t np) { 00379 nvar=nv; 00380 nproc=np; 00381 ao.allocate(x,nvar); 00382 new_x=new alloc_vec_t[np]; 00383 ao.allocate(old_x,nvar); 00384 for(size_t i=0;i<np;i++) { 00385 ao.allocate(new_x[i],nvar); 00386 } 00387 ao.allocate(new_E,np); 00388 ao.allocate(best_x,nvar); 00389 return 0; 00390 } 00391 00392 /// Free allocated memory 00393 virtual int free(size_t np) { 00394 ao.free(x); 00395 ao.free(old_x); 00396 ao.free(new_E); 00397 ao.free(best_x); 00398 for(size_t i=0;i<np;i++) { 00399 ao.free(new_x[i]); 00400 } 00401 delete[] new_x; 00402 return 0; 00403 } 00404 00405 /// Make a step to a new attempted minimum 00406 virtual int step(size_t nv, vec_t &sx) { 00407 size_t nstep=step_vec.size(); 00408 for(size_t i=0;i<nv;i++) { 00409 double u=this->def_rng.random(); 00410 sx[i]=(2.0*u-1.0)*step_vec[i%nstep]+sx[i]; 00411 } 00412 return 0; 00413 } 00414 00415 #endif 00416 00417 }; 00418 00419 #ifndef DOXYGENP 00420 } 00421 #endif 00422 00423 #endif
Documentation generated with Doxygen. Provided under the GNU Free Documentation License (see License Information).