core/vnl/algo/vnl_amoeba.cxx
Go to the documentation of this file.
00001 // This is core/vnl/algo/vnl_amoeba.cxx
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005 //:
00006 // \file
00007 // \author Andrew W. Fitzgibbon, Oxford RRG
00008 // \date   23 Oct 97
00009 //-----------------------------------------------------------------------------
00010 
00011 #include "vnl_amoeba.h"
00012 
00013 #include <vcl_cstdio.h> // for sprintf()
00014 #include <vcl_cstdlib.h> // for vcl_qsort
00015 #include <vcl_iostream.h>
00016 #include <vcl_vector.h>
00017 #include <vnl/vnl_math.h>
00018 #include <vnl/vnl_vector.h>
00019 #include <vnl/vnl_cost_function.h>
00020 #include <vnl/vnl_least_squares_function.h>
00021 
00022 bool vnl_amoeba::default_verbose = false;
00023 
00024 vnl_amoeba::vnl_amoeba(vnl_cost_function& f)
00025   : fptr(&f), num_evaluations_(0)
00026 {
00027   verbose = default_verbose;
00028   maxiter = f.get_number_of_unknowns() * 200;
00029   X_tolerance = 1e-8;
00030   F_tolerance = 1e-4;
00031   relative_diameter = 0.05;
00032 }
00033 
00034 
00035 struct vnl_amoebaFit : public vnl_amoeba
00036 {
00037   int cnt;
00038 
00039   vnl_amoebaFit(vnl_amoeba& a): vnl_amoeba(a) {
00040     cnt = 0;
00041   }
00042 
00043   //: Initialise the simplex given one corner, x (scale each element to get other corners)
00044   void set_up_simplex_relative(vcl_vector<vnl_amoeba_SimplexCorner>& simplex,
00045                                const vnl_vector<double>& x);
00046 
00047   //: Initialise the simplex given one corner, x and displacements of others
00048   void set_up_simplex_absolute(vcl_vector<vnl_amoeba_SimplexCorner>& simplex,
00049                                const vnl_vector<double>& x,
00050                                const vnl_vector<double>& dx);
00051 
00052   //: Perform optimisation.  Start simplex defined by scaling elements of x
00053   void amoeba(vnl_vector<double>& x);
00054 
00055   //: Perform optimisation.  Start simplex defined by adding dx[i] to each x[i]
00056   void amoeba(vnl_vector<double>& x, const vnl_vector<double>& dx);
00057 
00058   //: Perform optimisation, given simplex to start
00059   void amoeba(vnl_vector<double>& x, vcl_vector<vnl_amoeba_SimplexCorner>& simplex);
00060 
00061   double f(const vnl_vector<double>& x) {
00062     return fptr->f(x);
00063   }
00064 
00065   void set_corner(vnl_amoeba_SimplexCorner * s,
00066                   const vnl_vector<double>& v)
00067   {
00068     s->v = v;
00069     s->fv = f(v);
00070     cnt++;
00071   }
00072   void set_corner_a_plus_bl(vnl_amoeba_SimplexCorner * s,
00073                             const vnl_vector<double>& vbar,
00074                             const vnl_vector<double>& v,
00075                             double lambda)
00076   {
00077     s->v = (1 - lambda) * vbar + lambda * v;
00078     s->fv = f(s->v);
00079     cnt++;
00080   }
00081 };
00082 
00083 int vnl_amoeba_SimplexCorner::compare(vnl_amoeba_SimplexCorner const& s1,
00084                                       vnl_amoeba_SimplexCorner const& s2)
00085 {
00086   return vnl_math_sgn(s1.fv - s2.fv);
00087 }
00088 
00089 #ifdef VCL_SUNPRO_CC
00090 extern "C"
00091 #else
00092 static
00093 #endif
00094 int compare_aux(const void * s1, const void * s2)
00095 {
00096   return vnl_amoeba_SimplexCorner::compare(*(const vnl_amoeba_SimplexCorner*)s1,
00097                                            *(const vnl_amoeba_SimplexCorner*)s2);
00098 }
00099 
00100 static
00101 void sort_simplex(vcl_vector<vnl_amoeba_SimplexCorner>& simplex)
00102 {
00103   vcl_qsort(&simplex[0], simplex.size(), sizeof simplex[0], compare_aux);
00104 }
00105 
00106 static
00107 double maxabsdiff(const vnl_vector<double>& a, const vnl_vector<double>& b)
00108 {
00109   double v = 0;
00110   for (unsigned i = 0; i < a.size(); ++i) {
00111     double ad = vnl_math_abs(a[i] - b[i]);
00112     if (ad > v)
00113       v = ad;
00114   }
00115   return v;
00116 }
00117 
00118 static
00119 double sorted_simplex_fdiameter(const vcl_vector<vnl_amoeba_SimplexCorner>& simplex)
00120 {
00121   return simplex[simplex.size()-1].fv - simplex[0].fv;
00122 }
00123 
00124 #if 0
00125 static
00126 double simplex_fdiameter(const vcl_vector<vnl_amoeba_SimplexCorner>& simplex)
00127 {
00128   // simplex assumed sorted, so fdiam is n - 0
00129   double max = 0;
00130   for (unsigned i = 1; i < simplex.size(); i++) {
00131     double thismax = vnl_math_abs(simplex[0].fv - simplex[i].fv);
00132     if (thismax > max)
00133       max = thismax;
00134   }
00135   return max;
00136 }
00137 #endif
00138 
00139 static
00140 double simplex_diameter(const vcl_vector<vnl_amoeba_SimplexCorner>& simplex)
00141 {
00142   double max = 0;
00143   for (unsigned i = 0; i < simplex.size() - 1; i++) {
00144     double thismax = maxabsdiff(simplex[i].v, simplex[i+1].v);
00145     if (thismax > max)
00146       max = thismax;
00147   }
00148   return max;
00149 }
00150 
00151 
00152 vcl_ostream& operator<<(vcl_ostream& s, const vnl_amoeba_SimplexCorner& simplex)
00153 {
00154   s << 'S' << simplex.fv << ' ';
00155   return s;
00156 }
00157 
00158 vcl_ostream& operator<<(vcl_ostream& s, const vcl_vector<vnl_amoeba_SimplexCorner>& simplex)
00159 {
00160   for (unsigned i = 0; i < simplex.size(); ++i)
00161     s << simplex[i].fv << ' ';
00162   return s;
00163 }
00164 
00165 
00166 bool operator==(const vnl_amoeba_SimplexCorner& a, const vnl_amoeba_SimplexCorner& b)
00167 {
00168   return (&a) == (&b);
00169 }
00170 
00171 //: Initialise the simplex given one corner, x
00172 void vnl_amoebaFit::set_up_simplex_relative(vcl_vector<vnl_amoeba_SimplexCorner>& simplex,
00173                                             const vnl_vector<double>& x)
00174 {
00175   int n = x.size();
00176 
00177   simplex[0].v = x;
00178   simplex[0].fv = f(x);
00179 
00180   // Following improvement suggested by L.Pfeffer at Stanford
00181   const double usual_delta = relative_diameter;             // 5 percent deltas for non-zero terms
00182   const double zero_term_delta = 0.00025;      // Even smaller delta for zero elements of x
00183 //  vnl_vector<double> y(n);
00184   for (int j = 0; j < n; ++j) {
00185     vnl_amoeba_SimplexCorner *s = &simplex[j+1];
00186     s->v = x;
00187 
00188     // perturb s->v(j)
00189     if (vnl_math_abs(s->v[j]) > zero_term_delta)
00190       s->v[j] = (1 + usual_delta)*s->v[j];
00191     else
00192       s->v[j] = zero_term_delta;
00193 
00194     s->fv = f(s->v);
00195   }
00196 }
00197 
00198 //: Initialise the simplex given one corner, x and displacements of others
00199 void vnl_amoebaFit::set_up_simplex_absolute(vcl_vector<vnl_amoeba_SimplexCorner>& simplex,
00200                                             const vnl_vector<double>& x,
00201                                             const vnl_vector<double>& dx)
00202 {
00203   int n = x.size();
00204 
00205   simplex[0].v = x;
00206   simplex[0].fv = f(x);
00207 
00208   for (int j = 0; j < n; ++j) {
00209     vnl_amoeba_SimplexCorner *s = &simplex[j+1];
00210     s->v = x;
00211 
00212     // perturb s->v(j)
00213     s->v[j] = s->v[j] + dx[j];
00214 
00215     s->fv = f(s->v);
00216   }
00217 }
00218 
00219 //: FMINS Minimize a function of several variables.
00220 //  FMINS('F',X0) attempts to return a vector x which is a local minimizer
00221 //  of F(x) near the starting vector X0.  'F' is a string containing the
00222 //  name of the objective function to be minimized.  F(x) should be a
00223 //  scalar valued function of a vector variable.
00224 //
00225 //  FMINS('F',X0,OPTIONS) uses a vector of control parameters.
00226 //  If OPTIONS(1) is nonzero, intermediate steps in the solution are
00227 //  displayed; the default is OPTIONS(1) = 0.  OPTIONS(2) is the termination
00228 //  tolerance for x; the default is 1.e-4.  OPTIONS(3) is the termination
00229 //  tolerance for F(x); the default is 1.e-4.  OPTIONS(14) is the maximum
00230 //  number of steps; the default is OPTIONS(14) = 500.  The other components
00231 //  of OPTIONS are not used as input control parameters by FMIN.  For more
00232 //  information, see FOPTIONS.
00233 //
00234 //  FMINS('F',X0,OPTIONS,[],P1,P2,...) provides for up to 10 additional
00235 //  arguments which are passed to the objective function, F(X,P1,P2,...)
00236 //
00237 //  FMINS uses a simplex search method.
00238 //
00239 //  See also FMIN.
00240 //
00241 //  Reference: J. E. Dennis, Jr. and D. J. Woods, New Computing
00242 //  Environments: Microcomputers in Large-Scale Computing,
00243 //  edited by A. Wouk, SIAM, 1987, pp. 116-122.
00244 
00245 void vnl_amoebaFit::amoeba(vnl_vector<double>& x)
00246 {
00247 // Set up a simplex near the initial guess.
00248   int n = x.size();
00249   vcl_vector<vnl_amoeba_SimplexCorner> simplex(n+1, vnl_amoeba_SimplexCorner(n));
00250 
00251   set_up_simplex_relative(simplex,x);
00252   amoeba(x,simplex);
00253 }
00254 
00255 void vnl_amoebaFit::amoeba(vnl_vector<double>& x, const vnl_vector<double>& dx)
00256 {
00257 // Set up a simplex near the initial guess.
00258   int n = x.size();
00259   vcl_vector<vnl_amoeba_SimplexCorner> simplex(n+1, vnl_amoeba_SimplexCorner(n));
00260 
00261   set_up_simplex_absolute(simplex,x,dx);
00262   amoeba(x,simplex);
00263 }
00264 
00265     //: Perform optimisation, given simplex to start
00266 void vnl_amoebaFit::amoeba(vnl_vector<double>& x,
00267                            vcl_vector<vnl_amoeba_SimplexCorner>& simplex)
00268 {
00269   int n = x.size();
00270   sort_simplex(simplex);
00271 
00272   if (verbose > 1) {
00273     vcl_cerr << "initial\n" << simplex;
00274   } else if (verbose) {
00275     vcl_cerr << "initial: " << simplex << vcl_endl;
00276   }
00277 
00278   // Iterate until the diameter of the simplex is less than X_tolerance.
00279   vnl_amoeba_SimplexCorner reflect(n);
00280   vnl_amoeba_SimplexCorner expand(n);
00281   vnl_amoeba_SimplexCorner contract(n);
00282   vnl_amoeba_SimplexCorner shrink(n);
00283   vnl_amoeba_SimplexCorner *next;
00284 
00285   vnl_vector<double> vbar(n);
00286   while (cnt < maxiter) {
00287     if (simplex_diameter(simplex) < X_tolerance &&
00288         sorted_simplex_fdiameter(simplex) < F_tolerance)
00289       break;
00290 
00291     // One step of the Nelder-Mead simplex algorithm
00292     for (int k =  0; k < n; ++k)  {
00293       vbar[k] = 0;
00294       for (int i = 0; i < n; ++i)
00295         vbar[k] += simplex[i].v[k];
00296       vbar[k] /= n;
00297     }
00298 
00299     set_corner_a_plus_bl(&reflect, vbar, simplex[n].v, -1);
00300 
00301     next = &reflect;
00302     const char *how = "reflect ";
00303     if (reflect.fv < simplex[n-1].fv) {
00304       // Reflection not totally crap...
00305       if (reflect.fv < simplex[0].fv) {
00306         // Reflection actually the best, try expanding
00307         set_corner_a_plus_bl(&expand, vbar, reflect.v, 2);
00308 
00309         if (expand.fv < simplex[0].fv) {
00310           next = &expand;
00311           how = "expand  ";
00312         }
00313       }
00314     } else {
00315       // Reflection *is* totally crap...
00316       {
00317         vnl_amoeba_SimplexCorner *tmp = &simplex[n];
00318         if (reflect.fv < tmp->fv)
00319           // replace simplex[n] by reflection as at least it's better than that
00320           tmp = &reflect;
00321         set_corner_a_plus_bl(&contract, vbar, tmp->v, 0.5);
00322       }
00323 
00324       if (contract.fv < simplex[0].fv) {
00325         // The contraction point was really good, hold it there
00326         next = &contract;
00327         how = "contract";
00328       }
00329       else {
00330         // The contraction point was only average, shrink the entire simplex.
00331         for (int j = 1; j < n; ++j)
00332 
00333           set_corner_a_plus_bl(&simplex[j], simplex[0].v, simplex[j].v, 0.5);
00334         set_corner_a_plus_bl(&shrink, simplex[0].v, simplex[n].v, 0.5);
00335 
00336         next = &shrink;
00337         how = "shrink  ";
00338       }
00339     }
00340     simplex[n] = *next;
00341 
00342     sort_simplex(simplex);
00343 
00344     // Print debugging info
00345     if (verbose) {
00346       char buf[16383];
00347       vcl_sprintf(buf, "iter %5d: %s ", cnt, how);
00348       vcl_cerr << buf;
00349       if (verbose ==2)
00350         vcl_cerr << "\nFirst corner: " << simplex[0].v;
00351       if (verbose > 1)
00352       {
00353         vcl_streamsize a = vcl_cerr.width(10);
00354         vcl_cerr << vcl_endl << simplex << vcl_endl;
00355         vcl_cerr.width(a);
00356       }
00357       else if (verbose)
00358         vcl_cerr << simplex << vcl_endl;
00359     }
00360   }
00361   num_evaluations_ = cnt;
00362   x = simplex[0].v;
00363 }
00364 
00365 //: Modify x to minimise function supplied in constructor
00366 //  Start simplex defined by scaling elements of x
00367 void vnl_amoeba::minimize(vnl_vector<double>& x)
00368 {
00369   vnl_amoebaFit af(*this);
00370   af.amoeba(x);
00371   num_evaluations_ = af.num_evaluations_;
00372 }
00373 
00374 //: Perform optimisation.  Start simplex defined by adding dx[i] to each x[i]
00375 void vnl_amoeba::minimize(vnl_vector<double>& x, const vnl_vector<double>& dx)
00376 {
00377   vnl_amoebaFit af(*this);
00378   af.amoeba(x,dx);
00379   num_evaluations_ = af.num_evaluations_;
00380 }
00381 
00382 
00383 //: Static method
00384 void vnl_amoeba::minimize(vnl_cost_function& f, vnl_vector<double>& x)
00385 {
00386   minimize(f, x, 0);
00387 }
00388 
00389 //: Static method
00390 void vnl_amoeba::minimize(vnl_cost_function& f, vnl_vector<double>& x, double delta)
00391 {
00392   vnl_amoeba a(f);
00393   a.verbose = vnl_amoeba::default_verbose;
00394   if (delta != 0)
00395     a.relative_diameter = delta;
00396   vnl_amoebaFit amoeba(a);
00397   amoeba.amoeba(x);
00398 }
00399 
00400 //: Static method
00401 void vnl_amoeba::minimize(vnl_cost_function& f, vnl_vector<double>& x,
00402                           const vnl_vector<double>& dx)
00403 {
00404   vnl_amoeba a(f);
00405   a.verbose = vnl_amoeba::default_verbose;
00406   vnl_amoebaFit amoeba(a);
00407   amoeba.amoeba(x,dx);
00408 }
00409 
00410 
00411 class vnl_amoeba_LSCF : public vnl_cost_function
00412 {
00413   vnl_least_squares_function* ls_;
00414   vnl_vector<double> fx;
00415  public:
00416   vnl_amoeba_LSCF(vnl_least_squares_function& ls)
00417    : vnl_cost_function(ls.get_number_of_unknowns()),
00418      ls_(&ls), fx(ls.get_number_of_residuals()) {}
00419 
00420   ~vnl_amoeba_LSCF() {}
00421 
00422   double f(vnl_vector<double> const& x) {
00423     ls_->f(x, fx);
00424     return fx.squared_magnitude();
00425   }
00426 };
00427 
00428 void vnl_amoeba::minimize(vnl_least_squares_function& f, vnl_vector<double>& x)
00429 {
00430   vnl_amoeba_LSCF lsf(f);
00431   minimize(lsf, x);
00432 }
00433 
00434 /////////////////////////////////////////////////////////////////////////////
00435 vnl_amoeba_SimplexCorner::vnl_amoeba_SimplexCorner(int n) : v(n) {}
00436 
00437 vnl_amoeba_SimplexCorner& vnl_amoeba_SimplexCorner::operator=(const vnl_amoeba_SimplexCorner& that)
00438 {
00439   v = that.v;
00440   fv = that.fv;
00441   return *this;
00442 }
00443 
00444 //--------------------------------------------------------------------------------