![]() |
MechSys
1.0
Computing library for simulations in continuum and discrete mechanics
|
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