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