BLMinimize.hh

Go to the documentation of this file.
00001 //      BLMinimize.hh
00002 #define USE_GSL_MULTIMIN
00003 //#define USE_TMINUIT
00004 /*
00005 This source file is part of G4beamline, http://g4beamline.muonsinc.com
00006 Copyright (C) 2003,2004,2005,2006 by Tom Roberts, all rights reserved.
00007 
00008 This program is free software; you can redistribute it and/or
00009 modify it under the terms of the GNU General Public License
00010 as published by the Free Software Foundation; either version 2
00011 of the License, or (at your option) any later version.
00012 
00013 This program is distributed in the hope that it will be useful,
00014 but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016 GNU General Public License for more details.
00017 
00018 http://www.gnu.org/copyleft/gpl.html
00019 */
00020 
00021 
00022 #ifndef BLMINIMIZE_HH
00023 #define BLMINIMIZE_HH
00024 
00025 #include <stdio.h>
00026 #include <vector>
00027 #include <string>
00028 
00029 #include "globals.hh"
00030 #include "BLAssert.hh"
00031 
00032 /**     class BLMinimizeFunction is the base class for a class defining a
00033  *      function to minimize.
00034  **/
00035 class BLMinimizeFunction {
00036 public:
00037         BLMinimizeFunction() { }
00038         virtual ~BLMinimizeFunction() { }
00039 
00040         /// operator() is the function to minimize. Note it is not const
00041         /// for flexibility in derived classes.
00042         /// Its argument is necessarily const.
00043         virtual double operator()(const std::vector<double> &x) = 0;
00044 };
00045 
00046 #ifdef USE_GSL_MULTIMIN
00047 
00048 #include <gsl/gsl_multimin.h>
00049 
00050 /**     class BLMinimize implements a general function minimizer using
00051  *      the GSL multimin functions.
00052  *
00053  *      Uses the the Simplex algorithm of Nelder and Mead.
00054  *
00055  *      Compilation for simple programs:
00056  *              g++ -I... test.cc -lgsl -lgslcblas -lm
00057  *
00058  *      NOTE: the parameters to the minimizer must all have scales that are
00059  *      comparable, or the minimizer will often converge to a wildly incorrect
00060  *      value. This applies to track fitting, where positions have scales of
00061  *      1 (millimeters), but angles have scales of 0.001 (milliradians).
00062  *      This class scales the parameters appropriately. it also permits the
00063  *      function to have arguments that are held constant during the 
00064  *      minimization by setting their scale to 0.0.
00065  *
00066  *      NOTE: only a single instance of BLMinimize can be active at a time
00067  *      (i.e. inside the minimize() function). Any number of instances can
00068  *      exist. This basically means your BLMinimizeFunction cannot itself
00069  *      use a minimizer internally.
00070  **/
00071 class BLMinimize {
00072         std::vector<double> scale;
00073         std::vector<double> arg;
00074         std::vector<std::string> name;
00075         double val;
00076         double size;
00077         int iter;
00078         int print;
00079         double chisqMin;
00080         BLMinimizeFunction *func;
00081         gsl_multimin_function ff;
00082         gsl_multimin_fminimizer *m;
00083 private:
00084         // f() is the function the gsl routines call (they are in C)
00085         static double f(const gsl_vector *x, void *param) {
00086                 BLMinimize *p = (BLMinimize*)param;
00087                 return p->call(x);
00088         }
00089         // call() will call the user's function, scaling the argument vector.
00090         double call(const gsl_vector *x) {
00091                 if(print >= 2) printf("BLMinimize call args:");
00092                 int j=0;
00093                 for(unsigned i=0; i<scale.size(); ++i) {
00094                         if(scale[i] != 0.0) // fixed args are already set
00095                                 arg[i] = gsl_vector_get(x,j++) * scale[i];
00096                         if(print >= 2)
00097                                 printf(" %.4g",arg[i]);
00098                 }
00099                 if(print >= 2) printf("\n");
00100                 double v = (*func)(arg);
00101                 if(print >= 2) printf("BLMinimize call returns value=%.4g\n",v);
00102                 return v;
00103         }
00104 public:
00105         /// Constructor.
00106         BLMinimize(int _print=2) : scale(), arg(), name()
00107                 { val=GSL_NAN; size=GSL_NAN; iter=0; print=_print;
00108                   chisqMin=GSL_NAN; func=0; ff.f=0; ff.n=0; ff.params=0;  m=0;
00109                 }
00110 
00111         virtual ~BLMinimize() { if(m) gsl_multimin_fminimizer_free(m); }
00112 
00113         /// setFunc() will set the function to minimize and the scale of its
00114         /// arguments. The number of non-zero entries in _scale[] determines
00115         /// the number of variables to minimize. names is a colon-separated
00116         /// list of parameter names.
00117         void setFunc(BLMinimizeFunction &f, const std::vector<double> &_scale,
00118                                                 const std::string &names="") {
00119                 func = &f;
00120                 scale.clear();
00121                 arg.clear();
00122                 name.clear();
00123                 std::vector<std::string> n = splitString(names);
00124                 int nx = 0;
00125                 for(unsigned i=0; i<_scale.size(); ++i) {
00126                         double s=_scale[i];
00127                         scale.push_back(s);
00128                         arg.push_back(s);
00129                         name.push_back(i<n.size() ? n[i] : "");
00130                         if(s != 0.0) ++nx;
00131                 }
00132                 ff.f=BLMinimize::f;
00133                 ff.n=nx;
00134                 ff.params=this;
00135                 if(m) gsl_multimin_fminimizer_free(m);
00136                 m = 0;
00137                 if(nx == 0) return;
00138                 m = gsl_multimin_fminimizer_alloc(
00139                         gsl_multimin_fminimizer_nmsimplex,nx);
00140         }
00141 
00142         /// setPrintDetail() sets the detail of printing per iteration:
00143         /// 0=no print, 1=print value and size only, 2=print value, size, and x.
00144         /// the default is 2.
00145         void setPrintDetail(int v=0) { print = v; }
00146 
00147         /// setChisqMin() sets an additional convergence criterium of chisq
00148         /// less than the given value. Can only be used when the function is
00149         /// positive definite, usually a chi-squared. Required for track-
00150         /// fitting of test tracks that have truly 0 chisq -- the normal 
00151         /// convergence often fails for such tracks.
00152         void setChisqMin(double v) { chisqMin = v; }
00153 
00154         /// minimize() finds the minimum, returning status (0=success).
00155         ///   x         the initial and then the final argument values
00156         ///   tolerance the convergence limit on the simplex size
00157         ///             NOTE: this is after scaling!
00158         ///   maxIter   limits the number of iterations
00159         /// The initial step for x[i] is scale[i] (x[i] is scaled, so the
00160         /// step to the gsl routine is 1.0).
00161         int minimize(std::vector<double> &x, double tolerance=1e-3,
00162                                                         int maxIter=100) {
00163                 if(ff.n <= 0) return GSL_EINVAL;
00164                 BLAssert(x.size()==scale.size());
00165                 gsl_vector *init = gsl_vector_alloc(ff.n);
00166                 gsl_vector *istep = gsl_vector_alloc(ff.n);
00167                 unsigned j=0;
00168                 for(unsigned i=0; i<scale.size(); ++i) {
00169                         arg[i] = x[i]; // fixed args, variable ones overwritten
00170                         if(scale[i] != 0.0) {
00171                                 gsl_vector_set(init,j,x[i]/scale[i]); 
00172                                 gsl_vector_set(istep,j,1.0);
00173                                 ++j;
00174                         }
00175                 }
00176                 BLAssert(j==ff.n); BLAssert(m!=0);
00177                 gsl_multimin_fminimizer_set(m,&ff,init,istep);
00178                 for(iter=1; iter<=maxIter; ++iter) {
00179                         int status = gsl_multimin_fminimizer_iterate(m);
00180                         if(status) {
00181                                 if(print >= 1)
00182                                         printf("BLMinimize FAILED status=%d\n",
00183                                                                         status);
00184                                 gsl_vector_free(init);
00185                                 gsl_vector_free(istep);
00186                                 return status;
00187                         }
00188                         val = m->fval;
00189                         size = gsl_multimin_fminimizer_size(m);
00190                         if(print >= 1)
00191                             printf("BLMinimize Iter %d: val=%.4g size=%.4g\n",
00192                                                         iter,val,size);
00193                         status = gsl_multimin_test_size(size,tolerance);
00194                         if(status == GSL_SUCCESS || (!gsl_isnan(chisqMin) &&
00195                            val <= chisqMin)) {
00196                                 unsigned j=0;
00197                                 for(unsigned i=0; i<scale.size(); ++i) {
00198                                     if(scale[i] != 0.0)
00199                                         x[i] = gsl_vector_get(m->x,j++) *
00200                                                                 scale[i];
00201                                 }
00202                                 gsl_vector_free(init);
00203                                 gsl_vector_free(istep);
00204                                 if(print >= 1) printf("BLMinimize SUCCESS\n");
00205                                 return 0;
00206                         }
00207                 }
00208                 gsl_vector_free(init);
00209                 gsl_vector_free(istep);
00210                 if(print >= 1) printf("BLMinimize FAILED iter > %d\n",maxIter);
00211                 return GSL_EMAXITER;
00212         }
00213 
00214         /// value() returns the function value from the previous minimize().
00215         double value() const { return val; }
00216 
00217         /// getSize() returns the Simplex size from the previous minimize().
00218         double getSize() { return size; }
00219 
00220         /// getIterations() returns the # iterations of the previous minimize().
00221         int getIterations() { return iter; }
00222 
00223 private:
00224         std::vector<std::string> splitString(const std::string &s) {
00225                 std::string tmp(s);
00226                 std::vector<std::string> v;
00227                 while(tmp.size() > 0) {
00228                         unsigned i=tmp.find_first_of(":");
00229                         if(i > tmp.size()) i = tmp.size();
00230                         v.push_back(tmp.substr(0,i));
00231                         tmp.erase(0,i+1);
00232                 }
00233                 return v;
00234         }
00235 };
00236 
00237 #endif // USE_GSL_MULTIMIN
00238 
00239 #ifdef USE_TMINUIT
00240 
00241 #include "TMinuit.h"
00242 
00243 /**     class BLMinimize implements a general function minimizer using
00244  *      the TMinuit class from Root.
00245  *
00246  *      NOTE: only a single instance of BLMinimize can be active at a time
00247  *      (i.e. inside the minimize() function). Any number of instances can
00248  *      exist. This basically means your BLMinimizeFunction cannot itself
00249  *      use a minimizer internally.
00250  **/
00251 class BLMinimize {
00252         std::vector<double> scale;
00253         std::vector<std::string> name;
00254         std::vector<double> arg;
00255         double val;
00256         int iter;
00257         int print;
00258         double chisqMin;
00259         BLMinimizeFunction *func;
00260         TMinuit *tm;
00261         static BLMinimize *current;
00262 private:
00263         static void fcn(Int_t &npar, Double_t *grad, Double_t &val, Double_t *x,
00264                                                                 Int_t iflag) {
00265                 BLAssert(current != 0);
00266                 val = current->call(npar,x);
00267         }
00268         double call(int npar, double *x) {
00269                 // npar seems to be 1 too small assert(npar == (int)arg.size());
00270                 for(unsigned i=0; i<arg.size(); ++i)
00271                         arg[i] = x[i];
00272                 return (*func)(arg);
00273         }
00274 public:
00275         /// Constructor.
00276         BLMinimize(int _print=2) : scale(), name(), arg()
00277                 { val=0.0; iter=0; print=_print; chisqMin=0.0; func=0; tm=0; }
00278 
00279         virtual ~BLMinimize() {  }
00280 
00281         /// setFunc() will set the function to minimize and the scale of its
00282         /// arguments. The number of non-zero entries in _scale[] determines
00283         /// the number of variables to minimize. names is a colon-separated
00284         /// list of parameter names.
00285         void setFunc(BLMinimizeFunction &f, const std::vector<double> &_scale,
00286                                                 const std::string &names="") {
00287                 func = &f;
00288                 scale.clear();
00289                 name.clear();
00290                 arg.clear();
00291                 std::vector<std::string> n = splitString(names);
00292                 for(unsigned i=0; i<_scale.size(); ++i) {
00293                         double s=_scale[i];
00294                         scale.push_back(s);
00295                         name.push_back(i<n.size() ? n[i] : "");
00296                         arg.push_back(0.0);
00297                 }
00298                 
00299                 if(tm) delete tm;
00300                 tm = new TMinuit();
00301                 tm->SetFCN(fcn);
00302                 tm->SetPrintLevel(-1);
00303                 tm->Command("SET PRINTOUT -1");
00304         }
00305 
00306         /// setPrintDetail() sets the detail of printing per iteration:
00307         /// 0=no print, 1=print value and size only, 2=print value, size, and x.
00308         /// the default is 2.
00309         void setPrintDetail(int v=0) { print = v; }
00310 
00311         /// setChisqMin() sets an additional convergence criterium of chisq
00312         /// less than the given value. Can only be used when the function is
00313         /// positive definite, usually a chi-squared. Required for track-
00314         /// fitting of test tracks that have truly 0 chisq -- the normal 
00315         /// convergence often fails for such tracks.
00316         void setChisqMin(double v) { chisqMin = v; }
00317 
00318         /// minimize() finds the minimum, returning status (0=success).
00319         ///   x         the initial and then the final argument values
00320         ///   tolerance the convergence limit on the simplex size
00321         ///             NOTE: this is after scaling!
00322         ///   maxIter   limits the number of iterations
00323         /// The initial step for x[i] is scale[i].
00324         int minimize(std::vector<double> &x, double tolerance=1e-3,
00325                                                         int maxIter=100) {
00326                 BLAssert(tm != 0);
00327                 BLAssert(x.size() == scale.size());
00328                 for(unsigned i=0; i<scale.size(); ++i) {
00329                         tm->DefineParameter(i,name[i].c_str(),x[i],scale[i],0.0,0.0);
00330                 }
00331                 //@ What do I do with tolerance????
00332                 tm->SetMaxIterations(maxIter);
00333                 current = this;
00334                 int retval = tm->Migrad();
00335                 current = 0;
00336                 for(unsigned i=0; i<scale.size(); ++i) {
00337                         double err;
00338                         tm->GetParameter(i,x[i],err);
00339                 }
00340                 val = (*func)(x);
00341                 return retval;
00342         }
00343 
00344         /// value() returns the function value from the previous minimize().
00345         double value() const { return val; }
00346 
00347         /// getSize() returns the Simplex size from the previous minimize().
00348         double getSize() { return 0.0; }
00349 
00350         /// getIterations() returns the # iterations of the previous minimize().
00351         int getIterations() { return iter; }
00352 
00353 private:
00354         std::vector<std::string> splitString(const std::string &s) {
00355                 std::string tmp(s);
00356                 std::vector<std::string> v;
00357                 while(tmp.size() > 0) {
00358                         unsigned i=tmp.find_first_of(":");
00359                         if(i > tmp.size()) i = tmp.size();
00360                         v.push_back(tmp.substr(0,i));
00361                         tmp.erase(0,i+1);
00362                 }
00363                 return v;
00364         }
00365 };
00366 
00367 
00368 #endif // USE_TMINUIT
00369 
00370 #endif // BLMINIMIZE_HH
g4beamline