diff --git a/eo/src/eo b/eo/src/eo index ced1abdcc..b77e9bf2c 100644 --- a/eo/src/eo +++ b/eo/src/eo @@ -146,6 +146,7 @@ // Algorithms #include "eoEasyEA.h" #include "eoSGA.h" +#include "eoFastGA.h" // #include "eoEvolutionStrategy.h" removed for a while - until eoGenOp is done #include "eoAlgoReset.h" #include "eoAlgoRestart.h" diff --git a/eo/src/eoAlgoFoundryFastGA.h b/eo/src/eoAlgoFoundryFastGA.h index ca64f44e8..099507103 100644 --- a/eo/src/eoAlgoFoundryFastGA.h +++ b/eo/src/eoAlgoFoundryFastGA.h @@ -27,30 +27,30 @@ #include #include -/** A class that assemble an eoEasyEA on the fly, given a combination of available operators. +/** A class that assemble an eoFastGA on the fly, given a combination of available operators. * * The foundry should first be set up with sets of operators - * for the main modules of an EA: - * continuators, crossovers, mutations, selection and replacement operators. + * for the main modules of a FastGA: + * continuators, crossovers, mutations, selections, replacement operators, etc. * * This is done through public member variable's `add` method, * which takes the class name as template and its constructor's parameters * as arguments. For example: * @code - * foundry.selectors.add< eoStochTournamentSelect >( 0.5 ); + * foundry.selectors.add< eoRandomSelect >(); * @endcode * * @warning If the constructor takes a reference YOU SHOULD ABSOLUTELY wrap it * in a `std::ref`, or it will silently be passed as a copy, - * which would effectively disable any link between operators. + * which would effectively disable any link with other operator(s). * * In a second step, the operators to be used should be selected - * by indicating their index, passing an array of eight elements: + * by indicating their index, passing an array of 10 elements: * @code - * foundry.select({0, 1, 2, 3, 4, 5, 6, 7}); + * foundry.select({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); * @endcode * - * @note: by default, the firsts of the eight operators are selected. + * @note: by default, the firsts of the 10 operators are selected. * * If you don't (want to) recall the order of the operators in the encoding, * you can use the `index()` member, for example: @@ -58,7 +58,7 @@ * foundry.at(foundry.continuators.index()) = 2; // select the third continuator * @endcode * - * Now, you can call the fourdry just like any eoAlgo, by passing it an eoPop: + * Now, you can call the foundry just like any eoAlgo, by passing it an eoPop: * @code * foundry(pop); * @encode @@ -69,7 +69,7 @@ * Every instantiation is deferred upon actual use. That way, you can still reconfigure them * at any time with `eoForgeOperator::setup`, for example: * @code - * foundry.selector.at(0).setup(0.5); // using constructor's arguments + * foundry.selector.at(0).setup(0.5); // Will call constructor's arguments * @endcode * * @ingroup Foundry @@ -88,15 +88,20 @@ class eoAlgoFoundryFastGA : public eoAlgoFoundry size_t max_evals = 10000, size_t max_restarts = std::numeric_limits::max() ) : - eoAlgoFoundry(8), - continuators(0, true), // Always re-instantiate continuators, because they hold a state. - crossover_rates(1, false), + eoAlgoFoundry(10), + + crossover_rates(0, false), + crossover_selectors(1, false), crossovers(2, false), - mutation_rates(3, false), - mutations(4, false), - selectors(5, false), - pop_sizes(6, false), + aftercross_selectors(3, false), + + mutation_rates(4, false), + mutation_selectors(5, false), + mutations(6, false), + replacements(7, false), + continuators(8, true), // Always re-instantiate continuators, because they hold a state. + pop_sizes(9, false), _eval(eval), _init(init), _max_evals(max_evals), @@ -106,62 +111,53 @@ class eoAlgoFoundryFastGA : public eoAlgoFoundry public: /* Operators containers @{ */ - eoOperatorFoundry< eoContinue > continuators; eoOperatorFoundry< double > crossover_rates; + eoOperatorFoundry< eoSelectOne > crossover_selectors; eoOperatorFoundry< eoQuadOp > crossovers; + eoOperatorFoundry< eoSelectOne > aftercross_selectors; + eoOperatorFoundry< double > mutation_rates; + eoOperatorFoundry< eoSelectOne > mutation_selectors; eoOperatorFoundry< eoMonOp > mutations; - eoOperatorFoundry< eoSelectOne > selectors; - eoOperatorFoundry< size_t > pop_sizes; + eoOperatorFoundry< eoReplacement > replacements; + eoOperatorFoundry< eoContinue > continuators; + eoOperatorFoundry< size_t > pop_sizes; /* @} */ /** instantiate and call the pre-selected algorithm. */ void operator()(eoPop& pop) { - assert( continuators.size() > 0); assert(this->at( continuators.index()) < continuators.size()); - assert(crossover_rates.size() > 0); assert(this->at(crossover_rates.index()) < crossover_rates.size()); - assert( crossovers.size() > 0); assert(this->at( crossovers.index()) < crossovers.size()); - assert( mutation_rates.size() > 0); assert(this->at( mutation_rates.index()) < mutation_rates.size()); - assert( mutations.size() > 0); assert(this->at( mutations.index()) < mutations.size()); - assert( selectors.size() > 0); assert(this->at( selectors.index()) < selectors.size()); - assert( pop_sizes.size() > 0); assert(this->at( pop_sizes.index()) < pop_sizes.size()); - assert( replacements.size() > 0); assert(this->at( replacements.index()) < replacements.size()); - - // Crossover or clone - double cross_rate = this->crossover_rate(); - eoProportionalOp cross; - // Cross-over that produce only one offspring, - // made by wrapping the quad op (which produce 2 offsprings) - // in a bin op (which ignore the second offspring). - eoQuad2BinOp single_cross(this->crossover()); - cross.add(single_cross, cross_rate); - eoBinCloneOp cross_clone; - cross.add(cross_clone, 1 - cross_rate); // Clone - - // Mutation or clone - double mut_rate = this->mutation_rate(); - eoProportionalOp mut; - mut.add(this->mutation(), mut_rate); - eoMonCloneOp mut_clone; - mut.add(mut_clone, 1 - mut_rate); // FIXME TBC - - // Apply mutation after cross-over. - eoSequentialOp variator; - variator.add(cross,1.0); - variator.add(mut,1.0); - - // All variatiors - double lambda = this->pop_size(); - eoGeneralBreeder breeder(this->selector(), variator, lambda, /*as rate*/false); + assert( crossover_rates.size() > 0); assert(this->at( crossover_rates.index()) < crossover_rates.size()); + assert( crossover_selectors.size() > 0); assert(this->at( crossover_selectors.index()) < crossover_selectors.size()); + assert( crossovers.size() > 0); assert(this->at( crossovers.index()) < crossovers.size()); + assert(aftercross_selectors.size() > 0); assert(this->at(aftercross_selectors.index()) < aftercross_selectors.size()); + assert( mutation_rates.size() > 0); assert(this->at( mutation_rates.index()) < mutation_rates.size()); + assert( mutation_selectors.size() > 0); assert(this->at( mutation_selectors.index()) < mutation_selectors.size()); + assert( mutations.size() > 0); assert(this->at( mutations.index()) < mutations.size()); + assert( replacements.size() > 0); assert(this->at( replacements.index()) < replacements.size()); + assert( continuators.size() > 0); assert(this->at( continuators.index()) < continuators.size()); + assert( pop_sizes.size() > 0); assert(this->at( pop_sizes.index()) < pop_sizes.size()); // Objective function calls counter eoEvalCounterThrowException eval(_eval, _max_evals); eoPopLoopEval pop_eval(eval); // Algorithm itself - eoEasyEA algo = eoEasyEA(this->continuator(), pop_eval, breeder, this->replacement()); + eoFastGA algo( + this->crossover_rate(), + this->crossover_selector(), + this->crossover(), + this->aftercross_selector(), + this->mutation_rate(), + this->mutation_selector(), + this->mutation(), + pop_eval, + this->replacement(), + this->continuator(), + this->pop_size() + ); // Restart wrapper eoAlgoPopReset reset_pop(_init, pop_eval); @@ -172,27 +168,27 @@ class eoAlgoFoundryFastGA : public eoAlgoFoundry restart(pop); } catch(eoMaxEvalException e) { // In case some solutions were not evaluated when max eval occured. + // FIXME can this even be considered legal? eoPopLoopEval pop_last_eval(_eval); pop_last_eval(pop,pop); } } /** Return an approximate name of the selected algorithm. - * - * @note: does not take into account parameters of the operators, - * only show class names. */ std::string name() { std::ostringstream name; - name << this->at( continuators.index()) << " (" << this-> continuator().className() << ") + "; - name << this->at(crossover_rates.index()) << " (" << this->crossover_rate() << ") + "; - name << this->at( crossovers.index()) << " (" << this-> crossover().className() << ") + "; - name << this->at( mutation_rates.index()) << " (" << this-> mutation_rate() << ") + "; - name << this->at( mutations.index()) << " (" << this-> mutation().className() << ") + "; - name << this->at( selectors.index()) << " (" << this-> selector().className() << ") + "; - name << this->at( pop_sizes.index()) << " (" << this-> pop_size() << ")"; - name << this->at( replacements.index()) << " (" << this-> replacement().className() << ")"; + name << this->at( crossover_rates.index()) << " (" << this-> crossover_rate() << ") + "; + name << this->at( crossover_selectors.index()) << " (" << this-> crossover_selector().className() << ") + "; + name << this->at(aftercross_selectors.index()) << " (" << this->aftercross_selector().className() << ") + "; + name << this->at( crossovers.index()) << " (" << this-> crossover().className() << ") + "; + name << this->at( mutation_rates.index()) << " (" << this-> mutation_rate() << ") + "; + name << this->at( mutation_selectors.index()) << " (" << this-> mutation_selector().className() << ") + "; + name << this->at( mutations.index()) << " (" << this-> mutation().className() << ") + "; + name << this->at( replacements.index()) << " (" << this-> replacement().className() << ") + "; + name << this->at( continuators.index()) << " (" << this-> continuator().className() << ") + "; + name << this->at( pop_sizes.index()) << " (" << this-> pop_size() << ")"; return name.str(); } @@ -233,10 +229,22 @@ class eoAlgoFoundryFastGA : public eoAlgoFoundry return mutations.instantiate(this->at(mutations.index())); } - eoSelectOne& selector() + eoSelectOne& crossover_selector() { - assert(this->at(selectors.index()) < selectors.size()); - return selectors.instantiate(this->at(selectors.index())); + assert(this->at(crossover_selectors.index()) < crossover_selectors.size()); + return crossover_selectors.instantiate(this->at(crossover_selectors.index())); + } + + eoSelectOne& aftercross_selector() + { + assert(this->at(aftercross_selectors.index()) < aftercross_selectors.size()); + return aftercross_selectors.instantiate(this->at(aftercross_selectors.index())); + } + + eoSelectOne& mutation_selector() + { + assert(this->at(mutation_selectors.index()) < mutation_selectors.size()); + return mutation_selectors.instantiate(this->at(mutation_selectors.index())); } size_t& pop_size() diff --git a/eo/src/eoFastGA.h b/eo/src/eoFastGA.h new file mode 100644 index 000000000..ac38f2532 --- /dev/null +++ b/eo/src/eoFastGA.h @@ -0,0 +1,150 @@ + +/* + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _eoFastGA_H_ +#define _eoFastGA_H_ + +/** The Fast Genetic Algorithm. + * + * @ingroup Algorithms + */ +template +class eoFastGA : public eoAlgo +{ +protected: + double _rate_crossover; + eoSelectOne& _select_cross; + eoQuadOp& _crossover; + eoSelectOne& _select_aftercross; + + double _rate_mutation; + eoSelectOne& _select_mut; + eoMonOp& _mutation; + + eoPopEvalFunc& _pop_eval; + eoReplacement& _replace; + + eoContinue& _continuator; + + double _offsprings_size; + +public: + + eoFastGA( + double rate_crossover, + eoSelectOne& select_cross, + eoQuadOp& crossover, + eoSelectOne& select_aftercross, + double rate_mutation, + eoSelectOne& select_mut, + eoMonOp& mutation, + eoPopEvalFunc& pop_eval, + eoReplacement& replace, + eoContinue& continuator, + double offsprings_size = 0 + ) : + _rate_crossover(rate_crossover), + _select_cross(select_cross), + _crossover(crossover), + _select_aftercross(select_aftercross), + _rate_mutation(rate_mutation), + _select_mut(select_mut), + _mutation(mutation), + _pop_eval(pop_eval), + _replace(replace), + _continuator(continuator), + _offsprings_size(offsprings_size) + { + } + + void operator()(eoPop& pop) + { +#ifndef NDEBUG + assert(pop.size() > 0); + for(auto sol : pop) { + assert(not sol.invalid()); + } +#endif + // Set lambda to the pop size + // if it was not set up at construction. + if(_offsprings_size == 0) { + _offsprings_size = pop.size(); + } + + do { + eoPop offsprings; + + for(size_t i=0; i < _offsprings_size; ++i) { + + if(eo::rng.flip(_rate_crossover)) { + // Manual setup of eoSelectOne + // (usually they are setup in a + // wrapping eoSelect). + _select_cross.setup(pop); + + // Copy of const ref solutions, + // because one alter them hereafter. + EOT sol1 = _select_cross(pop); + EOT sol2 = _select_cross(pop); + + // If the operator returns true, + // olutions have been altered. + if(_crossover(sol1, sol2)) { + sol1.invalidate(); + sol2.invalidate(); + } + + // Select one of the two solutions + // which have been crossed. + eoPop crossed; + crossed.push_back(sol1); + crossed.push_back(sol2); + _select_aftercross.setup(crossed); + EOT sol3 = _select_aftercross(crossed); + + // Additional mutation (X)OR the crossed/cloned solution. + if(eo::rng.flip(_rate_mutation)) { + if(_mutation(sol3)) { + sol3.invalidate(); + } + } + offsprings.push_back(sol3); + + } else { // If not crossing, always mutate. + _select_mut.setup(pop); + EOT sol3 = _select_mut(pop); + _mutation(sol3); + offsprings.push_back(sol3); + } + } + assert(offsprings.size() == _offsprings_size); + + _pop_eval(pop, offsprings); + _replace(pop, offsprings); + + } while(_continuator(pop)); +#ifndef NDEBUG + assert(pop.size() > 0); + for(auto sol : pop) { + assert(not sol.invalid()); + } +#endif + } + +}; + +#endif // _eoFastGA_H_ diff --git a/eo/test/CMakeLists.txt b/eo/test/CMakeLists.txt index 07093d4fe..b6b9afee6 100644 --- a/eo/test/CMakeLists.txt +++ b/eo/test/CMakeLists.txt @@ -76,8 +76,10 @@ set (TEST_LIST t-algo-forged t-algo-forged-search t-FastGA + t-eoFastGA t-forge-FastGA t-eoFoundryFastGA + t-eoAlgoFoundryFastGA ) diff --git a/eo/test/t-FastGA.cpp b/eo/test/t-FastGA.cpp index 3dc08b370..548733673 100644 --- a/eo/test/t-FastGA.cpp +++ b/eo/test/t-FastGA.cpp @@ -1,3 +1,20 @@ + +/* + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + #include #include diff --git a/eo/test/t-eoAlgoFoundryFastGA.cpp b/eo/test/t-eoAlgoFoundryFastGA.cpp new file mode 100644 index 000000000..9c5881eb2 --- /dev/null +++ b/eo/test/t-eoAlgoFoundryFastGA.cpp @@ -0,0 +1,147 @@ +#include +#include + +#include +#include +#include "../../problems/eval/oneMaxEval.h" + + +int main(int /*argc*/, char** /*argv*/) +{ + size_t dim = 500; + size_t pop_size = 10; + + eo::log << eo::setlevel(eo::warnings); + + using EOT = eoBit; + + oneMaxEval eval; + + eoBooleanGenerator gen(0.5); + eoInitFixedLength init(dim, gen); + + eoAlgoFoundryFastGA foundry(init, eval, pop_size*10); + + /***** Variation rates *****/ + for(double r = 0.0; r < 1.0; r+=0.1) { + foundry.crossover_rates.add(r); + foundry. mutation_rates.add(r); + } + + /***** Crossovers ****/ + foundry.crossovers.add< eo1PtBitXover >(); + foundry.crossovers.add< eoUBitXover >(0.5); // preference over 1 + for(size_t i=1; i < 11; i+=4) { + foundry.crossovers.add< eoNPtsBitXover >(i); // nb of points + } + + /***** Mutations ****/ + foundry.mutations.add< eoBitMutation >(0.01); // proba of flipping one bit + for(size_t i=1; i < 11; i+=4) { + foundry.mutations.add< eoDetBitFlip >(i); // mutate k bits + } + + /***** Selectors *****/ + for(eoOperatorFoundry>& ops : + {std::ref(foundry.crossover_selectors), + std::ref(foundry.aftercross_selectors), + std::ref(foundry.mutation_selectors) }) { + + ops.add< eoRandomSelect >(); + ops.add< eoStochTournamentSelect >(0.5); + ops.add< eoSequentialSelect >(); + ops.add< eoProportionalSelect >(); + for(size_t i=2; i < 10; i+=4) { + ops.add< eoDetTournamentSelect >(i); + } + } + + /***** Replacements ****/ + foundry.replacements.add< eoCommaReplacement >(); + foundry.replacements.add< eoPlusReplacement >(); + foundry.replacements.add< eoSSGAWorseReplacement >(); + foundry.replacements.add< eoSSGAStochTournamentReplacement >(0.51); + for(size_t i=2; i < 10; i+=4) { + foundry.replacements.add< eoSSGADetTournamentReplacement >(i); + } + + /***** Continuators ****/ + for(size_t i=10; i < 30; i+=10 ) { + foundry.continuators.add< eoSteadyFitContinue >(10,i); + } + + /***** Offspring population size *****/ + foundry.pop_sizes.add(0); // 0 = same as parent pop + // for(size_t s = pop_size; s < 2*pop_size; s+=pop_size/10) { + // foundry.pop_sizes.add(s); + // } + + + size_t n = + foundry.crossover_rates.size() + * foundry.crossover_selectors.size() + * foundry.crossovers.size() + * foundry.aftercross_selectors.size() + * foundry.mutation_rates.size() + * foundry.mutation_selectors.size() + * foundry.mutations.size() + * foundry.replacements.size() + * foundry.continuators.size() + * foundry.pop_sizes.size(); + std::clog << n << " possible algorithms instances." << std::endl; + + EOT best_sol; + std::string best_algo = ""; + + size_t i=0; + for(size_t i_crossrate = 0; i_crossrate < foundry.crossover_rates.size(); ++i_crossrate ) { + for(size_t i_crossselect = 0; i_crossselect < foundry.crossover_selectors.size(); ++i_crossselect ) { + for(size_t i_cross = 0; i_cross < foundry.crossovers.size(); ++i_cross ) { + for(size_t i_aftercrosel = 0; i_aftercrosel < foundry.aftercross_selectors.size(); ++i_aftercrosel ) { + for(size_t i_mutrate = 0; i_mutrate < foundry.mutation_rates.size(); ++i_mutrate ) { + for(size_t i_mutselect = 0; i_mutselect < foundry.mutation_selectors.size(); ++i_mutselect ) { + for(size_t i_mut = 0; i_mut < foundry.mutations.size(); ++i_mut ) { + for(size_t i_rep = 0; i_rep < foundry.replacements.size(); ++i_rep ) { + for(size_t i_cont = 0; i_cont < foundry.continuators.size(); ++i_cont ) { + for(size_t i_pop = 0; i_pop < foundry.pop_sizes.size(); ++i_pop ) { + std::clog << "\r" << i++ << "/" << n-1; std::clog.flush(); + + eoPop pop; + pop.append(pop_size, init); + + foundry.select({ + i_crossrate, + i_crossselect, + i_cross, + i_aftercrosel, + i_mutrate, + i_mutselect, + i_mut, + i_rep, + i_cont, + i_pop + }); + + // Actually perform a search + foundry(pop); + + if(best_sol.invalid()) { + best_sol = pop.best_element(); + best_algo = foundry.name(); + } else if(pop.best_element().fitness() > best_sol.fitness()) { + best_sol = pop.best_element(); + best_algo = foundry.name(); + } + } + } + } + } + } + } + } + } + } + } + std::cout << std::endl << "Best algo: " << best_algo << ", with " << best_sol << std::endl; + +} diff --git a/eo/test/t-eoFastGA.cpp b/eo/test/t-eoFastGA.cpp new file mode 100644 index 000000000..965a63b23 --- /dev/null +++ b/eo/test/t-eoFastGA.cpp @@ -0,0 +1,72 @@ + +/* + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include + +#include +#include +#include "../../problems/eval/oneMaxEval.h" + +using EOT = eoBit; + +int main(int /*argc*/, char** /*argv*/) +{ + size_t dim = 100; + size_t pop_size = 10; + + oneMaxEval evalfunc; + eoPopLoopEval eval(evalfunc); + + eoBooleanGenerator gen(0.5); + eoInitFixedLength init(dim, gen); + + double cross_rate = 0.5; + eoSequentialSelect select_cross; + eoUBitXover crossover; + eoRandomSelect select_aftercross; + + double mut_rate = 0.5; + eoSequentialSelect select_mut; + eoStandardBitMutation mutation(0.5); + + eoPlusReplacement replacement; + + eoGenContinue common_cont(dim*2); + eoCombinedContinue gen_cont(common_cont); + + eoFastGA algo( + cross_rate, + select_cross, + crossover, + select_aftercross, + mut_rate, + select_mut, + mutation, + eval, + replacement, + common_cont + ); + + eoPop pop; + pop.append(pop_size, init); + eval(pop,pop); + + algo(pop); + + std::cout << pop.best_element() << std::endl; +}