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
1.2.7