refactor: add eoOperatorFoundry

- Simpler relationships between encoding and set of operators in foundry.
- Rename "instanciate" in "instantiate"
This commit is contained in:
Johann Dreo 2020-05-03 15:31:25 +02:00
commit ae81793f7c
8 changed files with 193 additions and 112 deletions

View file

@ -25,7 +25,88 @@
#include <vector> #include <vector>
/** /** A vector of eoForge which hold an index.
*
* To be used in conjunction with a subclass of an eoAlgoFoundry,
* where it can store all the alternative operators
* and hold the link to the encoding. @see eoAlgoFoundryEA
*
* As with eoForgeVector, adding a managed operator
*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
* eoOperatorFoundry< eoSelectOne<EOT> > selectors;
* selectors.add< eoStochTournamentSelect<EOT> >( 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.
*
* @ingroup Core
* @ingroup Foundry
*/
template<class Itf>
class eoOperatorFoundry : public eoForgeVector< Itf >
{
public:
eoOperatorFoundry(size_t encoding_index, bool always_reinstantiate = true ) :
eoForgeVector<Itf>(always_reinstantiate),
_index(encoding_index)
{ }
size_t index() const { return _index; }
protected:
size_t _index;
};
/** 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 foundry subclass should first be set up with sets of operators of the same interface,
* held within an eoOperatorFoundry member.
* @code
* eoOperatorFoundry< eoSelectOne<EOT> > selectors;
* @endcode
*
* In a second step, the operators to be used should be selected
* by indicating their index, just like if the foundry was a array:
* @code
* foundry = {0, 1, 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
* @endcode
*
* Now, you must implement the foundry just like any eoAlgo, by using the eoPop interface:
* @code
* 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); // 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.
* *
* @ingroup Core * @ingroup Core
* @ingroup Foundry * @ingroup Foundry
@ -43,6 +124,8 @@ class eoAlgoFoundry : public eoAlgo<EOT>
{ } { }
/** Select indices of all the operators. /** Select indices of all the operators.
*
* i.e. Select an algorithm to instantiate.
*/ */
void select( std::vector<size_t> encoding ) void select( std::vector<size_t> encoding )
{ {
@ -62,6 +145,8 @@ class eoAlgoFoundry : public eoAlgo<EOT>
return _size; return _size;
} }
/** Return the underlying encoding vector.
*/
std::vector<size_t> encoding() const std::vector<size_t> encoding() const
{ {
return _encoding; return _encoding;

View file

@ -57,20 +57,20 @@
* @note: by default, the firsts of the five operators are selected. * @note: by default, the firsts of the five operators are selected.
* *
* If you don't (want to) recall the order of the operators in the encoding, * If you don't (want to) recall the order of the operators in the encoding,
* you can use the `index_of` member, for example: * you can use the `index()` member, for example:
* @code * @code
* foundry.at(foundry.index_of.continuators) = 2; // select the third continuator * foundry.at(foundry.continuators.index()) = 2; // select the third continuator
* @endcode * @endcode
* *
* Now, you can call the fourdry just like any eoAlgo, by passing it an eoPop: * Now, you can call the fourdry just like any eoAlgo, by passing it an eoPop:
* @code * @code
* foundry(pop); * foundry(pop);
* @encode * @encode
* It will instanciate the needed operators (only) and the algorithm itself on-the-fly, * It will instantiate the needed operators (only) and the algorithm itself on-the-fly,
* and then run it. * and then run it.
* *
* @note: Thanks to the underlying eoForgeVector, not all the added operators are instanciated. * @note: Thanks to the underlying eoOperatorFoundry, not all the added operators are instantiated.
* Every instanciation is deferred upon actual use. That way, you can still reconfigure them * Every instantiation is deferred upon actual use. That way, you can still reconfigure them
* at any time with `eoForgeOperator::setup`, for example: * at any time with `eoForgeOperator::setup`, for example:
* @code * @code
* foundry.selector.at(0).setup(0.5); // using constructor's arguments * foundry.selector.at(0).setup(0.5); // using constructor's arguments
@ -88,47 +88,34 @@ class eoAlgoFoundryEA : public eoAlgoFoundry<EOT>
*/ */
eoAlgoFoundryEA( eoPopEvalFunc<EOT>& eval, size_t max_gen ) : eoAlgoFoundryEA( eoPopEvalFunc<EOT>& eval, size_t max_gen ) :
eoAlgoFoundry<EOT>(5), eoAlgoFoundry<EOT>(5),
index_of(), continuators(0, true), // Always re-instantiate continuators, because they hold a state.
continuators(true), // Always re-instanciate continuators, because they hold a state. crossovers(1, false),
crossovers(false), mutations(2, false),
mutations(false), selectors(3, false),
selectors(false), replacements(4, false),
replacements(false),
_eval(eval), _eval(eval),
_max_gen(max_gen) _max_gen(max_gen)
{ } { }
public: public:
struct Indices
{
static const size_t continuators = 0;
static const size_t crossovers = 1;
static const size_t mutations = 2;
static const size_t selectors = 3;
static const size_t replacements = 4;
};
/** Helper for keeping track of the indices of the underlying encoding. */
const Indices index_of;
/* Operators containers @{ */ /* Operators containers @{ */
eoForgeVector< eoContinue<EOT> > continuators; eoOperatorFoundry< eoContinue<EOT> > continuators;
eoForgeVector< eoQuadOp<EOT> > crossovers; eoOperatorFoundry< eoQuadOp<EOT> > crossovers;
eoForgeVector< eoMonOp<EOT> > mutations; eoOperatorFoundry< eoMonOp<EOT> > mutations;
eoForgeVector< eoSelectOne<EOT> > selectors; eoOperatorFoundry< eoSelectOne<EOT> > selectors;
eoForgeVector< eoReplacement<EOT> > replacements; eoOperatorFoundry< eoReplacement<EOT> > replacements;
/* @} */ /* @} */
/** Instanciate and call the pre-selected algorithm. /** instantiate and call the pre-selected algorithm.
*/ */
void operator()(eoPop<EOT>& pop) void operator()(eoPop<EOT>& pop)
{ {
assert(continuators.size() > 0); assert(this->at(index_of.continuators) < continuators.size()); assert(continuators.size() > 0); assert(this->at(continuators.index()) < continuators.size());
assert( crossovers.size() > 0); assert(this->at(index_of.crossovers) < crossovers.size()); assert( crossovers.size() > 0); assert(this->at( crossovers.index()) < crossovers.size());
assert( mutations.size() > 0); assert(this->at(index_of.mutations) < mutations.size()); assert( mutations.size() > 0); assert(this->at( mutations.index()) < mutations.size());
assert( selectors.size() > 0); assert(this->at(index_of.selectors) < selectors.size()); assert( selectors.size() > 0); assert(this->at( selectors.index()) < selectors.size());
assert(replacements.size() > 0); assert(this->at(index_of.replacements) < replacements.size()); assert(replacements.size() > 0); assert(this->at(replacements.index()) < replacements.size());
eoSequentialOp<EOT> variator; eoSequentialOp<EOT> variator;
variator.add(this->crossover(), 1.0); variator.add(this->crossover(), 1.0);
@ -153,11 +140,11 @@ class eoAlgoFoundryEA : public eoAlgoFoundry<EOT>
std::string name() std::string name()
{ {
std::ostringstream name; std::ostringstream name;
name << this->at(index_of.continuators) << " (" << this->continuator().className() << ") + "; name << this->at(continuators.index()) << " (" << this->continuator().className() << ") + ";
name << this->at(index_of.crossovers) << " (" << this->crossover().className() << ") + "; name << this->at(crossovers.index()) << " (" << this->crossover().className() << ") + ";
name << this->at(index_of.mutations) << " (" << this->mutation().className() << ") + "; name << this->at(mutations.index()) << " (" << this->mutation().className() << ") + ";
name << this->at(index_of.selectors) << " (" << this->selector().className() << ") + "; name << this->at(selectors.index()) << " (" << this->selector().className() << ") + ";
name << this->at(index_of.replacements) << " (" << this->replacement().className() << ")"; name << this->at(replacements.index()) << " (" << this->replacement().className() << ")";
return name.str(); return name.str();
} }
@ -168,32 +155,32 @@ class eoAlgoFoundryEA : public eoAlgoFoundry<EOT>
public: public:
eoContinue<EOT>& continuator() eoContinue<EOT>& continuator()
{ {
assert(this->at(index_of.continuators) < continuators.size()); assert(this->at(continuators.index()) < continuators.size());
return continuators.instanciate(this->at(index_of.continuators)); return continuators.instantiate(this->at(continuators.index()));
} }
eoQuadOp<EOT>& crossover() eoQuadOp<EOT>& crossover()
{ {
assert(this->at(index_of.crossovers) < crossovers.size()); assert(this->at(crossovers.index()) < crossovers.size());
return crossovers.instanciate(this->at(index_of.crossovers)); return crossovers.instantiate(this->at(crossovers.index()));
} }
eoMonOp<EOT>& mutation() eoMonOp<EOT>& mutation()
{ {
assert(this->at(index_of.mutations) < mutations.size()); assert(this->at(mutations.index()) < mutations.size());
return mutations.instanciate(this->at(index_of.mutations)); return mutations.instantiate(this->at(mutations.index()));
} }
eoSelectOne<EOT>& selector() eoSelectOne<EOT>& selector()
{ {
assert(this->at(index_of.selectors) < selectors.size()); assert(this->at(selectors.index()) < selectors.size());
return selectors.instanciate(this->at(index_of.selectors)); return selectors.instantiate(this->at(selectors.index()));
} }
eoReplacement<EOT>& replacement() eoReplacement<EOT>& replacement()
{ {
assert(this->at(index_of.replacements) < replacements.size()); assert(this->at(replacements.index()) < replacements.size());
return replacements.instanciate(this->at(index_of.replacements)); return replacements.instantiate(this->at(replacements.index()));
} }
}; };

View file

@ -62,14 +62,22 @@ public:
_subpb_eval(subpb_eval), _subpb_eval(subpb_eval),
_foundry(foundry), _foundry(foundry),
_penalization(penalization), _penalization(penalization),
_pop_size(pop_size) _pop_size(pop_size),
i_cont(foundry.continuators.index()),
i_cros(foundry.crossovers.index()),
i_muta(foundry.mutations.index()),
i_sele(foundry.selectors.index()),
i_repl(foundry.replacements.index())
{ } { }
static const size_t i_cont = eoAlgoFoundryEA<SUB>::Indices::continuators; protected:
static const size_t i_cros = eoAlgoFoundryEA<SUB>::Indices::crossovers ; const size_t i_cont;
static const size_t i_muta = eoAlgoFoundryEA<SUB>::Indices::mutations ; const size_t i_cros;
static const size_t i_sele = eoAlgoFoundryEA<SUB>::Indices::selectors ; const size_t i_muta;
static const size_t i_repl = eoAlgoFoundryEA<SUB>::Indices::replacements; const size_t i_sele;
const size_t i_repl;
public:
/** Decode the high-level problem encoding as an array of indices. /** Decode the high-level problem encoding as an array of indices.
* *

View file

@ -60,9 +60,9 @@
* Tools for automatic algorithms assembling, selection and search. * Tools for automatic algorithms assembling, selection and search.
*/ */
/** Interface for a "Forge": a class that can defer instanciation of EO's operator. /** Interface for a "Forge": a class that can defer instantiation of EO's operator.
* *
* This interface only declares an `instanciate` method, * This interface only declares an `instantiate` method,
* in order to be able to make containers of factories (@see eoForgeOperator). * in order to be able to make containers of factories (@see eoForgeOperator).
* *
* @ingroup Core * @ingroup Core
@ -72,15 +72,15 @@ template<class Itf>
class eoForgeInterface class eoForgeInterface
{ {
public: public:
virtual Itf& instanciate(bool no_cache = true) = 0; virtual Itf& instantiate(bool no_cache = true) = 0;
virtual ~eoForgeInterface() {} virtual ~eoForgeInterface() {}
}; };
/** This "Forge" can defer the instanciation of an EO's operator. /** This "Forge" can defer the instantiation of an EO's operator.
* *
* It allows to decouple the constructor's parameters setup from its actual call. * It allows to decouple the constructor's parameters setup from its actual call.
* You can declare a parametrized operator at a given time, * You can declare a parametrized operator at a given time,
* then actually instanciate it (with the given interface) at another time. * then actually instantiate it (with the given interface) at another time.
* *
* This allows for creating containers of pre-parametrized operators (@see eoForgeVector). * This allows for creating containers of pre-parametrized operators (@see eoForgeVector).
* *
@ -91,13 +91,13 @@ class eoForgeInterface
* *
* @code * @code
eoForgeOperator<eoselect<EOT>,eoRankMuSelect<EOT>> forge(mu); eoForgeOperator<eoselect<EOT>,eoRankMuSelect<EOT>> forge(mu);
// ^ desired ^ to-be-instanciated ^ operator's // ^ desired ^ to-be-instantiated ^ operator's
// interface operator parameters // interface operator parameters
// Actual instanciation: // Actual instantiation:
eoSelect<EOT>& select = forge.instanciate(); eoSelect<EOT>& select = forge.instantiate();
* @endcode * @endcode
* *
* @warning You may want to enable instanciation cache to grab some performances. * @warning You may want to enable instantiation cache to grab some performances.
* The default is set to disable the cache, because its use with operators * The default is set to disable the cache, because its use with operators
* which hold a state will lead to unwanted behaviour. * which hold a state will lead to unwanted behaviour.
* *
@ -111,10 +111,10 @@ class eoForgeOperator : public eoForgeInterface<Itf>
template<class ...Args2> template<class ...Args2>
eoForgeOperator(Args2... args) : eoForgeOperator(Args2... args) :
_args(std::forward<Args2>(args)...), _args(std::forward<Args2>(args)...),
_instanciated(nullptr) _instantiated(nullptr)
{ } { }
/** Instanciate the managed operator class. /** instantiate the managed operator class.
* *
* That is call its constructor with the set up arguments. * That is call its constructor with the set up arguments.
* *
@ -122,20 +122,20 @@ class eoForgeOperator : public eoForgeInterface<Itf>
* *
* @param no_cache If false, will enable caching previous instances. * @param no_cache If false, will enable caching previous instances.
*/ */
Itf& instanciate(bool no_cache = true) Itf& instantiate(bool no_cache = true)
{ {
if(no_cache or not _instanciated) { if(no_cache or not _instantiated) {
if(_instanciated) { if(_instantiated) {
delete _instanciated; delete _instantiated;
} }
_instanciated = op_constructor(_args); _instantiated = op_constructor(_args);
} }
return *_instanciated; return *_instantiated;
} }
virtual ~eoForgeOperator() virtual ~eoForgeOperator()
{ {
delete _instanciated; delete _instantiated;
} }
protected: protected:
@ -152,7 +152,7 @@ class eoForgeOperator : public eoForgeInterface<Itf>
/** @} */ /** @} */
protected: protected:
Itf* _instanciated; Itf* _instantiated;
}; };
/** Partial specialization for constructors without any argument. /** Partial specialization for constructors without any argument.
@ -162,30 +162,30 @@ class eoForgeOperator<Itf,Op> : public eoForgeInterface<Itf>
{ {
public: public:
eoForgeOperator() : eoForgeOperator() :
_instanciated(nullptr) _instantiated(nullptr)
{ } { }
Itf& instanciate( bool no_cache = true ) Itf& instantiate( bool no_cache = true )
{ {
if(no_cache or not _instanciated) { if(no_cache or not _instantiated) {
if(_instanciated) { if(_instantiated) {
delete _instanciated; delete _instantiated;
} }
_instanciated = new Op; _instantiated = new Op;
} }
return *_instanciated; return *_instantiated;
} }
virtual ~eoForgeOperator() virtual ~eoForgeOperator()
{ {
delete _instanciated; delete _instantiated;
} }
protected: protected:
Itf* _instanciated; Itf* _instantiated;
}; };
/** A vector holding an operator with deferred instanciation 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, * @note You can actually store several instances of the same class,
* with different parametrization (or not). * with different parametrization (or not).
@ -195,19 +195,19 @@ class eoForgeOperator<Itf,Op> : public eoForgeInterface<Itf>
* This is is a source of bug which your compiler will to detect and that would * This is is a source of bug which your compiler will to detect and that would
* disable any link between operators. * disable any link between operators.
* *
* @warning You may want to enable instanciation cache to grab some performances. * @warning You may want to enable instantiation cache to grab some performances.
* The default is set to disable the cache, because its use with operators * The default is set to disable the cache, because its use with operators
* which hold a state will lead to unwanted behaviour. * which hold a state will lead to unwanted behaviour.
* *
* @code * @code
eoForgeVector<eoSelect<EOT>> factories(false); eoForgeVector<eoSelect<EOT>> factories(false);
// Capture constructor's parameters and defer instanciation. // Capture constructor's parameters and defer instantiation.
factories.add<eoRankMuSelect<EOT>>(1); factories.add<eoRankMuSelect<EOT>>(1);
factories.setup<eoRankMuSelect<EOT>>(0, 5); // Edit factories.setup<eoRankMuSelect<EOT>>(0, 5); // Edit
// Actually instanciate. // Actually instantiate.
eoSelect<EOT>& op = factories.instanciate(0); eoSelect<EOT>& op = factories.instantiate(0);
// Call. // Call.
op(); op();
@ -219,11 +219,11 @@ template<class Itf>
class eoForgeVector : public std::vector<eoForgeInterface<Itf>*> class eoForgeVector : public std::vector<eoForgeInterface<Itf>*>
{ {
public: public:
/** Default constructor do not cache instanciations. /** Default constructor do not cache instantiations.
* *
* @warning * @warning
* You most probably want to disable caching for operators that hold a state. * You most probably want to disable caching for operators that hold a state.
* If you enable the cache, the last used instanciation will be used, * If you enable the cache, the last used instantiation will be used,
* at its last state. * at its last state.
* For example, continuators should most probably not be cached, * For example, continuators should most probably not be cached,
* as they very often hold a state in the form of a counter. * as they very often hold a state in the form of a counter.
@ -231,10 +231,10 @@ class eoForgeVector : public std::vector<eoForgeInterface<Itf>*>
* and thus always ask for a stop. * and thus always ask for a stop.
* Reusing an instance in this state will de facto disable further searches. * Reusing an instance in this state will de facto disable further searches.
* *
* @param always_reinstanciate If false, will enable cache for the forges in this container. * @param always_reinstantiate If false, will enable cache for the forges in this container.
*/ */
eoForgeVector( bool always_reinstanciate = true ) : eoForgeVector( bool always_reinstantiate = true ) :
_no_cache(always_reinstanciate) _no_cache(always_reinstantiate)
{ } { }
/** Add an operator to the list. /** Add an operator to the list.
@ -294,11 +294,11 @@ class eoForgeVector : public std::vector<eoForgeInterface<Itf>*>
this->at(index) = pfo; this->at(index) = pfo;
} }
/** Instanciate the operator managed at the given index. /** instantiate the operator managed at the given index.
*/ */
Itf& instanciate(size_t index) Itf& instantiate(size_t index)
{ {
return this->at(index)->instanciate(_no_cache); return this->at(index)->instantiate(_no_cache);
} }
virtual ~eoForgeVector() virtual ~eoForgeVector()

View file

@ -39,6 +39,7 @@
template <class FitT> class eoReal: public eoVector<FitT, double> template <class FitT> class eoReal: public eoVector<FitT, double>
{ {
public: public:
using FitnessType = FitT;
/** /**
* (Default) Constructor. * (Default) Constructor.

View file

@ -77,11 +77,11 @@ int main(int /*argc*/, char** /*argv*/)
pop.append(pop_size, init); pop.append(pop_size, init);
eval(pop,pop); eval(pop,pop);
foundry.at(foundry.index_of.continuators) = i_cont; foundry.at(foundry.continuators.index()) = i_cont;
foundry.at(foundry.index_of.crossovers) = i_cross; foundry.at(foundry.crossovers.index()) = i_cross;
foundry.at(foundry.index_of.mutations) = i_mut; foundry.at(foundry.mutations.index()) = i_mut;
foundry.at(foundry.index_of.selectors) = i_sel; foundry.at(foundry.selectors.index()) = i_sel;
foundry.at(foundry.index_of.replacements) = i_rep; foundry.at(foundry.replacements.index()) = i_rep;
// Or, if you know the order. // Or, if you know the order.
foundry.select({i_cont, i_cross, i_mut, i_sel, i_rep}); foundry.select({i_cont, i_cross, i_mut, i_sel, i_rep});

View file

@ -54,19 +54,19 @@ int main(int /*argc*/, char** /*argv*/)
std::string best_algo = ""; std::string best_algo = "";
for(auto& forge_cont : continuators) { for(auto& forge_cont : continuators) {
auto& continuator = forge_cont->instanciate(); auto& continuator = forge_cont->instantiate();
for(auto& forge_cross : crossovers) { for(auto& forge_cross : crossovers) {
auto& crossover = forge_cross->instanciate(); auto& crossover = forge_cross->instantiate();
for(auto& forge_mut : mutations ) { for(auto& forge_mut : mutations ) {
auto& mutation = forge_mut->instanciate(); auto& mutation = forge_mut->instantiate();
for(auto& forge_sel : selectors) { for(auto& forge_sel : selectors) {
auto& selector = forge_sel->instanciate(); auto& selector = forge_sel->instantiate();
for(auto& forge_rep : replacors) { for(auto& forge_rep : replacors) {
auto& replacor = forge_rep->instanciate(); auto& replacor = forge_rep->instantiate();
std::ostringstream algo_name; std::ostringstream algo_name;
algo_name << continuator.className() << " + " algo_name << continuator.className() << " + "

View file

@ -28,14 +28,14 @@ int main(int /*argc*/, char** /*argv*/)
// Forge container using indices. // Forge container using indices.
eoForgeVector<OpInterface> indexed_factories; eoForgeVector<OpInterface> indexed_factories;
// Capture constructor's parameters and defer instanciation. // Capture constructor's parameters and defer instantiation.
indexed_factories.add<OpA>("I'm A"); indexed_factories.add<OpA>("I'm A");
indexed_factories.setup<OpA>(0, "I'm actually A"); // Edit indexed_factories.setup<OpA>(0, "I'm actually A"); // Edit
indexed_factories.add<OpB>("I'm B", " prime"); indexed_factories.add<OpB>("I'm B", " prime");
indexed_factories.add<OpB>("I'm a B", " junior"); indexed_factories.add<OpB>("I'm a B", " junior");
// Actually instanciante and call. // Actually instantiante and call.
indexed_factories.instanciate(0)(); indexed_factories.instantiate(0)();
indexed_factories.instanciate(1)(); indexed_factories.instantiate(1)();
indexed_factories.instanciate(2)(); indexed_factories.instantiate(2)();
} }