From 02eb0e967da2ff05e87ac5f11d479f0d22d5295e Mon Sep 17 00:00:00 2001 From: nojhan Date: Wed, 26 Jan 2022 10:40:50 +0100 Subject: [PATCH] fix eoAlgoFoundry management of numeric parameters - Use a variant to avoid implicit casting to integer when selecting with brace-initialization. - Add more doc around parameter forges. --- eo/contrib/irace/fastga.cpp | 18 +-- eo/src/eoAlgoFoundry.h | 190 ++++++++++++++++++++++++------ eo/src/eoAlgoFoundryEA.h | 73 +++++++----- eo/src/eoAlgoFoundryFastGA.h | 155 +++++++++++++++--------- eo/src/eoEvalFoundryEA.h | 25 ++-- eo/src/eoFastGA.h | 10 ++ eo/src/eoForge.h | 34 +++++- eo/src/utils/eoRNG.h | 1 + eo/test/t-algo-forged.cpp | 15 ++- eo/test/t-eoAlgoFoundryFastGA.cpp | 22 ++-- eo/test/t-eoFoundryFastGA.cpp | 13 +- 11 files changed, 393 insertions(+), 163 deletions(-) diff --git a/eo/contrib/irace/fastga.cpp b/eo/contrib/irace/fastga.cpp index 73f0e5388..a7514f4b1 100644 --- a/eo/contrib/irace/fastga.cpp +++ b/eo/contrib/irace/fastga.cpp @@ -16,8 +16,6 @@ * ParadisEO algorithmic grammar definition. *****************************************************************************/ -// using Particle = eoRealParticle; -using Ints = eoInt, size_t>; using Bits = eoBit, int>; // by enumerating candidate operators and their parameters. @@ -341,7 +339,7 @@ int main(int argc, char* argv[]) "problem", "Problem ID", 'p', "Problem", /*required=*/true); const size_t problem = problem_p.value(); - assert(0 <= problem and problem < benchmark.size()); + assert(problem < benchmark.size()); // const size_t dimension = parser.getORcreateParam(1000, // "dimension", "Dimension size", @@ -624,7 +622,7 @@ int main(int argc, char* argv[]) eoInitFixedLength onemax_init(/*bitstring size=*/dimension, bgen); auto& foundry = make_foundry(store, onemax_init, eval_count, max_evals, generations, max_target); - Ints encoded_algo(foundry.size()); + eoAlgoFoundry::Encodings encoded_algo(foundry.size()); encoded_algo[foundry.crossover_rates .index()] = crossover_rate; encoded_algo[foundry.crossover_selectors .index()] = crossover_selector; @@ -641,16 +639,6 @@ int main(int argc, char* argv[]) foundry.select(encoded_algo); std::clog << foundry.name() << std::endl; - // // Evaluation of a forged encoded_algo on the sub-problem - // eoEvalFoundryFastGA eval_foundry( - // foundry, pop_size, - // onemax_init, onemax_eval, - // /*penalization=*/ dimension, // Worst case penalization. - // /*normalized=*/ false); // Use direct integer encoding. - // - // // Actually instanciate and run the algorithm. - // eval_foundry(encoded_algo); - /***************************************************************************** * Run and output results. *****************************************************************************/ @@ -661,7 +649,7 @@ int main(int argc, char* argv[]) onemax_eval(pop,pop); foundry(pop); // Actually run the selected algorithm. - } catch(eoMaxEvalException e) { + } catch(eoMaxEvalException & e) { eo::log << eo::debug << "Reached maximum evaluations: " << eval_count.getValue() << " / " << max_evals << std::endl; } diff --git a/eo/src/eoAlgoFoundry.h b/eo/src/eoAlgoFoundry.h index 2e7064c99..79591b085 100644 --- a/eo/src/eoAlgoFoundry.h +++ b/eo/src/eoAlgoFoundry.h @@ -15,6 +15,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA © 2020 Thales group + © 2022 Institut Pasteur Authors: Johann Dreo @@ -24,6 +25,7 @@ #define _eoAlgoFoundry_H_ #include +#include /** A vector of eoForge which hold an index. * @@ -36,13 +38,13 @@ * which takes the class name as template and its constructor's parameters * as arguments. For example: * @code - * eoOperatorFoundry< eoSelectOne > selectors; - * selectors.add< eoStochTournamentSelect >( 0.5 ); + * eoOperatorFoundry< eoSelectOne > selectors; + * selectors.add< eoStochTournamentSelect >( 0.5 ); * @endcode * * @warning If the managed constructor takes a reference YOU SHOULD ABSOLUTELY wrap it - * in a `std::ref` when using `add` or `setup`, or it will silently be passed as a copy, - * which would effectively disable any link between operators. + * in a `std::ref` when using `add` or `setup`, or it will silently be passed as a copy, + * which would effectively disable any link between operators. * * @ingroup Core * @ingroup Foundry @@ -51,11 +53,18 @@ template class eoOperatorFoundry : public eoForgeVector< Itf > { public: + /** Constructor + * + * @param encoding_index The slot position in the encodings, at which this operator is held. + * @param always_reinstantiate If false, will enable cache for the forges in this container. + */ eoOperatorFoundry(size_t encoding_index, bool always_reinstantiate = true ) : eoForgeVector(always_reinstantiate), _index(encoding_index) { } + /** Returns the slot index at which this is registered. + */ size_t index() const { return _index; } protected: @@ -63,6 +72,24 @@ class eoOperatorFoundry : public eoForgeVector< Itf > size_t _index; }; + +/** A vector of eoForge which hold a scalar numeric value. + * + * To be used in conjunction with a subclass of an eoAlgoFoundry, + * where it can hold a range of parameter values + * and hold the link to the encoding. @see eoAlgoFoundryEA + * + * As with eoForgeScalar, managed parameters + * are represented through a [min,max] range. + * + * For example: + * @code + * eoParameterFoundry< double > proba(0.0, 1.0); + * @endcode + * + * @ingroup Core + * @ingroup Foundry + */ template class eoParameterFoundry : public eoForgeScalar< Itf > { @@ -70,11 +97,26 @@ class eoParameterFoundry : public eoForgeScalar< Itf > "eoParameterFoundry should only be used on arithmetic types (i.e. integer or floating point types)"); public: + /** Underlying type of the parameter. + * + * @note: You probably only want to use either `double` or `size_t`. + * @see eoAlgoFoundry + */ + using Type = Itf; + + /** Constructor + * + * @param encoding_index The slot position in the encodings, at which this parameter is held. + * @param min Minimium possible value. + * @param max Maximum possible value. + */ eoParameterFoundry(size_t encoding_index, Itf min, Itf max) : eoForgeScalar(min, max), _index(encoding_index) { } + /** Returns the slot index at which this is registered. + */ size_t index() const { return _index; } protected: @@ -84,48 +126,54 @@ class eoParameterFoundry : public eoForgeScalar< Itf > /** Interface of a Foundry: a class that instantiate an eoAlgo on-the-fly, given a choice of its operators. * - * The chosen operators are encoded in a vector of indices. + * The chosen operators are encoded in a vector of numbers. * * The foundry subclass should first be set up with sets of operators of the same interface, * held within an eoOperatorFoundry member. * @code - * eoOperatorFoundry< eoSelectOne > selectors; + * eoOperatorFoundry< eoSelectOne > selectors; * @endcode * * In a second step, the operators to be used should be selected * by indicating their index, just like if the foundry was an array: * @code - * foundry.select({0, 1, 2}); - * // ^ ^ ^ - * // | | | - * // | | + 3d operator - * // | + 2d operator - * // + 1st operator + * foundry.select({size_t{0}, size_t{1}, size_t{2}}); + * // ^ ^ ^ + * // | | | + * // | | + 3d operator + * // | + 2d operator + * // + 1st operator * @endcode * * If you don't (want to) recall the order of the operators in the encoding, * you can use the `index()` member of eoOperatorFoundry, for example: * @code - * foundry.at(foundry.continuators.index()) = 2; // select the third continuator + * foundry.at(foundry.continuators.index()) = size_t{2}; // select the third continuator * @endcode * * Now, you must implement the foundry just like any eoAlgo, by using the eoPop interface: * @code - * foundry(pop); + * foundry(pop); * @encode * It will instantiate the needed operators (only) and the algorithm itself on-the-fly, * and then run it. + * + * @note: The "encoding" which represent the selected options, figuring the actual meta-algorithm, + * is a vector of `std::variant`, which can hold either a `size_t` or a `double`. + * The first one is used to indicate the index of an operator class + * *or* a parameter which is a size. + * The second is used to store numerical parameters values. * * @note: Thanks to the underlying eoOperatorFoundry, not all the added operators are instantiated. - * 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 - * @endcode + * 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 + * @endcode * * @warning If the managed constructor takes a reference YOU SHOULD ABSOLUTELY wrap it - * in a `std::ref` when using `add` or `setup`, or it will silently be passed as a copy, - * which would effectively disable any link between operators. + * in a `std::ref` when using `add` or `setup`, or it will silently be passed as a copy, + * which would effectively disable any link between operators. * * @ingroup Core * @ingroup Foundry @@ -135,30 +183,104 @@ template class eoAlgoFoundry : public eoAlgo { public: - /** + // We could use `std::any` instead of a variant, + // but this would be more prone to errors from the end user, at the end. + // Either the encoding is an index (of the operator within the list of instances) + // either it's a real-valued parameter, + // either it's a size. + // So there's no need for more types (AFAIK). + + /** The type use to represent a selected option in the meta-algorithm. + * + * This can figure, either: + * - the index of an operator in the list of possible ones, + * - the actual value of a numeric paramater, + * - the value of a parameter which is a size. */ - eoAlgoFoundry( size_t nb_operators ) : - _size(nb_operators), - _encoding(_size,0) + using Encoding = std::variant; + + /** The type use to store all selected options. + */ + using Encodings = std::vector; + + /** Constructor. + * + * @param nb_slots Number of operators or parameters that are assembled to make an algorithm. + */ + eoAlgoFoundry( size_t nb_slots ) : + _size(nb_slots) { } /** Select indices of all the operators. * * i.e. Select an algorithm to instantiate. + * + * @note: You need to indicate the type of each item + * if you want to call this with a brace-initialized vector. + * + * For example: + * @code + * foundry.select({ size_t{1}, double{0.5}, size_t{3} }); + * @endcode + * + * Or you can initialize the vector first: + * @code + * double crossover_rate = 0.5; + * size_t crossover_oper = 3; + * eoAlgoFoundry::Encodings encoded_algo(foundry.size()); + * encoded_algo[foundry.crossover_rates.index()] = crossover_rate; + * encoded_algo[foundry.crossover_opers.index()] = crossover_oper; + * @encdoe */ - void select( std::vector encoding ) + void select( Encodings encodings ) { - assert(encoding.size() == _encoding.size()); - _encoding = encoding; + assert(encodings.size() == _size); + _encodings = encodings; } - /** Access to the index of the currently selected operator. + /** Access to the encoding of the currently selected operator. + * + * @warning: This returns a `std::variant`, which you should `std::get`. + * + * For example: + * @code + * size_t opera_id = std::get(foundry.at(2)); + * double param_id = std::get(foundry.at(3)); + * @endcode + * + * @note: You can use rank, value or len to have automatic casting. */ - size_t& at(size_t i) + Encoding & at(size_t i) { - return _encoding.at(i); + return _encodings.at(i); } + /** Access to the currently selected ID of an operator. + */ + template + size_t rank(const OP& op) + { + return std::get( at(op.index()) ); + } + + /** Access to the currently selected value of a numeric parameter. + */ + template + double value(const OP& param) + { + return std::get( at(param.index()) ); + } + + /** Access to the currently selected value of a unsigned integer parameter. + */ + template + size_t len(const OP& param) + { + return std::get( at(param.index()) ); + } + + /** Returns the number of slots that makes this algorithm. + */ size_t size() const { return _size; @@ -166,14 +288,14 @@ class eoAlgoFoundry : public eoAlgo /** Return the underlying encoding vector. */ - std::vector encoding() const + Encodings encodings() const { - return _encoding; + return _encodings; } protected: const size_t _size; - std::vector _encoding; + std::vector _encodings; }; diff --git a/eo/src/eoAlgoFoundryEA.h b/eo/src/eoAlgoFoundryEA.h index 264072d2d..b88870208 100644 --- a/eo/src/eoAlgoFoundryEA.h +++ b/eo/src/eoAlgoFoundryEA.h @@ -15,9 +15,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA © 2020 Thales group + © 2022 Institut Pasteur Authors: - Johann Dreo + Johann Dreo */ #ifndef _eoAlgoFoundryEA_H_ @@ -36,7 +37,7 @@ * 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< eoStochTournamentSelect >( 0.5 ); * @endcode * * @warning If the constructor takes a reference YOU SHOULD ABSOLUTELY wrap it @@ -46,12 +47,12 @@ * In a second step, the operators to be used should be selected * by indicating their index, just like the foundry was a array of five elements: * @code - * foundry = {0, 1, 2, 0, 3}; - * // ^ ^ ^ ^ ^ replacement - * // | | | + selection - * // | | + mutation - * // | + crossover - * // + continue + * foundry = {size_t{0}, size_t{1}, size_t{2}, size_t{0}, size_t{3}}; + * // ^ ^ ^ ^ ^ replacement + * // | | | + selection + * // | | + mutation + * // | + crossover + * // + continue * @endcode * * @note: by default, the firsts of the five operators are selected. @@ -59,12 +60,12 @@ * If you don't (want to) recall the order of the operators in the encoding, * you can use the `index()` member, for example: * @code - * foundry.at(foundry.continuators.index()) = 2; // select the third continuator + * foundry.at(foundry.continuators.index()) = size_t{2}; // select the third continuator * @endcode * * Now, you can call the fourdry just like any eoAlgo, by passing it an eoPop: * @code - * foundry(pop); + * foundry(pop); * @encode * It will instantiate the needed operators (only) and the algorithm itself on-the-fly, * and then run it. @@ -73,7 +74,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.selectors.at(0).setup(0.5); // using constructor's arguments * @endcode * * @ingroup Foundry @@ -111,11 +112,11 @@ class eoAlgoFoundryEA : public eoAlgoFoundry */ void operator()(eoPop& pop) { - assert(continuators.size() > 0); assert(this->at(continuators.index()) < continuators.size()); - assert( crossovers.size() > 0); assert(this->at( crossovers.index()) < crossovers.size()); - assert( mutations.size() > 0); assert(this->at( mutations.index()) < mutations.size()); - assert( selectors.size() > 0); assert(this->at( selectors.index()) < selectors.size()); - assert(replacements.size() > 0); assert(this->at(replacements.index()) < replacements.size()); + assert(continuators.size() > 0); assert(this->rank(continuators) < continuators.size()); + assert( crossovers.size() > 0); assert(this->rank( crossovers) < crossovers.size()); + assert( mutations.size() > 0); assert(this->rank( mutations) < mutations.size()); + assert( selectors.size() > 0); assert(this->rank( selectors) < selectors.size()); + assert(replacements.size() > 0); assert(this->rank(replacements) < replacements.size()); eoSequentialOp variator; variator.add(this->crossover(), 1.0); @@ -140,11 +141,11 @@ class eoAlgoFoundryEA : public eoAlgoFoundry std::string name() { std::ostringstream name; - name << this->at(continuators.index()) << " (" << this->continuator().className() << ") + "; - name << this->at(crossovers.index()) << " (" << this->crossover().className() << ") + "; - name << this->at(mutations.index()) << " (" << this->mutation().className() << ") + "; - name << this->at(selectors.index()) << " (" << this->selector().className() << ") + "; - name << this->at(replacements.index()) << " (" << this->replacement().className() << ")"; + name << this->continuator().className() << " [" << this->rank(continuators) << "] + "; + name << this->crossover() .className() << " [" << this->rank(crossovers) << "] + "; + name << this->mutation() .className() << " [" << this->rank(mutations) << "] + "; + name << this->selector() .className() << " [" << this->rank(selectors) << "] + "; + name << this->replacement().className() << " [" << this->rank(replacements) << "]"; return name.str(); } @@ -153,34 +154,44 @@ class eoAlgoFoundryEA : public eoAlgoFoundry const size_t _max_gen; public: + /** Currently selected continuator. + */ eoContinue& continuator() { - assert(this->at(continuators.index()) < continuators.size()); - return continuators.instantiate(this->at(continuators.index())); + assert(this->rank(continuators) < continuators.size()); + return continuators.instantiate(this->rank(continuators)); } + /** Currently selected crossover. + */ eoQuadOp& crossover() { - assert(this->at(crossovers.index()) < crossovers.size()); - return crossovers.instantiate(this->at(crossovers.index())); + assert(this->rank(crossovers) < crossovers.size()); + return crossovers.instantiate(this->rank(crossovers)); } + /** Currently selected mutation. + */ eoMonOp& mutation() { - assert(this->at(mutations.index()) < mutations.size()); - return mutations.instantiate(this->at(mutations.index())); + assert(this->rank(mutations) < mutations.size()); + return mutations.instantiate(this->rank(mutations)); } + /** Currently selected selector. + */ eoSelectOne& selector() { - assert(this->at(selectors.index()) < selectors.size()); - return selectors.instantiate(this->at(selectors.index())); + assert(this->rank(selectors) < selectors.size()); + return selectors.instantiate(this->rank(selectors)); } + /** Currently selected replacement. + */ eoReplacement& replacement() { - assert(this->at(replacements.index()) < replacements.size()); - return replacements.instantiate(this->at(replacements.index())); + assert(this->rank(replacements) < replacements.size()); + return replacements.instantiate(this->rank(replacements)); } }; diff --git a/eo/src/eoAlgoFoundryFastGA.h b/eo/src/eoAlgoFoundryFastGA.h index 0417511cb..8ccbb7039 100644 --- a/eo/src/eoAlgoFoundryFastGA.h +++ b/eo/src/eoAlgoFoundryFastGA.h @@ -29,25 +29,37 @@ /** 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 + * The foundry should first be set up with sets of operators/parameters * for the main modules of a FastGA: - * continuators, crossovers, mutations, selections, replacement operators, etc. + * continuators, crossovers (and rate of call), mutations (and rate of call), + * selections, replacement operators, offspring size, 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< eoRandomSelect >(); + * 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 with other operator(s). + * in a `std::ref`, or it will silently be passed as a copy, + * 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 10 elements: + * by indicating their wanted index or value, passing an array of 10 elements: * @code - * foundry.select({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); + * foundry.select({ + * double{0.1}, // crossover rate + * size_t{1}, // crossover selector + * size_t{2}, // crossover + * size_t{3}, // selector after crossover + * double{0.4}, // mutation rate + * size_t{5}, // mutation selector + * size_t{6}, // mutation + * size_t{7}, // replacement + * size_t{8}, // continuator + * size_t{9} // nb of offsprings + * }); * @endcode * * @note: by default, the firsts of the 10 operators are selected. @@ -55,22 +67,22 @@ * If you don't (want to) recall the order of the operators in the encoding, * you can use the `index()` member, for example: * @code - * foundry.at(foundry.continuators.index()) = 2; // select the third continuator + * foundry.at(foundry.continuators.index()) = size_t{2}; // select the third continuator * @endcode * * Now, you can call the foundry just like any eoAlgo, by passing it an eoPop: * @code - * foundry(pop); + * foundry(pop); * @encode * It will instantiate the needed operators (only) and the algorithm itself on-the-fly, * and then run it. * * @note: Thanks to the underlying eoOperatorFoundry, not all the added operators are instantiated. - * 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); // Will call constructor's arguments - * @endcode + * 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); // Will call constructor's arguments + * @endcode * * @ingroup Foundry * @ingroup Algorithms @@ -111,31 +123,31 @@ class eoAlgoFoundryFastGA : public eoAlgoFoundry public: /* Operators containers @{ */ - eoParameterFoundry< double > crossover_rates; + eoParameterFoundry< double > crossover_rates; eoOperatorFoundry< eoSelectOne > crossover_selectors; eoOperatorFoundry< eoQuadOp > crossovers; eoOperatorFoundry< eoSelectOne > aftercross_selectors; - eoParameterFoundry< double > mutation_rates; + eoParameterFoundry< double > mutation_rates; eoOperatorFoundry< eoSelectOne > mutation_selectors; eoOperatorFoundry< eoMonOp > mutations; eoOperatorFoundry< eoReplacement > replacements; eoOperatorFoundry< eoContinue > continuators; - eoParameterFoundry< size_t > offspring_sizes; + eoParameterFoundry< size_t > offspring_sizes; /* @} */ /** instantiate and call the pre-selected algorithm. */ void operator()(eoPop& pop) { - 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_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( crossover_selectors.size() > 0); assert(this->rank( crossover_selectors) < crossover_selectors.size()); + assert( crossovers.size() > 0); assert(this->rank( crossovers) < crossovers.size()); + assert(aftercross_selectors.size() > 0); assert(this->rank(aftercross_selectors) < aftercross_selectors.size()); + assert( mutation_selectors.size() > 0); assert(this->rank( mutation_selectors) < mutation_selectors.size()); + assert( mutations.size() > 0); assert(this->rank( mutations) < mutations.size()); + assert( replacements.size() > 0); assert(this->rank( replacements) < replacements.size()); + assert( continuators.size() > 0); assert(this->rank( continuators) < continuators.size()); // Objective function calls counter eoEvalCounterThrowException eval(_eval, _max_evals); @@ -165,7 +177,7 @@ class eoAlgoFoundryFastGA : public eoAlgoFoundry try { // restart(pop); algo(pop); - } catch(eoMaxEvalException e) { + } catch(eoMaxEvalException & e) { #ifndef NDEBUG eo::log << eo::debug << "Reached maximum evaluations: " << eval.getValue() << " / " << _max_evals << std::endl; #endif @@ -181,17 +193,18 @@ class eoAlgoFoundryFastGA : public eoAlgoFoundry std::string name() { std::ostringstream name; - name << "crossover_rates: " << this->at( crossover_rates.index()) << " (" << this-> crossover_rate() << ") + "; - name << "crossover_selectors: " << this->at( crossover_selectors.index()) << " (" << this-> crossover_selector().className() << ") + "; - name << "aftercross_selector: " << this->at(aftercross_selectors.index()) << " (" << this->aftercross_selector().className() << ") + "; - name << "crossovers: " << this->at( crossovers.index()) << " (" << this-> crossover().className() << ") + "; - name << "mutation_rates: " << this->at( mutation_rates.index()) << " (" << this-> mutation_rate() << ") + "; - name << "mutation_selectors: " << this->at( mutation_selectors.index()) << " (" << this-> mutation_selector().className() << ") + "; - name << "mutations: " << this->at( mutations.index()) << " (" << this-> mutation().className() << ") + "; - name << "replacements: " << this->at( replacements.index()) << " (" << this-> replacement().className() << ") + "; - name << "continuators: " << this->at( continuators.index()) << " (" << this-> continuator().className() << ") + "; - name << "offspring_sizes: " << this->at( offspring_sizes.index()) << " (" << this-> offspring_size() << ")"; - return name.str(); + name << "crossover_rate: " << this-> crossover_rate() << " + "; + name << "crossover_selector: " << this-> crossover_selector().className() << " [" << this->rank( crossover_selectors) << "] + "; + name << "aftercross_selector: " << this->aftercross_selector().className() << " [" << this->rank(aftercross_selectors) << "] + "; + name << "crossover: " << this-> crossover().className() << " [" << this->rank( crossovers) << "] + "; + name << "mutation_rate: " << this-> mutation_rate() << " + "; + name << "mutation_selector: " << this-> mutation_selector().className() << " [" << this->rank( mutation_selectors) << "] + "; + name << "mutation: " << this-> mutation().className() << " [" << this->rank( mutations) << "] + "; + name << "replacement: " << this-> replacement().className() << " [" << this->rank( replacements) << "] + "; + name << "continuator: " << this-> continuator().className() << " [" << this->rank( continuators) << "] + "; + name << "offspring_size: " << this-> offspring_size() << ""; + + return name.str(); } protected: @@ -201,61 +214,97 @@ class eoAlgoFoundryFastGA : public eoAlgoFoundry const size_t _max_restarts; public: + /** Currently selected continuator. + */ eoContinue& continuator() { - assert(this->at(continuators.index()) < continuators.size()); - return continuators.instantiate(this->at(continuators.index())); + const size_t r = this->rank(continuators); + assert(r < continuators.size()); + return continuators.instantiate(r); } + /** Currently selected crossover_rate. + */ double& crossover_rate() { - return crossover_rates.instantiate(this->at(crossover_rates.index())); + // We could have used `decltype(crossover_rates)::Type` instead of `double`, here, + // but this is less readable and the type is declared just above, + // so we are supposed to know it. + const double val = this->value(crossover_rates); + assert(crossover_rates.min() <= val and val <= crossover_rates.max()); + return crossover_rates.instantiate(val); } + /** Currently selected crossover. + */ eoQuadOp& crossover() { - assert(this->at(crossovers.index()) < crossovers.size()); - return crossovers.instantiate(this->at(crossovers.index())); + const size_t r = this->rank(crossovers); + assert(r < crossovers.size()); + return crossovers.instantiate(r); } + /** Currently selected mutation_rate. + */ double& mutation_rate() { - return mutation_rates.instantiate(this->at(mutation_rates.index())); + const double val = this->value(mutation_rates); + assert(mutation_rates.min() <= val and val <= mutation_rates.max()); + return mutation_rates.instantiate(val); } + /** Currently selected mutation. + */ eoMonOp& mutation() { - assert(this->at(mutations.index()) < mutations.size()); - return mutations.instantiate(this->at(mutations.index())); + const size_t r = this->rank(mutations); + assert(r < mutations.size()); + return mutations.instantiate(r); } + /** Currently selected crossover_selector. + */ eoSelectOne& crossover_selector() { - assert(this->at(crossover_selectors.index()) < crossover_selectors.size()); - return crossover_selectors.instantiate(this->at(crossover_selectors.index())); + const size_t r = this->rank(crossover_selectors); + assert(r < crossover_selectors.size()); + return crossover_selectors.instantiate(r); } + /** Currently selected aftercross_selector. + */ eoSelectOne& aftercross_selector() { - assert(this->at(aftercross_selectors.index()) < aftercross_selectors.size()); - return aftercross_selectors.instantiate(this->at(aftercross_selectors.index())); + const size_t r = this->rank(aftercross_selectors); + assert(r < aftercross_selectors.size()); + return aftercross_selectors.instantiate(r); } + /** Currently selected mutation_selector. + */ eoSelectOne& mutation_selector() { - assert(this->at(mutation_selectors.index()) < mutation_selectors.size()); - return mutation_selectors.instantiate(this->at(mutation_selectors.index())); + const size_t r = this->rank(mutation_selectors); + assert(r < mutation_selectors.size()); + return mutation_selectors.instantiate(r); } + /** Currently selected offspring_size. + */ size_t& offspring_size() { - return offspring_sizes.instantiate(this->at(offspring_sizes.index())); + const size_t val = this->len(offspring_sizes); + assert(offspring_sizes.min() <= val and val <= offspring_sizes.max()); + return offspring_sizes.instantiate(val); } + /** Currently selected replacement. + */ eoReplacement& replacement() { - assert(this->at(replacements.index()) < replacements.size()); - return replacements.instantiate(this->at(replacements.index())); + const size_t r = this->rank(replacements); + assert(r < replacements.size()); + return replacements.instantiate(r); } }; diff --git a/eo/src/eoEvalFoundryEA.h b/eo/src/eoEvalFoundryEA.h index 76521c9b6..e28645b73 100644 --- a/eo/src/eoEvalFoundryEA.h +++ b/eo/src/eoEvalFoundryEA.h @@ -15,9 +15,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA © 2020 Thales group + © 2022 Institut Pasteur Authors: - Johann Dreo + Johann Dreo */ #ifndef _eoEvalFoundryEA_H_ @@ -90,7 +91,7 @@ public: * auto& cont = foundry.continuator(); // Get the configured operator * @encode */ - std::vector decode( const EOT& sol ) const + typename eoAlgoFoundry::Encodings decode( const EOT& sol ) const { // // Denormalize // size_t cont = static_cast(std::ceil( sol[i_cont] * _foundry.continuators.size() )); @@ -125,18 +126,18 @@ public: _subpb_eval(pop,pop); auto config = decode(sol); - double cont = config[i_cont]; - double cros = config[i_cros]; - double muta = config[i_muta]; - double sele = config[i_sele]; - double repl = config[i_repl]; + size_t cont = std::get(config[i_cont]); + size_t cros = std::get(config[i_cros]); + size_t muta = std::get(config[i_muta]); + size_t sele = std::get(config[i_sele]); + size_t repl = std::get(config[i_repl]); if( - 0 <= cont and cont < _foundry.continuators.size() - and 0 <= cros and cros < _foundry.crossovers .size() - and 0 <= muta and muta < _foundry.mutations .size() - and 0 <= sele and sele < _foundry.selectors .size() - and 0 <= repl and repl < _foundry.replacements.size() + cont < _foundry.continuators.size() + and cros < _foundry.crossovers .size() + and muta < _foundry.mutations .size() + and sele < _foundry.selectors .size() + and repl < _foundry.replacements.size() ) { _foundry.select(config); diff --git a/eo/src/eoFastGA.h b/eo/src/eoFastGA.h index f157377f1..91300d4a8 100644 --- a/eo/src/eoFastGA.h +++ b/eo/src/eoFastGA.h @@ -13,6 +13,11 @@ 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 + + © 2022 Institut Pasteur + + Authors: + Johann Dreo */ #ifndef _eoFastGA_H_ @@ -116,6 +121,11 @@ public: eoPop crossed; crossed.push_back(sol1); crossed.push_back(sol2); + + // The aftercross selector may need fitness, + // so we evaluate those two solutions, if needed. + _pop_eval(crossed,crossed); + _select_aftercross.setup(crossed); EOT sol3 = _select_aftercross(crossed); diff --git a/eo/src/eoForge.h b/eo/src/eoForge.h index f36476579..bc2dbb67d 100644 --- a/eo/src/eoForge.h +++ b/eo/src/eoForge.h @@ -15,9 +15,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA © 2020 Thales group + © 2022 Institut Pasteur Authors: - Johann Dreo + Johann Dreo */ #ifndef _eoForge_H_ @@ -185,7 +186,7 @@ class eoForgeOperator : public eoForgeInterface Itf* _instantiated; }; -/** A vector holding an operator with deferred instantiation at a given index. +/** A vector holding an operator (with deferred instantiation) at a given index. * * @note You can actually store several instances of the same class, * with different parametrization (or not). @@ -319,18 +320,41 @@ class eoForgeVector : public std::vector*> bool _no_cache; }; +/** A range holding a parameter value at a given index. + * + * This is essential a scalar numerical parameter, with bounds check + * and an interface similar to an eoForgeVector. + * + * @note Contrary to eoForgeVector, this does not store a set of possible values. + * + * @code + eoForgeScalar factories(0.0, 1.0); + + // Actually instantiate. + double param = factories.instantiate(0.5); + * @endcode + * + * @ingroup Foundry + */ template class eoForgeScalar { public: using Interface = Itf; + /** Constructor + * + * @param min Minimum possible value. + * @param may Maximum possible value. + */ eoForgeScalar(Itf min, Itf max) : _min(min), _max(max) { } /** Just return the same value, without managing any instantiation. + * + * Actually checks if value is in range. */ Itf& instantiate(double value) { @@ -353,18 +377,24 @@ class eoForgeScalar Itf min() const { return _min; } Itf max() const { return _max; } + /** Set the minimum possible value. + */ void min(Itf min) { assert(_min <= _max); _min = min; } + /** Set the maximum possible value. + */ void max(Itf max) { assert(_max >= _min); _max = max; } + /** Set the possible range of values. + */ void setup(Itf min, Itf max) { _min = min; diff --git a/eo/src/utils/eoRNG.h b/eo/src/utils/eoRNG.h index 5733f9e6b..e91c6af5f 100644 --- a/eo/src/utils/eoRNG.h +++ b/eo/src/utils/eoRNG.h @@ -55,6 +55,7 @@ typedef unsigned long uint32_t; #include "../eoObject.h" + /** Random Number Generator @class eoRng eoRNG.h utils/eoRNG.h diff --git a/eo/test/t-algo-forged.cpp b/eo/test/t-algo-forged.cpp index b2f6b4d46..194edc9aa 100644 --- a/eo/test/t-algo-forged.cpp +++ b/eo/test/t-algo-forged.cpp @@ -77,14 +77,13 @@ int main(int /*argc*/, char** /*argv*/) pop.append(pop_size, init); eval(pop,pop); - foundry.at(foundry.continuators.index()) = i_cont; - foundry.at(foundry.crossovers.index()) = i_cross; - foundry.at(foundry.mutations.index()) = i_mut; - foundry.at(foundry.selectors.index()) = i_sel; - foundry.at(foundry.replacements.index()) = i_rep; - - // Or, if you know the order. - foundry.select({i_cont, i_cross, i_mut, i_sel, i_rep}); + foundry.select({ + size_t{i_cont}, + size_t{i_cross}, + size_t{i_mut}, + size_t{i_sel}, + size_t{i_rep} + }); // Actually perform a search foundry(pop); diff --git a/eo/test/t-eoAlgoFoundryFastGA.cpp b/eo/test/t-eoAlgoFoundryFastGA.cpp index 2c37d8190..fa97ca1b8 100644 --- a/eo/test/t-eoAlgoFoundryFastGA.cpp +++ b/eo/test/t-eoAlgoFoundryFastGA.cpp @@ -16,6 +16,7 @@ int main(int /*argc*/, char** /*argv*/) using EOT = eoBit; oneMaxEval eval; + eoPopLoopEval popeval(eval); eoBooleanGenerator gen(0.5); eoInitFixedLength init(dim, gen); @@ -76,6 +77,8 @@ int main(int /*argc*/, char** /*argv*/) ; std::clog << n << " possible algorithms instances." << std::endl; + std::clog << "Running everything (this may take time)..." << std::endl; + EOT best_sol; std::string best_algo = ""; @@ -91,15 +94,20 @@ int main(int /*argc*/, char** /*argv*/) eoPop pop; pop.append(pop_size, init); + popeval(pop,pop); + // FIXME put the parameters in the test? foundry.select({ - i_crossselect, - i_cross, - i_aftercrosel, - i_mutselect, - i_mut, - i_rep, - i_cont + double{0.5}, // crossover_rate + size_t{i_crossselect}, + size_t{i_cross}, + size_t{i_aftercrosel}, + double{0.5}, // mutation_rate + size_t{i_mutselect}, + size_t{i_mut}, + size_t{i_rep}, + size_t{i_cont}, + size_t{pop_size} // offspring_size }); // Actually perform a search diff --git a/eo/test/t-eoFoundryFastGA.cpp b/eo/test/t-eoFoundryFastGA.cpp index b5960f55c..6a6ee0322 100644 --- a/eo/test/t-eoFoundryFastGA.cpp +++ b/eo/test/t-eoFoundryFastGA.cpp @@ -99,7 +99,18 @@ int main(int /*argc*/, char** /*argv*/) pop.append(5,init); ::apply(onemax_eval,pop); - foundry.select({0,0,0,0,0,0/*,0,0*/}); + foundry.select({ + /*crossover_rates */ double{0.8}, + /*crossover_selectors */ size_t{0}, + /*crossovers */ size_t{0}, + /*aftercross_selectors*/ size_t{0}, + /*mutation_rates */ double{0.9}, + /*mutation_selectors */ size_t{0}, + /*mutations */ size_t{0}, + /*replacements */ size_t{0}, + /*continuators */ size_t{0}, + /*offspring_sizes */ size_t{1}, + }); foundry(pop); std::cout << "Done" << std::endl;