00001 #ifndef animal_minimizer_hpp
00002 #define animal_minimizer_hpp
00003 
00004 #include <iostream>
00005 #include <fstream>
00006 
00007 namespace animal {
00008 
00009 using std::ofstream;
00010 using std::cout;
00011 using std::endl;
00012 using std::flush;
00013 
00017 template <class ValueFunction,      
00018           class GradientFunction,   
00019           class Parameter,          
00020           class Real                
00021           >
00022 class FrprMinimizer 
00023 {
00024 public:
00025     ValueFunction& valueFunction;       
00026     GradientFunction& gradientFunction; 
00027     
00028     
00029     Real       mainTol;                 
00030     unsigned int mainMaxIter;             
00031     int restartFreq;                     
00032 private:
00033     int nitermod;                     
00034     bool followGivenDirection;          
00035 public: 
00036     
00037     Real linminTol;   
00038     int linminMaxIter;   
00039     Real linminEps;   
00040     Real previousStep; 
00041     
00042     
00043     bool nullGradient;     
00044     bool precisionReached; 
00045     bool maxStepReached;  
00046     Real cumulatedStep;  
00047     bool tooManyIterationsDbrent;  
00048     bool negativeStep;  
00049 
00050     static Real nullGradientThreshold(){ return 1.0e-9; }
00051 
00052     
00053     Real finalValue;      
00054     unsigned int nbIterations;       
00055     bool profiling;   
00056     Real dumpThreshold; 
00057     int nbValueEvaluations;  
00058     int nbGradientEvaluations;  
00059     
00060 public:
00061     
00063     FrprMinimizer( 
00064         ValueFunction& valueFunction,
00065         GradientFunction& gradientFunction,
00066         Real mainTol=0.01, int mainMaxIter=1,
00067         Real linminTol=0.01, int linminMaxIter=10
00068         , bool profiling=false
00069         , int restartFreq=1000000 
00070         , Real linminEps=1.0e-10
00071         , Real dumpThreshold = 1.0e-9
00072         ):
00073         valueFunction(valueFunction),
00074         gradientFunction(gradientFunction),
00075         mainTol(mainTol), mainMaxIter(mainMaxIter)
00076         , restartFreq(restartFreq)
00077         , followGivenDirection(false)
00078         , linminTol(linminTol), linminMaxIter(linminMaxIter)
00079         , linminEps(linminEps)
00080         , previousStep(0)
00081         , tooManyIterationsDbrent(false)
00082         , profiling(profiling)
00083         , dumpThreshold(dumpThreshold)
00084     {}
00085     
00087     void profile( bool b ){ profiling=b; }
00088     
00089     
00093     void operator () ( Parameter& param, Real maxStep )
00094     {
00095         
00096         init(animal::size(param));
00097 
00098         while (
00099             nbIterations<mainMaxIter && 
00100             !nullGradient && 
00101             !precisionReached &&
00102             !maxStepReached &&
00103             !negativeStep ) 
00104         {
00105             
00106             Real fp=(valueFunction)(param); nbValueEvaluations++;
00107             
00108             
00109             if( followGivenDirection )
00110             {
00111                 animal::v_eq(xi,initialDirection);
00112                 followGivenDirection = false;
00113                 
00114                 
00115                 
00116             }
00117             else {
00118                 gradientFunction(param,xi); nbGradientEvaluations++;            
00119             }
00120             
00121             
00122             if( nitermod == 0 )  
00123             {
00124                 
00125                 for (std::size_t j=0;j<size;j++) {
00126                     g[j] = -xi[j];
00127                     xi[j]=h[j]=g[j];
00128                 }
00129             }
00130             else    
00131             {
00132                 Real dgg=0.0, gg=0.0;
00133                 
00134                 for (std::size_t j=0;j<size;j++) {
00135                     gg += g[j]*g[j];
00136                     dgg += (xi[j]+g[j])*xi[j];
00137                 }
00138                 
00139                 if ( gg <= nullGradientThreshold() )
00140                     nullGradient = true;
00141                 else {
00142                     Real gam=dgg/gg;
00143                     for (std::size_t j=0;j<size;j++) {
00144                         g[j] = -xi[j];
00145                         xi[j]=h[j]=g[j]+gam*h[j];
00146                     }
00147                 }
00148             }
00149 
00150             
00151             Real step = linemin(param,xi,&finalValue,maxStep,fp,linminTol);
00152             previousStep = step; 
00153             if (step<0) {
00154                 step=0;
00155                 negativeStep = true;
00156             }
00157             else if( (cumulatedStep + step) > maxStep ){ 
00158                 step = maxStep - cumulatedStep;
00159                 maxStepReached = true;
00160             }
00161             
00162             
00163             for (unsigned int j=0;j<size;j++) {
00164                 xi[j] *= step;
00165                 param[j] += xi[j];
00166             }
00167             cumulatedStep += step;
00168 
00169             
00170             if (2.0*Rfabs(finalValue-fp) <= mainTol*(Rfabs(finalValue)+Rfabs(fp)+linminEps))
00171                 precisionReached = true;
00172             
00173             
00174             nbIterations++;
00175             nitermod++;
00176             if( restartFreq!=0 ) 
00177                 nitermod = nitermod % restartFreq;
00178         }
00179         
00180         if( profiling ) printLog(maxStep);
00181     }
00182     
00186     void initSearchDirection( const Parameter& point )
00187     {
00188         followGivenDirection = true;
00189         gradientFunction(point); nbValueEvaluations++;
00190         initialDirection.resize(animal::size(point));
00191         xi.resize(animal::size(point));
00192         gradientFunction(point,initialDirection); nbGradientEvaluations++;
00193     }
00194     
00196     void printFunction( Parameter& p )
00197     {
00198         init( animal::size(p) );
00199         xi.resize( animal::size(p) );
00200         cout << "function value: " << valueFunction(p) << flush << endl; 
00201         nbValueEvaluations++;
00202         gradientFunction(p,xi); nbGradientEvaluations++;
00203         cout << "gradient: \n" << v_output(xi) << flush << endl;
00204     }
00205 
00206 
00207     
00208 private:
00209 
00211     void printLog( Real ) 
00212     {
00213 
00214         std::cerr<< nbValueEvaluations << " function evaluations " << endl;
00215         std::cerr<< nbGradientEvaluations << " gradient evaluations " << endl;
00216         if( maxStepReached ) std::cerr<< "max step reached " << endl;
00217         if( precisionReached ) std::cerr<< "precision reached " << endl;
00218         if( nullGradient ) std::cerr<< "nullGradient reached " << endl;
00219         if( nbIterations >= mainMaxIter ) std::cerr<< "maxiter reached " << endl;
00220         if( negativeStep ) std::cerr<< "negative step encountered " << endl;
00221         std::cerr << "minimization terminated in " << nbIterations << " iterations, endValue = " << finalValue << endl;
00222         if( cumulatedStep<0 ) cerr<<"*********** negative step ************** " << endl;
00223         std::cerr << "cumulated step = " << sqrt(cumulatedStep) << endl;
00224         valueFunction.display();
00225         
00226             
00227         std::cerr << endl;
00228     }
00229     
00231     void init(int s) 
00232     {
00233         size=s;
00234         g.resize(size);
00235         h.resize(size);
00236         if( !followGivenDirection ) xi.resize(size);
00237         pcom.resize(size);
00238         xicom.resize(size);
00239         xt.resize(size);
00240         df.resize(size);
00241         initialDirection.resize(size);
00242 
00243         maxStepReached = false;
00244         precisionReached = false;
00245         nullGradient = false;
00246         tooManyIterationsDbrent = false;
00247         negativeStep = false;
00248         nbIterations = 0;
00249         nitermod = 0;
00250         cumulatedStep = 0.0;
00251         
00252         nbValueEvaluations = nbGradientEvaluations = 0;
00253     }
00254     
00255 
00256     
00257     unsigned int size;  
00258     Parameter g;  
00259     Parameter h;  
00260     Parameter xi;  
00261     Parameter pcom;  
00262     Parameter xicom;  
00263     Parameter xt;  
00264     Parameter df;  
00265     Parameter initialDirection;  
00266 
00268 Real f1dim (Real x)
00269 {
00270     if( profiling ) cerr << "compute f1dim(" << x<< ")" << endl;
00271     for (unsigned int j=0;j<size;j++) 
00272         xt[j]=pcom[j]+x*xicom[j];
00273      nbValueEvaluations++;
00274      return (valueFunction)(xt);
00275 }
00276 
00278 Real df1dim (Real x)
00279 {
00280     unsigned int j;
00281     Real df1=0;
00282 
00283     for (j=0;j<size;j++) 
00284         xt[j]=pcom[j]+x*xicom[j];
00285     (gradientFunction)(xt,df); nbGradientEvaluations++;
00286     for (j=0;j<size;j++) 
00287         df1 += df[j]*xicom[j];
00288     return df1;
00289 }
00290 
00292 Real linemin (
00293     Parameter& p, 
00294     Parameter& xi, 
00295     Real *fret,
00296     Real maxStep,
00297     Real finit,
00298     Real TOL = 2.0e-4
00299 )
00300 {
00301     unsigned int j;
00302     Real xx,xmin,fx,fb,fa,bx,ax;
00303         
00304     for (j=0;j<size;j++) {   
00305         pcom[j]=p[j];
00306         xicom[j]=xi[j];
00307     }
00308     ax=0.0;     fa=finit;
00309     xx=maxStep; fx=f1dim(xx);
00310     
00311     if(profiling){
00312         
00313         
00314         
00315     }
00316 
00317     
00318     
00319     
00320     
00321     
00322     
00323     Real fcoherent = f1dim(previousStep); 
00324     if( fcoherent<fa && fcoherent< fx ) 
00325     {                                       
00326         bx=xx; fb=fx;
00327         xx=previousStep; fx=fcoherent;
00328         if(profiling) cerr << "temporal coherency succeeded" << endl;
00329     }
00330     else 
00331     mnbrak(&ax,&xx,&bx,&fa,&fx,&fb);
00332 
00333     if( profiling ) {
00334         cout<<"bracketed: ("<<ax<<", "<<fa
00335         <<"), ("<<xx<<", "<<fx
00336         <<"), ("<<bx<<", "<<fb << ")" << endl;
00337     }
00338 
00339     
00340     *fret= dbrent(ax,xx,bx,TOL,&xmin); 
00341     
00342     
00343     if( profiling )
00344         cout<<"dbrent finds the minimum at (" << xmin <<","<<*fret<<")" <<endl;
00345 
00346     return xmin;
00347 }
00348     
00349 
00355 void mnbrak
00356 (
00357     Real *ax, Real *bx, Real *cx,
00358     Real *fa, Real *fb, Real *fc
00359 )
00360 {
00361     const Real GOLD = 1.618034;
00362     const Real GLIMIT = 100.0;
00363     const Real TINY = 1.0e-20;
00364     const Real TWO = 2.0;
00365     #define SHFT(a,b,c,d) (a)=(b);(b)=(c);(c)=(d);
00366     #define SIGN(a,b) ((b) >= 0.0 ? Rfabs(a) : -Rfabs(a))
00367 
00368 
00369     int nbfunc=nbValueEvaluations;     
00370     int nbgrad=nbGradientEvaluations;
00371 
00372     Real ulim,u,r,q,fu,dum;
00373 
00374     
00375     
00376     if (*fb > *fa) {
00377         SHFT(dum,*ax,*bx,dum)
00378         SHFT(dum,*fb,*fa,dum)
00379     }
00380     *cx=(*bx)+GOLD*(*bx-*ax);
00381     *fc=f1dim(*cx);
00382     while (*fb > *fc) {
00383         r=(*bx-*ax)*(*fb-*fc);
00384         q=(*bx-*cx)*(*fb-*fa);
00385         u=(*bx)-((*bx-*cx)*q-(*bx-*ax)*r)/(TWO*SIGN(MAX(Rfabs(q-r),TINY),q-r));
00386         ulim=(*bx)+GLIMIT*(*cx-*bx);
00387         if ((*bx-u)*(u-*cx) > 0.0) {
00388             fu=f1dim(u);
00389             if (fu < *fc) {
00390                 *ax=(*bx);
00391                 *bx=u;
00392                 *fa=(*fb);
00393                 *fb=fu;
00394                 if( profiling ) cerr <<"mnbrak: " << nbValueEvaluations-nbfunc << " value evaluations, " << nbGradientEvaluations-nbgrad << " gradient evaluations" << endl;
00395                 return;
00396             } else if (fu > *fb) {
00397                 *cx=u;
00398                 *fc=fu;
00399                 if( profiling ) cerr <<"mnbrak: " << nbValueEvaluations-nbfunc << " value evaluations, " << nbGradientEvaluations-nbgrad << " gradient evaluations" << endl;
00400                 return;
00401             }
00402             u=(*cx)+GOLD*(*cx-*bx);
00403             fu=f1dim(u);
00404         } else if ((*cx-u)*(u-ulim) > 0.0) {
00405             fu=f1dim(u);
00406             if (fu < *fc) {
00407                 SHFT(*bx,*cx,u,*cx+GOLD*(*cx-*bx))
00408                 SHFT(*fb,*fc,fu,f1dim(u))
00409             }
00410         } else if ((u-ulim)*(ulim-*cx) >= 0.0) {
00411             u=ulim;
00412             fu=f1dim(u);
00413         } else {
00414             u=(*cx)+GOLD*(*cx-*bx);
00415             fu=f1dim(u);
00416         }
00417         SHFT(*ax,*bx,*cx,u)
00418         SHFT(*fa,*fb,*fc,fu)
00419     }
00420     #undef SHFT 
00421     #undef SIGN 
00422     if( profiling ) cerr <<"mnbrak: " << nbValueEvaluations-nbfunc << " value evaluations, " << nbGradientEvaluations-nbgrad << " gradient evaluations" << endl;
00423     
00424 }
00425 
00427 template <class T>
00428 inline T 
00429 MAX( const T& a, const T& b ) { return a > b ? a : b; }
00430 
00432 template <class T>
00433 inline T 
00434 SIGN(const T& a, const T& b) { return ((b) >= 0.0 ? Rfabs(a) : -Rfabs(a)); }
00435 
00436 
00448 Real dbrent (
00449     Real ax, Real bx, Real cx, 
00450     
00451     Real tol, 
00452     Real *xmin,
00453     Real ZEPS = 1.0e-10
00454 )
00455 {
00456     #define MOV3(a,b,c, d,e,f) (a)=(d);(b)=(e);(c)=(f);
00457     int ok1,ok2;
00458     Real a,b,d,d1,d2,du,dv,dw,dx,e=0.0;
00459     Real fu,fv,fw,fx,olde,tol1,tol2,u,u1,u2,v,w,x,xm;
00460 
00461     int nbfunc=nbValueEvaluations;     
00462     int nbgrad=nbGradientEvaluations;
00463 
00464     *xmin = bx; 
00465     a=(ax < cx ? ax : cx);
00466     b=(ax > cx ? ax : cx);
00467     x=w=v=bx;
00468     fw=fv=fx=f1dim(x);
00469     dw=dv=dx=df1dim(x);
00470     
00471     
00472     Real defaultValue=fx;
00473     for (int iter=1;iter<=linminMaxIter;iter++) 
00474     {
00475         xm=0.5*(a+b);
00476         tol1=tol*Rfabs(x)+ZEPS;
00477         tol2=2.0*tol1;
00478         if (Rfabs(x-xm) <= (tol2-0.5*(b-a))) {
00479             *xmin=x;    
00480             if( profiling ) cerr <<"dbrent: " << nbValueEvaluations-nbfunc << " value evaluations, " << nbGradientEvaluations-nbgrad << " gradient evaluations" << endl;
00481             return fx;
00482         }
00483         
00484         if (Rfabs(e) > tol1) {
00485             d1=2.0*(b-a);
00486             d2=d1;
00487             if (dw != dx) d1=(w-x)*dx/(dx-dw);
00488             if (dv != dx) d2=(v-x)*dx/(dx-dv);
00489             u1=x+d1;
00490             u2=x+d2;
00491             ok1 = (a-u1)*(u1-b) > 0.0 && dx*d1 <= 0.0;
00492             ok2 = (a-u2)*(u2-b) > 0.0 && dx*d2 <= 0.0;
00493             olde=e;
00494             e=d;
00495             if (ok1 || ok2) {
00496                 if (ok1 && ok2)
00497                     d=(Rfabs(d1) < Rfabs(d2) ? d1 : d2);
00498                 else if (ok1)
00499                     d=d1;
00500                 else
00501                     d=d2;
00502                 if (Rfabs(d) <= Rfabs(0.5*olde)) {
00503                     u=x+d;
00504                     if (u-a < tol2 || b-u < tol2)
00505                         d=SIGN(tol1,xm-x);
00506                 } else {
00507                     d=0.5*(e=(dx >= 0.0 ? a-x : b-x));
00508                 }
00509             } else {
00510                 d=0.5*(e=(dx >= 0.0 ? a-x : b-x));
00511             }
00512         } else {
00513             d=0.5*(e=(dx >= 0.0 ? a-x : b-x));
00514         }
00515         
00516         if (Rfabs(d) >= tol1) {
00517             u=x+d;
00518             fu=f1dim(u);
00519         } else {
00520             u=x+SIGN(tol1,d);
00521             fu=f1dim(u);
00522             if (fu > fx) {
00523                 *xmin=x;
00524                 if( profiling ) cerr <<"dbrent: " << nbValueEvaluations-nbfunc << " value evaluations, " << nbGradientEvaluations-nbgrad << " gradient evaluations" << endl;
00525                 return fx;
00526             }
00527         }
00528         
00529         du=df1dim(u);
00530         if (fu <= fx) {
00531             if (u >= x) a=x; else b=x;
00532             MOV3(v,fv,dv, w,fw,dw)
00533             MOV3(w,fw,dw, x,fx,dx)
00534             MOV3(x,fx,dx, u,fu,du)
00535         } else {
00536             if (u < x) a=u; else b=u;
00537             if (fu <= fw || w == x) {
00538                 MOV3(v,fv,dv, w,fw,dw)
00539                 MOV3(w,fw,dw, u,fu,du)
00540             } else if (fu < fv || v == x || v == w) {
00541                 MOV3(v,fv,dv, u,fu,du)
00542             }
00543         }
00544     }
00545     if( profiling ) cerr <<"Too many iterations in routine dbrent: " << nbValueEvaluations-nbfunc << " value evaluations, " << nbGradientEvaluations-nbgrad << " gradient evaluations" << endl;
00546     tooManyIterationsDbrent = true;
00547     return defaultValue;
00548     #undef MOV3
00549 }
00550 
00553 void dumpPotential ( Real stepMin, Real stepMax, int resolution, const char* filename = "potential.plo" )
00554 {
00555     ofstream out(filename);
00556     if(!out) {
00557         cerr << "could not open " << filename << "for writing" << endl;
00558         return;
00559     }
00560     
00561     Real delta = (stepMax-stepMin)/resolution;
00562     for( Real x=stepMin; x<stepMax; x+=delta ){
00563         out << x << "\t" << f1dim(x) << endl;
00564     }
00565     valueFunction.display(); 
00566 }
00567 
00569 Real Rfabs( Real a ) { return a<0 ? -a : a; }
00570 
00571 
00573     
00574     
00575     
00576     
00577     
00578     
00579     
00580     
00581 
00583     
00584     
00586 
00587     
00588     
00589     
00590     
00591     
00592     
00593 
00594     
00595     
00596     
00597     
00598     
00599     
00600     
00603     
00604     
00605     
00606     
00607     
00609     
00610 
00611 
00613     
00614     
00615     
00616     
00617     
00618 
00619     
00620 
00621     
00622     
00623     
00624     
00625     
00626     
00627     
00628     
00629     
00630     
00631     
00632     
00633     
00634     
00635     
00636     
00637     
00638     
00647 
00648     
00649     
00650     
00651     
00652     
00653     
00654     
00655     
00656     
00657     
00658     
00661     
00662     
00663     
00664     
00665     
00666     
00667     
00668     
00688     
00689     
00690 
00691     
00692     
00694     
00695 
00696 
00721 };
00722 
00723 }
00724 #endif