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>
/**
/** 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 Foundry
@ -43,6 +124,8 @@ class eoAlgoFoundry : public eoAlgo<EOT>
{ }
/** Select indices of all the operators.
*
* i.e. Select an algorithm to instantiate.
*/
void select( std::vector<size_t> encoding )
{
@ -62,6 +145,8 @@ class eoAlgoFoundry : public eoAlgo<EOT>
return _size;
}
/** Return the underlying encoding vector.
*/
std::vector<size_t> encoding() const
{
return _encoding;

View file

@ -57,20 +57,20 @@
* @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,
* you can use the `index_of` member, for example:
* you can use the `index()` member, for example:
* @code
* foundry.at(foundry.index_of.continuators) = 2; // select the third continuator
* 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:
* @code
* foundry(pop);
* @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.
*
* @note: Thanks to the underlying eoForgeVector, not all the added operators are instanciated.
* Every instanciation is deferred upon actual use. That way, you can still reconfigure them
* @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
@ -88,47 +88,34 @@ class eoAlgoFoundryEA : public eoAlgoFoundry<EOT>
*/
eoAlgoFoundryEA( eoPopEvalFunc<EOT>& eval, size_t max_gen ) :
eoAlgoFoundry<EOT>(5),
index_of(),
continuators(true), // Always re-instanciate continuators, because they hold a state.
crossovers(false),
mutations(false),
selectors(false),
replacements(false),
continuators(0, true), // Always re-instantiate continuators, because they hold a state.
crossovers(1, false),
mutations(2, false),
selectors(3, false),
replacements(4, false),
_eval(eval),
_max_gen(max_gen)
{ }
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 @{ */
eoForgeVector< eoContinue<EOT> > continuators;
eoForgeVector< eoQuadOp<EOT> > crossovers;
eoForgeVector< eoMonOp<EOT> > mutations;
eoForgeVector< eoSelectOne<EOT> > selectors;
eoForgeVector< eoReplacement<EOT> > replacements;
eoOperatorFoundry< eoContinue<EOT> > continuators;
eoOperatorFoundry< eoQuadOp<EOT> > crossovers;
eoOperatorFoundry< eoMonOp<EOT> > mutations;
eoOperatorFoundry< eoSelectOne<EOT> > selectors;
eoOperatorFoundry< eoReplacement<EOT> > replacements;
/* @} */
/** Instanciate and call the pre-selected algorithm.
/** instantiate and call the pre-selected algorithm.
*/
void operator()(eoPop<EOT>& pop)
{
assert(continuators.size() > 0); assert(this->at(index_of.continuators) < continuators.size());
assert( crossovers.size() > 0); assert(this->at(index_of.crossovers) < crossovers.size());
assert( mutations.size() > 0); assert(this->at(index_of.mutations) < mutations.size());
assert( selectors.size() > 0); assert(this->at(index_of.selectors) < selectors.size());
assert(replacements.size() > 0); assert(this->at(index_of.replacements) < replacements.size());
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());
eoSequentialOp<EOT> variator;
variator.add(this->crossover(), 1.0);
@ -153,11 +140,11 @@ class eoAlgoFoundryEA : public eoAlgoFoundry<EOT>
std::string name()
{
std::ostringstream name;
name << this->at(index_of.continuators) << " (" << this->continuator().className() << ") + ";
name << this->at(index_of.crossovers) << " (" << this->crossover().className() << ") + ";
name << this->at(index_of.mutations) << " (" << this->mutation().className() << ") + ";
name << this->at(index_of.selectors) << " (" << this->selector().className() << ") + ";
name << this->at(index_of.replacements) << " (" << this->replacement().className() << ")";
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() << ")";
return name.str();
}
@ -168,32 +155,32 @@ class eoAlgoFoundryEA : public eoAlgoFoundry<EOT>
public:
eoContinue<EOT>& continuator()
{
assert(this->at(index_of.continuators) < continuators.size());
return continuators.instanciate(this->at(index_of.continuators));
assert(this->at(continuators.index()) < continuators.size());
return continuators.instantiate(this->at(continuators.index()));
}
eoQuadOp<EOT>& crossover()
{
assert(this->at(index_of.crossovers) < crossovers.size());
return crossovers.instanciate(this->at(index_of.crossovers));
assert(this->at(crossovers.index()) < crossovers.size());
return crossovers.instantiate(this->at(crossovers.index()));
}
eoMonOp<EOT>& mutation()
{
assert(this->at(index_of.mutations) < mutations.size());
return mutations.instanciate(this->at(index_of.mutations));
assert(this->at(mutations.index()) < mutations.size());
return mutations.instantiate(this->at(mutations.index()));
}
eoSelectOne<EOT>& selector()
{
assert(this->at(index_of.selectors) < selectors.size());
return selectors.instanciate(this->at(index_of.selectors));
assert(this->at(selectors.index()) < selectors.size());
return selectors.instantiate(this->at(selectors.index()));
}
eoReplacement<EOT>& replacement()
{
assert(this->at(index_of.replacements) < replacements.size());
return replacements.instanciate(this->at(index_of.replacements));
assert(this->at(replacements.index()) < replacements.size());
return replacements.instantiate(this->at(replacements.index()));
}
};

View file

@ -62,14 +62,22 @@ public:
_subpb_eval(subpb_eval),
_foundry(foundry),
_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;
static const size_t i_cros = eoAlgoFoundryEA<SUB>::Indices::crossovers ;
static const size_t i_muta = eoAlgoFoundryEA<SUB>::Indices::mutations ;
static const size_t i_sele = eoAlgoFoundryEA<SUB>::Indices::selectors ;
static const size_t i_repl = eoAlgoFoundryEA<SUB>::Indices::replacements;
protected:
const size_t i_cont;
const size_t i_cros;
const size_t i_muta;
const size_t i_sele;
const size_t i_repl;
public:
/** 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.
*/
/** 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).
*
* @ingroup Core
@ -72,15 +72,15 @@ template<class Itf>
class eoForgeInterface
{
public:
virtual Itf& instanciate(bool no_cache = true) = 0;
virtual Itf& instantiate(bool no_cache = true) = 0;
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.
* 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).
*
@ -91,13 +91,13 @@ class eoForgeInterface
*
* @code
eoForgeOperator<eoselect<EOT>,eoRankMuSelect<EOT>> forge(mu);
// ^ desired ^ to-be-instanciated ^ operator's
// ^ desired ^ to-be-instantiated ^ operator's
// interface operator parameters
// Actual instanciation:
eoSelect<EOT>& select = forge.instanciate();
// Actual instantiation:
eoSelect<EOT>& select = forge.instantiate();
* @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
* which hold a state will lead to unwanted behaviour.
*
@ -111,10 +111,10 @@ class eoForgeOperator : public eoForgeInterface<Itf>
template<class ...Args2>
eoForgeOperator(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.
*
@ -122,20 +122,20 @@ class eoForgeOperator : public eoForgeInterface<Itf>
*
* @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(_instanciated) {
delete _instanciated;
if(no_cache or not _instantiated) {
if(_instantiated) {
delete _instantiated;
}
_instanciated = op_constructor(_args);
_instantiated = op_constructor(_args);
}
return *_instanciated;
return *_instantiated;
}
virtual ~eoForgeOperator()
{
delete _instanciated;
delete _instantiated;
}
protected:
@ -152,7 +152,7 @@ class eoForgeOperator : public eoForgeInterface<Itf>
/** @} */
protected:
Itf* _instanciated;
Itf* _instantiated;
};
/** Partial specialization for constructors without any argument.
@ -162,30 +162,30 @@ class eoForgeOperator<Itf,Op> : public eoForgeInterface<Itf>
{
public:
eoForgeOperator() :
_instanciated(nullptr)
_instantiated(nullptr)
{ }
Itf& instanciate( bool no_cache = true )
Itf& instantiate( bool no_cache = true )
{
if(no_cache or not _instanciated) {
if(_instanciated) {
delete _instanciated;
if(no_cache or not _instantiated) {
if(_instantiated) {
delete _instantiated;
}
_instanciated = new Op;
_instantiated = new Op;
}
return *_instanciated;
return *_instantiated;
}
virtual ~eoForgeOperator()
{
delete _instanciated;
delete _instantiated;
}
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,
* 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
* 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
* which hold a state will lead to unwanted behaviour.
*
* @code
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.setup<eoRankMuSelect<EOT>>(0, 5); // Edit
// Actually instanciate.
eoSelect<EOT>& op = factories.instanciate(0);
// Actually instantiate.
eoSelect<EOT>& op = factories.instantiate(0);
// Call.
op();
@ -219,11 +219,11 @@ template<class Itf>
class eoForgeVector : public std::vector<eoForgeInterface<Itf>*>
{
public:
/** Default constructor do not cache instanciations.
/** Default constructor do not cache instantiations.
*
* @warning
* 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.
* For example, continuators should most probably not be cached,
* 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.
* 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 ) :
_no_cache(always_reinstanciate)
eoForgeVector( bool always_reinstantiate = true ) :
_no_cache(always_reinstantiate)
{ }
/** Add an operator to the list.
@ -294,11 +294,11 @@ class eoForgeVector : public std::vector<eoForgeInterface<Itf>*>
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()

View file

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

View file

@ -77,11 +77,11 @@ int main(int /*argc*/, char** /*argv*/)
pop.append(pop_size, init);
eval(pop,pop);
foundry.at(foundry.index_of.continuators) = i_cont;
foundry.at(foundry.index_of.crossovers) = i_cross;
foundry.at(foundry.index_of.mutations) = i_mut;
foundry.at(foundry.index_of.selectors) = i_sel;
foundry.at(foundry.index_of.replacements) = i_rep;
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});

View file

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

View file

@ -28,14 +28,14 @@ int main(int /*argc*/, char** /*argv*/)
// Forge container using indices.
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.setup<OpA>(0, "I'm actually A"); // Edit
indexed_factories.add<OpB>("I'm B", " prime");
indexed_factories.add<OpB>("I'm a B", " junior");
// Actually instanciante and call.
indexed_factories.instanciate(0)();
indexed_factories.instanciate(1)();
indexed_factories.instanciate(2)();
// Actually instantiante and call.
indexed_factories.instantiate(0)();
indexed_factories.instantiate(1)();
indexed_factories.instantiate(2)();
}