Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

newton.cc

Go to the documentation of this file.
00001 // SuperMix version 1.0  C++ source file
00002 //
00003 // Copyright (c) 1999 California Institute of Technology.
00004 // All rights reserved.
00005 //
00006 // Redistribution and use in source and binary forms for noncommercial
00007 // purposes are permitted provided that the above copyright notice and
00008 // this paragraph are duplicated in all such forms and that any
00009 // documentation and other materials related to such distribution and
00010 // use acknowledge that the software was developed by California
00011 // Institute of Technology. Redistribution and/or use in source or
00012 // binary forms is not permitted for any commercial purpose. Use of
00013 // this software does not include a permitted use of the Institute's
00014 // name or trademark for any purpose.
00015 //
00016 // DISCLAIMER:
00017 // THIS SOFTWARE AND/OR RELATED MATERIALS ARE PROVIDED "AS-IS" WITHOUT
00018 // WARRANTY OF ANY KIND INCLUDING ANY WARRANTIES OF PERFORMANCE OR
00019 // MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE OR PURPOSE (AS SET
00020 // FORTH IN UCC 23212-2313) OR FOR ANY PURPOSE WHATSOEVER, FOR THE
00021 // LICENSED PRODUCT, HOWEVER USED.  IN NO EVENT SHALL CALTECH/JPL BE
00022 // LIABLE FOR ANY DAMAGES AND/OR COSTS, INCLUDING BUT NOT LIMITED TO
00023 // INCIDENTAL OR CONSEQUENTIAL DAMAGES OF ANY KIND, INCLUDING ECONOMIC
00024 // DAMAGE OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF
00025 // WHETHER CALTECH/JPL SHALL BE ADVISED, HAVE REASON TO KNOW, OR IN
00026 // FACT SHALL KNOW OF THE POSSIBILITY.  THE USER BEARS ALL RISK
00027 // RELATING TO QUALITY AND PERFORMANCE OF THE SOFTWARE AND/OR RELATED
00028 // MATERIALS.
00029 //
00030 // ************************************************************************
00031 //
00032 // Implementation of Newton-Raphson solver for class newton
00033 // Based on Numerical Recipes in C, Section 9.7
00034 //
00035 // J. Z.  7/13/98
00036 //
00037 // 4/20/00:  uses drand48()/srand48() rather than drandom(); no longer
00038 //           Numerical Recipes code in solve(), although the algorithm is.
00039 // 11/10/98: minor change to support new vector access
00040 //
00041 // ************************************************************************
00042 
00043 #include <math.h>
00044 #include "error.h"
00045 #include "newton.h"
00046 
00047 // the following includes are needed only for generating random numbers
00048 #include <stdlib.h>
00049 #include <time.h>
00050 
00051 
00052 // helper routines declared static
00053 
00054 // return the larger of two doubles
00055 static inline double fmax(double a, double b)
00056 { return (a > b) ? a : b; }
00057 
00058 // return maximum absolute value found in a vector
00059 static inline double vabs_max(const real_vector &v)
00060 { return sqrt(max_norm(v)); }
00061 
00062 
00063 // ************************************************************************
00064 // default constructor initializes the parameters which control
00065 // the root finder and seeds drand48()
00066 
00067 newton::newton() :
00068   max_iter(100),
00069   f_tol(1.e-6),  
00070   F_tol(1.e-8),
00071   dx_tol(1.e-7),
00072   rate_factor(1.e-4)
00073 { srand48(time(0)); }
00074 
00075 
00076 
00077 // ************************************************************************
00078 // solve(): the main solver routine
00079 
00080 void newton::solve()
00081 {
00082   solution_flag = 1 ;             // No solution found yet
00083 
00084   int ixmin = xlast.minindex() ;  // index limits on xlast; we'll use often
00085   int ixmax = xlast.maxindex() ;
00086   int ixnum = ixmax - ixmin +1 ;
00087 
00088   // check that xlast isn't empty
00089   if(ixmax < ixmin) {
00090     error::warning("Empty vector xlast in newton::solve()") ;
00091   }
00092 
00093   // calculate fval and Jacobian matrix at initial point, in xlast
00094   calc() ;
00095 
00096   // ---------------------------------------------------------------------
00097   // Check if fval and Jacobian have the right size and indexing
00098 
00099   int ifmin = fval.minindex() ;
00100   int ifmax = fval.maxindex() ;
00101   if(ifmax-ifmin != ixmax-ixmin) {
00102     error::warning("Number of equations and unknowns do not match in"
00103                    " newton::solve()") ;
00104     return ;
00105   }
00106   if(Jacobian.Rminindex() != ixmin || 
00107      Jacobian.Rmaxindex() != ixmax) {
00108     error::warning("Right index of Jacobian does not match xlast in"
00109                    " newton::solve()") ;
00110     return ;
00111   }
00112     if(Jacobian.Lminindex() != ifmin || 
00113      Jacobian.Lmaxindex() != ifmax) {
00114     error::warning("Left index of Jacobian does not match fval in"
00115                    " newton::solve()") ;
00116     return ;
00117   }
00118 
00119   // Check if by sheer luck we're already at the solution
00120   if(vabs_max(fval) < 0.01*f_tol) {
00121     solution_flag = 0 ;
00122     return ;
00123   }
00124 
00125 
00126   // ---------------------------------------------------------------------
00127   // now the fun begins:
00128 
00129   real_vector x(xlast); // will store solution here
00130   double fold ;         // result from previous iteration
00131   real_vector xold(x);  // result from previous iteration
00132   real_vector gradf(x); // will hold the gradient of f
00133   real_vector p(x);     // will hold the Newton-Raphson step
00134 
00135   // solve() tries to make sure the following value is always shrinking
00136   double f = 0.5*norm(fval) ; // norm of fval should be 0 at the solution
00137 
00138   // some variables we will need in the main iteration loop:
00139   double slope ;       // directional derivative of f along step direction
00140   double test, temp ;  // used to find a value from vector elements
00141   int i ;              // loop index over vector elements
00142   double lambda_min, lambda ;      // control size of step
00143   double rhs1, rhs2, a, b, disc ;  // used in polynomial calculations
00144   double f2 = 0, fold2 = 0, lambda_tmp = 0, lambda2 = 0 ; // misc temp's
00145 
00146   // ---------------------------------------------------------------------
00147   // Here's the main iteration loop
00148 
00149   for(int its = 1; its <= max_iter; ++its) {
00150 
00151     gradf = fval * Jacobian ;  // gradf = 1/2 gradient(fval*fval)
00152     xold = x ;
00153     fold = f ;
00154 
00155     // ---------------------------------------------------------------------
00156     // Calculate ordinary Newton-Raphson step (using matmath's solve() here)
00157     p = ::solve(Jacobian, -fval) ;
00158 
00159     // check if Jacobian was singular
00160     if(p.maxindex()-p.minindex() != ixnum-1) {
00161       // p is not the right size, so it was. Take a random search direction
00162       p.resize(xlast).maximize() ;
00163       for(i=p.minindex(); i<=p.maxindex(); i++)  p[i] = drand48()*maxstep ;
00164     }
00165 
00166     // Limit length of p to no more than maxstep
00167     test = sqrt(norm(p)) ;
00168     if(test > maxstep)  p *= maxstep/test ;
00169 
00170 
00171     // ---------------------------------------------------------------------
00172     // Do a search along the direction of p for a good value of lambda
00173 
00174     // Calculate rate of change of f along p
00175     slope = dot(gradf, p) ;
00176     if(slope > 0.) {    // should only happen for random search directions
00177       slope = -slope ;
00178       p = -p ;
00179     }
00180       
00181     // Compute lambda_min
00182     test = 0. ;
00183     for(i = ixmin; i <= ixmax; ++i) {
00184       temp  = fabs(p[i])*fmax(fabs(xold[i]),1.0) ;
00185       if(temp > test)  test = temp ;
00186     }
00187     lambda_min = dx_tol/test ;
00188    
00189     // Here's the search:
00190     bool check = false ;  // goes true if we might be at a local min
00191     bool done  = false ;  // goes true when we've found a lambda
00192     lambda = 1.0 ;        // the full Newton-Raphson step
00193     while(!done) {
00194 
00195       x = xold + lambda*p ;   // Try new position x
00196       calc(x) ;               // and calculate function & Jacobian
00197       f = 0.5*norm(fval) ;    // and a new f.
00198 
00199       if(lambda < lambda_min) {
00200         // The new x and old x are very close
00201         x = xold ;
00202         check = done = true ;
00203       }
00204 
00205       else if(f <= fold + rate_factor*lambda*slope) {
00206         // OK, function decreasing
00207         done = 1;
00208       }
00209 
00210       else {
00211         // Not OK, function not decreasing, we need to backtrack
00212         if(lambda == 1.0)    
00213           // first time through while(), use quadratic approx
00214           lambda_tmp = -slope/(2.0*(f-fold-slope)) ;
00215         else {
00216           // not first time, so use cubic approx
00217           rhs1 = f-fold-lambda*slope ;
00218           rhs2 = f2-fold2-lambda2*slope ;
00219           a = (rhs1/(lambda*lambda)-rhs2/(lambda2*lambda2))/(lambda-lambda2) ;
00220           b = (-lambda2*rhs1/(lambda*lambda)+lambda*rhs2/(lambda2*lambda2))
00221             /(lambda-lambda2) ;
00222           if(a == 0.) 
00223             lambda_tmp = -slope/(2.*b) ;
00224           else {
00225             disc = b*b-3.*a*slope ;
00226             if(disc<0.0) {
00227               error::warning("Roundoff problem in newton::solve()") ;
00228               return ;
00229             }
00230             else
00231               lambda_tmp = (-b+sqrt(disc))/(3.*a) ;
00232           }
00233           if(lambda_tmp > 0.5*lambda)
00234             lambda_tmp = 0.5*lambda ;
00235         }
00236 
00237       } // else Not OK, ...
00238 
00239       lambda2 = lambda ;
00240       f2 = f ;
00241       fold2 = fold ;
00242       lambda = fmax(lambda_tmp, 0.1*lambda) ;
00243 
00244     } // while(!done)
00245 
00246 
00247     // ---------------------------------------------------------------------
00248     // Now we perform the convergence checks
00249 
00250     // Test for convergence of function values
00251     if(vabs_max(fval) < f_tol) {
00252       solution_flag = 0 ;
00253       return ;
00254     }
00255     
00256     // Test if the gradient of f is almost zero
00257     if(check) {
00258       test = 0. ;
00259       for(i = ixmin; i <= ixmax; i++) {
00260         temp = fabs(gradf[i])*fmax(fabs(x[i]),1.0) ;
00261         if (temp > test) test = temp ;
00262       }
00263       test /= fmax(ixnum*0.5, f);
00264 
00265       if(test < F_tol) {  // Too bad, didn't find a solution...
00266         error::warning("newton::solve() converged to a local minimum"
00267                        " instead of finding a root");
00268         return ;
00269       }
00270     }
00271 
00272     // Test convergence on x
00273     test = 0. ;
00274     for(i = ixmin; i <= ixmax; i++) {
00275       temp = fabs(x[i] - xold[i])/fmax(fabs(x[i]),1.0) ;
00276       if(temp > test)   test = temp ;
00277     }
00278     if(test < dx_tol) {  // We've converged
00279       solution_flag = 0 ;
00280       return ;
00281     }
00282 
00283   } // Main for(;;) loop
00284 
00285   // ---------------------------------------------------------------------
00286   // We've dropped out of the iteration loop without converging, so:
00287   error::warning("Maximum number of iterations exceeded in newton::solve()") ;
00288   return ;
00289   
00290 }

Please direct comments and corrections to supermix@submm.caltech.edu
Go to the supermix home page
Generated by doxygen1.2.7