temporarily added fitness_traits to keep people up to date with this proposal
This commit is contained in:
parent
8ff63ff776
commit
0f243e4d92
1 changed files with 429 additions and 0 deletions
429
eo/test/fitness_traits.cpp
Normal file
429
eo/test/fitness_traits.cpp
Normal file
|
|
@ -0,0 +1,429 @@
|
|||
#include <map> // for pair
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <math.h> // for exp
|
||||
|
||||
using namespace std;
|
||||
|
||||
/* fitness_traits.h */
|
||||
|
||||
// default traits: defaults to a double that needs to be maximized
|
||||
template <class T = double>
|
||||
struct fitness_traits
|
||||
{
|
||||
// Needs mapping can be used to figure out whether you need to do fitness scaling (or not)
|
||||
const static bool needs_mapping = false;
|
||||
|
||||
// storage_type: what to store next to the genotype
|
||||
typedef T storage_type;
|
||||
|
||||
// performance_type: what the eoEvalFunc calculates
|
||||
typedef T performance_type;
|
||||
|
||||
// worth_type: what the scaling function does
|
||||
typedef T worth_type;
|
||||
|
||||
// access_performance: how to get from what is stored to a mutable performance
|
||||
static performance_type& access_performance(storage_type& a) { return a; }
|
||||
|
||||
// access_worth: how to get from what is stored to a mutable worth
|
||||
static worth_type& access_worth(storage_type& a) { return a; }
|
||||
|
||||
// get_performance: from storage_type to a performance figure
|
||||
static performance_type get_performance(storage_type a) { return a; }
|
||||
|
||||
// get_worth: from storage_type to a worth figure
|
||||
static worth_type get_worth(storage_type a) { return a; }
|
||||
|
||||
// get the fitness out of the individual
|
||||
template <class EOT>
|
||||
static worth_type get_fitness(const EOT& _eo) { return _eo.performance(); }
|
||||
|
||||
// compare the two individuals
|
||||
template <class EOT>
|
||||
static bool is_better(const EOT& _eo1, const EOT& _eo2)
|
||||
{
|
||||
return _eo1.performance() > _eo2.performance();
|
||||
}
|
||||
};
|
||||
|
||||
struct minimization {};
|
||||
struct maximization {};
|
||||
|
||||
struct fitness_traits<minimization> : public fitness_traits<double>
|
||||
{
|
||||
// for minimization, invert the is_better
|
||||
template <class EOT>
|
||||
static bool is_better(const EOT& _eo1, const EOT& _eo2)
|
||||
{
|
||||
return _eo1.performance() < _eo2.performance();
|
||||
}
|
||||
};
|
||||
|
||||
// for maximization, just take the default behaviour
|
||||
struct fitness_traits<maximization> : public fitness_traits<double> {};
|
||||
|
||||
// forward declaration
|
||||
//template <class EOT> class eoPop;
|
||||
//template <class Fitness, class Traits> class EO;
|
||||
|
||||
// unfortunately, partial template specialization is not approved by Microsoft (though ANSI says it's ok)
|
||||
// Probably need some macro-magic to make this work (MicroSoft == MacroHard)
|
||||
// A pair class: first == performance, second == worth, redefine all types, data and functions
|
||||
template <class Performance, class Worth>
|
||||
struct fitness_traits< pair<Performance, Worth> >
|
||||
{
|
||||
typedef pair<Performance, Worth> storage_type;
|
||||
typedef Performance performance_type;
|
||||
typedef Worth worth_type;
|
||||
|
||||
const static bool needs_mapping = true;
|
||||
|
||||
static performance_type& access_performance(storage_type& a) { return a.first; }
|
||||
static worth_type& access_worth(storage_type& a) { return a.second; }
|
||||
|
||||
static performance_type get_performance(const storage_type& a) { return a.first; }
|
||||
static worth_type get_worth(const storage_type& a) { return a.second; }
|
||||
|
||||
// This function calls _eo.worth() which in turn checks the fitness flag and calls get_worth above
|
||||
// The compiler should be able to inline all these calls and come up with a very compact solution
|
||||
template <class EOT>
|
||||
static worth_type get_fitness(const EOT& _eo) { return _eo.worth(); }
|
||||
|
||||
template <class EOT>
|
||||
static bool is_better(const EOT& _eo1, const EOT& _eo2)
|
||||
{
|
||||
return _eo1.worth() > _eo2.worth();
|
||||
}
|
||||
};
|
||||
|
||||
/* end fitness_traits.h */
|
||||
|
||||
/* EO.h */
|
||||
|
||||
template <class Fitness, class Traits = fitness_traits<Fitness> >
|
||||
class EO
|
||||
{
|
||||
public :
|
||||
|
||||
typedef Traits fitness_traits;
|
||||
typedef typename Traits::storage_type storage_type;
|
||||
typedef typename Traits::performance_type performance_type;
|
||||
typedef typename Traits::worth_type worth_type;
|
||||
|
||||
EO() : valid_performance(false), valid_worth(false), rep_fitness() {}
|
||||
|
||||
// for backwards compatibility
|
||||
void fitness(performance_type perf)
|
||||
{
|
||||
performance(perf);
|
||||
}
|
||||
|
||||
void performance(performance_type perf)
|
||||
{
|
||||
valid_performance = true;
|
||||
Traits::access_performance(rep_fitness) = perf;
|
||||
}
|
||||
|
||||
performance_type performance(void) const
|
||||
{
|
||||
if(!valid_performance) throw runtime_error("no performance");
|
||||
return Traits::get_performance(rep_fitness);
|
||||
}
|
||||
|
||||
void worth(worth_type worth)
|
||||
{
|
||||
valid_worth = true;
|
||||
Traits::access_worth(rep_fitness) = worth;
|
||||
}
|
||||
|
||||
worth_type worth(void) const
|
||||
{
|
||||
if(!valid_worth) throw runtime_error("no worth");
|
||||
if(!Traits::needs_mapping) throw runtime_error("no mapping");
|
||||
return Traits::get_worth(rep_fitness);
|
||||
}
|
||||
|
||||
worth_type fitness(void) const
|
||||
{
|
||||
return Traits::get_fitness(*this);
|
||||
}
|
||||
|
||||
void invalidate(void)
|
||||
{
|
||||
valid_performance = false;
|
||||
valid_worth = false;
|
||||
}
|
||||
|
||||
void invalidate_worth(void)
|
||||
{
|
||||
valid_worth = false;
|
||||
}
|
||||
|
||||
bool operator<(const EO<Fitness, Traits>& other) const
|
||||
{
|
||||
return !Traits::is_better(other, *this);
|
||||
}
|
||||
|
||||
bool operator>(const EO<Fitness, Traits>& other) const
|
||||
{
|
||||
return Traits::is_better(other, *this);
|
||||
}
|
||||
|
||||
private :
|
||||
|
||||
bool valid_performance;
|
||||
bool valid_worth;
|
||||
storage_type rep_fitness;
|
||||
};
|
||||
|
||||
/* end EO.h */
|
||||
|
||||
/* eoPerf2Worth.h */
|
||||
|
||||
// get the name known
|
||||
template <class EOT> class eoPop;
|
||||
|
||||
template <class EOT>
|
||||
void exponential_scaling(eoPop<EOT>& _pop)
|
||||
{
|
||||
for (unsigned i = 0; i < _pop.size(); ++i)
|
||||
{ // change minimimization into maximization
|
||||
_pop[i].worth(exp(-_pop[i].performance()));
|
||||
}
|
||||
}
|
||||
|
||||
template <class EOT>
|
||||
class eoPerf2Worth /* : public eoUF<eoPop<EOT>&, void> */
|
||||
{
|
||||
public :
|
||||
virtual void operator()(eoPop<EOT>& _pop)
|
||||
{
|
||||
return exponential_scaling(_pop);
|
||||
}
|
||||
};
|
||||
|
||||
/* end eoPerf2Worth.h */
|
||||
|
||||
|
||||
/* eoPop.h */
|
||||
|
||||
template <class EOT>
|
||||
class eoPop : public vector<EOT>
|
||||
{
|
||||
public :
|
||||
|
||||
typedef typename EOT::fitness_traits fitness_traits;
|
||||
|
||||
eoPop(void) : p2w(0) {}
|
||||
|
||||
void sort()
|
||||
{
|
||||
scale(); // get the worths up to date
|
||||
|
||||
std::sort(begin(), end(), greater<EOT>());
|
||||
}
|
||||
|
||||
void scale()
|
||||
{
|
||||
if (p2w)
|
||||
{
|
||||
if (!fitness_traits::needs_mapping)
|
||||
{
|
||||
throw runtime_error("eoPop: no scaling needed, yet a scaling function is defined");
|
||||
}
|
||||
|
||||
(*p2w)(*this);
|
||||
}
|
||||
else if (fitness_traits::needs_mapping)
|
||||
{
|
||||
throw runtime_error("eoPop: no scaling function attached to the population, while one was certainly called for");
|
||||
}
|
||||
}
|
||||
|
||||
void setPerf2Worth(eoPerf2Worth<EOT>& _p2w)
|
||||
{
|
||||
p2w = &_p2w;
|
||||
}
|
||||
|
||||
void setPerf2Worth(eoPerf2Worth<EOT>* _p2w)
|
||||
{
|
||||
p2w = _p2w;
|
||||
}
|
||||
|
||||
eoPerf2Worth<EOT>* getPerf2Worth() { return p2w; }
|
||||
|
||||
void swap(eoPop<EOT>& other)
|
||||
{
|
||||
vector<EOT>::swap(other);
|
||||
eoPerf2Worth<EOT>* tmp = p2w;
|
||||
p2w = other.p2w;
|
||||
other.p2w = tmp;
|
||||
}
|
||||
|
||||
private :
|
||||
|
||||
// a pointer as it can be emtpy
|
||||
eoPerf2Worth<EOT>* p2w;
|
||||
};
|
||||
|
||||
// need this one to be able to swap the members as well...
|
||||
template <class EOT>
|
||||
void swap(eoPop<EOT>& _p1, eoPop<EOT>& _p2)
|
||||
{
|
||||
_p1.swap(_p2);
|
||||
}
|
||||
|
||||
/* end eoPop.h */
|
||||
|
||||
/* main and test */
|
||||
|
||||
template <class EOT>
|
||||
void algo(eoPop<EOT>& _pop)
|
||||
{
|
||||
eoPop<EOT> offspring; // how to get the scaling info into this guy??
|
||||
offspring.setPerf2Worth(_pop.getPerf2Worth()); // like this!
|
||||
|
||||
copy(_pop.begin(), _pop.end(), back_inserter(offspring));
|
||||
|
||||
offspring.sort(); // should call scale
|
||||
|
||||
swap(_pop, offspring);
|
||||
}
|
||||
|
||||
void minimization_test()
|
||||
{
|
||||
typedef EO<minimization> eo_type;
|
||||
|
||||
eo_type eo1;
|
||||
eo_type eo2;
|
||||
|
||||
eo1.performance(1.0);
|
||||
eo2.performance(2.0);
|
||||
|
||||
cout << "With minimizing fitness" << endl;
|
||||
cout << eo1.fitness() << " < " << eo2.fitness() << " returns " << (eo1 < eo2) << endl;
|
||||
cout << eo2.fitness() << " < " << eo1.fitness() << " returns " << (eo2 < eo1) << endl;
|
||||
}
|
||||
|
||||
void the_main()
|
||||
{
|
||||
typedef EO<double> simple_eo;
|
||||
typedef EO<pair<double, double> > scaled_eo;
|
||||
|
||||
simple_eo eo1;
|
||||
simple_eo eo3;
|
||||
|
||||
/* First test some simple comparisons */
|
||||
|
||||
eo1.fitness(10); // could also use performance()
|
||||
eo3.fitness(5);
|
||||
|
||||
cout << eo1.fitness() << endl;
|
||||
cout << eo3.fitness() << endl;
|
||||
|
||||
cout << "eo1 < eo3 = " << (eo1 < eo3) << endl;
|
||||
|
||||
|
||||
scaled_eo eo2;
|
||||
scaled_eo eo4;
|
||||
eo2.performance(10);
|
||||
eo4.performance(8);
|
||||
|
||||
/* Now test if the worth gets accessed and if the flag protects it */
|
||||
|
||||
try
|
||||
{
|
||||
cout << eo2.fitness() << endl;
|
||||
cout << "did not throw" << endl;
|
||||
assert(false); // should throw
|
||||
}
|
||||
catch(exception& e)
|
||||
{
|
||||
cout << "Fitness threw exception, as it should" << endl;
|
||||
cout << e.what() << endl;
|
||||
}
|
||||
|
||||
/* Set the worth and all is well (this is normally done by some perf2worth functor */
|
||||
|
||||
eo2.worth(3);
|
||||
eo4.worth(5);
|
||||
|
||||
cout << "with maximization " << endl;
|
||||
cout << eo2.fitness() << endl;
|
||||
cout << eo4.fitness() << endl;
|
||||
cout << eo2.fitness() << " < " << eo4.fitness() << " returns " << (eo2 < eo4) << endl;
|
||||
|
||||
/* Test the minimization of fitness */
|
||||
minimization_test();
|
||||
|
||||
|
||||
/* Populations */
|
||||
|
||||
// test pop without scaling, should have no overhead save for a single empty pointer in pop
|
||||
eoPop<simple_eo> pop0;
|
||||
pop0.resize(1);
|
||||
pop0[0].fitness(1);
|
||||
|
||||
algo(pop0);
|
||||
|
||||
cout << pop0[0].fitness() << endl;
|
||||
|
||||
assert(pop0[0].fitness() == 1);
|
||||
|
||||
/* test pop with scaling */
|
||||
|
||||
eoPerf2Worth<scaled_eo> perf2worth;
|
||||
eoPop<scaled_eo> pop1;
|
||||
|
||||
pop1.resize(1);
|
||||
|
||||
pop1[0].fitness(1.0); // emulate evaluation
|
||||
|
||||
// at this point getting the fitness should throw
|
||||
try
|
||||
{
|
||||
cout << pop1[0].fitness() << endl;
|
||||
cout << "did not throw" << endl;
|
||||
assert(false); // should throw
|
||||
}
|
||||
catch(exception& e)
|
||||
{
|
||||
cout << "Fitness threw exception, as it should" << endl;
|
||||
cout << e.what() << endl;
|
||||
}
|
||||
|
||||
// at this point trying to scale should throw
|
||||
try
|
||||
{
|
||||
algo(pop1); // should complain that it cannot scale
|
||||
assert(false); // so it would never get here
|
||||
}
|
||||
catch(exception& e)
|
||||
{ // but rather ends here
|
||||
cout << e.what() << endl;
|
||||
}
|
||||
|
||||
// ok, now set the scaling
|
||||
pop1.set_scalef(perf2worth);
|
||||
|
||||
algo(pop1);
|
||||
|
||||
cout << "the fitness has been transformed from " << pop1[0].performance() << " to exp(-1) = " << pop1[0].fitness() << endl;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
try
|
||||
{
|
||||
the_main();
|
||||
}
|
||||
catch(exception& e)
|
||||
{
|
||||
cout << e.what() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in a new issue