Object-oriented Scientific Computing Library: Version 0.910
anneal_mt.h
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=&in;
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
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Documentation generated with Doxygen. Provided under the GNU Free Documentation License (see License Information).

Get Object-oriented Scientific Computing
Lib at SourceForge.net. Fast, secure and Free Open Source software
downloads.