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 // Definitions of functions in minimize1.h 00032 // 00033 // F. Rice 4/9/99 00034 // 00035 // Changes: 00036 // 00037 // 8/17/99: Fixed an error in the parabolic formula in refine_minimum() 00038 // 00039 // ******************************************************************** 00040 00041 #include "error.h" 00042 00043 // some helper functions, enclosed in a class for global namespace conservation: 00044 struct minimize1_util 00045 { 00046 // golden ratio for searches 00047 static inline double gold() { return 1.61803399; } 00048 static inline double Cgold() { return 2 - gold(); } 00049 00050 // and a couple of utilities: 00051 static inline void shift(double & a, double & b, double & c, const double & d) 00052 { a = b; b = c; c = d; } 00053 static inline double fmax(double a, double b) 00054 { return (a > b) ? a : b;} 00055 }; 00056 00057 // ******************************************************************** 00058 00059 template <class F> inline 00060 int bracket_minimum( F f, // the function 00061 double & a, double & b, double & c, // x values 00062 double & fa, double & fb, double & fc, // f(x) values 00063 unsigned n_max ) 00064 { 00065 // must be given two different points a and b: 00066 if (a == b) { 00067 c = a; 00068 fc = fb = fa = f(a); 00069 return 1; 00070 } 00071 00072 // rearrange a and b so that f(a) >= f(b): 00073 fa = f(a); 00074 fb = f(b); 00075 if (fb > fa) { 00076 double t; 00077 minimize1_util::shift(t,a,b,t); // note how this call simply swaps a and b 00078 minimize1_util::shift(t,fa,fb,t); 00079 } 00080 00081 // find a useable third point c, such that fc != fb: 00082 c = minimize1_util::gold()*(b-a) + b; // default is to expand outward by golden ratio 00083 fc = f(c); 00084 unsigned n; // iteration counter 00085 for (n = 0; n < n_max && fc == fb; ++n) { 00086 c *= minimize1_util::gold(); c -= (minimize1_util::gold()-1)*b; // continue expanding 00087 fc = f(c); 00088 } 00089 if (n >= n_max) return 1; // couldn't find a suitable c 00090 00091 if (fc > fb) { 00092 // we may be done... 00093 if (fa > fb) return 0; 00094 else { 00095 // must have had fa == fb; swap c and a 00096 double t; 00097 minimize1_util::shift(t,a,c,t); 00098 minimize1_util::shift(t,fa,fc,t); 00099 } 00100 } 00101 00102 // here, we must have either: fa >= fb > fc ; or: fa > fb >= fc 00103 00104 // now refine guesses: 00105 for ( ; n < n_max && fb >= fc; ++n) { 00106 double t1 = (b-a)*(fb-fc); 00107 double t2 = (b-c)*(fb-fa); 00108 00109 if (t1 != t2) { 00110 // (a,fa),(b,fb),(c,fc) are not colinear, so 00111 // we try a quadratic fit to a,b,c to find a possible minimum 00112 double t = b - 0.5*((b-a)*t1 - (b-c)*t2)/(t1-t2); 00113 double ft = f(t); 00114 00115 if ((b-t)*(t-c) > 0.0) { 00116 // t is between b and c, so we must have had fa > fb 00117 if (fc > ft) { 00118 // t is a minimum; fix a and b and return 00119 a = b; fa = fb; 00120 b = t; fb = ft; 00121 return 0; 00122 } 00123 else if (ft > fb) { 00124 // b is a minimum 00125 c = t; fc = ft; 00126 return 0; 00127 } 00128 // fb >= ft >= fc, so this t is of no help; use default expansion 00129 } 00130 00131 else if ((b-c)*(c-t) > 0.0 && ft > fc) { 00132 // c is a minimum between b and t 00133 minimize1_util::shift(a,b,c,t); 00134 minimize1_util::shift(fa,fb,fc,ft); 00135 return 0; 00136 } 00137 00138 } // if (t1 != t2) 00139 00140 // eliminate "oldest" point, add an expansion by gold and loop 00141 minimize1_util::shift(a,b,c, minimize1_util::gold()*(c-b)+c); 00142 minimize1_util::shift(fa,fb,fc, f(c)); 00143 00144 } // for 00145 00146 // here either fb < fc or iteration limit was reached 00147 return (n >= n_max || fa == fb)? 1 : 0; 00148 00149 } // bracket_minimum() 00150 00151 // ******************************************************************** 00152 00153 template <class F> inline 00154 double refine_minimum( F f, // the function 00155 double & a, double & b, double & c, // x values 00156 double tol, unsigned n_max) 00157 { 00158 // order a and c: 00159 if (a > c) { 00160 double t; 00161 minimize1_util::shift(t,a,c,t); // note how this call simply swaps a and c 00162 } 00163 00164 // fix up a too small tol: 00165 tol = minimize1_util::fmax(tol, 1.0e-7); 00166 00167 // temporary working variables: 00168 double x = b, // location with the min value of f(x) seen so far 00169 w = b, // location with the 2nd least f(x) seen 00170 v = b, // mostly the previous value of w 00171 u, // location where f(x) most recently evaluated 00172 dx = 0, ddx = 0, // delta x moved on this step and mostly step before last 00173 fx = f(b), fw = fx, fv = fx, fu; // function values 00174 00175 for (unsigned n = 0; n < n_max; ++n) { 00176 double abs_tol = tol*(fabs(x) + .001); // .001 keeps abs tol reasonable for small x 00177 double mid = 0.5*(a+c); 00178 00179 // check for convergence 00180 if(fabs(x - mid) <= 2*abs_tol - 0.5*(c-a)) { 00181 // half interval meets the tolerance requirement for convergence 00182 b = x; return fx; 00183 } 00184 00185 // if ddx is big enough, try parabolic acceleration 00186 if (fabs(ddx) > abs_tol) { 00187 double old_ddx = ddx; ddx = dx; 00188 00189 double t1 = (x-w)*(fx-fv); 00190 double t2 = (x-v)*(fx-fw); 00191 double num = (x-w)*t1 - (x-v)*t2; 00192 double denom = 2.0*(t2-t1); // (num/denom) is the parabolic step 00193 if (denom < 0.0) { num *= -1; denom *= -1; } // make denom positive 00194 00195 // check that parabolic step is small and keeps us within (a,c): 00196 if ( (fabs(num) < fabs(denom * 0.5 * old_ddx)) // step less than 0.5 ddx 00197 && (num > denom*(a-x)) // stay within (a,c) 00198 && (num < denom*(c-x)) ) { 00199 // take the parabolic step 00200 dx = num/denom; 00201 u = x + dx; // use u as a temporary 00202 if (u - a < 2*abs_tol || c - u < 2*abs_tol) 00203 // don't move too close to the endpoints 00204 dx = (mid > x) ? abs_tol : -abs_tol; 00205 } 00206 else { // parabolic step fails tests 00207 // take a golden section step instead 00208 ddx = (mid > x) ? c-x : a-x; 00209 dx = minimize1_util::Cgold() * ddx; 00210 } 00211 } 00212 00213 else { // ddx too small 00214 // take a golden section step instead 00215 ddx = (mid > x) ? c-x : a-x; 00216 dx = minimize1_util::Cgold() * ddx; 00217 } 00218 00219 // make sure dx is at least abs_tol; compute f() 00220 if (fabs(dx) < abs_tol) dx = (dx < 0) ? -abs_tol : abs_tol; 00221 u = x + dx; 00222 fu = f(u); 00223 00224 // now shrink the interval and decide what to do with u,x,v,w: 00225 if (fu <= fx) { 00226 // x becomes a new endpoint; u becomes x 00227 if (u < x) c = x; else a = x; 00228 minimize1_util::shift(v,w,x,u); 00229 minimize1_util::shift(fv,fw,fx,fu); 00230 } 00231 00232 else { 00233 // u becomes a new endpoint; x still the min seen so far 00234 if (u < x) a = u; else c = u; 00235 if (fu <= fw || w == x) { 00236 // then u becomes w 00237 v = w; fv = fw; w = u; fw = fu; 00238 } 00239 else if (fu <= fv || v == x || v == w) { 00240 // we'll at least make some use of u and f(u) 00241 v = u; fv = fu; 00242 } 00243 } 00244 00245 } // for 00246 00247 // normal exit of loop means there were too many iterations: 00248 error::warning("Iteration count reached in min_1d::refine_minimum()."); 00249 b = x; return fx; 00250 00251 } // refine_minimum() 00252 00253 // ******************************************************************** 00254 00255 template <class F> inline 00256 double minimize1( F f, // the function 00257 double & x1, double x2, // x values 00258 double tol, unsigned n_max) 00259 { 00260 double a = x2, c, fa, fx1, fc; 00261 int flag = bracket_minimum(f,a,x1,c,fa,fx1,fc,n_max); 00262 if (flag) { 00263 error::warning("Failed to find a minimum in minimize()."); 00264 return fx1; 00265 } 00266 else { 00267 return refine_minimum(f,a,x1,c,tol,n_max); 00268 } 00269 }
Please direct comments and corrections to
supermix@submm.caltech.edu
Go to the supermix home page
Generated by
1.2.7