Back to Lesson 3 - Tutorial main page - Algorithm-Based - Component-Based - Programming hints - EO documentation

SecondBitEA.cpp

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.

For this particular program, as all new lines are concerned with program parameter input and information output, the font color for program-parameter-related sections will refer to the section where the parameters are used whereas the background color will remain blue.


//-----------------------------------------------------------------------------
// SecondGA.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
// and to twidle the output to your preferences!
//-----------------------------------------------------------------------------
// standard includes
#include <stdexcept>  // runtime_error 
#include <iostream>   // cout
#include <strstream>  // ostrstream, istrstream
#include <fstream>
// the general include for eo
#include <eo>
// a simple fitness function that computes the number of ones of a bitstring
#include "binary_value.h"
//-----------------------------------------------------------------------------
// define your genotype and fitness types
typedef eoBit<double> Indi;
// the main_function: nothing changed(!), except variable initialization
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, define Parameter, read it through the parser,
// and assign the value to the variable

eoValueParam<uint32> seedParam(time(0), "seed", "Random number seed", 'S');
parser.processParam( seedParam );
unsigned seed = seedParam.value();

// decription of genotype
eoValueParam<unsigned int>& vecSizeParam(8, "vecSize", "Genotype size",'V');
parser.processParam( vecSizeParam, "Representation" );
unsigned vecSize = vecSizeParam.value();

// parameters for evolution engine
eoValueParam<unsigned int>& popSizeParam(10, "popSize", "Population size",'P');
parser.processParam( popSizeParam, "Evolution engine" );
unsigned popSize = popSizeParam.value();

eoValueParam<unsigned int>& tSizeParam(10, "tSize", "Tournament size",'T');
parser.processParam( seedParam );
unsigned tSize = tSizeParam.value();

   // init and stop
eoValueParam<string> loadNameParam("", "Load","A save file to restart from",'L');
parser.processParam( loadNameParam, "Persistence" );
string loadName = loadNameParam.value();

eoValueParam<unsigned int> maxGenParam(100, "maxGen", "Maximum number of generations",'G');
parser.processParam( maxGenParam, "Stopping criterion" );
unsigned maxGen = maxGenParam.value();

eoValueParam<unsigned int> minGenParam(100, "minGen", "Minimum number of generations",'g');
parser.processParam( minGenParam, "Stopping criterion" );
unsigned minGen = minGenParam.value();

eoValueParam<unsigned int> steadyGenParam(100, "steadyGen", "Number of generations with no improvement",'s');
parser.processParam( steadyGenParam, "Stopping criterion" );
unsigned steadyGen = steadyGenParam.value();

// operators probabilities at the algorithm level
eoValueParam<double> pCrossParam(0.6, "pCross", "Probability of Crossover", 'C'); 
parser.processParam( pCrossParam, "Genetic Operators" );
double pCross = pCrossParam.value();

eoValueParam<double> pMutParam(0.1, "pMut", "Probability of Mutation", 'M');
parser.processParam( pMutParam, "Genetic Operators" );
double pMut = pMutParam.value();

// relative rates for crossovers
eoValueParam<double> onePointRateParam(1, "onePointRate", "Relative rate for one point crossover", '1');
parser.processParam( onePointRateParam, "Genetic Operators" );
double onePointRate = onePointRateParam.value();

eoValueParam<double> twoPointsRateParam(1, "twoPointRate", "Relative rate for two point crossover", '2');
parser.processParam( twoPointsRateParam, "Genetic Operators" );
double twoPointsRate = twoPointsRateParam.value();

eoValueParam<double> uRateParam(2, "uRate", "Relative rate for uniform crossover", 'U');
parser.processParam( uRateParam, "Genetic Operators" );
double URate =  uRateParam.value();

// relative rates and private parameters for mutations;
eoValueParam<double> pMutPerBitParam(0.01, "pMutPerBit", "Probability of flipping 1 bit in bit-flip mutation", 'b');
parser.processParam( pMutPerBitParam, "Genetic Operators" );
double pMutPerBit = pMutPerBitParam.value();

eoValueParam<double> bitFlipRateParam(0.01, "bitFlipRate", "Relative rate for bit-flip mutation", 'B');
parser.processParam( bitFlipRateParam, "Genetic Operators" );
double bitFlipRate =  bitFlipRateParam.value();

eoValueParam<double> oneBitRateParam(0.01, "oneBitRate", "Relative rate for deterministic bit-flip mutation", 'D');
parser.processParam( oneBitRateParam, "Genetic Operators" );
double oneBitRate = oneBitRateParam.value();

// the name of the "status" file where all actual parameter values will be saved
string str_status = parser.ProgramName() + ".status"; // default value
eoValueParam<string> statusParam(str_status.c_str(), "status","Status file",'S');
parser.processParam( statusParam, "Persistence" );

// 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 (statusParam.value() != "")
   {
      ofstream os(statusParam.value().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 ...
 eoEvalFuncPtr<Indi, double, const vector<bool>& > plainEval( binary_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 (load_name != "")
     {
         inState.load(load_name); //  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 eoUniformGenerator class (see utils/eoRndGenerators.h)
       eoUniformGenerator<bool> uGen;
       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 initialization of the population

 // sort pop for pretty printout
 pop.sort();
 // Print (sorted) intial population (raw printout)
 cout << "Initial Population" << endl << pop << endl;
 /////////////////////////////////////
 // selection and replacement
 ////////////////////////////////////
 // The robust tournament selection
 eoDetTournamentSelect<Indi> selectOne(tSize);       // tSize in [2,POPSIZE]
 // is now encapsulated in a eoSelectPerc (entage)
 eoSelectPerc<Indi> select(selectOne);
 // or eoSelectPerc<Indi> select(selectOne, rate); 
 // but by default rate==1
 // And we now have the full slection/replacement - though with 
 // the same generational replacement at the moment :-)
 eoGenerationalReplacement<Indi> replace; 
 //////////////////////////////////////
 // The variation operators
 //////////////////////////////////////
 // 1-point crossover for bitstring
 eo1PtBitXover<Indi> xover1;
 // uniform crossover for bitstring
 eoUBitXover<Indi> xoverU;
 // 2-pots xover
 eoNPtsBitXover<Indi> xover2(2);
 // Combine them with relative rates
 eoPropCombinedQuadOp<Indi> xover(xover1, onePointRate);
 xover.add(xoverU, URate);
 xover.add(xover2, twoPointsRate, true);
 // standard bit-flip mutation for bitstring
 eoBitMutation<Indi>  mutationBitFlip(pMutPerBit);
 // mutate exactly 1 bit per individual
 eoDetBitFlip<Indi> mutationOneBit; 
 // Combine them with relative rates
 eoPropCombinedMonOp<Indi> mutation(mutationBitFlip, bitFlipRate);
 mutation.add(mutationOneBit, oneBitRate, 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(vecSize);
 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
     outState.registerObject(parser);
     outState.registerObject(pop);
     outState.registerObject(rng);
     // and feed the state to state savers
// save state every 100th  generation
     eoCountedStateSaver stateSaver1(100, 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) final population
 pop.sort();
 cout << "FINAL Population\n" << pop << endl;
}
// A main that catches the exceptions
int main(int argc, char **argv)
{
#ifdef _MSC_VER
     int flag = _CrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF);
       flag |= _CRTDBG_LEAK_CHECK_DF;
     _CrtSetDbgFlag(flag);
//    _CrtSetBreakAlloc(100);
#endif
     try
     {
             main_function(argc, argv);
     }
     catch(exception& e)
     {
             cout << "Exception: " << e.what() << '\n';
     }
     return 1;
}


Back to Lesson 3 - Tutorial main page - Algorithm-Based - Component-Based - Programming hints - EO documentation
Marc Schoenauer

Last modified: Sun Nov 26 09:31:04 2000