Changed a few things in the eoPerf2Worth classes, EO.h and eoSelectOne.h are functionally unchanged

This commit is contained in:
maartenkeijzer 2001-03-21 10:55:22 +00:00
commit 665e20b0f8
10 changed files with 331 additions and 80 deletions

View file

@ -115,6 +115,7 @@
// Perf2Worth
#include <eoParetoRanking.h>
#include <eoNDSorting.h>
// Algorithms
#include <eoEasyEA.h>

View file

@ -93,12 +93,12 @@ class eoDominanceMap : public eoUF<const eoPop<EoType>&, void>, public std::vect
for (unsigned j = 0; j < i; ++j)
{
if (_pop[i].fitness() > _pop[j].fitness())
if (_pop[i].fitness().dominates(_pop[j].fitness()))
{
operator[](i)[j] = true;
operator[](j)[i] = false;
}
else if (_pop[j].fitness() > _pop[i].fitness())
else if (_pop[j].fitness().dominates(_pop[i].fitness()))
{
operator[](i)[j] = false;
operator[](j)[i] = true;

View file

@ -35,12 +35,12 @@
* with given selective pressure
* Pselect(Best) == pressure/sizePop
* Pselect(average) == 1.0/sizePop
* truncate negative values to 0 -
* truncate negative values to 0 -
*
* to be used within an eoSelectFromWorth object
*/
template <class EOT>
class eoLinearFitScaling : public eoPerf2Worth<EOT>
class eoLinearFitScaling : public eoPerf2Worth<EOT> // false: do not cache fitness
{
public:
/* Ctor:
@ -50,7 +50,7 @@ public:
eoLinearFitScaling(double _p=2.0):
pressure(_p) {}
/* COmputes the ranked fitness: fitnesses range in [m,M]
/* COmputes the ranked fitness: fitnesses range in [m,M]
with m=2-pressure/popSize and M=pressure/popSize.
in between, the progression depends on exponent (linear if 1).
*/

View file

@ -29,7 +29,6 @@
#include <algorithm>
#include <eoPerf2Worth.h>
#include <eoDominanceMap.h>
/**
Non dominated sorting, it *is a* vector of doubles, the integer part is the rank (to which front it belongs),
@ -37,63 +36,88 @@
the bits.
*/
template <class EOT>
class eoNDSorting : public eoPerf2Worth<EOT, double>
class eoNDSorting : public eoPerf2WorthCached<EOT, double>
{
public :
eoNDSorting(eoDominanceMap<EOT>& _dominanceMap) :
eoPerf2Worth<EOT, double>(), dominanceMap(_dominanceMap) {}
/** Pure virtual function that calculates the 'distance' for each element to the current front
Implement to create your own nondominated sorting algorithm. The size of the returned vector
should be equal to the size of the current_front.
*/
virtual vector<double> niche_penalty(const vector<unsigned>& current_front, const eoPop<EOT>& _pop) = 0;
/// Do the work
void operator()(const eoPop<EOT>& _pop)
/** implements fast nondominated sorting
*/
void calculate_worths(const eoPop<EOT>& _pop)
{
dominanceMap(_pop);
vector<bool> excluded(_pop.size(), false);
value().resize(_pop.size());
bool finished = false;
typedef typename EOT::Fitness::fitness_traits traits;
int dominance_level = 0;
while(!finished)
{
vector<double> ranks = dominanceMap.sum_dominators();
vector<unsigned> current_front;
current_front.reserve(_pop.size());
finished = true;
if (traits::nObjectives() == 1)
{ // no need to do difficult sorting,
for (unsigned i = 0; i < _pop.size(); ++i)
{
if (excluded[i])
{
continue; // next please
}
value()[i] = _pop[i].fitness()[0];
}
if (ranks[i] < 1e-6)
{// it's part of the current front
excluded[i] = true;
current_front.push_back(i);
dominanceMap.remove(i); // remove from consideration
}
else
if (!traits::maximizing(0))
{
for (unsigned i = 0; i < value().size(); ++i)
{
finished = false; // we need another go
value()[i] = exp(-value()[i]);
}
}
return;
}
vector<vector<unsigned> > S(_pop.size()); // which individuals does guy i dominate
vector<unsigned> n(_pop.size(), 0); // how many individuals dominate guy i
for (unsigned i = 0; i < _pop.size(); ++i)
{
for (unsigned j = 0; j < _pop.size(); ++j)
{
if (_pop[i].fitness().dominates(_pop[j].fitness()))
{ // i dominates j
S[i].push_back(j); // add j to i's domination list
//n[j]++; // as i dominates j
}
else if (_pop[j].fitness().dominates(_pop[i].fitness()))
{ // j dominates i, increment count for i, add i to the domination list of j
n[i]++;
//S[j].push_back(i);
}
}
}
vector<unsigned> current_front;
current_front.reserve(_pop.size());
// get the first front out
for (unsigned i = 0; i < _pop.size(); ++i)
{
if (n[i] == 0)
{
current_front.push_back(i);
}
}
vector<unsigned> next_front;
next_front.reserve(_pop.size());
unsigned front_index = 0; // which front are we processing
while (!current_front.empty())
{
// Now we have the indices to the current front in current_front, do the niching
vector<double> niche_count = niche_penalty(current_front, _pop);
// Check whether the derived class was nice
if (niche_count.size() != current_front.size())
{
throw logic_error("eoNDSorting: niche and front should have the same size");
@ -103,10 +127,28 @@ class eoNDSorting : public eoPerf2Worth<EOT, double>
for (unsigned i = 0; i < current_front.size(); ++i)
{
value()[current_front[i]] = dominance_level + niche_count[i] / (max_niche + 1);
value()[current_front[i]] = front_index + niche_count[i] / (max_niche + 1.); // divide by max_niche + 1 to ensure that this front does not overlap with the next
}
dominance_level++; // go to the next front
// Calculate which individuals are in the next front;
for (unsigned i = 0; i < current_front.size(); ++i)
{
for (unsigned j = 0; j < S[current_front[i]].size(); ++j)
{
unsigned dominated_individual = S[current_front[i]][j];
n[dominated_individual]--; // As we remove individual i -- being part of the current front -- it no longer dominates j
if (n[dominated_individual] == 0) // it should be in the current front
{
next_front.push_back(dominated_individual);
}
}
}
front_index++; // go to the next front
swap(current_front, next_front); // make the next front current
next_front.clear(); // clear it for the next iteration
}
// now all that's left to do is to transform lower rank into higher worth
@ -115,15 +157,10 @@ class eoNDSorting : public eoPerf2Worth<EOT, double>
for (unsigned i = 0; i < value().size(); ++i)
{
value()[i] = max_fitness - value()[i];
assert(n[i] == 0);
}
}
const eoDominanceMap<EOT>& map() const;
private :
eoDominanceMap<EOT>& dominanceMap;
};
/**
@ -133,7 +170,7 @@ template <class EOT>
class eoNDSorting_I : public eoNDSorting<EOT>
{
public :
eoNDSorting_I(eoDominanceMap<EOT>& _map, double _nicheSize) : eoNDSorting<EOT>(_map), nicheSize(_nicheSize) {}
eoNDSorting_I(double _nicheSize) : eoNDSorting<EOT>(), nicheSize(_nicheSize) {}
vector<double> niche_penalty(const vector<unsigned>& current_front, const eoPop<EOT>& _pop)
{
@ -182,7 +219,6 @@ template <class EOT>
class eoNDSorting_II : public eoNDSorting<EOT>
{
public:
eoNDSorting_II(eoDominanceMap<EOT>& _map) : eoNDSorting<EOT>(_map) {}
typedef std::pair<double, unsigned> double_index_pair;
@ -222,9 +258,9 @@ class eoNDSorting_II : public eoNDSorting<EOT>
double max_dist = *max_element(nc.begin(), nc.end());
// set boundary penalty at 0 (so it will get chosen over all the others
nc[performance[0].second] = 0;
nc[performance.back().second] = 0;
// set boundary at max_dist + 1 (so it will get chosen over all the others
nc[performance[0].second] = max_dist + 1;
nc[performance.back().second] = max_dist + 1;
for (unsigned i = 0; i < nc.size(); ++i)
{

View file

@ -56,12 +56,15 @@ class eoParetoFitness : public std::vector<double>
{
public :
typedef FitnessTraits fitness_traits;
eoParetoFitness(void) : std::vector<double>(FitnessTraits::nObjectives(),0.0) {}
/// Partial order based on Pareto-dominance
bool operator<(const eoParetoFitness<FitnessTraits>& _other) const
//bool operator<(const eoParetoFitness<FitnessTraits>& _other) const
bool dominates(const eoParetoFitness<FitnessTraits>& _other) const
{
bool smaller = false;
bool dom = false;
double tol = FitnessTraits::tol();
const vector<double>& performance = *this;
@ -75,17 +78,41 @@ public :
if (fabs(aval - bval) > tol)
{
if (aval > bval)
if (aval < bval)
{
return false; // cannot dominate
}
// else aval < bval
smaller = true; // goto next objective
dom = true; // for the moment: goto next objective
}
//else they're equal in this objective, goto next
}
return smaller;
return dom;
}
/// compare *not* on dominance, but on the first, then the second, etc
bool operator<(const eoParetoFitness<FitnessTraits>& _other) const
{
double tol = FitnessTraits::tol();
const vector<double>& performance = *this;
const vector<double>& otherperformance = _other;
for (unsigned i = 0; i < FitnessTraits::nObjectives(); ++i)
{
bool maxim = FitnessTraits::maximizing(i);
double aval = maxim? performance[i] : -performance[i];
double bval = maxim? otherperformance[i] : -otherperformance[i];
if (fabs(aval-bval) > tol)
{
if (aval < bval)
return true;
return false;
}
}
return false;
}
bool operator>(const eoParetoFitness<FitnessTraits>& _other) const

View file

@ -37,17 +37,27 @@
on a single spot on the front.
*/
template <class EOT>
class eoParetoRanking : public eoPerf2Worth<EOT, double>
class eoParetoRanking : public eoPerf2WorthCached<EOT, double>
{
public :
eoParetoRanking(eoDominanceMap<EOT>& _dominanceMap) :
eoPerf2Worth<EOT, double>(), dominanceMap(_dominanceMap) {}
eoPerf2WorthCached<EOT, double>(), dominanceMap(_dominanceMap) {}
void operator()(const eoPop<EOT>& _pop)
void calculate_worths(const eoPop<EOT>& _pop)
{
dominanceMap(_pop);
value() = dominanceMap.sum_dominants();
value() = dominanceMap.sum_dominators(); // get rank: 0 means part of current front
// calculate maximum
double maxim = *max_element(value().begin(), value().end());
// higher is better, so invert the value
for (unsigned i = 0; i < value().size(); ++i)
{
value()[i] = maxim - value()[i];
}
}
private :

View file

@ -1,9 +1,9 @@
/** -*- mode: c++; c-indent-level: 4; c++-member-init-indent: 8; comment-column: 35; -*-
-----------------------------------------------------------------------------
eoPerf2Worth.h
eoPerf2Worth.h
(c) Maarten Keijzer, Marc Schoenauer, 2001
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; either
@ -27,22 +27,198 @@
#ifndef eoPerf2Worth_h
#define eoPerf2Worth_h
#include <utils/eoStat.h>
#include <utils/eoParam.h>
#include <algorithm>
/** Base class to transform raw fitnesses into fitness for selection
It is an eoStat so
- it is updated inside a checkpoint (i.e. at start of every generation)
- it can be monitored or whatever else you wish through its value()
@see eoSelectFromWorth
*/
template <class EOT, class WorthT = typename EOT::Fitness>
class eoPerf2Worth : public eoStat<EOT, vector<WorthT> >
template <class EOT, class WorthT = double>
class eoPerf2Worth : public eoUF<const eoPop<EOT>&, void>, public eoValueParam<vector<WorthT> >
{
public:
eoPerf2Worth(std::string _description = "Worths"):eoStat<EOT, vector<WorthT> >(vector<WorthT>(0),
eoPerf2Worth(std::string _description = "Worths"):eoValueParam<vector<WorthT> >(vector<WorthT>(0),
_description) {}
/**
Sort population according to worth, will keep the worths and fitness_cache in sync with the population.
*/
virtual void sort_pop(eoPop<EOT>& _pop)
{ // start with a vector of indices
vector<unsigned> indices(_pop.size());
for (unsigned i = 0; i < _pop.size();++i)
{ // could use generate, but who cares
indices[i] = i;
}
sort(indices.begin(), indices.end(), compare_worth(value()));
eoPop<EOT> tmp_pop;
tmp_pop.resize(_pop.size());
vector<WorthT> tmp_worths(value().size());
for (unsigned i = 0; i < _pop.size(); ++i)
{
tmp_pop[i] = _pop[indices[i]];
tmp_worths[i] = value()[indices[i]];
}
swap(_pop, tmp_pop);
swap(value(), tmp_worths);
}
/** helper class used to sort indices into populations/worths
*/
class compare_worth
{
public :
compare_worth(const vector<WorthT>& _worths) : worths(_worths) {}
bool operator()(unsigned a, unsigned b) const
{
return worths[b] < worths[a]; // sort in descending (!) order
}
private :
const vector<WorthT>& worths;
};
virtual void resize(eoPop<EOT>& _pop, unsigned sz)
{
_pop.resize(sz);
value().resize(sz);
}
};
/**
Perf2Worth with fitness cache
*/
template <class EOT, class WorthT = typename EOT::Fitness>
class eoPerf2WorthCached : public eoPerf2Worth<EOT, WorthT>
{
public:
eoPerf2WorthCached(std::string _description = "Worths") : eoPerf2Worth<EOT, WorthT>(_description) {}
/**
Implementation of the operator(), updating a cache of fitnesses. Calls the virtual function
calculate_worths when one of the fitnesses has changed. It is not virtual, but derived classes
can remove the fitness caching trough the third template element
*/
void operator()(const eoPop<EOT>& _pop)
{
if (fitness_cache.size() == _pop.size())
{
bool in_sync = true;
for (unsigned i = 0; i < _pop.size(); ++i)
{
if (fitness_cache[i] != _pop[i].fitness())
{
in_sync = false;
fitness_cache[i] = _pop[i].fitness();
}
}
if (in_sync)
{ // worths are up to date
return;
}
}
else // just cache the fitness
{
fitness_cache.resize(_pop.size());
for (unsigned i = 0; i < _pop.size(); ++i)
{
fitness_cache[i] = _pop[i].fitness();
}
}
// call derived implementation of perf2worth mapping
calculate_worths(_pop);
}
/** The actual virtual function the derived classes should implement*/
virtual void calculate_worths(const eoPop<EOT>& _pop) = 0;
/**
Sort population according to worth, will keep the worths and fitness_cache in sync with the population.
*/
virtual void sort_pop(eoPop<EOT>& _pop)
{ // start with a vector of indices
vector<unsigned> indices(_pop.size());
for (unsigned i = 0; i < _pop.size();++i)
{ // could use generate, but who cares
indices[i] = i;
}
sort(indices.begin(), indices.end(), eoPerf2Worth<EOT,WorthT>::compare_worth(value()));
eoPop<EOT> tmp_pop;
tmp_pop.resize(_pop.size());
vector<WorthT> tmp_worths(value().size());
vector<typename EOT::Fitness> tmp_cache(_pop.size());
for (unsigned i = 0; i < _pop.size(); ++i)
{
tmp_pop[i] = _pop[indices[i]];
tmp_worths[i] = value()[indices[i]];
tmp_cache[i] = fitness_cache[indices[i]];
}
swap(_pop, tmp_pop);
swap(value(), tmp_worths);
swap(fitness_cache, tmp_cache);
}
/** helper class used to sort indices into populations/worths
*/
class compare_worth
{
public :
compare_worth(const vector<WorthT>& _worths) : worths(_worths) {}
bool operator()(unsigned a, unsigned b) const
{
return worths[b] < worths[a]; // sort in descending (!) order
}
private :
const vector<WorthT>& worths;
};
virtual void resize(eoPop<EOT>& _pop, unsigned sz)
{
_pop.resize(sz);
value().resize(sz);
fitness_cache.resize(sz);
}
private :
vector <typename EOT::Fitness> fitness_cache;
};
/**
A dummy perf2worth, just in case you need it
*/
template <class EOT>
class eoNoPerf2Worth : public eoPerf2Worth<EOT, typename EOT::Fitness>
{
public:
// default behaviour, just copy fitnesses
void operator()(const eoPop<EOT>& _pop)
{
value.resize(_pop.size());
for (unsigned i = 0; i < _pop.size(); ++i)
value()[i]=_pop[i];
}
};
#endif

View file

@ -42,7 +42,7 @@ some other thing that a vector, but if somebody thinks of it, this concrete
implementation will be moved to "generic" and an abstract Population
interface will be provided.
It can be instantiated with anything, provided that it accepts a "size" and a
It can be instantiated with anything, provided that it accepts a "size" and a
random generator in the ctor. This happens to all the eo1d chromosomes declared
so far. EOT must also have a copy ctor, since temporaries are created and copied
to the population.

View file

@ -1,9 +1,9 @@
/** -*- mode: c++; c-indent-level: 4; c++-member-init-indent: 8; comment-column: 35; -*-
-----------------------------------------------------------------------------
eoRanking.h
eoRanking.h
(c) Maarten Keijzer, Marc Schoenauer, 2001
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; either
@ -30,12 +30,12 @@
#include <eoPerf2Worth.h>
/** An instance of eoPerfFromWorth
* COmputes the ranked fitness: fitnesses range in [m,M]
* COmputes the ranked fitness: fitnesses range in [m,M]
* with m=2-pressure/popSize and M=pressure/popSize.
* in between, the progression depends on exponent (linear if 1).
*/
template <class EOT>
class eoRanking : public eoPerf2Worth<EOT>
class eoRanking : public eoPerf2Worth<EOT> // false: do not cache fitness
{
public:
/* Ctor:
@ -57,7 +57,7 @@ public:
throw runtime_error("Not found in eoLinearRanking");
}
/* COmputes the ranked fitness: fitnesses range in [m,M]
/* COmputes the ranked fitness: fitnesses range in [m,M]
with m=2-pressure/popSize and M=pressure/popSize.
in between, the progression depends on exponent (linear if 1).
*/

View file

@ -31,19 +31,20 @@
#include <eoFunctor.h>
//-----------------------------------------------------------------------------
/** eoSelectOne selects only one element from a whole population.
/** eoSelectOne selects only one element from a whole population.
Most selection techniques are simply repeated applications
of eoSelectOne.
@see eoSelectMany, eoSelectRandom, eoDetTournament, eoStochTournament, eoProportional
*/
template<class EOT>
template<class EOT, class WorthT = typename EOT::Fitness>
class eoSelectOne : public eoUF<const eoPop<EOT>&, const EOT&>
{
public :
/// virtual function to setup some population stats (for instance eoProportional can benefit greatly from this)
virtual void setup(const eoPop<EOT>&) {}
/// virtual function to setup some population stats (for instance eoProportional can benefit greatly from this)
virtual void setup(const eoPop<EOT>& _pop)
{}
};
#endif