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