Back to Lesson 3 - Tutorial main page - Top-Down page - Bottom-up page - Programming hints - EO documentation

# SecondRealEA

Click on the figure to see the corresponding code.
In the code, the colors are meaningfull
The actual code is in boldface and the comment in normal face.

`//----------------------------------------------------------------------------- // SecondRealEA.cpp //----------------------------------------------------------------------------- //* // Same code than FirstBitEA as far as Evolutionary Computation is concerned // but now you learn to enter the parameters in a more flexible way // (also slightly different than in SecondBitEA.cpp) // and to twidle the output to your preferences (as in SecondBitEA.cpp) // //----------------------------------------------------------------------------- // standard includes #include <stdexcept> // runtime_error #include <iostream> // cout #include <strstream> // ostrstream, istrstream // the general include for eo #include <eo> #include <es.h>`

`//----------------------------------------------------------------------------- // define your individuals typedef eoReal<eoMinimizingFitness> Indi;`

`//----------------------------------------------------------------------------- // a simple fitness function that computes the euclidian norm of a real vector // Now in a separate file, and declared as binary_value(const vector<bool> &) #include "real_value.h"`

`//----------------------------------------------------------------------------- void main_function(int argc, char **argv) {`

`//----------------------------------------------------------------------------- // instead of having all values of useful parameters as constants, read them: // either on the command line (--option=value or -o=value) // or in a parameter file (same syntax, order independent, // # = usual comment character // or in the environment (TODO) // First define a parser from the command-line arguments eoParser parser(argc, argv); // For each parameter, you can in on single line // define the parameter, read it through the parser, and assign it unsigned seed = parser.createParam(unsigned(time(0)), "seed", "Random number seed", 'S').value(); // will be in default section General // description of genotype unsigned vecSize = parser.createParam(unsigned(8), "vecSize", "Genotype size",'V', "Representation" ).value(); // parameters for evolution engine unsigned popSize = parser.createParam(unsigned(10), "popSize", "Population size",'P', "Evolution engine" ).value(); unsigned tSize = parser.createParam(unsigned(2), "tSize", "Tournament size",'T', "Evolution Engine" ).value(); // init and stop string loadName = parser.createParam(string(""), "Load","A save file to restart from",'L', "Persistence" ).value(); unsigned maxGen = parser.createParam(unsigned(100), "maxGen", "Maximum number of generations",'G', "Stopping criterion" ).value(); unsigned minGen = parser.createParam(unsigned(100), "minGen", "Minimum number of generations",'g', "Stopping criterion" ).value(); unsigned steadyGen = parser.createParam(unsigned(100), "steadyGen", "Number of generations with no improvement",'s', "Stopping criterion" ).value(); // operators probabilities at the algorithm level double pCross = parser.createParam(double(0.6), "pCross", "Probability of Crossover", 'C', "Genetic Operators" ).value(); double pMut = parser.createParam(double(0.1), "pMut", "Probability of Mutation", 'M', "Genetic Operators" ).value(); // relative rates for crossovers double hypercubeRate = parser.createParam(double(1), "hypercubeRate", "Relative rate for hypercube crossover", '\0', "Genetic Operators" ).value(); double segmentRate = parser.createParam(double(1), "segmentRate", "Relative rate for segment crossover", '\0', "Genetic Operators" ).value(); // internal parameters for the mutations double EPSILON = parser.createParam(double(0.01), "EPSILON", "Width for uniform mutation", '\0', "Genetic Operators" ).value(); double SIGMA = parser.createParam(double(0.3), "SIGMA", "Sigma for normal mutation", '\0', "Genetic Operators" ).value(); // relative rates for mutations double uniformMutRate = parser.createParam(double(1), "uniformMutRate", "Relative rate for uniform mutation", '\0', "Genetic Operators" ).value(); double detMutRate = parser.createParam(double(1), "detMutRate", "Relative rate for det-uniform mutation", '\0', "Genetic Operators" ).value(); double normalMutRate = parser.createParam(double(1), "normalMutRate", "Relative rate for normal mutation", '\0', "Genetic Operators" ).value(); // the name of the "status" file where all actual parameter values will be saved string str_status = parser.ProgramName() + ".status"; // default value string statusName = parser.createParam(str_status, "status","Status file",'S', "Persistence" ).value(); // do the following AFTER ALL PARAMETERS HAVE BEEN PROCESSED // i.e. in case you need parameters somewhere else, postpone these if (parser.userNeedsHelp()) { parser.printHelp(cout); exit(1); } if (statusName != "") { ofstream os(statusName.c_str()); os << parser; // and you can use that file as parameter file }`

`///////////////////////////// // Fitness function //////////////////////////// // Evaluation: from a plain C++ fn to an EvalFunc Object // you need to give the full description of the function eoEvalFuncPtr<Indi, double, const vector<double>& > plainEval( real_value ); // ... to an object that counts the nb of actual evaluations eoEvalFuncCounter<Indi> eval(plainEval);`

`//////////////////////////////// // Initilisation of population //////////////////////////////// // Either load or initialize // create an empty pop eoPop<Indi> pop; // create a state for reading eoState inState; // a state for loading - WITHOUT the parser // register the rng and the pop in the state, so they can be loaded, // and the present run will be the exact conitnuation of the saved run // eventually with different parameters inState.registerObject(rng); inState.registerObject(pop); if (loadName != "") { inState.load(loadName); // load the pop and the rng // the fitness is read in the file: // do only evaluate the pop if the fitness has changed } else { rng.reseed(seed); // a Indi random initializer // based on boolean_generator class (see utils/rnd_generator.h) eoUniformGenerator<double> uGen(-1.0, 1.0); eoInitFixedLength<Indi> random(vecSize, uGen); // Init pop from the randomizer: need to use the append function pop.append(popSize, random); // and evaluate pop (STL syntax) apply<Indi>(eval, pop); } // end of initializatio of the population`

`// sort pop before printing it! pop.sort(); // Print (sorted) intial population (raw printout) cout << "Initial Population" << endl; cout << pop;`

`///////////////////////////////////// // selection and replacement ////////////////////////////////////`

`// The robust tournament selection eoDetTournamentSelect<Indi> selectOne(tSize); // is now encapsulated in a eoSelectPerc (entage) eoSelectPerc<Indi> select(selectOne);// by default rate==1`

`// And we now have the full slection/replacement - though with // no replacement (== generational replacement) at the moment :-) eoGenerationalReplacement<Indi> replace;`

`////////////////////////////////////// // The variation operators //////////////////////////////////////`

`// uniform chooce on segment made by the parents eoSegmentCrossover<Indi> xoverS; // uniform choice in hypercube built by the parents eoHypercubeCrossover<Indi> xoverA; // Combine them with relative weights eoPropCombinedQuadOp<Indi> xover(xoverS, segmentRate); xover.add(xoverA, hypercubeRate, true);`

`// offspring(i) uniformly chosen in [parent(i)-epsilon, parent(i)+epsilon] eoUniformMutation<Indi> mutationU(EPSILON); // k (=1) coordinates of parents are uniformly modified eoDetUniformMutation<Indi> mutationD(EPSILON); // all coordinates of parents are normally modified (stDev SIGMA) eoNormalMutation<Indi> mutationN(SIGMA); // Combine them with relative weights eoPropCombinedMonOp<Indi> mutation(mutationU, uniformMutRate); mutation.add(mutationD, detMutRate); mutation.add(mutationN, normalMutRate, true); // The operators are encapsulated into an eoTRansform object eoSGATransform<Indi> transform(xover, pCross, mutation, pMut);`

`////////////////////////////////////// // termination condition see FirstBitEA.cpp ///////////////////////////////////// eoGenContinue<Indi> genCont(maxGen); eoSteadyFitContinue<Indi> steadyCont(minGen, steadyGen); eoFitContinue<Indi> fitCont(0); eoCombinedContinue<Indi> continuator(genCont); continuator.add(steadyCont); continuator.add(fitCont);`

`// but now you want to make many different things every generation // (e.g. statistics, plots, ...). // the class eoCheckPoint is dedicated to just that: // Declare a checkpoint (from a continuator: an eoCheckPoint // IS AN eoContinue and will be called in the loop of all algorithms) eoCheckPoint<Indi> checkpoint(continuator); // Create a counter parameter eoValueParam<unsigned> generationCounter(0, "Gen."); // Create an incrementor (sub-class of eoUpdater). Note that the // parameter's value is passed by reference, // so every time the incrementer is updated (every generation), // the data in generationCounter will change. eoIncrementor<unsigned> increment(generationCounter.value()); // Add it to the checkpoint, // so the counter is updated (here, incremented) every generation checkpoint.add(increment); // now some statistics on the population: // Best fitness in population eoBestFitnessStat<Indi> bestStat; // Second moment stats: average and stdev eoSecondMomentStats<Indi> SecondStat; // Add them to the checkpoint to get them called at the appropriate time checkpoint.add(bestStat); checkpoint.add(SecondStat); // The Stdout monitor will print parameters to the screen ... eoStdoutMonitor monitor(false); // when called by the checkpoint (i.e. at every generation) checkpoint.add(monitor); // the monitor will output a series of parameters: add them monitor.add(generationCounter); monitor.add(eval); // because now eval is an eoEvalFuncCounter! monitor.add(bestStat); monitor.add(SecondStat); // A file monitor: will print parameters to ... a File, yes, you got it! eoFileMonitor fileMonitor("stats.xg", " "); // the checkpoint mechanism can handle multiple monitors checkpoint.add(fileMonitor); // the fileMonitor can monitor parameters, too, but you must tell it! fileMonitor.add(generationCounter); fileMonitor.add(bestStat); fileMonitor.add(SecondStat); // Last type of item the eoCheckpoint can handle: state savers: eoState outState; // Register the algorithm into the state (so it has something to save!!) outState.registerObject(parser); outState.registerObject(pop); outState.registerObject(rng); // and feed the state to state savers // save state every 100th generation eoCountedStateSaver stateSaver1(20, outState, "generation"); // save state every 1 seconds eoTimedStateSaver stateSaver2(1, outState, "time"); // Don't forget to add the two savers to the checkpoint checkpoint.add(stateSaver1); checkpoint.add(stateSaver2); // and that's it for the (control and) output`

`///////////////////////////////////////// // the algorithm //////////////////////////////////////// // Easy EA requires // stopping criterion, eval, selection, transformation, replacement eoEasyEA<Indi> gga(checkpoint, eval, select, transform, replace); // Apply algo to pop - that's it! gga(pop);`

`// Print (sorted) intial population pop.sort(); cout << "FINAL Population\n" << pop << endl;`

`} // A main that catches the exceptions int main(int argc, char **argv) { try { main_function(argc, argv); } catch(exception& e) { cout << "Exception: " << e.what() << '\n'; } return 1; }`

Back to Lesson 3 - Tutorial main page - Top-Down page - Bottom-up page - Programming hints - EO documentation

Last modified: Sun Apr 28 06:42:44 2002