core/vnl/algo/vnl_brent_minimizer.cxx
Go to the documentation of this file.
00001 // This is core/vnl/algo/vnl_brent_minimizer.cxx
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005 //:
00006 // \file
00007 #include "vnl_brent_minimizer.h"
00008 
00009 #include <vcl_cmath.h>
00010 #include <vcl_iostream.h>
00011 #include <vcl_algorithm.h>
00012 #include <vcl_cassert.h>
00013 
00014 #include <vnl/vnl_vector.h>
00015 #include <vnl/algo/vnl_bracket_minimum.h>
00016 
00017 static const double GOLDEN_RATIO = 1.618033988749894848; // = 0.5*(vcl_sqrt(5)-1);
00018 static const double COMPL_GOLD   = 0.381966011250105152; // = 0.5*(3-vcl_sqrt(5));
00019 static const double EPS          = 1e-8;
00020 
00021 // Wrapper to make it easy to evaluate the cost function
00022 class vnl_brent_minimizer_func
00023 {
00024   vnl_vector<double> v;
00025   vnl_cost_function* f;
00026  public:
00027   vnl_brent_minimizer_func(vnl_cost_function& fn)
00028     { f=&fn; v.set_size(1); }
00029 
00030   double operator()(double x) { v[0]=x; return f->f(v); }
00031 };
00032 
00033 vnl_brent_minimizer::vnl_brent_minimizer(vnl_cost_function& functor)
00034 {
00035   f_=&functor;
00036   set_x_tolerance(1e-6);
00037 }
00038 
00039 vnl_brent_minimizer::~vnl_brent_minimizer()
00040 {
00041 }
00042 
00043 //: Find the minimum x of f(x) within a<= x <= c using pure golden section
00044 // The minimum x is the return value.
00045 // You need to provide a bracket for the minimum (a<b<c s.t. f(a)>f(b)<f(c).
00046 // The tolerance can be set using prior call to set_x_tolerance(tol).
00047 // Use f_at_last_minimum() to get function evaluation at the returned minima.
00048 double vnl_brent_minimizer::minimize_golden(double a, double b, double c,
00049                                             double fa, double fb, double fc)
00050 {
00051   // Set up object to evaluate function as f(x)
00052   // Note that *f_ takes a vector input - f converts a scalar to a vector
00053   vnl_brent_minimizer_func f(*f_);
00054 
00055   double m = 0.5*(a+c);  // Midpoint of (a,c)
00056   double tol = EPS*vcl_fabs(b)+xtol;  // Tolerance to use
00057   double tol2 = 2*tol;                // Twice the tolerance
00058 
00059   double u,fu;
00060 
00061   // Loop until bracket sufficiently small
00062   while (vcl_fabs(b-m)>(tol2-0.5*(c-a)))
00063   {
00064     double e = (b<m?c:a) - b;
00065     double d = COMPL_GOLD * e;
00066 
00067     // Do not evaluate too close to current x
00068     if (vcl_fabs(d)>=tol)
00069       u = b+d;
00070     else
00071     {
00072       if (d>0) u=b+tol;
00073       else     u=b-tol;
00074     }
00075 
00076     if (u<a) vcl_cout<<"u<a!"<<vcl_endl;
00077     if (u>c) vcl_cout<<"u>c!"<<vcl_endl;
00078 
00079     // Perform the function evaluation
00080     fu = f(u);
00081 
00082     // Update the bounds
00083     if (fu<fb)
00084     {
00085       // u becomes central point
00086       if (u<b)
00087       {
00088         // Bracket becomes (a,u,b)
00089         c=b; fc=fb;
00090         b=u; fb=fu;
00091       }
00092       else
00093       {
00094         // Bracket becomes (b,u,c)
00095         a=b; fa=fb;
00096         b=u; fb=fu;
00097       }
00098     }
00099     else
00100     {
00101       // f(b)<f(u), so b remains central point
00102       if (u<b)
00103       {
00104         // Bracket becomes (u,b,c)
00105         a=u; fa=fu;
00106       }
00107       else
00108       {
00109         // Bracket becomes (a,b,u)
00110         c=u; fc=fu;
00111       }
00112     }
00113 
00114     // Recompute mid-point and suitable tolerances
00115     m = 0.5*(a+c);  // Midpoint of (a,c)
00116     tol = EPS*vcl_fabs(b)+xtol;  // Tolerance to use
00117     tol2 = 2*tol;                // Twice the tolerance
00118 
00119     vcl_cout<<"Bracket: ("<<a<<','<<b<<','<<c<<") width: "<<c-a<<vcl_endl;
00120   }
00121 
00122   f_at_last_minimum_ = fb;
00123   return b;
00124 }
00125 
00126 //: Find the minimum value of f(x) within a<= x <= c.
00127 // The minimum x is the return value.
00128 // You need to provide a bracket for the minimum (a<b<c s.t. f(a)>f(b)<f(c).
00129 // The tolerance can be set using prior call to set_x_tolerance(tol).
00130 // Use f_at_last_minimum() to get function evaluation at the returned minima.
00131 double vnl_brent_minimizer::minimize_given_bounds(double ax, double bx, double cx)
00132 {
00133   vnl_brent_minimizer_func f(*f_);
00134   double fb = f(bx);
00135 
00136   return minimize_given_bounds_and_one_f(ax,bx,cx,fb);
00137 }
00138 
00139 //: Find the minimum value of f(x) within a<= x <= c.
00140 // The minimum x is the return value.
00141 // You need to provide a bracket for the minimum (a<b<c s.t. f(a)>f(b)<f(c),
00142 // and the known value at b (fb=f(b)).
00143 // The tolerance can be set using prior call to set_x_tolerance(tol).
00144 // Use f_at_last_minimum() to get function evaluation at the returned minima.
00145 double vnl_brent_minimizer::minimize_given_bounds_and_one_f(double ax, double bx, double cx,
00146                                                             double fb)
00147 {
00148   // Check that the bracket is valid
00149   assert(ax<bx);
00150   assert(bx<cx);
00151 
00152   // Set up object to evaluate function as f(x)
00153   // Note that *f_ takes a vector input - f converts a scalar to a vector
00154   vnl_brent_minimizer_func f(*f_);
00155 
00156   double x=bx;  // Current best estimate of minimum
00157   double w=x;  // Next best point
00158   double v=w;  // Third best point
00159   double u;  // Most recently evaluated point
00160 
00161   // Function evaluations at these points
00162   double fx = fb;
00163   double fw = fx;
00164   double fv = fw;
00165   double fu;
00166 
00167   double m = 0.5*(ax+cx);  // Midpoint of (a,c)
00168   double tol = EPS*vcl_fabs(x)+xtol;  // Tolerance to use
00169   double tol2 = 2*tol;                // Twice the tolerance
00170 
00171   double d=0.0;  // Record of last p/q
00172   double e=0.0;  // Value of p/q in 2nd-to-last cycle of parabolic interp.
00173 
00174   // Loop until bracket sufficiently small
00175   while (vcl_fabs(x-m)>(tol2-0.5*(cx-ax)))
00176   {
00177     // Variables for parabolic interpolation
00178     double p=0.0,q=0.0,r=0.0;
00179     if (vcl_fabs(e)>tol)
00180     {
00181       // Fit a parabola
00182       r = (x-w)*(fx-fv);
00183       q = (x-v)*(fx-fw);
00184       p = (x-v)*q - (x-w)*r;
00185       q = 2*(q-r);
00186       if (q>0) p*=-1.0; else q*=-1.0;  // q always positive
00187       r = e; e = d;
00188     }
00189 
00190     if (vcl_fabs(p)<vcl_fabs(0.5*q*r) &&
00191         p>(q*(ax-x)) && p<(q*(cx-x)) )  // So that ax<x+p/q<cx
00192     {
00193       // Parabolic interpolation - set u to estimate of minimum
00194       d = p/q;
00195       u = x + d;
00196 
00197       // u must not be too close to ax or cx
00198       if (u-ax<tol2 || cx-u<tol2) d = (x<m?tol:-tol);
00199     }
00200     else
00201     {
00202       // Golden section step
00203       e = (x<m?cx:ax) - x;
00204       d = COMPL_GOLD * e;
00205     }
00206 
00207     // Do not evaluate too close to current x
00208     if (vcl_fabs(d)>=tol)
00209       u = x+d;
00210     else
00211     {
00212       if (d>0) u=x+tol;
00213       else     u=x-tol;
00214     }
00215 
00216     // Perform the function evaluation
00217     fu = f(u);
00218 
00219     // Update our current bounds
00220     if (fu<=fx)
00221     {
00222       if (u<x) cx=x; else ax=x;
00223       v=w; fv=fw;
00224       w=x; fw=fx;
00225       x=u; fx=fu;
00226     }
00227     else
00228     {
00229       if (u<x) ax=u; else cx=u;
00230       if (fu<=fw || w==x)
00231       {
00232         v=w; fv=fw;
00233         w=u; fw=fu;
00234       }
00235       else if (fu<=fv || v==x || v==w) { v=u; fv=fu; }
00236     }
00237 
00238     // Recompute mid-point and suitable tolerances
00239     m = 0.5*(ax+cx);  // Midpoint of (a,c)
00240     tol = EPS*vcl_fabs(x)+xtol;  // Tolerance to use
00241     tol2 = 2*tol;                // Twice the tolerance
00242   }
00243 
00244   f_at_last_minimum_ = fx;
00245   return x;
00246 }
00247 
00248 //: Find the minimum value of f(x) within a<= x <= c.
00249 // The minimum x is the return value.
00250 // You need to provide a bracket for the minimum (a<b<c s.t. f(a)>f(b)<f(c)),
00251 // and the values fa=f(a), fb=f(b), fc=f(c). This avoids recalculating
00252 // them if you have them already.
00253 // The tolerance can be set using prior call to set_x_tolerance(tol).
00254 // Use f_at_last_minimum() to get function evaluation at the returned minima.
00255 double vnl_brent_minimizer::minimize_given_bounds_and_all_f(double ax, double bx, double cx,
00256                                                             double fa, double fb, double fc)
00257 {
00258   // Check that the bracket is valid
00259   assert(ax<bx);
00260   assert(bx<cx);
00261   assert(fb<fa);
00262   assert(fb<fc);
00263 
00264   // Set up object to evaluate function as f(x)
00265   // Note that *f_ takes a vector input - f converts a scalar to a vector
00266   vnl_brent_minimizer_func f(*f_);
00267 
00268   double x=bx;  // Current best estimate of minimum
00269   double fx = fb;
00270   double w, fw;  // Next best point
00271   double v, fv;  // Third best point
00272 
00273   if (fa<fc) { w=ax; fw=fa;  v=cx; fv=fc; }
00274   else       { w=cx; fw=fc;  v=ax; fv=fa; }
00275 
00276   double u, fu;  // Most recently evaluated point and its value
00277 
00278   double m = 0.5*(ax+cx);  // Midpoint of (a,c)
00279   double tol = EPS*vcl_fabs(x)+xtol;  // Tolerance to use
00280   double tol2 = 2*tol;                // Twice the tolerance
00281 
00282   double d=vcl_min(bx-ax,cx-bx);  // Record of last p/q
00283   double e=vcl_max(bx-ax,cx-bx);  // Value of p/q in 2nd-to-last cycle of parabolic interp.
00284 
00285   // Loop until bracket sufficiently small
00286   while (vcl_fabs(x-m)>(tol2-0.5*(cx-ax)))
00287   {
00288     // Variables for parabolic interpolation
00289     double p=0.0,q=0.0,r=0.0;
00290     if (vcl_fabs(e)>tol)
00291     {
00292       // Fit a parabola
00293       r = (x-w)*(fx-fv);
00294       q = (x-v)*(fx-fw);
00295       p = (x-v)*q - (x-w)*r;
00296       q = 2*(q-r);
00297       if (q>0) p*=-1.0; else q*=-1.0;  // q always positive
00298       r = e; e = d;
00299     }
00300 
00301     if (vcl_fabs(p)<vcl_fabs(0.5*q*r) &&
00302         p>(q*(ax-x)) && p<(q*(cx-x)) )  // So that ax<x+p/q<cx
00303     {
00304       // Parabolic interpolation - set u to estimate of minimum
00305       d = p/q;
00306       u = x + d;
00307 
00308       // u must not be too close to ax or cx
00309       if (u-ax<tol2 || cx-u<tol2) d = (x<m?tol:-tol);
00310     }
00311     else
00312     {
00313       // Golden section step
00314       e = (x<m?cx:ax) - x;
00315       d = COMPL_GOLD * e;
00316     }
00317 
00318     // Do not evaluate too close to current x
00319     if (vcl_fabs(d)>=tol)
00320       u = x+d;
00321     else
00322     {
00323       if (d>0) u=x+tol;
00324       else     u=x-tol;
00325     }
00326 
00327     // Perform the function evaluation
00328     fu = f(u);
00329 
00330     // Update our current bounds
00331     if (fu<=fx)
00332     {
00333       if (u<x) cx=x; else ax=x;
00334       v=w; fv=fw;
00335       w=x; fw=fx;
00336       x=u; fx=fu;
00337     }
00338     else
00339     {
00340       if (u<x) ax=u; else cx=u;
00341       if (fu<=fw || w==x)
00342       {
00343         v=w; fv=fw;
00344         w=u; fw=fu;
00345       }
00346       else if (fu<=fv || v==x || v==w) { v=u; fv=fu; }
00347     }
00348 
00349     // Recompute mid-point and suitable tolerances
00350     m = 0.5*(ax+cx);  // Midpoint of (a,c)
00351     tol = EPS*vcl_fabs(x)+xtol;  // Tolerance to use
00352     tol2 = 2*tol;                // Twice the tolerance
00353   }
00354 
00355   f_at_last_minimum_ = fx;
00356   return x;
00357 }
00358 
00359 double vnl_brent_minimizer::minimize(double x)
00360 {
00361   double ax=x-1.0;
00362   double cx=x+1.0;
00363   double fa,fx,fc;
00364   vnl_bracket_minimum(*f_, ax,x,cx, fa,fx,fc);
00365 
00366   return minimize_given_bounds_and_all_f(ax,x,cx,  fa,fx,fc);
00367 }