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 // adaptive.h : contains class adaptive<> and function adaptive_fill() 00032 // 00033 // ******************************************************************** 00034 // adaptive_fill(): 00035 // 00036 // Fills and builds an interpolator (using class adaptive<>) so that it 00037 // approximates a given function. 00038 // 00039 // usage: 00040 // 00041 // Given a function f() returning a Y_type: 00042 // Y_type f(double x); 00043 // 00044 // And an interpolator of Y_types: 00045 // interpolator<Y_type> Y; 00046 // 00047 // calling: 00048 // adaptive_fill(Y, f, min_x, max_x); // to use default tolerances 00049 // (or) adaptive_fill(Y, f, min_x, max_x, abs_error, rel_error); 00050 // 00051 // will build Y so that Y(x) approximates f(x) within the range 00052 // [min_x .. max_x]. adaptive_fill() returns an int which equals 0 if all 00053 // went well. The tolerances will be calculated using the default global 00054 // norm() function for Y_type objects: double norm(Y_type); 00055 // 00056 // ******************************************************************** 00057 // Class adaptive<>: 00058 // 00059 // A templated class used to adaptively build an interpolator 00060 // (cf interpolate.h) of an arbitrary function which meets specified 00061 // accuracy targets. If the default norm() function is adequate, function 00062 // adaptive_fill() is a simple alternative (see below). 00063 // 00064 // Given a function f(x) which calculates some Y_type object as a function 00065 // of the double x, we wish to approximate this function using an 00066 // interpolator Y(x). Class adaptive accomplishes the task of building 00067 // Y(x). 00068 // 00069 // Here's how to use it: 00070 // 00071 // double x; 00072 // Y_type f(double); // The function to be interpolated. It could 00073 // // return a const Y_type & instead. 00074 // 00075 // ** Note that if f() is a MEMBER FUNCTION of a class object, it can't ** 00076 // ** be used directly by class adaptive. It must be used as described ** 00077 // ** below!!! ** 00078 // 00079 // interpolator<Y_type> Y; // declare the interpolator object, then 00080 // adaptive<Y_type> a(Y); // construct an adaptive object from it. 00081 // 00082 // a.min_x = x1; 00083 // a.max_x = x2; // set the interpolation limits 00084 // 00085 // int bad = a(f); // this call fills and builds Y to approximate f. 00086 // or // The optional second argument is a function for 00087 // int bad = a(f,n); // the "squared magnitude" or "norm" of a Y_type y. 00088 // // If no second argument is provided, a will use 00089 // // the global function norm(y), if it is defined. 00090 // 00091 // if(bad) ... // bad == 1 if a problem occured with the build. 00092 // 00093 // If either the function to be interpolated or the norm to be used is 00094 // defined as a member function, it must be converted for use with adaptive. 00095 // If, for example, the function to be interpolated is a member function 00096 // of the object Obj of class Cl: ie, f(x) is really Obj.f(x), then the 00097 // function must be passed to a() as follows: 00098 // 00099 // a(member_function(& Cl::f, Obj)) 00100 // 00101 // member_function() returns a function object properly bound to Obj.f() for 00102 // use by adaptive. This extra step is required because pointers to member 00103 // functions (ie, the name of a member function) are not pointers to functions. 00104 // The same conversion must be applied to a norm function as well, if it is 00105 // really a member function of some class. 00106 // 00107 // 00108 // Now we can use the interpolator to approximate f(x): 00109 // 00110 // Y_type approx_f = Y(x); 00111 // 00112 // If you wish to change the interpolation, use Y.clear() to throw away 00113 // the old points, then a(f) to rebuild Y. 00114 // 00115 // There are member variables for adaptive which control the accuracy and 00116 // other aspects of approximation; see the class declaration below for 00117 // details. 00118 // 00119 // ******************************************************************** 00120 // Change history: 00121 // 00122 // 7/19/99: Added adaptive_fill() 00123 // 7/8/99: Moved some definitions into num_utility.h 00124 // 00125 // ******************************************************************** 00126 00127 #ifndef ADAPTIVE_H 00128 #define ADAPTIVE_H 00129 00130 #include "interpolate.h" 00131 #include "matmath.h" 00132 #include "error.h" 00133 #include <set> 00134 #include "num_utility.h" 00135 00136 template < class Y_type > // Y_type is the type of object we're interpolating 00137 class adaptive 00138 { 00139 public: 00140 00141 // the constructor must be called with an interpolator<Y_type>, which it 00142 // will at a later time adaptively fill with data to meet the desired 00143 // interpolation accuracy. 00144 00145 explicit adaptive( interpolator<Y_type> & Y_interp ); 00146 00147 00148 // the following variables control or limit the adaptive sampling process 00149 // (default values in parentheses) 00150 00151 unsigned min_points; // (25) the minimum number of points in the interpolator 00152 unsigned max_points; // (1000) the maximum number of points allowed 00153 unsigned recursion_limit; // (10) the max allowable number of interval subdivisions 00154 00155 // if either of the following accuracy limits is met, then accuracy is o.k: 00156 double abs_tolerance; // (1.0e-100) the max absolute error target in the interpolation 00157 double rel_tolerance; // (1.0e-6) the max relative error target in the interpolation 00158 00159 double min_x; // (0.0) the minimum x for the range being interpolated 00160 double max_x; // (0.0) the maximum x for the range being interpolated 00161 00162 00163 // The following function (operator ()) actually builds the interpolator, filling 00164 // it with appropriate data points in order to meet the accuracy targets. It returns 00165 // 0 if all went well, 1 if something went wrong (an appropriate error msg will be 00166 // generated in this case as well.) It must be called with a function object to be 00167 // interpolated and a function object returning a double "norm", or "magnitude 00168 // squared" of a Y_type object. The function object f must have an operator () 00169 // defined which takes a single double argument and returns a Y_type or a const 00170 // reference to a Y_type, that is, it will be called like: "Y_type y = f(x)", 00171 // x a double. The function object n must have an operator () defined which takes a 00172 // single Y_type argument and returns a double which represents the squared 00173 // "magnitude" of the argument; it will be compared to rel_tolerance*rel_tolerance 00174 // and abs_tolerance*abs_tolerance. If no norm function is specified, a default of 00175 // the globally-defined funtion double norm(Y_type) is used, if it exists. 00176 00177 template <class F, class N> 00178 int operator ()(F f, N n) { return build(f,n); } 00179 00180 template <class F> 00181 inline int operator ()(F f) { return (*this)(f,norm); } 00182 00183 00184 private: 00185 00186 interpolator <Y_type> & Y; // the interpolator to build 00187 set<double> chk_pts; // temporarily holds x values during builds 00188 template <class F, class N> int build(F, N); 00189 template <class F> double init(F); 00190 template <class F, class N> void check_and_add(F, N, double); 00191 00192 // the following functional provides access to the overloaded global norm(). 00193 default_norm<Y_type> norm ; 00194 00195 }; // end of adaptive class declaration 00196 00197 00198 template < class Y_type > 00199 inline adaptive<Y_type>::adaptive( interpolator<Y_type> & Y_interp ) 00200 : 00201 min_points ( 25 ), 00202 max_points ( 1000 ), 00203 recursion_limit ( 10 ), 00204 abs_tolerance ( 1.0e-100 ), 00205 rel_tolerance ( 1.0e-6 ), 00206 min_x ( 0.0 ), 00207 max_x ( 0.0 ), 00208 Y ( Y_interp ) 00209 { } 00210 00211 00212 template < class Y_type > template <class F, class N> 00213 inline int adaptive<Y_type>::build(F f, N n) 00214 { 00215 int r; // counts down recursive interval subdivisions 00216 double dx = init(f); // fill interpolator with initial points 00217 00218 // the recursive build loop 00219 for (r = recursion_limit; r && chk_pts.size(); --r, dx /= 2.0) { 00220 check_and_add(f, n, dx); // checks accuracy and picks new points to add 00221 Y.build(); // rebuild the interpolator with more points 00222 if(Y.size() >= max_points) break; 00223 // normal exit is for chk_pts to be empty 00224 } 00225 00226 // check for an abnormal termination of the build loop 00227 if(chk_pts.size()) { 00228 if (r == 0) 00229 error::warning("Recursion limit reached in adaptive interpolator build."); 00230 else if (Y.size() >= max_points) 00231 error::warning("Table size limit reached in adaptive interpolator build."); 00232 return 1; 00233 } 00234 00235 // everthing went as planned 00236 return 0; 00237 00238 } 00239 00240 00241 template < class Y_type > template < class F > 00242 inline double adaptive<Y_type>::init(F f) 00243 { 00244 // initializes: 00245 // (1) the interpolator with min_points/2 + 1 equally-spaced points 00246 // (2) chk_pts with the midpoints of the intervals between points 00247 // (3) dx (returned value) of 1/4 the interval between points 00248 // Since any points in chk_pts get added to the interpolator, we 00249 // anticipate this by not using min_points initially. 00250 00251 double x1, x2, dx; 00252 unsigned i; 00253 00254 dx = (max_x - min_x)/(min_points/2); // interval between points 00255 Y.add(min_x,f(min_x)); 00256 for (i = 1, x1 = min_x+dx, x2 = min_x+dx/2.0; 00257 i <= min_points/2; 00258 ++i, x1 += dx, x2 += dx) { 00259 Y.add(x1,f(x1)); // adds points to the interpolator 00260 chk_pts.insert(x2); // interval midpoints to be checked for accuracy 00261 } 00262 00263 Y.build(); // initial build of the interpolator 00264 return dx/4.0; // the interval at which new check points should be added to s 00265 } 00266 00267 00268 template < class Y_type > template <class F, class N> 00269 inline void adaptive<Y_type>::check_and_add(F f, N n, double dx) 00270 { 00271 // steps through the points in chk_pts (which are sorted and unique, since 00272 // chk_pts is of type set. The value of dx SHOULD BE 1/4 the minimum 00273 // separation between adjacent points, and determines the spacing to new 00274 // points which may be added. n() is the norm function for a Y_type. 00275 00276 set<double>::iterator i; // iterator into chk_pts 00277 double x; 00278 double abs_lim = abs_tolerance*abs_tolerance; // since norm() = magnitude^2 00279 double rel_lim = rel_tolerance*rel_tolerance; 00280 00281 // examine and delete each point in chk_pts; add others if required 00282 for (i = chk_pts.begin(); i != chk_pts.end(); /* no increment here */) { 00283 00284 // destructively fetch the next x from s and increment iterator: 00285 x = *i; 00286 chk_pts.erase(i++); 00287 00288 // get f(x) and Y(x) and compare them 00289 Y_type y = f(x); 00290 Y.add(x,y); // add it to the interpolator, since we calculated it 00291 double normf = n(y); // for relative error calculation 00292 y -= Y(x); // the error 00293 double error = n(y); // the norm of the error 00294 00295 // examine norm of the error and add points to s if necessary 00296 if (error > abs_lim && (normf == 0.0 || error/normf > rel_lim)) { 00297 // accuracy insufficient; add additional points 00298 chk_pts.insert(i, x+dx); 00299 chk_pts.insert(i, x-dx); 00300 } 00301 } 00302 } 00303 00304 00305 // ******************************************************************** 00306 // fill an interpolator with values to interpolate the function f: 00307 00308 template < class Y_type, class F > 00309 inline void adaptive_fill( 00310 interpolator<Y_type> & fi, // the interpolator to fill 00311 F f, // the function to interpolate 00312 double min, double max, // interpolation x limits 00313 double abs_error = 0.0, // target absolute error 00314 double rel_error = 0.0 // target relative error 00315 ) // 0 gives the default errors 00316 { 00317 adaptive<Y_type> a(fi); 00318 a.min_x = min; a.max_x = max; 00319 if (abs_error != 0.0) a.abs_tolerance = abs_error; 00320 if (rel_error != 0.0) a.rel_tolerance = rel_error; 00321 a(f); 00322 } 00323 00324 00325 #endif /* ADAPTIVE_H */ 00326 00327
Please direct comments and corrections to
supermix@submm.caltech.edu
Go to the supermix home page
Generated by
1.2.7