MechSys  1.0
Computing library for simulations in continuum and discrete mechanics
/home/dorival/mechsys/lib/numerical/min.h
Go to the documentation of this file.
00001 /************************************************************************
00002  * MechSys - Open Library for Mechanical Systems                        *
00003  * Copyright (C) 2005 Dorival M. Pedroso, Raul Durand                   *
00004  * Copyright (C) 2009 Sergio Galindo                                    *
00005  *                                                                      *
00006  * This program is free software: you can redistribute it and/or modify *
00007  * it under the terms of the GNU General Public License as published by *
00008  * the Free Software Foundation, either version 3 of the License, or    *
00009  * any later version.                                                   *
00010  *                                                                      *
00011  * This program is distributed in the hope that it will be useful,      *
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of       *
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the         *
00014  * GNU General Public License for more details.                         *
00015  *                                                                      *
00016  * You should have received a copy of the GNU General Public License    *
00017  * along with this program. If not, see <http://www.gnu.org/licenses/>  *
00018  ************************************************************************/
00019 
00020 #ifndef MECHSYS_NUMERICAL_MIN_H
00021 #define MECHSYS_NUMERICAL_MIN_H
00022 
00023 // STL
00024 #include <cmath>   // for sqrt
00025 #include <cfloat>  // for DBL_EPSILON
00026 #include <cstring> // for strcmp
00027 #include <sstream> // for ostringstream
00028 
00029 // GSL
00030 #include <gsl/gsl_errno.h> // for GSL_SUCCESS
00031 #include <gsl/gsl_multimin.h>
00032 
00033 // MechSys
00034 #include <mechsys/util/fatal.h>
00035 #include <mechsys/util/util.h>
00036 #include <mechsys/util/numstreams.h>
00037 
00038 namespace Numerical
00039 {
00040 
00041 template<typename Instance>
00042 class Min
00043 {
00044 public:
00045     // Typedefs
00046     typedef double (Instance::*pF)  (double const X[]);                
00047     typedef void   (Instance::*pdF) (double const X[], double dFdX[]); 
00048 
00049     // Constructor
00050     Min (Instance * p2Inst, pF p2F, pdF p2dF, size_t NDim, char const * Scheme="BFGS"); 
00051 
00052     // Destructor
00053     ~Min ();
00054 
00055     // Methods
00056     void   SetScheme (char const * Scheme="BFGS"); 
00057     double Find      (double X[], double StepSize=0.01, double Tol=1.0e-3, size_t MaxIt=1000); 
00058 
00059     // Data
00060     bool Debug;
00061 
00062     // Internal methods
00063     double _call_F  (double const X[])                { return (_p2inst->*_p2F) (X); }
00064     void   _call_dF (double const X[], double dFdX[]) { if (_p2dF==NULL) throw new Fatal("Numerical::_call_dF: function for the gradients (p2dF) is not available"); (_p2inst->*_p2dF) (X, dFdX); }
00065 
00066 private:
00067     // Auxiliary variables
00068     Instance                  * _p2inst;
00069     pF                          _p2F;
00070     pdF                         _p2dF;
00071     size_t                      _ndim;
00072     gsl_multimin_function_fdf   _gsl_fdf;
00073     gsl_multimin_function       _gsl_f;
00074     gsl_multimin_fdfminimizer * _gsl_fdfmin;
00075     gsl_multimin_fminimizer   * _gsl_fmin;
00076     gsl_vector                * _x;
00077     gsl_vector                * _ss;
00078 };
00079 
00080 // Trick to pass pointers to member functions to GSL. This will use (*params) to store the pointer to an instance of Min, therefore (*params) will no longer be available.
00081 template<typename Instance>
00082 double __min_call_F__ (gsl_vector const * X, void * not_used_for_params)
00083 {
00084     Min<Instance> * p2min = static_cast<Min<Instance>*>(not_used_for_params);
00085     return p2min->_call_F (X->data);
00086 }
00087 
00088 // Trick to pass pointers to member functions to GSL. This will use (*params) to store the pointer to an instance of Min, therefore (*params) will no longer be available.
00089 template<typename Instance>
00090 void __min_call_dF__ (gsl_vector const * X, void * not_used_for_params, gsl_vector * dFdX)
00091 {
00092     Min<Instance> * p2min = static_cast<Min<Instance>*>(not_used_for_params);
00093     p2min->_call_dF (X->data, dFdX->data);
00094 }
00095 
00096 // Trick to pass pointers to member functions to GSL. This will use (*params) to store the pointer to an instance of Min, therefore (*params) will no longer be available.
00097 template<typename Instance>
00098 void __min_call_FdF__ (gsl_vector const * X, void * not_used_for_params, double * F, gsl_vector * dFdX)
00099 {
00100     Min<Instance> * p2min = static_cast<Min<Instance>*>(not_used_for_params);
00101     (*F) = p2min->_call_F (X->data);
00102     p2min->_call_dF (X->data, dFdX->data);
00103 }
00104 
00105 
00107 
00108 
00109 template<typename Instance>
00110 inline Min<Instance>::Min (Instance * p2Inst, pF p2F, pdF p2dF, size_t NDim, char const * Scheme)
00111     : Debug       (false),
00112       _p2inst     (p2Inst),
00113       _p2F        (p2F),
00114       _p2dF       (p2dF),
00115       _ndim       (NDim),
00116       _gsl_fdfmin (NULL),
00117       _gsl_fmin   (NULL),
00118       _ss         (NULL)
00119 {
00120     // functions (with gradients)
00121     _gsl_fdf.n      = _ndim;
00122     _gsl_fdf.f      = &__min_call_F__<Instance>;
00123     _gsl_fdf.df     = &__min_call_dF__<Instance>;
00124     _gsl_fdf.fdf    = &__min_call_FdF__<Instance>;
00125     _gsl_fdf.params = this;
00126 
00127     // functions
00128     _gsl_f.n      = _ndim;
00129     _gsl_f.f      = &__min_call_F__<Instance>;
00130     _gsl_f.params = this;
00131 
00132     // allocate vector
00133     _x = gsl_vector_alloc (_ndim);
00134 
00135     // set scheme
00136     SetScheme (Scheme);
00137 }
00138 
00139 template<typename Instance>
00140 inline Min<Instance>::~Min ()
00141 {
00142     if (_gsl_fdfmin!=NULL) gsl_multimin_fdfminimizer_free (_gsl_fdfmin);
00143     if (_gsl_fmin  !=NULL) gsl_multimin_fminimizer_free   (_gsl_fmin);
00144     if (_x         !=NULL) gsl_vector_free                (_x);
00145     if (_ss        !=NULL) gsl_vector_free                (_ss);
00146 }
00147 
00148 template<typename Instance>
00149 inline void Min<Instance>::SetScheme (char const * Scheme)
00150 {
00151     // set scheme
00152     gsl_multimin_fdfminimizer_type const * fdftype = NULL;
00153     gsl_multimin_fminimizer_type   const * ftype   = NULL;
00154     if      (strcmp(Scheme,"ConjFR"      )==0) { fdftype = gsl_multimin_fdfminimizer_conjugate_fr;     }
00155     else if (strcmp(Scheme,"ConjPR"      )==0) { fdftype = gsl_multimin_fdfminimizer_conjugate_pr;     }
00156     else if (strcmp(Scheme,"BFGS"        )==0) { fdftype = gsl_multimin_fdfminimizer_vector_bfgs2;     }
00157     else if (strcmp(Scheme,"Steep"       )==0) { fdftype = gsl_multimin_fdfminimizer_steepest_descent; }
00158     else if (strcmp(Scheme,"NMSimplex"   )==0) { ftype   = gsl_multimin_fminimizer_nmsimplex2;         }
00159     else if (strcmp(Scheme,"NMSimplexRnd")==0) { ftype   = gsl_multimin_fminimizer_nmsimplex2rand;     }
00160     else throw new Fatal("Numerical::Min::SetScheme: Scheme %s is invalid. Valid ones are: ConjFR, ConjPR, BFGS, Steep, NMSimplex, NMSimplexRnd",Scheme);
00161 
00162     // clear previous minimizers
00163     if (_gsl_fdfmin!=NULL) { gsl_multimin_fdfminimizer_free (_gsl_fdfmin);  _gsl_fdfmin = NULL; }
00164     if (_gsl_fmin  !=NULL) { gsl_multimin_fminimizer_free   (_gsl_fmin);    _gsl_fmin   = NULL; }
00165 
00166     // set minimizer
00167     if (ftype==NULL) // with gradients
00168     {
00169         _gsl_fdfmin = gsl_multimin_fdfminimizer_alloc (fdftype, _ndim);
00170     }
00171     else
00172     {
00173         _gsl_fmin = gsl_multimin_fminimizer_alloc (ftype, _ndim);
00174         if (_ss==NULL) _ss = gsl_vector_alloc (_ndim);
00175     }
00176 }
00177 
00178 template<typename Instance>
00179 inline double Min<Instance>::Find (double X[], double StepSize, double Tol, size_t MaxIt)
00180 {
00181     // set initial X
00182     for (size_t i=0; i<_ndim; ++i) gsl_vector_set (_x, i, X[i]);
00183 
00184    // with gradients
00185     if (_gsl_fmin==NULL)
00186     {
00187         // set minimizer
00188         gsl_multimin_fdfminimizer_set (_gsl_fdfmin, &_gsl_fdf, _x, StepSize, Tol/10.0);
00189 
00190         // solve
00191         int    status;
00192         size_t it;
00193         for (it=0; it<MaxIt; ++it)
00194         {
00195             status = gsl_multimin_fdfminimizer_iterate (_gsl_fdfmin);
00196             if (status==GSL_ENOPROG) break;
00197             status = gsl_multimin_test_gradient (_gsl_fdfmin->gradient, Tol);
00198             if (status==GSL_SUCCESS)
00199             {
00200                 for (size_t i=0; i<_ndim; ++i) X[i] = gsl_vector_get (_gsl_fdfmin->x, i);
00201                 return _gsl_fdfmin->f;
00202             }
00203             if (Debug)
00204             {
00205                 std::cout << Util::_6 << it;
00206                 for (size_t i=0; i<_ndim; ++i) std::cout << Util::_8s << gsl_vector_get(_gsl_fdfmin->x, i);
00207                 std::cout << TERM_CLR_MAGENTA_H << Util::_8s << _gsl_fdfmin->f << TERM_RST << std::endl; 
00208             }
00209         }
00210 
00211         // error
00212         throw new Fatal("Numerical::Min::Find: (With gradients) Could not find minimum (it=%zd, MaxIt=%zd, GSL_STATUS=%d)",it,MaxIt,status);
00213     }
00214 
00215     // no gradients
00216     else
00217     {
00218         // set initial step sizes
00219         gsl_vector_set_all (_ss, StepSize);
00220 
00221         // set minimizer
00222         gsl_multimin_fminimizer_set (_gsl_fmin, &_gsl_f, _x, _ss);
00223 
00224         // solve
00225         int    status;
00226         size_t it;
00227         double size;
00228         for (it=0; it<MaxIt; ++it)
00229         {
00230             status = gsl_multimin_fminimizer_iterate (_gsl_fmin);
00231             if (status==GSL_ENOPROG) break;
00232             size   = gsl_multimin_fminimizer_size (_gsl_fmin);
00233             status = gsl_multimin_test_size       (size, Tol);
00234             if (status==GSL_SUCCESS)
00235             {
00236                 for (size_t i=0; i<_ndim; ++i) X[i] = gsl_vector_get (_gsl_fmin->x, i);
00237                 return _gsl_fmin->fval;
00238             }
00239             if (Debug)
00240             {
00241                 std::cout << Util::_6 << it;
00242                 for (size_t i=0; i<_ndim; ++i) std::cout << Util::_8s << gsl_vector_get(_gsl_fmin->x, i);
00243                 std::cout << TERM_CLR_MAGENTA_H << Util::_8s << _gsl_fmin->fval << TERM_RST << std::endl; 
00244             }
00245         }
00246 
00247         // error
00248         throw new Fatal("Numerical::Min::Find: (no gradients) Could not find minimum (it=%zd, MaxIt=%zd, GSL_STATUS=%d)",it,MaxIt,status);
00249     }
00250 }
00251 
00252 }; // namespace Numerical
00253 
00254 #endif // MECHSYS_NUMERICAL_MIN_H
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines