Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

siman.cpp

00001 #include "qscr.h"
00002 #include "siman.h"
00003 #include "linrdist.h"
00004 #include "util.h"
00005 #include <iostream>
00006 #include <cmath>
00007 #include <stdlib.h>
00008 #include <cassert>
00009 
00010 
00011 
00012 SimAnneal::SimAnneal(Function *ff, matrix simplex, 
00013                      double temptr, unsigned it, double tol, long s,
00014                      unsigned steps) {
00015   unsigned n = (*ff).getdim();
00016   assert(simplex.row()==int(n+1) && simplex.column()==int(n));
00017   p = simplex;
00018   funk = ff;
00019   ndim = n;
00020   tt = -temptr;
00021   seed = s;
00022   ftol = tol;
00023   iter = it;
00024   static_iter = it;
00025   nosteps = steps;
00026 
00027   psum.newsize(ndim);
00028   calc_psum();
00029 
00030   pb.newsize(ndim);
00031   pb = p.getrow(0).trans();
00032   yb = (*funk)(pb);
00033 
00034   y.newsize(ndim);
00035   y = pval(p);  
00036 }
00037 
00038 
00039 matrix SimAnneal::pval(matrix const& pp) {
00040   assert(pp.row()==int(ndim+1) && pp.column()==int(ndim));
00041   matrix res(ndim+1);
00042   for (unsigned i=0; i<ndim+1; i++) {
00043     res.set((*funk)(pp.getrow(i).trans()),i);
00044   }
00045   return res;
00046 }
00047 
00048 
00049 void SimAnneal::calc_psum()
00050 {
00051  
00052   // ndim = p.rows
00053   // ndim+1 = p.cols
00054   for (unsigned j=0;j<ndim;j++) {
00055     double sum = 0.0;
00056     for (unsigned i=0;i<ndim+1;i++)
00057       sum += p.get(i,j);
00058     psum.set(sum,j);
00059   }
00060 }
00061 
00062 
00063 double SimAnneal::amotsa(int ihi, double &yhi, 
00064                          double const fac) {
00065   // Extrapolates by a factor fac through the face of the simplex across from the high point,
00066   // tries it, and replaces the high point if the new point is better.
00067   
00068   unsigned j;
00069   double fac1, fac2, yflu, ytry;
00070   matrix ptry(ndim);
00071 
00072   fac1 = (1.0-fac)/ndim;
00073   fac2 = fac1-fac;
00074   for (j=0; j<ndim; j++) {
00075     double val = psum.get(j)*fac1 - p.get(ihi,j)*fac2;
00076     ptry.set(val,j);
00077   }
00078   //  cerr << "ptry="; ptry.print();
00079   ytry = (*funk)(ptry);
00080   //  cerr << "f(ptry)=ytry=" << ytry << endl;
00081   if (ytry<=yb) {
00082     for (j=0; j<ndim; j++) { // Save the best ever
00083       pb.set(ptry.get(j),j);
00084     }
00085     yb = ytry; 
00086   }
00087   // We added a thermal fluctuation to all the current vertices, but we subtract
00088   // it here, so as to give the simplex a thermal Brownian motion: It "likes" to
00089   // accept any suggested change.
00090   yflu = ytry-tt*log(ran1(&seed)); 
00091   if (yflu<yhi) {
00092     y.set(ytry,ihi);
00093     yhi=yflu;
00094     for (j=0; j<ndim; j++) {
00095       double val = ptry.get(j)-p.get(ihi,j);
00096       psum.set(psum.get(j) + val,j);
00097       p.set(ptry.get(j),ihi,j);
00098     }
00099   }
00100   return yflu;
00101 }
00102 
00103 
00104 /*
00105   Multidimensional minimization of the function funk(x) where x[1..ndim] is a 
00106   vector in ndim dimensions, by simulated annealing combined with the downhill
00107   simplex method of Nelder and Mead. The input matrix p[1..ndim+1][1..ndim] has
00108   ndim+1 rows, each an ndim-dimensional vector which is a vertex of the starting
00109   simplex. Also input are the following: the vector y[1..ndim+1] whose components
00110   must be pre-initialized to the values of funk evaluated at the ndim+1 vertices
00111   (rows) of p; ftol, the fractional convergence tolerance to be archived in the
00112   function value for an early return; iter, and temptr. The routine makes iter
00113   function evaulations at an annealing temperature temptr, the returns. You
00114   should then decrease temptr according to your annealing schedule, reset iter
00115   and call the routine again (leaving other arguments unaltered between calls).
00116   If iter is returned with a postive value, then early convergence and return
00117   occurred . If you initialize yb to a very large value on the first call, 
00118   then yb and pb[1..ndim] will subsequently return the best function value
00119   and point ever encountered (even if it is no longer a point in the simplex).
00120 */
00121 
00122 void SimAnneal::amebsa() {
00123 
00124   unsigned ihi,ilo,mpts=ndim+1;
00125   double rtol, swap, ylo, ynhi, ysave, yt, ytry, yhi;
00126   matrix psum(ndim);
00127   unsigned cnt = 1;
00128   std::string st;
00129 
00130    // Determine which point is the highest (worst), next-highest and lowest (best).
00131   calc_psum();
00132   for (;;) {
00133 
00134     st = "Min-value so far=" + numStr(yb) + 
00135       " found at ( ";
00136     for (unsigned i=0; i<ndim; i++) {
00137       st += numStr(pb.get(i)) + " ";
00138     }
00139     st += ")";
00140     qmvaddstr(22,5, st.c_str()); qclrtoeol();
00141 
00142     switch(cnt) {
00143     case 1: 
00144       qmvaddstr(24,5, "---");
00145       break;
00146     case 2:
00147       qmvaddstr(24,5, "///");
00148       break;
00149     case 3: 
00150       qmvaddstr(24,5, "|||");
00151       break;
00152     case 4: 
00153       qmvaddstr(24,5, "\\\\\\");
00154       break;
00155     case 5: 
00156       qmvaddstr(24,5, "---");
00157       break;
00158     case 6: 
00159       qmvaddstr(24,5, "\\\\\\");
00160       break;
00161     case 7: 
00162       qmvaddstr(24,5, "|||");
00163       break;
00164     case 8: 
00165       qmvaddstr(24,5, "///");
00166       cnt = 0;
00167       break;
00168     }
00169 
00170     cnt++;
00171     
00172 //     cerr << "Min-value so far=" << yb 
00173 //       << " found at (" << pb.get(0) << ", " << pb.get(1) << ", " 
00174 //       << pb.get(2) << ")" 
00175 //       << endl;
00176 
00177 
00178     
00179     
00180     qmvaddstr(6,55, "Current simplex:");
00181     for (int i=0; i<p.row(); i++) {
00182       qmvaddstr(7+i,56, p.row2string(i)); qclrtoeol();
00183     }
00184     st = "Temperature: " + numStr(-tt); 
00185     qmvaddstr(12,55, st.c_str()); qclrtoeol();
00186     st = "Iteration: " + numStr(iter);
00187     qmvaddstr(13,55, st.c_str()); qclrtoeol();
00188 
00189 //     cerr << "Current simplex="; p.print();
00190 //     cerr << "values here="; y.print();
00191 
00192     qmove(24,8);
00193 
00194     ilo=0;
00195     ihi=1;
00196     // Whenever we "look at" a vertex, it gets a random thermal fluctuation.
00197     ynhi=ylo=y.get(0)+tt*log(ran1(&seed));
00198     yhi=y.get(1)+tt*log(ran1(&seed));
00199     if (ylo>yhi) {
00200       ihi=0;
00201       ilo=1;
00202       swap = ynhi; // ??
00203       ynhi=yhi;
00204       yhi=ylo;
00205       ylo=ynhi;  // OBS: skulle dette være den gamle værdi af ynhi?!?!?
00206       //      ylo = swap;
00207     }
00208     for (unsigned i=2; i<mpts; i++) {
00209       yt=y.get(i)+tt*log(ran1(&seed));      
00210       if (yt<=ylo) {
00211         ilo=i;
00212         ylo=yt;
00213       }
00214       if (yt>yhi) {
00215         ynhi=yhi;
00216         ihi=i;
00217         yhi=yt;
00218       } else if (yt>ynhi) {
00219         ynhi = yt;
00220       } 
00221     }
00222 
00223     // Compute the fractional range from highest to lowest and return if satisfactory.
00224     rtol = 2.0*fabs(yhi-ylo)/(fabs(yhi)+fabs(ylo));
00225     if (rtol<ftol || iter<0) {
00226       // If returning put best point and value in first slot
00227       swap=y.get(0);
00228       y.set(y.get(ilo),0);
00229       y.set(swap,ilo);
00230       for (unsigned n=0;n<ndim;n++) {
00231         swap = p.get(0,n);
00232         p.set(p.get(ilo,n),0,n);
00233         p.set(swap,ilo,n);
00234       }
00235       break;
00236     }
00237   
00238     iter -= 2;
00239     // Begin a new iteration. First extrapolate by a factor -1 through the face of the simplex
00240     // across from the high point, i.e. reflect the simplex from the high point.
00241     ytry = amotsa(ihi,yhi,-1.0);
00242     if (ytry<=ylo) {
00243       // Gives a result better than the best point, so try an additional extrapolation by a
00244       // factor 2
00245       ytry = amotsa(ihi,yhi,2.0);
00246       
00247     } else if (ytry>=ynhi) {
00248       // The reflected point is worse than the second-highest, so look for an intermediate
00249       // lower point, i.e., do a one-dimensional contraction.
00250       ysave=yhi;
00251       ytry = amotsa(ihi,yhi,0.5);
00252       
00253       if (ytry>=ysave) {
00254         // Can't seem to get rid of that high point. Better contract around the lowest (best) point.
00255         for (unsigned i=0;i<mpts;i++) {
00256           if (i != ilo) {
00257             for (unsigned j=0;j<ndim;j++) {
00258               double val = 0.5*(p.get(i,j)+p.get(ilo,j));
00259               psum.set(val,j);
00260               p.set(val,i,j);
00261             }
00262             y.set((*funk)(psum),i);
00263           }
00264         }
00265         iter -= ndim;
00266         calc_psum(); // Recompute psum    
00267       }
00268     } else ++(iter);  // Correct the evaluation count.
00269   }
00270   
00271 }
00272  
00273 void SimAnneal::optimize() {
00274   amebsa();
00275   iter = static_iter;
00276 }
00277 
00278 
00279 
00280 void SimAnneal::anneal() {
00281   double step = tt/10;
00282   double origtt = tt;
00283   double curtt = tt;
00284   for (unsigned i=0; i<nosteps; i++) {
00285     tt = curtt;
00286     p = newsimplex(pb);
00287     optimize();
00288     curtt -= step;
00289   }
00290   tt = origtt;
00291 }
00292 
00293 
00294 
00295 
00296 /////////////// NON-member function follows:
00297 
00298 
00299 matrix newsimplex(matrix const& x, double epsilon) {
00300   // P = (pb0,pb1,pb2) + lambda*e_i
00301 
00302   unsigned nn = x.row();
00303 
00304   matrix p(nn+1,nn);
00305   for (unsigned i=0; i<nn; i++) {
00306      p.set(x.get(i),0,i);
00307   }
00308 
00309   for (unsigned j=1; j<=nn; j++) {
00310     for (unsigned i=0; i<nn; i++) {
00311       p.set(x.get(i),j,i);
00312     }
00313     p.set(p.get(j,j-1)+epsilon,j,j-1);
00314   }
00315 
00316   return p;
00317 }
00318 
00319 
00320 
00321 #ifdef TEST
00322 
00323 // Functions to minimize:
00324 
00325 // Assumes input is a 2x1-matrix. f1(x,y) = x^2+y^2
00326 double f2(matrix const& x) {
00327   double xx = (x.get(0)-1);
00328   double xy = (x.get(1)-1);
00329   return xx*xx+xy*xy;
00330 }
00331 
00332 // Assumes input is a singleton matrix. Function with many local minima. Global minimum somewhere in [1.2, 1.5]
00333 double f1(matrix const& x) {
00334   double xx = x.get(0);
00335   return exp(-pow((xx-1.0),2.0))*sin(8*xx);
00336 }
00337 
00338 
00339 int main(int argc, char *argv[]) {
00340   try {
00341 
00342     qinit();
00343 
00344 //     matrix p(3,2);
00345 //     p.set(6,0,0);  p.set(1,0,1);
00346 //     p.set(2,1,0);  p.set(2,1,1);
00347 //     p.set(3,2,0);  p.set(3,2,1);
00348 
00349 //     SimAnneal sa(f2,p);
00350 
00351 
00352     Function FF(f1,1);
00353     matrix simplex(2);  // Sets simplex to the interval [6,7]
00354     simplex.set(6,0); simplex.set(7,1);
00355 
00356     /*
00357     SimAnneal(double (*f)(matrix), matrix simplex, 
00358             double temptr=0.01, int it=100, double tol=0.001, long s=12345);
00359     */
00360     std::cerr << std::endl;
00361 
00362     matrix minP_anneal, minP_simplex;
00363     double minV_anneal, minV_simplex;
00364 
00365     { // Demonstrates simulated annealing (SA)
00366       SimAnneal sa(&FF, simplex, 0.01, 500);
00367       sa.anneal();
00368       minP_anneal = sa.get_pb();
00369       minV_anneal = sa.get_yb();
00370     } // Destruct sa
00371 
00372     std::cerr << "balblablalblaba" << std::endl;
00373     { // Demonstrates simplex algorithm (temperature=0.0 in SA-algorithm)
00374       SimAnneal sa(&FF, simplex, 0.0, 2000);
00375       sa.optimize();
00376       minP_simplex = sa.get_pb();
00377       minV_simplex = sa.get_yb();
00378     } // Destruct sa
00379     
00380 
00381     std::string line = "";
00382     for (unsigned i=0; i<50; i++) 
00383       line += "-";
00384     line += "\n";
00385 
00386     qend();
00387 
00388 
00389     std::cerr << line;
00390     std::cerr << "Minimum found with SA-method " <<
00391       (minP_anneal.trans()).row2string(0) << std::endl; 
00392     std::cerr << "With function value " << minV_anneal << std::endl << std::endl;
00393 
00394     std::cerr << line;
00395     std::cerr << "Minimum found with Simplex-method " << 
00396       (minP_simplex.trans()).row2string(0) << std::endl; 
00397     std::cerr << "With function value " << minV_simplex << std::endl << std::endl;
00398 
00399     std::cerr << line;
00400 
00401     } catch(std::string const& err) {
00402     std::cerr << err << std::endl;
00403     return 1;
00404   }
00405   return 0;
00406 }
00407 
00408 
00409 
00410 #endif//TEST

Generated on Tue Feb 14 16:05:52 2006 for estfunc by doxygen 1.3.6