diff --git a/.gitignore b/.gitignore index 285690863..5de8ffbd3 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ # ignore object and archive files *.[oa] +tags # ignore auto-saved files *~ diff --git a/eo/src/eo b/eo/src/eo index c0fe2b8d7..4bb6a69a8 100644 --- a/eo/src/eo +++ b/eo/src/eo @@ -158,6 +158,8 @@ #include "other/external_eo" #include "eoCounter.h" +#include "eoForge.h" + //----------------------------------------------------------------------------- // to be continued ... diff --git a/eo/src/eoDetTournamentSelect.h b/eo/src/eoDetTournamentSelect.h index 8026f7a9b..7c06b3dd1 100644 --- a/eo/src/eoDetTournamentSelect.h +++ b/eo/src/eoDetTournamentSelect.h @@ -42,7 +42,8 @@ @ingroup Selectors */ -template class eoDetTournamentSelect: public eoSelectOne +template +class eoDetTournamentSelect: public eoSelectOne { public: /* (Default) Constructor - @@ -66,6 +67,8 @@ template class eoDetTournamentSelect: public eoSelectOne private: unsigned tSize; + public: + virtual std::string className() const {return "eoDetTournamentSelect";} }; //----------------------------------------------------------------------------- diff --git a/eo/src/eoForge.h b/eo/src/eoForge.h new file mode 100644 index 000000000..c4a3e78cd --- /dev/null +++ b/eo/src/eoForge.h @@ -0,0 +1,201 @@ + +/* + 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 + + © 2020 Thales group + + Authors: + Johann Dreo +*/ + +#ifndef _eoForge_H_ +#define _eoForge_H_ + +#include +#include +#include +#include +#include + +/** Interface for a "Forge": a class that can defer instanciation of EO's operator. + * + * This interface only declares an `instanciate` method, + * in order to be able to make containers of factories (@see eoForgeOperator). + * + * @ingroup Core + * @defgroup Forge Wrap and defer operators' instanciations. + * @ingroup Forge + */ +template +class eoForgeInterface +{ + public: + virtual Itf& instanciate() = 0; + virtual ~eoForgeInterface() {} +}; + +/** This "Forge" can defer the instanciation of an EO's operator. + * + * It allows to decouple the constructor's parameters setup from its actual call. + * You can declare a parametrized operator at a given time, + * then actually instanciate it (with the given interface) at another time. + * + * This allows for creating containers of pre-parametrized operators (@see eoForgeMap or @see eoForgeVector). + * + * @code + eoForgeOperator,eoRankMuSelect> forge(mu); + // ^ desired ^ to-be-instanciated ^ operator's + // interface operator parameters + + // Actual instanciation: + eoSelect& select = forge.instanciate(); + * @endcode + * + * @ingroup Forge + */ +template +class eoForgeOperator : public eoForgeInterface +{ + public: + eoForgeOperator(Args&&... args) : + _args(std::forward(args)...) + { } + + Itf& instanciate() + { + return *(constructor(_args)); + } + + virtual ~eoForgeOperator() {} + + protected: + std::tuple _args; + + private: + template + struct index {}; + + template + struct gen_seq : gen_seq {}; + + template + struct gen_seq<0, Idx...> : index {}; + + template + Op* constructor(std::tuple& args, index) + { + Op* p_op = new Op(std::get(args)...); + return p_op; + } + + template + Op* constructor(std::tuple& args) + { + return constructor(args, gen_seq{}); + } + +}; + +/** A map holding an operator with deferred instanciation at a given key. + * + * @note You can actually store several instances of the same class, + * with different parametrization (or not). + * + * @code + eoForgeMap> named_factories; + + // Capture constructor's parameters and defer instanciation. + named_factories.add>("RMS", 1); + named_factories.setup>("RMS", 5); // Edit + + // Actually instanciate. + eoSelect& op = named_factories.instanciate("RMS"); + + // Call. + op(); + * @endcode + * + * @ingroup Forge + */ +template +class eoForgeMap : public std::map> > +{ + public: + template + void setup(std::string key, Args&&... args) + { + auto opf = std::make_shared< eoForgeOperator >(std::forward(args)...); + (*this)[key] = opf; + } + + template + void add(std::string key, Args&&... args) + { + setup(key, std::forward(args)...); + } + + Itf& instanciate(std::string key) + { + return this->at(key)->instanciate(); + } +}; + +/** A vector holding an operator with deferred instanciation at a given index. + * + * @note You can actually store several instances of the same class, + * with different parametrization (or not). + * + * @code + eoForgeVector> named_factories; + + // Capture constructor's parameters and defer instanciation. + named_factories.add>(1); + named_factories.setup>(0, 5); // Edit + + // Actually instanciate. + eoSelect& op = named_factories.instanciate("RMS"); + + // Call. + op(); + * @endcode + * + * @ingroup Forge + */ +template +class eoForgeVector : public std::vector> > +{ + public: + template + void add(Args&&... args) + { + auto opf = std::make_shared< eoForgeOperator >(std::forward(args)...); + this->push_back(opf); + } + + template + void setup(size_t index, Args&&... args) + { + auto opf = std::make_shared< eoForgeOperator >(std::forward(args)...); + this->at(index) = opf; + } + + Itf& instanciate(size_t index) + { + return this->at(index)->instanciate(); + } +}; + +#endif // _eoForge_H_ + diff --git a/eo/src/eoFunctor.h b/eo/src/eoFunctor.h index 465f0bc46..78202e84b 100644 --- a/eo/src/eoFunctor.h +++ b/eo/src/eoFunctor.h @@ -59,6 +59,8 @@ public : struct unary_function_tag {}; /// tag to identify a binary function in compile time function selection @see functor_category struct binary_function_tag {}; + + virtual std::string className() const { return "unknown";} }; /** @example t-eoFunctor.cpp */ diff --git a/eo/src/eoMergeReduce.h b/eo/src/eoMergeReduce.h index e5e8c2bcc..94ad7edbc 100644 --- a/eo/src/eoMergeReduce.h +++ b/eo/src/eoMergeReduce.h @@ -80,6 +80,8 @@ class eoPlusReplacement : public eoMergeReduce private : eoPlus plus; eoTruncate truncate; + public: + virtual std::string className() const {return "eoPlusReplacement";} }; /** @@ -103,6 +105,8 @@ class eoCommaReplacement : public eoMergeReduce private : eoNoElitism no_elite; eoTruncate truncate; + public: + virtual std::string className() const {return "eoCommaReplacement";} }; /** diff --git a/eo/src/eoProportionalSelect.h b/eo/src/eoProportionalSelect.h index 8da608f6f..842d2b34e 100644 --- a/eo/src/eoProportionalSelect.h +++ b/eo/src/eoProportionalSelect.h @@ -42,7 +42,8 @@ @ingroup Selectors */ -template class eoProportionalSelect: public eoSelectOne +template +class eoProportionalSelect: public eoSelectOne { public: /// Sanity check @@ -80,6 +81,8 @@ private : typedef std::vector FitVec; FitVec cumulative; +public: + virtual std::string className() const {return "eoProportionalSelect";} }; /** @example t-eoRoulette.cpp */ diff --git a/eo/src/eoReduceMerge.h b/eo/src/eoReduceMerge.h index d9d77b12c..c7d7f55db 100644 --- a/eo/src/eoReduceMerge.h +++ b/eo/src/eoReduceMerge.h @@ -86,6 +86,8 @@ class eoSSGAWorseReplacement : public eoReduceMerge private : eoLinearTruncate truncate; eoPlus plus; + public: + virtual std::string className() const {return "eoSSGAWorseReplacement";} }; /** @@ -101,6 +103,8 @@ class eoSSGADetTournamentReplacement : public eoReduceMerge private : eoDetTournamentTruncate truncate; eoPlus plus; + public: + virtual std::string className() const {return "eoSSGADetTournamentReplacement";} }; /** SSGA stochastic tournament replacement. Is an eoReduceMerge. @@ -118,6 +122,8 @@ class eoSSGAStochTournamentReplacement : public eoReduceMerge private : eoStochTournamentTruncate truncate; eoPlus plus; + public: + virtual std::string className() const {return "eoSSGAStochTournamentReplacement";} }; /** @} */ diff --git a/eo/src/eoReplacement.h b/eo/src/eoReplacement.h index a705f5254..8183bc8af 100644 --- a/eo/src/eoReplacement.h +++ b/eo/src/eoReplacement.h @@ -89,6 +89,8 @@ class eoGenerationalReplacement : public eoReplacement { _parents.swap(_offspring); } +public: + virtual std::string className() const {return "eoGenerationalReplacement ";} }; /** @@ -123,6 +125,8 @@ public : } private: eoReplacement & replace; +public: + virtual std::string className() const {return "eoWeakElitistReplacement ";} }; #endif diff --git a/eo/src/eoSequentialSelect.h b/eo/src/eoSequentialSelect.h index 53ffdd3b2..002aca7ab 100644 --- a/eo/src/eoSequentialSelect.h +++ b/eo/src/eoSequentialSelect.h @@ -84,6 +84,8 @@ private: bool ordered; unsigned current; std::vector eoPters; +public: + virtual std::string className() const {return "eoSequentialSelect";} }; diff --git a/eo/src/eoStochTournamentSelect.h b/eo/src/eoStochTournamentSelect.h index c3b6b2d21..95caabc5f 100644 --- a/eo/src/eoStochTournamentSelect.h +++ b/eo/src/eoStochTournamentSelect.h @@ -36,7 +36,8 @@ @ingroup Selectors */ -template class eoStochTournamentSelect: public eoSelectOne +template +class eoStochTournamentSelect: public eoSelectOne { public: @@ -62,6 +63,8 @@ template class eoStochTournamentSelect: public eoSelectOne private: double Trate; +public: + virtual std::string className() const {return "eoStochTournamentSelect";} }; #endif diff --git a/eo/test/CMakeLists.txt b/eo/test/CMakeLists.txt index d776237f6..d8c44b516 100644 --- a/eo/test/CMakeLists.txt +++ b/eo/test/CMakeLists.txt @@ -71,6 +71,8 @@ set (TEST_LIST t-eoParser t-eoPartiallyMappedXover t-eoEvalCmd + t-operator-forge + t-forge-algo ) diff --git a/eo/test/t-forge-algo.cpp b/eo/test/t-forge-algo.cpp new file mode 100644 index 000000000..b1304e05f --- /dev/null +++ b/eo/test/t-forge-algo.cpp @@ -0,0 +1,110 @@ +#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); + + eoForgeVector< eoContinue > continuators; + continuators.add< eoSteadyFitContinue >(10,10); + continuators.add< eoGenContinue >(100); + + eoForgeVector< eoQuadOp > crossovers; + crossovers.add< eo1PtBitXover >(); + crossovers.add< eoUBitXover >(0.5); // preference over 1 + crossovers.add< eoNPtsBitXover >(2); // nb of points + + eoForgeVector< eoMonOp > mutations; + mutations.add< eoBitMutation >(0.01); // proba of flipping one bit + mutations.add< eoDetBitFlip >(1); // mutate k bits + + eoForgeVector< eoSelectOne > selectors; + selectors.add< eoDetTournamentSelect >(pop_size/2); + selectors.add< eoStochTournamentSelect >(0.5); + selectors.add< eoSequentialSelect >(); + selectors.add< eoProportionalSelect >(); + + eoForgeVector< eoReplacement > replacors; + replacors.add< eoCommaReplacement >(); + replacors.add< eoPlusReplacement >(); + replacors.add< eoSSGAWorseReplacement >(); + replacors.add< eoSSGADetTournamentReplacement >(pop_size/2); + replacors.add< eoSSGAStochTournamentReplacement >(0.51); + + std::clog << continuators.size() * crossovers.size() * mutations.size() * selectors.size() * replacors.size() + << " algorithm instances to test." << std::endl; + + EOT best_sol; + std::string best_algo = ""; + + for(auto& forge_cont : continuators) { + auto& continuator = forge_cont->instanciate(); + + for(auto& forge_cross : crossovers) { + auto& crossover = forge_cross->instanciate(); + + for(auto& forge_mut : mutations ) { + auto& mutation = forge_mut->instanciate(); + + for(auto& forge_sel : selectors) { + auto& selector = forge_sel->instanciate(); + + for(auto& forge_rep : replacors) { + auto& replacor = forge_rep->instanciate(); + + std::ostringstream algo_name; + algo_name << continuator.className() << " + " + << crossover.className() << " + " + << mutation.className() << " + " + << selector.className() << " + " + << replacor.className(); + + std::clog << "ALGO: " << algo_name.str(); + std::clog.flush(); + + eoSequentialOp variator; + variator.add(crossover, 1.0); + variator.add(mutation, 1.0); + + eoGeneralBreeder breeder(selector, variator, 1.0); + + eoEasyEA algo(continuator, eval, breeder, replacor); + + eoPop pop; + pop.append(pop_size, init); + apply(eval,pop); + + algo(pop); + + std::clog << " = " << pop.best_element().fitness() << std::endl; + + if(best_sol.invalid()) { + best_sol = pop.best_element(); + best_algo = algo_name.str(); + } else if(pop.best_element().fitness() > best_sol.fitness()) { + best_sol = pop.best_element(); + best_algo = algo_name.str(); + } + + } + } + } + } + } + std::cout << "Best algo: " << best_algo << ", with " << best_sol << std::endl; + +} diff --git a/eo/test/t-operator-forge.cpp b/eo/test/t-operator-forge.cpp new file mode 100644 index 000000000..a764b9b30 --- /dev/null +++ b/eo/test/t-operator-forge.cpp @@ -0,0 +1,61 @@ +#include +#include + +#include + +struct OpInterface +{ + std::string _name; + OpInterface(std::string name) : _name(name) {} + virtual void operator()() = 0; +}; + +struct OpA : public OpInterface +{ + OpA(std::string name) : OpInterface(name) {} + void operator()(){std::cout << _name << std::endl;} +}; + +struct OpB : public OpInterface +{ + OpB(std::string name, std::string suffix) : OpInterface(name+suffix) {} + void operator()(){std::cout << _name << std::endl;} +}; + +int main(int /*argc*/, char** /*argv*/) +{ + // Forge container using names. + eoForgeMap named_factories; + + // Capture constructor's parameters and defer instanciation. + named_factories.add("OpA", "I'm A"); + named_factories.setup("OpA", "I'm actually A"); // Edit + named_factories.add("OpB1", "I'm B", " prime"); + named_factories.add("OpB2", "I'm a B", " junior"); + + // Actually instanciante. + OpInterface& opa = named_factories.instanciate("OpA"); + OpInterface& opb1 = named_factories.instanciate("OpB1"); + + // Call. + opa(); + opb1(); + + // Instanciate and call. + named_factories.instanciate("OpB2")(); + + + // Forge container using indices. + eoForgeVector indexed_factories; + + // Capture constructor's parameters and defer instanciation. + indexed_factories.add("I'm A"); + indexed_factories.setup(0, "I'm actually A"); // Edit + indexed_factories.add("I'm B", " prime"); + indexed_factories.add("I'm a B", " junior"); + + // Actually instanciante and call. + indexed_factories.instanciate(0)(); + indexed_factories.instanciate(1)(); + indexed_factories.instanciate(2)(); +}