# 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 }