MechSys  1.0
Computing library for simulations in continuum and discrete mechanics
/home/dorival/mechsys/lib/numerical/root.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_ROOT_H
00021 #define MECHSYS_ROOT_H
00022 
00023 // STL
00024 #include <cmath>
00025 #include <cfloat> // for DBL_EPSILON
00026 
00027 // MechSys
00028 #include <mechsys/util/fatal.h>
00029 #include <mechsys/util/string.h>
00030 
00031 namespace Numerical
00032 {
00033 
00034 template<typename Instance>
00035 class Root
00036 {
00037 public:
00038     // Typedefs
00039     typedef double (Instance::*pF)    (double x, void * UserData); 
00040     typedef double (Instance::*pdFdx) (double x, void * UserData); 
00041 
00043     Root (Instance * Inst, pF F, pdFdx dFdx=NULL);
00044 
00045     // Methods
00046     double Solve (double xa, double xb, double * x_guess=NULL, void * UserData=NULL); 
00047 
00048     // Data
00049     double Tol;
00050     int    It;
00051     int    MaxIt;
00052     String Scheme;
00053     bool   Verbose;
00054 
00055 private:
00056     Instance * _pInst; 
00057     pF         _pF;    
00058     pdFdx      _pdFdx; 
00059 };
00060 
00061 
00063 
00064 
00065 template<typename Instance>
00066 inline Root<Instance>::Root (Instance * Inst, pF F, pdFdx dFdx)
00067     : Tol     (sqrt(DBL_EPSILON)),
00068       It      (0),
00069       MaxIt   (30),
00070       Scheme  ("Brent"),
00071       Verbose (false),
00072       _pInst  (Inst),
00073       _pF     (F),
00074       _pdFdx  (dFdx)
00075 {
00076 }
00077 
00078 template<typename Instance>
00079 inline double Root<Instance>::Solve (double xa, double xb, double * x_guess, void * UserData)
00080 {
00081     if (Scheme=="Brent")
00082     {
00083         /* Based on ZEROIN C math library: http://www.netlib.org/c/
00084 
00085            Algorithm:
00086            G.Forsythe, M.Malcolm, C.Moler, Computer methods for mathematical
00087            computations. M., Mir, 1980, p.180 of the Russian edition
00088 
00089            The function makes use of the bissection procedure combined with
00090            the linear or quadric inverse interpolation.
00091            At every step program operates on three abscissae: a, b, and c:
00092             a: the last but one approximation
00093             b: the last and the best approximation to the root
00094             c: the last but one or even earlier approximation than a such that:
00095                 1) |f(b)| <= |f(c)|
00096                 2) f(b) and f(c) have opposite signs, i.e. b and c confine the root
00097 
00098            At every step this algorithm selects one of the two new approximations, the former being
00099            obtained by the bissection procedure and the latter resulting in the interpolation.
00100            If a,b and c are all different, the quadric interpolation is utilized, otherwise the linear one.
00101            If the latter (i.e. obtained by the interpolation) point is reasonable (i.e. lies within
00102            the current interval [b,c] not being too close to the boundaries) it is accepted.
00103            The bissection result is used in the other case. Therefore, the range of uncertainty is
00104            ensured to be reduced at least by the factor 1.6.
00105 
00106            Examples: <a href="http://cvs.savannah.gnu.org/viewvc/mechsys/mechsys/lib/numerical/tst/tbrentroot.cpp?view=markup"> ttbrentroot.cpp Test Brent's method</a>
00107         */
00108 
00109         double a  = xa; // the last but one approximation
00110         double b  = xb; // the last and the best approximation to the root
00111         double c  = a;  // the last but one or even earlier approximation than a that
00112         double fa = (_pInst->*_pF) (a, UserData);
00113         double fb = (_pInst->*_pF) (b, UserData);
00114         double fc = fa;
00115 
00116         // Check input
00117         if ((fa>0.0 && fb>0.0) || (fa<0.0 && fb<0.0)) throw new Fatal("Root::Solve: Brent method: Root must be bracketed.");
00118 
00119         // Solve
00120         for (It=0; It<MaxIt; ++It)
00121         {
00122             // Distance from the last but one to the last approximation
00123             double prev_step = b-a;
00124 
00125             // Swap data for b to be the best approximation
00126             if (fabs(fc)<fabs(fb))
00127             {
00128                  a =  b;     b =  c;     c =  a;
00129                 fa = fb;    fb = fc;    fc = fa;
00130             }
00131             double tol_act  = 2.0*DBL_EPSILON*fabs(b) + Tol/2.0;  // Actual tolerance
00132             double new_step = (c-b)/2.0;                          // Step at this iteration
00133 
00134             // Check for convergence
00135             if (Verbose) printf("It=%d, xnew=%g, f(xnew)=%g, err=%g\n", It, b, fb, fabs(new_step));
00136             if (fabs(new_step)<=tol_act || fb==0.0) return b; // Acceptable approx. is found
00137 
00138             // Decide if the interpolation can be tried
00139             if (fabs(prev_step)>=tol_act && fabs(fa)>fabs(fb)) // If prev_step was large enough and was in true direction
00140             {
00141                 // Interpolation may be tried
00142                 double p,q; // Interpolation step is calculated in the form p/q; division operations is delayed until the last moment
00143                 double t1,cb,t2;
00144                 cb = c-b;
00145                 if(a==c) // If we have only two distinct points, linear interpolation can only be applied
00146                 {
00147                     t1 = fb/fa;
00148                     p  = cb*t1;
00149                     q  = 1.0-t1;
00150                 }
00151                 else // Quadric inverse interpolation
00152                 {
00153                     q = fa/fc;     t1 = fb/fc;     t2 = fb/fa;
00154                     p = t2 * ( cb*q*(q-t1) - (b-a)*(t1-1.0) );
00155                     q = (q-1.0) * (t1-1.0) * (t2-1.0);
00156                 }
00157 
00158                 // p was calculated with the oposite sign; make p positive and assign possible minus to q
00159                 if (p>0.0) q = -q;
00160                 else       p = -p;
00161 
00162                 // If b+p/q falls in [b,c] and isn't too large
00163                 if (p<(0.75*cb*q-fabs(tol_act*q)/2.0) && p<fabs(prev_step*q/2.0))
00164                     new_step = p/q;// it is accepted
00165 
00166                 // If p/q is too large then the bissection procedure can reduce [b,c] range to more extent
00167             }
00168 
00169             // Adjust the step to be not less than tolerance
00170             if (fabs(new_step)<tol_act)
00171             {
00172                 if (new_step>0.0) new_step =  tol_act;
00173                 else              new_step = -tol_act;
00174             }
00175 
00176             // Save the previous approx
00177              a =  b;
00178             fa = fb;
00179 
00180             // Do step to a new approxim.
00181             b += new_step;
00182             fb = (_pInst->*_pF) (b, UserData);
00183 
00184             // Adjust c for it to have a sign opposite to that of b
00185             if ((fb>0.0 && fc>0.0) || (fb<0.0 && fc<0.0))
00186             {
00187                  c =  a;
00188                 fc = fa;
00189             }
00190         }
00191 
00192         // Did not converge
00193         throw new Fatal("Root::Solve: Brent method did not converge after %d iterations",It);
00194     }
00195     else if (Scheme=="Newton")
00196     {
00197         if (_pdFdx==NULL) throw new Fatal("Root:Solve: Newton method: For this method, dFdx cannot be NULL");
00198         double fa = (_pInst->*_pF) (xa, UserData);
00199         double fb = (_pInst->*_pF) (xb, UserData);
00200         if (fabs(xb-xa)<Tol) throw new Fatal("Root:Solve: Newton method: |xb-xa| must be greater than %g",Tol);
00201         if (fabs(fb-fa)<Tol) throw new Fatal("Root:Solve: Newton method: |fb-fa| must be greater than %g",Tol);
00202         double alp  = (x_guess==NULL ? fa/(fa-fb) : ((*x_guess)-xa)/(xb-xa));
00203         double xsol = xa + alp*(xb-xa);
00204         for (It=0; It<MaxIt; ++It)
00205         {
00206             double F    = (_pInst->*_pF)    (xsol, UserData);
00207             double dFdx = (_pInst->*_pdFdx) (xsol, UserData);
00208             double xnew = xsol - F/dFdx;
00209             double err  = fabs((xnew-xsol)/(1.0+xnew));
00210             if (Verbose) printf("alp=%g, xsol=%g, F=%g, dFdx=%g, F/dFdx=%g, xnew=%g, err=%g\n", alp, xsol, F, dFdx, F/dFdx, xnew, err);
00211             if (err<Tol) return xnew;
00212             xsol = xnew;
00213         }
00214 
00215         // Did not converge
00216         throw new Fatal("Root::Solve: Newton-Rhapson method did not converge after %d iterations",It);
00217     }
00218     else throw new Fatal("Root::Solve: scheme==%s is not available",Scheme.CStr());
00219     return 0;
00220 }
00221 
00222 }; // namespace Numerical
00223 
00224 #endif // MECHSYS_ROOT_H
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines