//----------------------------------------------------------------------------- // 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 // runtime_error #include // cout #include // ostrstream, istrstream #include // the general include for eo #include // EVAL #include "binary_value.h" // REPRESENTATION //----------------------------------------------------------------------------- // define your genotype and fitness types typedef eoBin Indi; // PARAMETRES //----------------------------------------------------------------------------- // 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) // note that the parameters are passed by reference so they can be updated void read_param(eoParser & _parser, uint32 & _seed, unsigned int & _vecSize, unsigned int & _popSize, unsigned int & _tSize, double & _pCross, double & _pMut, string & _load_name, unsigned int & _maxGen, unsigned int & _minGen, unsigned int & _steadyGen, double & _onePointRate, double & _twoPointsRate, double & _uRate, double & _pMutPerBit, double & _bitFlipRate, double & _oneBitRate ) { // For each parameter, define Parameter, read it through the parser, // and assign the value to the variable eoValueParam seedParam(time(0), "seed", "Random number seed", 'S'); _parser.processParam( seedParam ); _seed = seedParam.value(); eoValueParam vecSizeParam(8, "vecSize", "Genotype size",'V'); _parser.processParam( vecSizeParam, "Representation" ); _vecSize = vecSizeParam.value(); eoValueParam popSizeParam(10, "popSize", "Population size",'P'); _parser.processParam( popSizeParam, "Evolution engine" ); _popSize = popSizeParam.value(); eoValueParam tSizeParam(10, "tSize", "Tournament size",'T'); _parser.processParam( tSizeParam, "Evolution Engine" ); _tSize = tSizeParam.value(); eoValueParam loadNameParam("", "Load","A save file to restart from",'L'); _parser.processParam( loadNameParam, "Persistence" ); _load_name = loadNameParam.value(); eoValueParam maxGenParam(100, "maxGen", "Maximum number of generations",'G'); _parser.processParam( maxGenParam, "Stopping criterion" ); _maxGen = maxGenParam.value(); eoValueParam minGenParam(100, "minGen", "Minimum number of generations",'g'); _parser.processParam( minGenParam, "Stopping criterion" ); _minGen = minGenParam.value(); eoValueParam steadyGenParam(100, "steadyGen", "Number of generations with no improvement",'s'); _parser.processParam( steadyGenParam, "Stopping criterion" ); _steadyGen = steadyGenParam.value(); eoValueParam pCrossParam(0.6, "pCross", "Probability of Crossover", 'C'); _parser.processParam( pCrossParam, "Genetic Operators" ); _pCross = pCrossParam.value(); eoValueParam pMutParam(0.1, "pMut", "Probability of Mutation", 'M'); _parser.processParam( pMutParam, "Genetic Operators" ); _pMut = pMutParam.value(); eoValueParam onePointRateParam(1, "onePointRate", "Relative rate for one point crossover", '1'); _parser.processParam( onePointRateParam, "Genetic Operators" ); _onePointRate = onePointRateParam.value(); eoValueParam twoPointsRateParam(1, "twoPointRate", "Relative rate for two point crossover", '2'); _parser.processParam( twoPointsRateParam, "Genetic Operators" ); _twoPointsRate = twoPointsRateParam.value(); eoValueParam uRateParam(2, "uRate", "Relative rate for uniform crossover", 'U'); _parser.processParam( uRateParam, "Genetic Operators" ); _uRate = uRateParam.value(); eoValueParam pMutPerBitParam(0.01, "pMutPerBit", "Probability of flipping 1 bit in bit-flip mutation", 'b'); _parser.processParam( pMutPerBitParam, "Genetic Operators" ); _pMutPerBit = pMutPerBitParam.value(); eoValueParam bitFlipRateParam(0.01, "bitFlipRate", "Relative rate for bit-flip mutation", 'B'); _parser.processParam( bitFlipRateParam, "Genetic Operators" ); _bitFlipRate = bitFlipRateParam.value(); eoValueParam oneBitRateParam(0.01, "oneBitRate", "Relative rate for deterministic bit-flip mutation", 'D'); _parser.processParam( oneBitRateParam, "Genetic Operators" ); _oneBitRate = oneBitRateParam.value(); // the name of the "status" file where all actual parameter values will be saved string str_status = _parser.ProgramName() + ".status"; eoValueParam 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 } } // GENERAL // now the main_function: nothing changed, except input/output void main_function(int argc, char **argv) { // PARAMETRES uint32 seed; // decription of genotype unsigned int vecSize; // parameters for evolution engine unsigned int popSize; unsigned int tSize; // operators probabilities at the algorithm level double pCross; double pMut; // init and stop string load_name; unsigned int maxGen; unsigned int minGen; unsigned int steadyGen; // rates for crossovers double onePointRate; double twoPointsRate; double URate; // rates and private parameters for mutations; double pMutPerBit; double bitFlipRate; double oneBitRate; // define a parser from the command-line arguments eoParser parser(argc, argv); // Now read the parameters of the program read_param(parser, seed, vecSize, popSize, tSize, pCross, pMut, load_name, maxGen, minGen, steadyGen, onePointRate, twoPointsRate, URate, pMutPerBit, bitFlipRate, oneBitRate ); // EVAL ///////////////////////////// // Fitness function //////////////////////////// // Evaluation: from a plain C++ fn to an EvalFunc Object ... eoEvalFuncPtr& > plainEval( binary_value ); // ... to an object that counts the nb of actual evaluations eoEvalFuncCounter eval(plainEval); // INIT //////////////////////////////// // Initilisation of population //////////////////////////////// // Either load or initialize // create an empty pop eoPop 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 boolean_generator class (see utils/rnd_generator.h) eoInitFixedLength random(vecSize, boolean_generator()); // Init pop from the randomizer: need to use the append function pop.append(popSize, random); // and evaluate pop (STL syntax) apply(eval, pop); } // end of initializatio of the population // OUTPUT // sort pop for pretty printout pop.sort(); // Print (sorted) intial population (raw printout) cout << "Initial Population" << endl << pop << endl; // ENGINE ///////////////////////////////////// // selection and replacement //////////////////////////////////// // SELECT // The robust tournament selection eoDetTournament selectOne(tSize); // tSize in [2,POPSIZE] // is now encapsulated in a eoSelectPerc (entage) eoSelectPerc select(selectOne);// by default rate==1 // REPLACE // And we now have the full slection/replacement - though with // the same generational replacement at the moment :-) eoGenerationalReplacement replace; // OPERATORS ////////////////////////////////////// // The variation operators ////////////////////////////////////// // CROSSOVER // 1-point crossover for bitstring eoBinCrossover xover1; // uniform crossover for bitstring eoBinUxOver xoverU; // 2-pots xover eoBinNxOver xover2(2); // Combine them with relative rates eoPropCombinedQuadOp xover(xover1, onePointRate); xover.add(xoverU, URate); xover.add(xover2, twoPointsRate, true); // MUTATION // standard bit-flip mutation for bitstring eoBinMutation mutationBitFlip(pMutPerBit); // mutate exactly 1 bit per individual eoDetBitFlip mutationOneBit; // Combine them with relative rates eoPropCombinedMonOp mutation(mutationBitFlip, bitFlipRate); mutation.add(mutationOneBit, oneBitRate, true); // The operators are encapsulated into an eoTRansform object eoSGATransform transform(xover, pCross, mutation, pMut); // STOP ////////////////////////////////////// // termination condition see FirstBitEA.cpp ///////////////////////////////////// eoGenContinue genCont(maxGen); eoSteadyFitContinue steadyCont(minGen, steadyGen); eoFitContinue fitCont(vecSize); eoCombinedContinue continuator(genCont); continuator.add(steadyCont); continuator.add(fitCont); // CHECKPOINT // 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 checkpoint(continuator); // Create a counter parameter eoValueParam 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 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 bestStat; // Second moment stats: average and stdev eoSecondMomentStats 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 // GENERATION ///////////////////////////////////////// // the algorithm //////////////////////////////////////// // Easy EA requires // selection, transformation, eval, replacement, and stopping criterion eoEasyEA gga(checkpoint, eval, select, transform, replace); // Apply algo to pop - that's it! gga(pop); // OUTPUT // Print (sorted) intial population pop.sort(); cout << "FINAL Population\n" << pop << endl; // GENERAL } // 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; }