feat: adds eoForgeScalar and eoParameterFoundry

- Get arithmetic types out of eoOperatorFoundry in eoParameterFoundry.
- Allow for metric space variables management within irace.
This commit is contained in:
Johann Dreo 2021-07-07 21:00:13 +02:00
commit cd7787bbab
4 changed files with 202 additions and 73 deletions

View file

@ -41,17 +41,17 @@ eoAlgoFoundryFastGA<Bits>& make_foundry(
// foundry.continuators.add< eoSteadyFitContinue<Bits> >(10,i); // foundry.continuators.add< eoSteadyFitContinue<Bits> >(10,i);
// } // }
for(double i=0.0; i<1.0; i+=0.2) { // for(double i=0.0; i<1.0; i+=0.2) {
foundry.crossover_rates.add<double>(i); // foundry.crossover_rates.add<double>(i);
foundry.mutation_rates.add<double>(i); // foundry.mutation_rates.add<double>(i);
} // }
/***** Offsprings size *****/ /***** Offsprings size *****/
// for(size_t i=5; i<100; i+=10) { // for(size_t i=5; i<100; i+=10) {
// foundry.offspring_sizes.add<size_t>(i); // foundry.offspring_sizes.add<size_t>(i);
// } // }
foundry.offspring_sizes.add<size_t>(0); // 0 = use parents fixed pop size. foundry.offspring_sizes.setup(0,100); // 0 = use parents fixed pop size.
/***** Crossovers ****/ /***** Crossovers ****/
for(double i=0.1; i<1.0; i+=0.2) { for(double i=0.1; i<1.0; i+=0.2) {
@ -61,7 +61,7 @@ eoAlgoFoundryFastGA<Bits>& make_foundry(
foundry.crossovers.add< eoNPtsBitXover<Bits> >(i); // nb of points foundry.crossovers.add< eoNPtsBitXover<Bits> >(i); // nb of points
} }
foundry.crossovers.add< eo1PtBitXover<Bits> >(); // foundry.crossovers.add< eo1PtBitXover<Bits> >(); // Same as NPts=1
/***** Mutations ****/ /***** Mutations ****/
double p = 1.0; // Probability of flipping each bit. double p = 1.0; // Probability of flipping each bit.
@ -119,10 +119,8 @@ eoAlgoFoundryFastGA<Bits>& make_foundry(
Bits::Fitness fake_func(const Bits&) { return 0; } Bits::Fitness fake_func(const Bits&) { return 0; }
void print_irace_full(const eoParam& param, const size_t slot_size, std::string type="i", std::ostream& out = std::cout) void print_irace_categorical(const eoParam& param, const size_t slot_size, std::string type="c", std::ostream& out = std::cout)
{ {
assert(type == "i" or type =="c");
// If there is no choice to be made on this operator, comment it out. // If there is no choice to be made on this operator, comment it out.
if(slot_size - 1 <= 0) { if(slot_size - 1 <= 0) {
out << "# "; out << "# ";
@ -136,41 +134,77 @@ void print_irace_full(const eoParam& param, const size_t slot_size, std::string
<< "\t\"--" << param.longName() << "=\"" << "\t\"--" << param.longName() << "=\""
<< "\t" << type; << "\t" << type;
if(type == "i") {
if(slot_size -1 <= 0) {
out << "\t(0)";
} else {
out << "\t(0," << slot_size-1 << ")";
}
out << std::endl;
} else if(type == "c") {
out << "\t(0"; out << "\t(0";
for(size_t i=1; i<slot_size; ++i) { for(size_t i=1; i<slot_size; ++i) {
out << "," << i; out << "," << i;
} }
out << ")" << std::endl; out << ")" << std::endl;
}
} }
template<class T>
void print_irace_ranged(const eoParam& param, const T min, const T max, std::string type="r", std::ostream& out = std::cout)
{
// If there is no choice to be made on this operator, comment it out.
if(max - min <= 0) {
out << "# ";
}
// irace doesn't support "-" in names.
std::string irace_name = param.longName();
irace_name.erase(std::remove(irace_name.begin(), irace_name.end(), '-'), irace_name.end());
out << irace_name
<< "\t\"--" << param.longName() << "=\""
<< "\t" << type;
if(max-min <= 0) {
out << "\t(?)";
} else {
out << "\t(" << min << "," << max << ")";
}
out << std::endl;
}
template<class ITF> template<class ITF>
void print_irace_typed(const eoParam& param, const eoForgeVector<ITF>& op_foundry, std::ostream& out = std::cout) void print_irace_oper(const eoParam& param, const eoOperatorFoundry<ITF>& op_foundry, std::ostream& out = std::cout)
{ {
print_irace_full(param, op_foundry.size(), "c", out); print_irace_categorical(param, op_foundry.size(), "c", out);
} }
template<> // FIXME generalize to any scalar type with enable_if
void print_irace_typed(const eoParam& param, const eoForgeVector<double>& op_foundry, std::ostream& out) // template<class ITF>
void print_irace_param(
const eoParam& param,
// const eoParameterFoundry<typename std::enable_if< std::is_floating_point<ITF>::value >::type>& op_foundry,
const eoParameterFoundry<double>& op_foundry,
std::ostream& out)
{ {
print_irace_full(param, op_foundry.size(), "i", out); print_irace_ranged(param, op_foundry.min(), op_foundry.max(), "r", out);
} }
template<class OPF> // template<class ITF>
void print_irace(const eoParam& param, const OPF& op_foundry, std::ostream& out = std::cout) void print_irace_param(
const eoParam& param,
// const eoParameterFoundry<typename std::enable_if< std::is_integral<ITF>::value >::type>& op_foundry,
const eoParameterFoundry<size_t>& op_foundry,
std::ostream& out)
{ {
print_irace_typed<typename OPF::Interface>(param, op_foundry, out); print_irace_ranged(param, op_foundry.min(), op_foundry.max(), "i", out);
} }
template<class ITF>
void print_irace(const eoParam& param, const eoOperatorFoundry<ITF>& op_foundry, std::ostream& out = std::cout)
{
print_irace_oper<ITF>(param, op_foundry, out);
}
template<class ITF>
void print_irace(const eoParam& param, const eoParameterFoundry<ITF>& op_foundry, std::ostream& out = std::cout)
{
print_irace_param/*<ITF>*/(param, op_foundry, out);
}
void print_operator_typed(const eoFunctorBase& op, std::ostream& out) void print_operator_typed(const eoFunctorBase& op, std::ostream& out)
{ {
@ -182,8 +216,8 @@ void print_operator_typed(const double& op, std::ostream& out)
out << op; out << op;
} }
template<class OPF> template<class ITF>
void print_operators(const eoParam& param, OPF& op_foundry, std::ostream& out = std::cout, std::string indent=" ") void print_operators(const eoParam& param, eoOperatorFoundry<ITF>& op_foundry, std::ostream& out = std::cout, std::string indent=" ")
{ {
out << indent << op_foundry.size() << " " << param.longName() << ":" << std::endl; out << indent << op_foundry.size() << " " << param.longName() << ":" << std::endl;
for(size_t i=0; i < op_foundry.size(); ++i) { for(size_t i=0; i < op_foundry.size(); ++i) {
@ -194,6 +228,11 @@ void print_operators(const eoParam& param, OPF& op_foundry, std::ostream& out =
} }
} }
template<class ITF>
void print_operators(const eoParam& param, eoParameterFoundry<ITF>& op_foundry, std::ostream& out = std::cout, std::string indent=" ")
{
out << indent << "[" << op_foundry.min() << "," << op_foundry.max() << "] " << param.longName() << "." << std::endl;
}
// Problem configuration. // Problem configuration.
struct Problem { struct Problem {
@ -322,17 +361,23 @@ int main(int argc, char* argv[])
// const size_t generations = std::numeric_limits<size_t>::max(); // const size_t generations = std::numeric_limits<size_t>::max();
eo::log << eo::debug << "Number of generations: " << generations << std::endl; eo::log << eo::debug << "Number of generations: " << generations << std::endl;
/***** operators / parameters *****/ /***** metric parameters *****/
auto crossover_rate_p = parser.getORcreateParam<double>(0.5,
"crossover-rate", "",
'C', "Operator Choice", /*required=*/true);
const double crossover_rate = crossover_rate_p.value();
auto mutation_rate_p = parser.getORcreateParam<double>(0,
"mutation-rate", "",
'M', "Operator Choice", /*required=*/true);
const double mutation_rate = mutation_rate_p.value();
/***** operators *****/
auto continuator_p = parser.getORcreateParam<size_t>(0, auto continuator_p = parser.getORcreateParam<size_t>(0,
"continuator", "Stopping criterion", "continuator", "Stopping criterion",
'o', "Operator Choice", /*required=*/false); // Single alternative, not required. 'o', "Operator Choice", /*required=*/false); // Single alternative, not required.
const size_t continuator = continuator_p.value(); const size_t continuator = continuator_p.value();
auto crossover_rate_p = parser.getORcreateParam<size_t>(0,
"crossover-rate", "",
'C', "Operator Choice", /*required=*/true);
const size_t crossover_rate = crossover_rate_p.value();
auto crossover_selector_p = parser.getORcreateParam<size_t>(0, auto crossover_selector_p = parser.getORcreateParam<size_t>(0,
"cross-selector", "How to selects candidates for cross-over", "cross-selector", "How to selects candidates for cross-over",
's', "Operator Choice", /*required=*/true); 's', "Operator Choice", /*required=*/true);
@ -348,11 +393,6 @@ int main(int argc, char* argv[])
'a', "Operator Choice", /*required=*/false); // Single alternative, not required. 'a', "Operator Choice", /*required=*/false); // Single alternative, not required.
const size_t aftercross_selector = aftercross_selector_p.value(); const size_t aftercross_selector = aftercross_selector_p.value();
auto mutation_rate_p = parser.getORcreateParam<size_t>(0,
"mutation-rate", "",
'M', "Operator Choice", /*required=*/true);
const size_t mutation_rate = mutation_rate_p.value();
auto mutation_selector_p = parser.getORcreateParam<size_t>(0, auto mutation_selector_p = parser.getORcreateParam<size_t>(0,
"mut-selector", "How to selects candidate for mutation", "mut-selector", "How to selects candidate for mutation",
'u', "Operator Choice", /*required=*/true); 'u', "Operator Choice", /*required=*/true);
@ -393,19 +433,21 @@ int main(int argc, char* argv[])
print_operators( replacement_p, fake_foundry.replacements , std::clog); print_operators( replacement_p, fake_foundry.replacements , std::clog);
print_operators( offspring_size_p, fake_foundry.offspring_sizes , std::clog); print_operators( offspring_size_p, fake_foundry.offspring_sizes , std::clog);
size_t fake_sample_size = 10;
size_t n = size_t n =
fake_foundry.crossover_rates.size() fake_sample_size //crossover_rates
* fake_foundry.crossover_selectors.size() * fake_foundry.crossover_selectors.size()
* fake_foundry.crossovers.size() * fake_foundry.crossovers.size()
* fake_foundry.aftercross_selectors.size() * fake_foundry.aftercross_selectors.size()
* fake_foundry.mutation_rates.size() * fake_sample_size //mutation_rates
* fake_foundry.mutation_selectors.size() * fake_foundry.mutation_selectors.size()
* fake_foundry.mutations.size() * fake_foundry.mutations.size()
* fake_foundry.replacements.size() * fake_foundry.replacements.size()
* fake_foundry.continuators.size() * fake_foundry.continuators.size()
* fake_foundry.offspring_sizes.size(); * fake_sample_size //offspring_sizes
;
std::clog << std::endl; std::clog << std::endl;
std::clog << n << " possible algorithms configurations." << std::endl; std::clog << "~" << n << " possible algorithms configurations." << std::endl;
std::clog << "Ranges of configurable parameters (redirect the stdout in a file to use it with iRace): " << std::endl; std::clog << "Ranges of configurable parameters (redirect the stdout in a file to use it with iRace): " << std::endl;
@ -445,16 +487,22 @@ int main(int argc, char* argv[])
// Build up an algorithm name from main parameters. // Build up an algorithm name from main parameters.
std::ostringstream name; std::ostringstream name;
name << "FastGA"; name << "FastGA";
for(auto& p : {pop_size_p, for(auto& p : {
crossover_rate_p,
crossover_selector_p, crossover_selector_p,
crossover_p, crossover_p,
aftercross_selector_p, aftercross_selector_p,
mutation_rate_p,
mutation_selector_p, mutation_selector_p,
mutation_p, mutation_p,
replacement_p, replacement_p }) {
offspring_size_p}) { name << "_" << p.shortName() << "=" << p.getValue();
}
for(auto& p : {
crossover_rate_p,
mutation_rate_p }) {
name << "_" << p.shortName() << "=" << p.getValue();
}
for(auto& p : {pop_size_p,
offspring_size_p }) {
name << "_" << p.shortName() << "=" << p.getValue(); name << "_" << p.shortName() << "=" << p.getValue();
} }
std::clog << name.str() << std::endl; std::clog << name.str() << std::endl;
@ -580,7 +628,8 @@ int main(int argc, char* argv[])
std::clog << "Attainment matrix distribution: " << std::endl; std::clog << "Attainment matrix distribution: " << std::endl;
assert(mat.size() > 0); assert(mat.size() > 0);
assert(mat[0].size() > 1); assert(mat[0].size() > 1);
for(size_t i = mat.size()-1; i >= 0; --i) { for(size_t i = mat.size()-1; i > 0; --i) {
assert(mat[i].size() >= 1);
std::cout << mat[i][0]; std::cout << mat[i][0];
for(size_t j = 1; j < mat[i].size(); ++j) { for(size_t j = 1; j < mat[i].size(); ++j) {
std::cout << "," << mat[i][j]; std::cout << "," << mat[i][j];

View file

@ -59,9 +59,28 @@ class eoOperatorFoundry : public eoForgeVector< Itf >
size_t index() const { return _index; } size_t index() const { return _index; }
protected: protected:
//! Unique index in the eoAlgoFoundry.
size_t _index; size_t _index;
}; };
template<class Itf>
class eoParameterFoundry : public eoForgeScalar< Itf >
{
static_assert(std::is_arithmetic<Itf>::value,
"eoParameterFoundry should only be used on arithmetic types (i.e. integer or floating point types)");
public:
eoParameterFoundry(size_t encoding_index, Itf min, Itf max) :
eoForgeScalar<Itf>(min, max),
_index(encoding_index)
{ }
size_t index() const { return _index; }
protected:
//! Unique index in the eoAlgoFoundry.
size_t _index;
};
/** Interface of a Foundry: a class that instantiate an eoAlgo on-the-fly, given a choice of its operators. /** Interface of a Foundry: a class that instantiate an eoAlgo on-the-fly, given a choice of its operators.
* *

View file

@ -90,18 +90,18 @@ class eoAlgoFoundryFastGA : public eoAlgoFoundry<EOT>
) : ) :
eoAlgoFoundry<EOT>(10), eoAlgoFoundry<EOT>(10),
crossover_rates(0, false), crossover_rates(0, 0.0, 1.0),
crossover_selectors(1, false), crossover_selectors(1, false),
crossovers(2, false), crossovers(2, false),
aftercross_selectors(3, false), aftercross_selectors(3, false),
mutation_rates(4, false), mutation_rates(4, 0.0, 1.0),
mutation_selectors(5, false), mutation_selectors(5, false),
mutations(6, false), mutations(6, false),
replacements(7, false), replacements(7, false),
continuators(8, true), // Always re-instantiate continuators, because they hold a state. continuators(8, true), // Always re-instantiate continuators, because they hold a state.
offspring_sizes(9, false), offspring_sizes(9, 0, std::numeric_limits<size_t>::max()),
_eval(eval), _eval(eval),
_init(init), _init(init),
_max_evals(max_evals), _max_evals(max_evals),
@ -111,34 +111,31 @@ class eoAlgoFoundryFastGA : public eoAlgoFoundry<EOT>
public: public:
/* Operators containers @{ */ /* Operators containers @{ */
eoOperatorFoundry< double > crossover_rates; eoParameterFoundry< double > crossover_rates;
eoOperatorFoundry< eoSelectOne<EOT> > crossover_selectors; eoOperatorFoundry< eoSelectOne<EOT> > crossover_selectors;
eoOperatorFoundry< eoQuadOp<EOT> > crossovers; eoOperatorFoundry< eoQuadOp<EOT> > crossovers;
eoOperatorFoundry< eoSelectOne<EOT> > aftercross_selectors; eoOperatorFoundry< eoSelectOne<EOT> > aftercross_selectors;
eoOperatorFoundry< double > mutation_rates; eoParameterFoundry< double > mutation_rates;
eoOperatorFoundry< eoSelectOne<EOT> > mutation_selectors; eoOperatorFoundry< eoSelectOne<EOT> > mutation_selectors;
eoOperatorFoundry< eoMonOp<EOT> > mutations; eoOperatorFoundry< eoMonOp<EOT> > mutations;
eoOperatorFoundry< eoReplacement<EOT> > replacements; eoOperatorFoundry< eoReplacement<EOT> > replacements;
eoOperatorFoundry< eoContinue<EOT> > continuators; eoOperatorFoundry< eoContinue<EOT> > continuators;
eoOperatorFoundry< size_t > offspring_sizes; eoParameterFoundry< size_t > offspring_sizes;
/* @} */ /* @} */
/** instantiate and call the pre-selected algorithm. /** instantiate and call the pre-selected algorithm.
*/ */
void operator()(eoPop<EOT>& pop) void operator()(eoPop<EOT>& pop)
{ {
assert( crossover_rates.size() > 0); assert(this->at( crossover_rates.index()) < crossover_rates.size());
assert( crossover_selectors.size() > 0); assert(this->at( crossover_selectors.index()) < crossover_selectors.size()); assert( 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( 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(aftercross_selectors.size() > 0); assert(this->at(aftercross_selectors.index()) < aftercross_selectors.size());
assert( mutation_rates.size() > 0); assert(this->at( mutation_rates.index()) < mutation_rates.size());
assert( mutation_selectors.size() > 0); assert(this->at( mutation_selectors.index()) < mutation_selectors.size()); assert( 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( mutations.size() > 0); assert(this->at( mutations.index()) < mutations.size());
assert( replacements.size() > 0); assert(this->at( replacements.index()) < replacements.size()); assert( replacements.size() > 0); assert(this->at( replacements.index()) < replacements.size());
assert( continuators.size() > 0); assert(this->at( continuators.index()) < continuators.size()); assert( continuators.size() > 0); assert(this->at( continuators.index()) < continuators.size());
assert( offspring_sizes.size() > 0); assert(this->at( offspring_sizes.index()) < offspring_sizes.size());
// Objective function calls counter // Objective function calls counter
eoEvalCounterThrowException<EOT> eval(_eval, _max_evals); eoEvalCounterThrowException<EOT> eval(_eval, _max_evals);
@ -212,7 +209,6 @@ class eoAlgoFoundryFastGA : public eoAlgoFoundry<EOT>
double& crossover_rate() double& crossover_rate()
{ {
assert(this->at(crossover_rates.index()) < crossover_rates.size());
return crossover_rates.instantiate(this->at(crossover_rates.index())); return crossover_rates.instantiate(this->at(crossover_rates.index()));
} }
@ -224,7 +220,6 @@ class eoAlgoFoundryFastGA : public eoAlgoFoundry<EOT>
double& mutation_rate() double& mutation_rate()
{ {
assert(this->at(mutation_rates.index()) < mutation_rates.size());
return mutation_rates.instantiate(this->at(mutation_rates.index())); return mutation_rates.instantiate(this->at(mutation_rates.index()));
} }
@ -254,7 +249,6 @@ class eoAlgoFoundryFastGA : public eoAlgoFoundry<EOT>
size_t& offspring_size() size_t& offspring_size()
{ {
assert(this->at(offspring_sizes.index()) < offspring_sizes.size());
return offspring_sizes.instantiate(this->at(offspring_sizes.index())); return offspring_sizes.instantiate(this->at(offspring_sizes.index()));
} }

View file

@ -215,7 +215,7 @@ class eoForgeOperator<Itf,Op> : public eoForgeInterface<Itf>
* *
* @ingroup Foundry * @ingroup Foundry
*/ */
template<class Itf> template<class Itf, typename Enable = void>
class eoForgeVector : public std::vector<eoForgeInterface<Itf>*> class eoForgeVector : public std::vector<eoForgeInterface<Itf>*>
{ {
public: public:
@ -238,6 +238,19 @@ class eoForgeVector : public std::vector<eoForgeInterface<Itf>*>
_no_cache(always_reinstantiate) _no_cache(always_reinstantiate)
{ } { }
/** instantiate the operator managed at the given index.
*/
Itf& instantiate(double index)
{
double frac_part, int_part;
frac_part = std::modf(index, &int_part);
if(frac_part != 0) {
eo::log << eo::errors << "there is a fractional part in the given index (" << index << ")" << std::endl;
assert(frac_part != 0);
}
return this->at(static_cast<size_t>(index))->instantiate(_no_cache);
}
/** Add an operator to the list. /** Add an operator to the list.
* *
* @warning When passing a reference (as it is often the case within ParadisEO), * @warning When passing a reference (as it is often the case within ParadisEO),
@ -295,13 +308,6 @@ class eoForgeVector : public std::vector<eoForgeInterface<Itf>*>
this->at(index) = pfo; this->at(index) = pfo;
} }
/** instantiate the operator managed at the given index.
*/
Itf& instantiate(size_t index)
{
return this->at(index)->instantiate(_no_cache);
}
virtual ~eoForgeVector() virtual ~eoForgeVector()
{ {
for(auto p : *this) { for(auto p : *this) {
@ -313,5 +319,66 @@ class eoForgeVector : public std::vector<eoForgeInterface<Itf>*>
bool _no_cache; bool _no_cache;
}; };
template<class Itf>
class eoForgeScalar
{
public:
using Interface = Itf;
eoForgeScalar(Itf min, Itf max) :
_min(min),
_max(max)
{ }
/** Just return the same value, without managing any instantiation.
*/
Itf& instantiate(double value)
{
this->_value = value;
if(not (_min <= value and value <= _max) ) {
eo::log << eo::errors << "ERROR: the given value is out of range, I'll cap it." << std::endl;
assert(_min <= value and value <= _max);
if(value < _min) {
this->_value = _min;
return this->_value;
}
if(value > _max) {
this->_value = _max;
return this->_value;
}
}
return this->_value;
}
Itf min() const { return _min; }
Itf max() const { return _max; }
void min(Itf min)
{
assert(_min <= _max);
_min = min;
}
void max(Itf max)
{
assert(_max >= _min);
_max = max;
}
void setup(Itf min, Itf max)
{
_min = min;
_max = max;
assert(_min <= _max);
}
// Nothing else, as it would not make sense.
protected:
Itf _value;
Itf _min;
Itf _max;
};
#endif // _eoForge_H_ #endif // _eoForge_H_