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 // error_func.h
00032 //
00033 // Optimization error function classes for SuperMix.
00034 //
00035 // Contains:
00036 // class error_func
00037 // class error_term
00038 // class error_term_mode
00039 // class scaled_match_error_term
00040 //
00041 // Class error_func is a fully-implemented error function class which can
00042 // be used with optimizers derived from class minimizer (optimizer.h). It can
00043 // be used to build complicated error functions involving multiple terms and
00044 // sweeps over parameter ranges and provides for the optimization of any
00045 // number of program variables.
00046 //
00047 // The error term classes are abstract classes which provide the interface
00048 // between a single term in an error function calculation and the full
00049 // calculation performed by class error_func. Concrete implementations of
00050 // these classes for use with error_func can be found in the header file
00051 // error_terms.h. It is also very simple to write your own custom error term
00052 // class derived from one of these classes.
00053 //
00054 // 4/21/99 by John Ward
00055 //
00056 // Based on err_func.h written 7/22/98 J.Z.
00057 //
00058 // 9/21/00: Major error_func structural changes; added get_func_breakdown().
00059 // 9/15/00: Some minor changes, except major ico scaled_match_error_term
00060 // 9/11/00: Changed class simple_error_func to error_func_parameters
00061 // 9/8/00: Added more modes to error_term_mode; deleted flatness_error_term.
00062 //
00063 // ************************************************************************
00064 // Class error_func
00065 //
00066 // The concrete class "error_func" is an error function class that should be
00067 // suitable for most SuperMix simulations. Its error function value is built
00068 // up by summing the results from any number of more primitive calculations
00069 // (provided by objects derived from class "error_term"). Each of these
00070 // primitive terms may be individually weighted.
00071 //
00072 // By using sweeper objects (see sweeper.h), error_func can include error
00073 // terms whose values are calculated and summed over a swept range of parameter
00074 // values (such as a sweep over a frequency range); error_func manages the
00075 // sweepers and error terms so that the calculations are performed efficiently
00076 // in the case that multiple terms must be swept using the same sweeper. This
00077 // could be the case, for example, when summing a gain and a noise term over
00078 // a design frequency band.
00079 //
00080 // error_func is derived from error_func_parameters (see simple_error_func.h)
00081 // which provides the interface between an optimizer and the program parameters
00082 // to be optimized.
00083 //
00084 //
00085 // USAGE:
00086 //
00087 // Construction of a complicated error function calculation for a circuit
00088 // optimization problem using error_func can be divided into 7 steps:
00089 //
00090 // 1. Identify the circuit parameters which may need to be optimized and
00091 // define variables of type parameter to represent them.
00092 //
00093 // 2. Identify the other parameters which control the circuit operating
00094 // state (frequency, temperature, etc) and ensure that variables of
00095 // type parameter represent them as well.
00096 //
00097 // 3. Build the circuit model, using the parameters defined in steps
00098 // 1 and 2 to control its behavior.
00099 //
00100 // 4. Identify the individual circuit characteristics (S11, noise, etc.)
00101 // that are critical to the performance of the circuit. Choose or
00102 // write error terms that measure the performance of each characteristic
00103 // and create the error term objects.
00104 //
00105 // 5. Define sweeper objects which cover the appropriate ranges of the
00106 // operating state control parameters (from step 2) for the circuit
00107 // performance goals. Use sweeper::sweep() for parameters to be swept
00108 // (eg, a frequency sweep) and sweeper::initialize() for parameters
00109 // to be held constant (eg, SIS bias voltage). Use of initialize() is
00110 // especially important if one sweeper may be adjusting a control
00111 // parameter which must then be reset to a specific value for another
00112 // sweeper.
00113 //
00114 // 6. Create an error_func object. Use its vary() member function to
00115 // define the allowable limits and initial values for the parameters
00116 // to be optimized (from step 1). Use its add_term() member function
00117 // to weight the individual error terms (step 4) and associate them
00118 // with the appropriate sweepers for the control parameters (step 5).
00119 //
00120 // 7. Pass the error_func object as a parameter to the constructor for
00121 // the optimizer. The optimizer will communicate with the error
00122 // function using the abstract_error_func interface defined in
00123 // optimizer.h
00124 //
00125 // Refer to the header files simple_error_func.h and sweeper.h for more
00126 // information about controlling parameters (steps 1 and 2) using the
00127 // vary() member function (step 6) and sweepers (step 5). Error terms
00128 // (step 4) are described in more detail below; header file error_terms.h
00129 // defines several ready-to-use error term classes suitable for circuit
00130 // performance optimizations.
00131 //
00132 // Once the error function has been created and built, its value is
00133 // calculated using abstract_error_function::operator(). Here's an
00134 // example:
00135 //
00136 // error_func ef;
00137 // ... // calls to ef.vary() and ef.add_term()
00138 //
00139 // double error = ef(); // ef() calculates the error function and
00140 // // returns its value.
00141 //
00142 // The member function get_func_breakdown() returns a real_vector object
00143 // whose elements contain a term-by-term breakdown of the most recent
00144 // error function calculation, allowing you to check the weighted
00145 // error contribution of each term.
00146 //
00147 //
00148 // THE vary() MEMBER FUNCTION:
00149 //
00150 // Refer to the description of class error_func_parameters in header file
00151 // simple_error_func.h for a detailed description of the vary() function.
00152 // Here's its declaration and an example of its use, both from that file:
00153 //
00154 // abstract_real_parameter * vary(double min, double init, double max,
00155 // double units = 1.0);
00156 //
00157 // parameter R; // a parameter we need the optimizer to control
00158 // error_func ef; // our error function
00159 // R = ef.vary(10, 25, 100, Ohm); // Range: 10*Ohm <= R <= 100*Ohm
00160 // // Initial value: R = 25*Ohm
00161 //
00162 //
00163 // THE add_term() MEMBER FUNCTION:
00164 //
00165 // Class error_func builds up a full error function calculation by summing
00166 // individually-weighted terms. These terms may be calculated and averaged
00167 // over one or more sweeps of various operating state parameters. The
00168 // scheme for identifying, weighting and sweeping error terms is described
00169 // to an error_func object by calls to its add_term() member function.
00170 //
00171 // Individual error terms are created using classes derived from class
00172 // error_term (also described in this header file). Operating state
00173 // parameter sweeps are implemented using objects of class sweeper (see
00174 // sweeper.h). add_term() allows error_func to take control of these
00175 // objects.
00176 //
00177 // Here's how to use add_term():
00178 // Assume you've declared 2 error terms (derived from error_term) and
00179 // a sweeper, along with an error_func:
00180 //
00181 // my_term_1 term1;
00182 // my_term_2 term2;
00183 // sweeper sweep; // will be used with term2
00184 // error_func ef;
00185 //
00186 // Then:
00187 //
00188 // ef.add_term(2.0, term1); // a "sweeperless" term
00189 //
00190 // adds term1's error to the error function calculation with weight 2.
00191 // Whenever ef.func_value() is called by the optimizer, term1 is reset and
00192 // asked for its error value before any sweeps are started. Its error
00193 // value is multiplied by its weight (2.0 in this case) and added into
00194 // the error value to be returned by error_func ef.
00195 //
00196 // ef.add_term(1.0, term2, sweep); // a term with an associated sweeper
00197 //
00198 // adds term2 to the error function calculation with weight 1. Whenever
00199 // ef.func_value() is called, First sweep and then term2 will be reset, then
00200 // the sweeper will be swept, with term2 being calculated for each point of
00201 // the sweep. The error values returned by term2 will be averaged over the
00202 // total number of points in the sweep. This average value will be
00203 // multiplied by the specified weight (1.0 in this case) and added into the
00204 // error function value to be returned by error_func.
00205 //
00206 // More than one error term may be added with the same sweeper. When
00207 // func_value() executes, it sweeps sweepers with more than one error term
00208 // only once; for each point of the sweep the individual terms are calculated,
00209 // weighted, and added into their individual running sums. The final results
00210 // are divided by the number of points in the sweeper to get an average for
00211 // each term. func_value() uses the state_tag argument to errror_term::get()
00212 // to try to maximize the efficiency of the calculations (see the description
00213 // of class error_term).
00214 //
00215 // The algorithm for error_func::func_value() is described in more detail
00216 // in the declaration of class error_func.
00217 //
00218 //
00219 // THE get_func_breakdown() MEMBER FUNCTION:
00220 //
00221 // Once the error function has been calculated, calling get_func_breakdown()
00222 // returns a real_vector object (see SuperMix's vector.h) containing a
00223 // term-by-term breakdown of the error function value. Each element of the
00224 // real_vector contains the weighted error contribution of a single error
00225 // term created by a call to add_term(). The real_vector uses the Index_1
00226 // indexing mode (first valid element has index 1); the first element holds
00227 // the weighted result of the term added by the first call to add_term().
00228 // The breakdown can be displayed using "<<" for real_vectors, so:
00229 //
00230 // cout << ef.get_func_breakdown() << endl;
00231 //
00232 // outputs the term-by-term breakdown on a single line, the values separated
00233 // by " ".
00234 //
00235 // ************************************************************************
00236 // class error_term
00237 //
00238 // Class error_term is an abstract class that provides the interface between
00239 // simple error function terms and class error_func. Actual, useful, concrete
00240 // subclasses of error_term can be found in the file error_terms.h.
00241 //
00242 //
00243 // USAGE: WRITING YOUR OWN ERROR TERMS USING ERROR_TERM
00244 //
00245 // Defining your own error terms is very simple: just derive a class from
00246 // error_term in which you define the virtual function get(). Better yet,
00247 // check out abstract class error_term_mode (described below). This latter
00248 // class may actually be the class of choice for deriving your own error
00249 // terms. If you don't need the added sophistication of error_term_mode,
00250 // here's an example of an error term derived from class error_term:
00251 //
00252 // Suppose you have a function f(p1,p2) that you need to minimize. Then all
00253 // you would need in order to define an error term for this function is:
00254 //
00255 // // Define the new error term class:
00256 //
00257 // class my_term_class : public error_term {
00258 // public:
00259 //
00260 // // get() must return an error value given the current program
00261 // // state (eg, the current values of p1 and p2 for this example)
00262 //
00263 // double get(state_tag) { return f(p1,p2); }
00264 //
00265 // }; // don't forget the ";" after the "}"
00266 //
00267 //
00268 // // create an actual error term object (my_term) using the class:
00269 //
00270 // my_term_class my_term;
00271 //
00272 // where f(), p1, and p2 are global identifiers declared somewhere previously
00273 // in the source. Note that in this example we ignore the state_tag argument
00274 // in the call to get(). The use of state_tags is described later.
00275 //
00276 // IMPORTANT: Try to avoid side effects in derived classes of error_term.
00277 // An error term's get() function should attempt to leave the system in
00278 // the same state as before get() was called to prevent one error_term from
00279 // affecting another in a single error function calculation involving
00280 // multiple error terms.
00281 //
00282 //
00283 // USE OF THE error_term::reset() FUNCTION
00284 //
00285 // Some error term calculations involve saving results from previous get()
00286 // calls. This is useful if the function is performing some running sum
00287 // over a parameter sweep, for example, which it uses in its calculation.
00288 // The virtual function reset() is used to tell the error term that it
00289 // should clear any such previous results and prepare to start over. It is
00290 // called by class error_func, for example, before it begins a parameter
00291 // sweep. The default error_term::reset() does nothing, so if you don't
00292 // need the reset capability for your error term, then ignore it.
00293 //
00294 //
00295 // USE OF THE STATE_TAG ARGUMENT
00296 //
00297 // A state_tag is used to avoid repeating a lengthy function calculation
00298 // when more than one error term relies on the results of that calculation
00299 // for its error term value. Refer to the brief intro to state tags in the
00300 // header file state_tag.h before proceeding.
00301 //
00302 // Let's consider a specific example involving a circuit calculation,
00303 // although the state_tag mechanism could be useful in other contexts as
00304 // well. Assume your error function calculation needs to know the gain and
00305 // the noise of the circuit at a single operating condition (specific set
00306 // of program variable values). The circuit returns both gain and noise
00307 // when it calculates its response; this may be a complicated, lengthy
00308 // calculation that you wouldn't want to have the program repeat
00309 // unnecessarily. However, you use two error_term objects, one for gain
00310 // and one for noise. By using state_tag arguments, you can have each
00311 // term call the circuit's response function and still avoid the
00312 // recalculation.
00313 //
00314 // The idea is this: you define the response function (which calculates
00315 // both noise and gain, both returned in some data structure) so that it
00316 // accepts a state_tag argument. When the function is called, it compares
00317 // the state_tag argument to a saved copy of the argument provided on the
00318 // previous function call; if the two state_tags match, then the function
00319 // assumes that none of the parameters on which it depends has changed.
00320 // In this case it can just return a copy of the previously returned
00321 // output rather than perform a recalculation. This is the way that an
00322 // nport object's get_data(state_tag) member function works (see nport.h).
00323 //
00324 // As for an error_term implementation, all you would do is pass on the
00325 // state_tag argument in the error_term::get(state_tag) function to the
00326 // response function you are calling (it, of course, has to be defined so
00327 // that it accepts the state_tag argument as well). The error_term objects
00328 // just assume that the caller of get() knows what it is doing if it
00329 // sends along the same state_tag value to more than one get() call.
00330 //
00331 // ************************************************************************
00332 // class error_term_mode
00333 //
00334 // In many cases the function to be optimized must not be simply minimized,
00335 // but must be made to match, stay below, or exceed a given target value.
00336 // This class provides the calculating muscle to perform these or similar
00337 // optimizations using an error term.
00338 //
00339 // Class error_term_mode is a much-refined derivative of class error_term.
00340 // Most of the concrete error terms in error_terms.h are derived from this
00341 // class. It provides various "modes" for calculating an error term based
00342 // on a comparison of a function value and a target. These modes are
00343 // designed to be used in error function calculations using error_func with
00344 // sweepers.
00345 //
00346 //
00347 // CALCULATION OF THE ERROR VALUE:
00348 //
00349 // The basic error value (e) is given by the square of the difference between
00350 // a function value (f) and a target value (t). How this value is used in the
00351 // calculation of the returned error value is determined by the mode that has
00352 // been set:
00353 //
00354 // Mode Returned Error Value
00355 // ---- --------------------
00356 // MATCH, M_MATCH, FLAT return e
00357 // ABOVE, M_ABOVE if (f < t) return e; else return 0
00358 // BELOW, M_BELOW if (f > t) return e; else return 0
00359 //
00360 // The target value is set by the user before the error term's get() function
00361 // is called, for all modes except FLAT. In the case of mode FLAT, the target
00362 // value is calculated internally by the error term. The error calculation is
00363 // performed by error_term_mode::checkval(double), which considers the mode,
00364 // the target, and any internally saved results of previous error
00365 // calculations. Its argument is the function value; it returns the
00366 // calculated error:
00367 //
00368 // checkval(double f); // using the saved target value and other data
00369 // // (depending on the error mode), return the
00370 // // error value generated by function value f.
00371 //
00372 //
00373 // DETAILED MODE DESCRIPTIONS:
00374 //
00375 // MATCH, ABOVE, BELOW:
00376 // A fixed target value is specified by the user. Anytime checkval() is
00377 // called, the argument is compared with the target and an error value
00378 // is generated as described in the table above. These modes have no
00379 // "memory" of previous error values returned. When error_func sweeps
00380 // an error term using one of these modes, it calculates the average
00381 // value of the error over the points in the sweep.
00382 //
00383 // M_MATCH, M_ABOVE, M_BELOW:
00384 // A fixed target value is specified by the user. These modes are
00385 // designed to be used in an error term with a sweeper. When error_func
00386 // sweeps an error term using one of these modes, then instead of
00387 // calculating the average value of the error over the sweep, it
00388 // calculates the MAXIMUM ERROR VALUE seen during the sweep. The code
00389 // in checkval() ensures that this result is returned by keeping a
00390 // running tally of the number of points seen so far in the sweep and
00391 // the maximum error value seen (both internal variables are cleared
00392 // by the reset() member function). When a new maximum error is
00393 // encountered, checkval() returns that value plus a correction which
00394 // compensates for the lower error values it had returned on previous
00395 // calls. Because of this correction, when error_func calculates the
00396 // mean error value upon sweep completion, the result is the maximum
00397 // error value encountered during the sweep. ( the M_ prefix is a
00398 // mnemonic for "maximum error").
00399 //
00400 // FLAT:
00401 // Like the M_-prefixed modes, mode FLAT is designed to be used in an
00402 // error term with a sweeper. Unlike the other modes, no fixed target
00403 // value need be specified; FLAT uses the mean value of the function
00404 // as the target value, so this error term mode measures the "flatness"
00405 // of the function value over the range of a sweep. The code in
00406 // checkval() essentially keeps a running calculation of the function
00407 // mean value. When it returns an error value, it also includes a
00408 // correction for all previously returned values to compensate for
00409 // changes in the function's mean as the sweep proceeds. Consequently,
00410 // when error_func calculates the mean error value upon sweep completion,
00411 // the result is the mean squared deviation from the mean of the
00412 // function value. Calling reset() sets up the error term to start the
00413 // calculation over again from scratch.
00414 //
00415 // IMPORTANT: One must be precise when defining "flat." Returning a
00416 // simple value, such as the magnitude of S21, will minimize the absolute
00417 // deviation from flatness. This may have the unwanted side effect of making
00418 // S21 go to zero. To avoid this problem, either include another error term
00419 // which gets large if S21 gets too small, or optimize the flatness of
00420 // log(S21). This way, the fractional deviation from flatness will be
00421 // minimized instead of the absolute deviation.
00422 //
00423 //
00424 // USAGE:
00425 //
00426 // Use one of the provided error terms derived from class error_term_mode
00427 // (see error_terms.h) or derive a new class of your own. Set the error
00428 // term mode and target value using error_term_mode member functions or
00429 // whatever other methods are available for the specific error term you
00430 // are using. Add the error term to an error_func object using
00431 // error_func::add_term().
00432 //
00433 // Member Functions for Setting the Target and Mode:
00434 //
00435 // target(t); // set the target value to t (a double)
00436 //
00437 // match() // set the mode as indicated; target is unchanged
00438 // above()
00439 // below()
00440 // flat()
00441 // worst_match() // set mode to M_MATCH
00442 // worst_above() // M_ABOVE
00443 // worst_below() // M_BELOW
00444 //
00445 // match(t) // set both the mode and target (t a double)
00446 // above(t)
00447 // below(t)
00448 // worst_match(t)
00449 // worst_above(t)
00450 // worst_below(t)
00451 //
00452 // Each of the above functions returns a reference to error_term_mode which
00453 // refers to the error term object, so they can be concatenated using ".":
00454 // ef.match().target(10); is the same as: ef.match(10);
00455 //
00456 //
00457 // WRITING YOUR OWN error_term_mode CLASS:
00458 //
00459 // Here's an example, similar to the one included for class error_term.
00460 // Again assume you have some function f(p1,p2) to be optimized, only this
00461 // time you want to compare its value to a target using the capabilities
00462 // of class error_term_mode. You could write:
00463 //
00464 // class my_term_class : public error_term_mode {
00465 // public:
00466 //
00467 // // get() uses error_term_mode::checkval() to do the comparison:
00468 // double get(state_tag) { return checkval(f(p1,p2)); }
00469 //
00470 // } my_term; // the actual error term object is "my_term"
00471 //
00472 // where f(), p1, and p2 are global identifiers declared somewhere previously
00473 // in the source. Note that in this example we ignore the state_tag argument
00474 // in the call to get(). Now we can use our object my_term with an error_func:
00475 //
00476 // error_func ef;
00477 // sweeper sweep;
00478 // ef.add_term(1.0, my_term, sweep);
00479 // my_term.above(0.5);
00480 //
00481 // If you would like to be able to initialize the mode and target to some
00482 // particular combination at construction, then you could include a constructor
00483 // in your class definition:
00484 //
00485 // my_term_class(error_term_mode::mode m, double t)
00486 // : error_term_mode(m,t) { }
00487 //
00488 // The type error_term_mode::mode is an enumeration of the available modes:
00489 //
00490 // enum error_term_mode:mode
00491 // {MATCH, ABOVE, BELOW, FLAT, M_MATCH, M_ABOVE, M_BELOW};
00492 //
00493 // ************************************************************************
00494 // class scaled_match_error_term
00495 //
00496 // Class scaled_match_error_term is an abstract class that provides an
00497 // error function term to compare the shape of two curves which may not have
00498 // the same scale. For example, it is used by class fts_match (error_terms.h)
00499 // which compares a measured mixer FTS response in arbitrary units to that of
00500 // a simulated mixer. It is meant to be used by error_func with a sweeper.
00501 //
00502 // USAGE:
00503 //
00504 // Derive a class from this class in which you implement functions get_a()
00505 // and get_b(), where:
00506 //
00507 // double get_a(state_tag); // returns values for the reference data
00508 // double get_b(state_tag); // returns values for the data to be scaled
00509 //
00510 // IMPORTANT: Note that get_b() values will be scaled to match get_a()
00511 // values. Beware that if the get_a() values are allowed to go to zero under
00512 // control of the optimizer, then the calculation will find a scale factor
00513 // for the get_b() values equal to zero as well, resulting in a very small
00514 // error value. To avoid this limitation, the function get_a() should not be
00515 // changeable by the optimizer, but should represent a fixed reference
00516 // function. For example, get_a() may return data read from a file.
00517 //
00518 // The error calculation for the scaled match is:
00519 //
00520 // Sum[(get_a() - K*get_b())*(get_a() - K*get_b())]
00521 // error = --------------------------------------------------
00522 // Sum[get_a()*get_a()]
00523 //
00524 // Where "Sum[]" represents a sum over all terms, and we scale the return
00525 // values from get_b() by the factor K to best match the return values from
00526 // get_a(). The error = 0 if the get_a() all vanish, since K = 0 gives a
00527 // perfect match regardless of the values of get_b(). Otherwise, the K which
00528 // results in the best match is given by:
00529 //
00530 // K = Sum[get_a()*get_b()]/Sum[get_b()*get_b()]
00531 //
00532 // Using this formula for the scale factor, we must have:
00533 //
00534 // 0.0 <= error <= 1.0
00535 //
00536 // If the get_b() all vanish, then K is undefined and error = 1.0. Since
00537 // the error will be less than 1.0 even if the match is terrible, you will
00538 // want to adjust the weight used with error_func::add_term() appropriately.
00539 // A weight of ~100 might be a good starting value, depending on what other
00540 // error terms you are also including in the error function.
00541 //
00542 // The scale factor that minimizes the error can be accessed using the
00543 // member function scale():
00544 //
00545 // double scale() const; // return the scale factor for get_b() data
00546 //
00547 // Since error_func will sum the error values returned by the error term
00548 // during a sweep, scaled_match_error_term::get() only returns an error
00549 // increment at each iteration. Since error_func divides by the number of
00550 // points in a sweep to calculate an average, scaled_match_error_term
00551 // inflates the error by a factor equal to the number of get() calls to
00552 // compensate. The term's internal registers are reset by the member
00553 // function reset(); if your derived class needs to overload reset() in
00554 // order to perform its own internal housekeeping, then your reset() must
00555 // remember to call the function scaled_match_error_term::zero() so that
00556 // the scaled_match_error_term registers are properly reset.
00557 //
00558 // ************************************************************************
00559
00560 #ifndef ERROR_FUNC_H
00561 #define ERROR_FUNC_H
00562
00563 #include <vector> // STL vector template class
00564 #include <map> // STL associative array template class
00565
00566 #include "simple_error_func.h"
00567 #include "state_tag.h"
00568 #include "sweeper.h"
00569
00570
00571 // ************************************************************************
00572 class error_term
00573 {
00574 public:
00575
00576 // This is the interface. Calculate error function term.
00577 virtual double get(state_tag) = 0;
00578
00579 // The method reset() is called before a sweep is started. It is normally
00580 // not needed. Some error terms, such as flatness_error_term, must keep
00581 // running sums during a sweep. For them, reset clears these running
00582 // sums to 0.
00583 virtual void reset() { }
00584
00585 // Virtual destructor is necessary to ensure proper subclass destruction.
00586 virtual ~error_term() { }
00587 };
00588
00589
00590 // ************************************************************************
00591 class error_func : public error_func_parameters
00592 {
00593
00594 public:
00595
00596 // The add_term() member function, the major addition to the error function
00597 // interface provided by class error_func:
00598
00599 // Add a simple, "sweeperless" error term.
00600 void add_term(double weight, error_term & et);
00601
00602 // Add an error term whose values will be averaged over the points of the
00603 // associated sweeper. The function allows the sweeper and error term
00604 // arguments to be listed in either order.
00605 void add_term(double weight, error_term & et, sweeper & swp);
00606 void add_term(double weight, sweeper & swp, error_term & et)
00607 { add_term(weight,et,swp); }
00608
00609 // This function breaks down an error_func return value (calculated by
00610 // error_func::func_value()) into a real_vector of component terms, each
00611 // of which holds the contribution to the error function value from a
00612 // term added with add_term(). The first (lowest index) entry contains
00613 // the contribution of the earliest add_term() call; the last to the
00614 // latest add_term() call.
00615 real_vector get_func_breakdown() const;
00616
00617
00618 // Refer to simple_error_func.h for the use of the vary() member function,
00619 // which error_func inherits.
00620
00621
00622 // the constructor:
00623 // Refer to the definition of abstract_error_func in optimizer.h for the
00624 // purpose of the argument no_limits_flag. Just using the default value
00625 // (ie, don't provide a constructor argument) is appropriate in nearly all
00626 // cases.
00627 error_func(bool no_limits_flag = false)
00628 : error_func_parameters(no_limits_flag) { }
00629
00630
00631 // func_value(): the only virtual function of abstract_error_func which
00632 // still needs to be defined.
00633
00634 double func_value();
00635 // Returns the final error function, after summing over all error terms,
00636 // sweepers, etc. Calls error terms via their associated weighted_term
00637 // objects held in error_func::terms. func_value() algorithm: When called,
00638 // func_value() does the following:
00639 // (1) initializes its return value accumulator to 0.0
00640 // (2) gets a new state_tag for the "sweeperless" terms
00641 // (3) Loop: for each "sweeperless" term added using add_term():
00642 // (3.1) call the weighted_term's reset(),
00643 // which clears its result and resets the error_term
00644 // (3.2) call the weighted_term's get(state_tag), which:
00645 // (3.2.1) calls the term's error_term::get(state_tag)
00646 // (3.2.2) weights the error and adds it into the term's result
00647 // (3.3) add the term's result into the return value
00648 // (4) Loop: for each unique sweeper identified by an add_term():
00649 // (4.1) reset the sweeper using its sweeper::reset()
00650 // (4.2) call the associated sweeper_info::reset_terms(), which:
00651 // (4.2.1) Loop: for each term added using this sweeper:
00652 // (4.2.1.1) call the weighted_term's reset()
00653 // (4.3) Loop: until sweeper::is_finished() goes true:
00654 // (4.3.1) get a new state_tag
00655 // (4.3.2) call the associated sweeper_info::calc_terms(), which:
00656 // (4.3.2.1) Loop: for each term added using this sweeper:
00657 // (4.3.2.1.1) weighted_term::get(state_tag)
00658 // (4.3.3) increment sweeper
00659 // (4.4) call the associated sweeper_info::get_error(), which:
00660 // (4.4.1) Loop: for each term added using this sweeper:
00661 // (4.4.1) divide result by the number of sweeper points
00662 // (4.4.2) add the result into the return value
00663 // (5) return the accumulated return value
00664
00665
00666 private:
00667
00668 // This class takes care of multiplying error terms by weighting factors
00669 // and maintaining accumulators for their error contributions
00670 class weighted_term
00671 {
00672 public:
00673 double weight; // weight of this term
00674 error_term * et; // pointer to the function
00675 double result; // accumulate the result for this term
00676
00677 // The constructor initializes parameters.
00678 weighted_term(double w, error_term & etr) : weight(w), et(&etr), result(0.0) { }
00679
00680 // Reset the term and clear the accumulator
00681 weighted_term & reset() { result = 0.0; et->reset(); return *this; }
00682
00683 // Calculate and the weighted error and add into the accumulator.
00684 weighted_term & get(state_tag s) { result += weight*(et->get(s)); return *this; }
00685 } ;
00686
00687 // to make the code a bit more readable, we add typedefs using weighted_term :
00688 typedef std::vector<error_func::weighted_term> term_list;
00689 typedef term_list::size_type term_index;
00690 typedef std::vector<term_index> term_index_list;
00691 typedef term_index_list::size_type term_index_list_index;
00692
00693 // This class has the information on terms for a single sweeper. It keeps a
00694 // record of the weighted_terms which use the sweeper, and a few functions
00695 // which loop over all of these associated terms.
00696 class sweeper_info
00697 {
00698 public:
00699 // The terms vector keeps track of all the weighted_terms to be summed
00700 // by this sweeper. It holds indexes into the container with all terms.
00701 error_func::term_index_list sweeper_terms;
00702
00703 // Method reset() loops through sweeper_terms, calling reset() for each.
00704 // It is called with a reference to error_func::terms
00705 void reset_terms(error_func::term_list &);
00706
00707 // Method get_terms loops through sweeper_terms, calling get() for each.
00708 void calc_terms(state_tag, error_func::term_list &);
00709
00710 // Method get_error averages the terms by dividing each term's result
00711 // by npoints; it adds them all and returns the sum
00712 double get_error(int npoints, error_func::term_list &);
00713 };
00714
00715
00716 // Here we store all the weighted_terms added using add_term:
00717 term_list terms;
00718
00719 // Here we store all the sweepers and their associated information in an
00720 // STL associative array (map)
00721 std::map<sweeper *, sweeper_info> swp_map ;
00722
00723 // Keep a vector of all the terms that don't have sweepers.
00724 term_index_list sweeperless_terms;
00725
00726 } ;
00727
00728
00729 // ************************************************************************
00730 class error_term_mode : public error_term
00731 {
00732 public:
00733 enum mode {MATCH, ABOVE, BELOW, FLAT, M_MATCH, M_ABOVE, M_BELOW};
00734
00735 // Constructor sets mode and target, initializes other values to 0.
00736 // If the mode is FLAT, then target_val will be ignored.
00737 error_term_mode(mode m = MATCH, double t = 0.0):
00738 flag(m), target_val(t)
00739 { reset(); }
00740
00741 // Method to set the target.
00742 error_term_mode & target(double t) { target_val = t; return *this; }
00743
00744 // The following methods may be called to set the mode.
00745 error_term_mode & match() {flag = MATCH; return *this;}
00746 error_term_mode & above() {flag = ABOVE; return *this;}
00747 error_term_mode & below() {flag = BELOW; return *this;}
00748 error_term_mode & flat() {flag = FLAT; return *this;}
00749 error_term_mode & worst_match() {flag = M_MATCH; return *this;}
00750 error_term_mode & worst_above() {flag = M_ABOVE; return *this;}
00751 error_term_mode & worst_below() {flag = M_BELOW; return *this;}
00752
00753 // The following methods may be called to set both the mode and target.
00754 error_term_mode & match(double t) {flag = MATCH; target_val = t; return *this;}
00755 error_term_mode & above(double t) {flag = ABOVE; target_val = t; return *this;}
00756 error_term_mode & below(double t) {flag = BELOW; target_val = t; return *this;}
00757 error_term_mode & worst_match(double t) {flag = M_MATCH; target_val = t; return *this;}
00758 error_term_mode & worst_above(double t) {flag = M_ABOVE; target_val = t; return *this;}
00759 error_term_mode & worst_below(double t) {flag = M_BELOW; target_val = t; return *this;}
00760
00761 // Compute the error for the given mode.
00762 double checkval(double x);
00763
00764 // Reset is called before a sweep, and clears all memory of past calculations.
00765 void reset()
00766 {
00767 sum_xx = 0.0;
00768 sum_x = 0.0;
00769 n = 0;
00770 old_error = 0.0;
00771 }
00772
00773 // Virtual destructor is necessary to ensure proper subclass destruction.
00774 virtual ~error_term_mode() { }
00775
00776 private:
00777 // Which type of error to calculate.
00778 mode flag;
00779
00780 // Target for MATCH, minimum for ABOVE, etc.
00781 // If the mode is FLAT, then target_val is ignored.
00782 double target_val;
00783
00784 // The following variables are used for FLAT, M_MATCH, M_ABOVE, M_BELOW.
00785 double sum_xx; // Keep a running sum of the squares of the values.
00786 double sum_x; // Keep a running sum of the values.
00787 unsigned n; // The number of points since the last reset() call.
00788 double old_error; // The value of the error from the last iteration.
00789
00790 };
00791
00792
00793 // ************************************************************************
00794 class scaled_match_error_term : public error_term
00795 {
00796 protected:
00797 // Zero is called by reset, and clears all running sums to 0.
00798 // This function is provided for subclasses that overload reset().
00799 void zero()
00800 {
00801 sum_aa = 0.0;
00802 sum_bb = 0.0;
00803 sum_ab = 0.0;
00804 old_error = 0.0;
00805 n = 0;
00806 }
00807
00808 public:
00809 // Constructor sets initial values to 0.
00810 scaled_match_error_term() :
00811 sum_aa(0.0), sum_bb(0.0), sum_ab(0.0), old_error(0.0), n(0)
00812 { }
00813
00814 // Reset is called before a sweep, and clears all running sums to 0.
00815 // Any derived classes that implement reset() must remember to call zero().
00816 virtual void reset() { zero(); }
00817
00818 // Return the values that we want to match.
00819 // These are the functions that must be defined in concrete subclasses.
00820
00821 virtual double get_a(state_tag) = 0; // The reference data
00822 virtual double get_b(state_tag) = 0; // The data to be scaled and compared
00823
00824 // This function returns the scale factor for b to make it match a
00825 double scale() const
00826 { return (sum_bb == 0.0 && sum_ab == 0.0) ? 1.0 : sum_ab/sum_bb; }
00827
00828 // The method get will calculate the error function by calling get_value.
00829 // It should not be modified or redefined in subclasses.
00830 double get(state_tag);
00831
00832 // Virtual destructor is necessary to ensure proper subclass destruction.
00833 virtual ~scaled_match_error_term() { }
00834
00835 private:
00836 double sum_aa; // Keep a running sum of the squares of value a.
00837 double sum_bb; // Keep a running sum of the squares of value b.
00838 double sum_ab; // Keep a running sum of (value a) * (value b).
00839 double old_error; // The value of the error from the last iteration.
00840 unsigned n; // iteration counter
00841 };
00842
00843 #endif /* ERROR_FUNC_H */
Please direct comments and corrections to
supermix@submm.caltech.edu
Go to the supermix home page
Generated by
1.2.7