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

num_minimize1.h

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 // 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 doxygen1.2.7