SecondRealEA.cpp

00001 //-----------------------------------------------------------------------------
00002 // SecondRealEA.cpp
00003 //-----------------------------------------------------------------------------
00004 //*
00005 // Same code than FirstBitEA as far as Evolutionary Computation is concerned
00006 // but now you learn to enter the parameters in a more flexible way
00007 // (also slightly different than in SecondBitEA.cpp)
00008 // and to twidle the output to your preferences (as in SecondBitEA.cpp)
00009 //
00010 //-----------------------------------------------------------------------------
00011 #ifdef HAVE_CONFIG_H
00012 #include <config.h>
00013 #endif
00014 
00015 // standard includes
00016 #include <fstream>
00017 #include <iostream>   // cout
00018 #include <stdexcept>  // runtime_error
00019 
00020 // the general include for eo
00021 #include <eo>
00022 #include <es.h>
00023 
00024 // REPRESENTATION
00025 //-----------------------------------------------------------------------------
00026 // define your individuals
00027 typedef eoReal<eoMinimizingFitness> Indi;
00028 
00029 // Use functions from namespace std
00030 using namespace std;
00031 
00032 // EVALFUNC
00033 //-----------------------------------------------------------------------------
00034 // a simple fitness function that computes the euclidian norm of a real vector
00035 // Now in a separate file, and declared as binary_value(const vector<bool> &)
00036 
00037 #include "real_value.h"
00038 
00039 // GENERAL
00040 //-----------------------------------------------------------------------------
00041 
00042 void main_function(int argc, char **argv)
00043 {
00044 // PARAMETRES
00045 //-----------------------------------------------------------------------------
00046 // instead of having all values of useful parameters as constants, read them:
00047 // either on the command line (--option=value or -o=value)
00048 //     or in a parameter file (same syntax, order independent,
00049 //                             # = usual comment character
00050 //     or in the environment (TODO)
00051 
00052   // First define a parser from the command-line arguments
00053   eoParser parser(argc, argv);
00054 
00055   // For each parameter, you can in on single line
00056   // define the parameter, read it through the parser, and assign it
00057 
00058   unsigned seed = parser.createParam(unsigned(time(0)), "seed", "Random number seed", 'S').value(); // will be in default section General
00059 
00060   // description of genotype
00061   unsigned vecSize = parser.createParam(unsigned(8), "vecSize", "Genotype size",'V', "Representation" ).value();
00062 
00063    // parameters for evolution engine
00064     unsigned popSize = parser.createParam(unsigned(10), "popSize", "Population size",'P', "Evolution engine" ).value();
00065 
00066     unsigned tSize = parser.createParam(unsigned(2), "tSize", "Tournament size",'T', "Evolution Engine" ).value();
00067 
00068    // init and stop
00069     string loadName = parser.createParam(string(""), "Load","A save file to restart from",'L', "Persistence" ).value();
00070 
00071     unsigned maxGen = parser.createParam(unsigned(100), "maxGen", "Maximum number of generations",'G', "Stopping criterion" ).value();
00072 
00073     unsigned minGen = parser.createParam(unsigned(100), "minGen", "Minimum number of generations",'g', "Stopping criterion" ).value();
00074 
00075     unsigned steadyGen = parser.createParam(unsigned(100), "steadyGen", "Number of generations with no improvement",'s', "Stopping criterion" ).value();
00076 
00077    // operators probabilities at the algorithm level
00078     double pCross = parser.createParam(double(0.6), "pCross", "Probability of Crossover", 'C', "Genetic Operators" ).value();
00079 
00080     double pMut = parser.createParam(double(0.1), "pMut", "Probability of Mutation", 'M', "Genetic Operators" ).value();
00081 
00082    // relative rates for crossovers
00083     double hypercubeRate = parser.createParam(double(1), "hypercubeRate", "Relative rate for hypercube crossover", '\0', "Genetic Operators" ).value();
00084 
00085     double segmentRate = parser.createParam(double(1), "segmentRate", "Relative rate for segment crossover", '\0', "Genetic Operators" ).value();
00086 
00087     // internal parameters for the mutations
00088     double EPSILON = parser.createParam(double(0.01), "EPSILON", "Width for uniform mutation", '\0', "Genetic Operators" ).value();
00089 
00090     double SIGMA = parser.createParam(double(0.3), "SIGMA", "Sigma for normal mutation", '\0', "Genetic Operators" ).value();
00091 
00092    // relative rates for mutations
00093     double uniformMutRate = parser.createParam(double(1), "uniformMutRate", "Relative rate for uniform mutation", '\0', "Genetic Operators" ).value();
00094 
00095     double detMutRate = parser.createParam(double(1), "detMutRate", "Relative rate for det-uniform mutation", '\0', "Genetic Operators" ).value();
00096 
00097     double normalMutRate = parser.createParam(double(1), "normalMutRate", "Relative rate for normal mutation", '\0', "Genetic Operators" ).value();
00098 
00099     // the name of the "status" file where all actual parameter values will be saved
00100     string str_status = parser.ProgramName() + ".status"; // default value
00101     string statusName = parser.createParam(str_status, "status","Status file",'S', "Persistence" ).value();
00102 
00103    // do the following AFTER ALL PARAMETERS HAVE BEEN PROCESSED
00104    // i.e. in case you need parameters somewhere else, postpone these
00105     if (parser.userNeedsHelp())
00106       {
00107         parser.printHelp(cout);
00108         exit(1);
00109       }
00110     if (statusName != "")
00111       {
00112         ofstream os(statusName.c_str());
00113         os << parser;           // and you can use that file as parameter file
00114       }
00115 
00116 // EVAL
00118   // Fitness function
00120   // Evaluation: from a plain C++ fn to an EvalFunc Object
00121   // you need to give the full description of the function
00122   eoEvalFuncPtr<Indi, double, const vector<double>& > plainEval(  real_value );
00123   // ... to an object that counts the nb of actual evaluations
00124   eoEvalFuncCounter<Indi> eval(plainEval);
00125 
00126 // INIT
00128   // Initilisation of population
00130   // Either load or initialize
00131   // create an empty pop
00132   eoPop<Indi> pop;
00133   // create a state for reading
00134   eoState inState;              // a state for loading - WITHOUT the parser
00135   // register the rng and the pop in the state, so they can be loaded,
00136   // and the present run will be the exact conitnuation of the saved run
00137   // eventually with different parameters
00138   inState.registerObject(rng);
00139   inState.registerObject(pop);
00140 
00141   if (loadName != "")
00142     {
00143       inState.load(loadName); //  load the pop and the rng
00144       // the fitness is read in the file:
00145       // do only evaluate the pop if the fitness has changed
00146     }
00147   else
00148     {
00149       rng.reseed(seed);
00150       // a Indi random initializer
00151       // based on boolean_generator class (see utils/rnd_generator.h)
00152       eoUniformGenerator<double> uGen(-1.0, 1.0);
00153       eoInitFixedLength<Indi> random(vecSize, uGen);
00154 
00155       // Init pop from the randomizer: need to use the append function
00156       pop.append(popSize, random);
00157       // and evaluate pop (STL syntax)
00158       apply<Indi>(eval, pop);
00159     } // end of initializatio of the population
00160 
00161 // OUTPUT
00162   // sort pop before printing it!
00163   pop.sort();
00164   // Print (sorted) intial population (raw printout)
00165   cout << "Initial Population" << endl;
00166   cout << pop;
00167 
00168 // ENGINE
00170   // selection and replacement
00172 // SELECT
00173   // The robust tournament selection
00174   eoDetTournamentSelect<Indi> selectOne(tSize);
00175   // is now encapsulated in a eoSelectPerc (entage)
00176   eoSelectPerc<Indi> select(selectOne);// by default rate==1
00177 
00178 // REPLACE
00179   // And we now have the full slection/replacement - though with
00180   // no replacement (== generational replacement) at the moment :-)
00181   eoGenerationalReplacement<Indi> replace;
00182 
00183 // OPERATORS
00185   // The variation operators
00187 // CROSSOVER
00188   // uniform chooce on segment made by the parents
00189   eoSegmentCrossover<Indi> xoverS;
00190   // uniform choice in hypercube built by the parents
00191   eoHypercubeCrossover<Indi> xoverA;
00192   // Combine them with relative weights
00193   eoPropCombinedQuadOp<Indi> xover(xoverS, segmentRate);
00194   xover.add(xoverA, hypercubeRate, true);
00195 
00196 // MUTATION
00197   // offspring(i) uniformly chosen in [parent(i)-epsilon, parent(i)+epsilon]
00198   eoUniformMutation<Indi>  mutationU(EPSILON);
00199   // k (=1) coordinates of parents are uniformly modified
00200   eoDetUniformMutation<Indi>  mutationD(EPSILON);
00201   // all coordinates of parents are normally modified (stDev SIGMA)
00202   eoNormalMutation<Indi>  mutationN(SIGMA);
00203   // Combine them with relative weights
00204   eoPropCombinedMonOp<Indi> mutation(mutationU, uniformMutRate);
00205   mutation.add(mutationD, detMutRate);
00206   mutation.add(mutationN, normalMutRate, true);
00207 
00208   // The operators are  encapsulated into an eoTRansform object
00209   eoSGATransform<Indi> transform(xover, pCross, mutation, pMut);
00210 
00211 // STOP
00213   // termination condition see FirstBitEA.cpp
00215   eoGenContinue<Indi> genCont(maxGen);
00216   eoSteadyFitContinue<Indi> steadyCont(minGen, steadyGen);
00217   eoFitContinue<Indi> fitCont(0);
00218   eoCombinedContinue<Indi> continuator(genCont);
00219   continuator.add(steadyCont);
00220   continuator.add(fitCont);
00221 
00222 
00223 // CHECKPOINT
00224   // but now you want to make many different things every generation
00225   // (e.g. statistics, plots, ...).
00226   // the class eoCheckPoint is dedicated to just that:
00227 
00228   // Declare a checkpoint (from a continuator: an eoCheckPoint
00229   // IS AN eoContinue and will be called in the loop of all algorithms)
00230   eoCheckPoint<Indi> checkpoint(continuator);
00231 
00232     // Create a counter parameter
00233     eoValueParam<unsigned> generationCounter(0, "Gen.");
00234 
00235     // Create an incrementor (sub-class of eoUpdater). Note that the
00236     // parameter's value is passed by reference,
00237     // so every time the incrementer is updated (every generation),
00238     // the data in generationCounter will change.
00239     eoIncrementor<unsigned> increment(generationCounter.value());
00240 
00241     // Add it to the checkpoint,
00242     // so the counter is updated (here, incremented) every generation
00243     checkpoint.add(increment);
00244 
00245     // now some statistics on the population:
00246     // Best fitness in population
00247     eoBestFitnessStat<Indi> bestStat;
00248     // Second moment stats: average and stdev
00249     eoSecondMomentStats<Indi> SecondStat;
00250 
00251     // Add them to the checkpoint to get them called at the appropriate time
00252     checkpoint.add(bestStat);
00253     checkpoint.add(SecondStat);
00254 
00255     // The Stdout monitor will print parameters to the screen ...
00256     eoStdoutMonitor monitor(false);
00257 
00258     // when called by the checkpoint (i.e. at every generation)
00259     checkpoint.add(monitor);
00260 
00261     // the monitor will output a series of parameters: add them
00262     monitor.add(generationCounter);
00263     monitor.add(eval);          // because now eval is an eoEvalFuncCounter!
00264     monitor.add(bestStat);
00265     monitor.add(SecondStat);
00266 
00267     // A file monitor: will print parameters to ... a File, yes, you got it!
00268     eoFileMonitor fileMonitor("stats.xg", " ");
00269 
00270     // the checkpoint mechanism can handle multiple monitors
00271     checkpoint.add(fileMonitor);
00272 
00273     // the fileMonitor can monitor parameters, too, but you must tell it!
00274     fileMonitor.add(generationCounter);
00275     fileMonitor.add(bestStat);
00276     fileMonitor.add(SecondStat);
00277 
00278     // Last type of item the eoCheckpoint can handle: state savers:
00279     eoState outState;
00280     // Register the algorithm into the state (so it has something to save!!)
00281     outState.registerObject(parser);
00282     outState.registerObject(pop);
00283     outState.registerObject(rng);
00284 
00285     // and feed the state to state savers
00286     // save state every 100th  generation
00287     eoCountedStateSaver stateSaver1(20, outState, "generation");
00288     // save state every 1 seconds
00289     eoTimedStateSaver   stateSaver2(1, outState, "time");
00290 
00291     // Don't forget to add the two savers to the checkpoint
00292     checkpoint.add(stateSaver1);
00293     checkpoint.add(stateSaver2);
00294     // and that's it for the (control and) output
00295 
00296 // GENERATION
00298   // the algorithm
00300 
00301   // Easy EA requires
00302   // stopping criterion, eval, selection, transformation, replacement
00303   eoEasyEA<Indi> gga(checkpoint, eval, select, transform, replace);
00304 
00305   // Apply algo to pop - that's it!
00306   gga(pop);
00307 
00308 // OUTPUT
00309   // Print (sorted) intial population
00310   pop.sort();
00311   cout << "FINAL Population\n" << pop << endl;
00312 // GENERAL
00313 }
00314 
00315 // A main that catches the exceptions
00316 
00317 int main(int argc, char **argv)
00318 {
00319     try
00320     {
00321         main_function(argc, argv);
00322     }
00323     catch(exception& e)
00324     {
00325         cout << "Exception: " << e.what() << '\n';
00326     }
00327 
00328     return 1;
00329 }

Generated on Thu Apr 19 11:02:29 2007 for EO by  doxygen 1.4.7