added epsilon moea
This commit is contained in:
parent
1be65a94ad
commit
29816fc197
5 changed files with 347 additions and 63 deletions
118
eo/src/moo/eoEpsMOEA.h
Normal file
118
eo/src/moo/eoEpsMOEA.h
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
#ifndef eoEpsMOEA_h
|
||||
#define eoEpsMOEA_h
|
||||
|
||||
#include <eoAlgo.h>
|
||||
#include <moo/eoEpsilonArchive.h>
|
||||
|
||||
template <class EOT>
|
||||
class eoEpsMOEA : public eoAlgo<EOT> {
|
||||
|
||||
public:
|
||||
|
||||
eoEpsMOEA(
|
||||
eoContinue<EOT>& _continuator,
|
||||
eoEvalFunc<EOT>& _eval,
|
||||
eoGenOp<EOT>& _op,
|
||||
const std::vector<double>& eps
|
||||
) : continuator(_continuator),
|
||||
eval (_eval),
|
||||
loopEval(_eval),
|
||||
popEval(loopEval),
|
||||
op(_op),
|
||||
archive(eps)
|
||||
{}
|
||||
|
||||
|
||||
void operator()(eoPop<EOT>& pop) {
|
||||
|
||||
eoPop<EOT> offspring;
|
||||
popEval(offspring, pop);
|
||||
for (unsigned i = 0; i < pop.size(); ++i) pop[i].fitnessReference().setWorth(1.0);
|
||||
|
||||
do {
|
||||
unsigned nProcessed = 0;
|
||||
|
||||
while (nProcessed < pop.size()) {
|
||||
offspring.clear();
|
||||
|
||||
epsPopulator populator(archive, pop, offspring);
|
||||
op(populator);
|
||||
|
||||
nProcessed += offspring.size();
|
||||
popEval(pop, offspring);
|
||||
|
||||
for (unsigned i = 0; i < offspring.size(); ++i) {
|
||||
offspring[i].fitnessReference().setWorth(1.0);
|
||||
archive(offspring[i]);
|
||||
update_pop(pop, offspring[i]);
|
||||
}
|
||||
}
|
||||
|
||||
} while (continuator(pop));
|
||||
|
||||
// return archive
|
||||
pop.clear();
|
||||
archive.appendTo(pop);
|
||||
|
||||
}
|
||||
|
||||
private :
|
||||
void update_pop(eoPop<EOT>& pop, const EOT& offspring) {
|
||||
for (unsigned i = 0; i < pop.size(); ++i) {
|
||||
int dom = offspring.fitness().check_dominance(pop[i].fitness());
|
||||
if (dom == 1) {
|
||||
pop[i] = offspring;
|
||||
return;
|
||||
}
|
||||
if (dom == -1) return; //
|
||||
}
|
||||
|
||||
// non-dominated everywhere, overwrite random one
|
||||
pop[ rng.random(pop.size()) ] = offspring;
|
||||
}
|
||||
|
||||
class epsPopulator : public eoPopulator<EOT> {
|
||||
|
||||
using eoPopulator< EOT >::src;
|
||||
|
||||
eoEpsilonArchive<EOT>& archive;
|
||||
bool fromArchive;
|
||||
public:
|
||||
epsPopulator(eoEpsilonArchive<EOT>& arch, const eoPop<EOT>& pop, eoPop<EOT>& res) : eoPopulator<EOT>(pop, res), archive(arch), fromArchive(true) {}
|
||||
|
||||
const EOT& select() {
|
||||
fromArchive = !fromArchive;
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
if (fromArchive && !archive.empty()) {
|
||||
return archive.selectRandom();
|
||||
}
|
||||
|
||||
// tournament selection on population
|
||||
const EOT& eo1 = rng.choice(src);
|
||||
const EOT& eo2 = rng.choice(src);
|
||||
|
||||
if (eo1.fitness().dominates(eo2.fitness())) return eo1;
|
||||
return eo2; // they are drawn at random, so no need to do an extra randomization step
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
eoContinue<EOT>& continuator;
|
||||
|
||||
eoEvalFunc <EOT> & eval ;
|
||||
eoPopLoopEval<EOT> loopEval;
|
||||
|
||||
eoPopEvalFunc<EOT>& popEval;
|
||||
|
||||
eoGenOp<EOT>& op;
|
||||
|
||||
eoEpsilonArchive<EOT> archive;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
123
eo/src/moo/eoEpsilonArchive.h
Normal file
123
eo/src/moo/eoEpsilonArchive.h
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
#ifndef eoEpsilonArchive_h
|
||||
#define eoEpsilonArchive_h
|
||||
|
||||
#include <moo/eoMOFitness.h>
|
||||
#include <list>
|
||||
|
||||
template <class EOT>
|
||||
class eoEpsilonArchive {
|
||||
|
||||
typedef typename EOT::Fitness::fitness_traits Traits;
|
||||
|
||||
struct Node {
|
||||
EOT element;
|
||||
std::vector<double> discretized;
|
||||
|
||||
Node(const EOT& eo) : element(eo), discretized(Traits::nObjectives()) {}
|
||||
};
|
||||
|
||||
typedef std::list<Node> archive_t;
|
||||
|
||||
archive_t archive;
|
||||
std::vector<double> eps;
|
||||
|
||||
public:
|
||||
|
||||
static double direction(unsigned i) { return 1; }
|
||||
static double tol() { return 1e-6; }
|
||||
|
||||
|
||||
eoEpsilonArchive(const std::vector<double>& eps_) : eps(eps_) {
|
||||
if (eps.size() != Traits::nObjectives()) throw std::logic_error("eoEpsilonArchive: need one epsilon for each objective");
|
||||
}
|
||||
|
||||
bool empty() { return archive.size() == 0; }
|
||||
|
||||
void operator()(const EOT& eo) {
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
// discretize
|
||||
Node node(eo);
|
||||
for (unsigned i = 0; i < eo.fitness().size(); ++i) {
|
||||
double val = Traits::direction(i) * eo.fitness()[i];
|
||||
|
||||
node.discretized[i] = floor(val/eps[i]);
|
||||
}
|
||||
|
||||
using namespace dominance;
|
||||
|
||||
typename archive_t::iterator boxIt = archive.end();
|
||||
// update archive
|
||||
|
||||
for (typename archive_t::iterator it = archive.begin(); it != archive.end(); ++it) {
|
||||
dominance_result result = check<eoEpsilonArchive<EOT> >(node.discretized, it->discretized);
|
||||
|
||||
switch (result) {
|
||||
case first : { // remove dominated archive member
|
||||
it = archive.erase(it);
|
||||
break;
|
||||
}
|
||||
case second : {
|
||||
//cout << it->discretized[0] << ' ' << it->discretized[1] << " dominates " << node.discretized[0] << ' ' << node.discretized[1] << endl;
|
||||
return; // new one does not belong in archive
|
||||
}
|
||||
case non_dominated : break; // both non-dominated, put in different boxes
|
||||
case non_dominated_equal : { // in same box
|
||||
boxIt = it;
|
||||
goto exitLoop; // break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
exitLoop:
|
||||
// insert
|
||||
if (boxIt == archive.end()) { // non-dominated, new box
|
||||
archive.push_back(node);
|
||||
} else { // fight it out
|
||||
int dom = node.element.fitness().check_dominance( boxIt->element.fitness() );
|
||||
|
||||
switch (dom) {
|
||||
case 1: *boxIt = node; break;
|
||||
case -1: break;
|
||||
case 0: {
|
||||
double d1 = 0.0;
|
||||
double d2 = 0.0;
|
||||
|
||||
for (unsigned i = 0; i < node.element.fitness().size(); ++i) {
|
||||
double a = Traits::direction(i) * node.element.fitness()[i];
|
||||
double b = Traits::direction(i) * boxIt->element.fitness()[i];
|
||||
|
||||
d1 += pow( (a - node.discretized[i]) / eps[i], 2.0);
|
||||
d2 += pow( (b - node.discretized[i]) / eps[i], 2.0);
|
||||
}
|
||||
|
||||
if (d1 > d2) {
|
||||
*boxIt = node;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void appendTo(eoPop<EOT>& pop) const {
|
||||
for (typename archive_t::const_iterator it = archive.begin(); it != archive.end(); ++it) {
|
||||
pop.push_back( it->element );
|
||||
}
|
||||
}
|
||||
|
||||
const EOT& selectRandom() const {
|
||||
int i = rng.random(archive.size());
|
||||
typename archive_t::const_iterator it = archive.begin();
|
||||
while (i-- > 0) it++;
|
||||
return it->element;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -67,12 +67,7 @@ class eoFrontSorter : public eoUF< const eoPop<EOT>&, const std::vector< std::ve
|
|||
{
|
||||
fitness.resize(_pop.size());
|
||||
for (unsigned i = 0; i < _pop.size(); ++i) {
|
||||
std::vector<double> f;
|
||||
|
||||
for (unsigned j = 0; j < Traits::nObjectives(); ++j) {
|
||||
if (Traits::maximizing(j) != 0) f.push_back( Traits::maximizing(j) * _pop[i].fitness()[j]);
|
||||
}
|
||||
fitness[i] = detail::FitnessInfo(f, i);
|
||||
fitness[i] = detail::FitnessInfo(_pop[i].fitness().normalized(), i);
|
||||
}
|
||||
|
||||
detail::front_sorter_impl(fitness, fronts, Traits::tol());
|
||||
|
|
@ -84,12 +79,7 @@ class eoFrontSorter : public eoUF< const eoPop<EOT>&, const std::vector< std::ve
|
|||
{
|
||||
fitness.resize(_pop.size());
|
||||
for (unsigned i = 0; i < _pop.size(); ++i) {
|
||||
std::vector<double> f;
|
||||
|
||||
for (unsigned j = 0; j < Traits::nObjectives(); ++j) {
|
||||
if (Traits::maximizing(j) != 0) f.push_back( Traits::maximizing(j) * _pop[i]->fitness()[j]);
|
||||
}
|
||||
fitness[i] = detail::FitnessInfo(f, i);
|
||||
fitness[i] = detail::FitnessInfo(_pop[i]->fitness().normalized(), i);
|
||||
}
|
||||
|
||||
detail::front_sorter_impl(fitness, fronts, Traits::tol());
|
||||
|
|
|
|||
|
|
@ -30,11 +30,11 @@
|
|||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
|
||||
#include <limits>
|
||||
|
||||
/**
|
||||
* eoMOFitnessTraits: a traits class to specify
|
||||
* the number of objectives and which one are maximizing or not
|
||||
* the number of objectives and which one are direction or not
|
||||
* See test/t-eoParetoFitness for its use.
|
||||
*
|
||||
* If you define your own, make sure you make the functions static!
|
||||
|
|
@ -43,11 +43,77 @@ class eoMOFitnessTraits
|
|||
{
|
||||
public :
|
||||
|
||||
/// Number of Objectives
|
||||
static unsigned nObjectives() { return 2; }
|
||||
static double maximizing(int which) { return 1; } // by default: all are maximizing, zero will lead to ignored fitness, negative minimizes
|
||||
static double tol() { return 1e-6; } // tolerance for distance calculations
|
||||
|
||||
/// by default: all are maximizing, zero will lead to ignored fitness, negative to minimization for that objective
|
||||
static double direction(unsigned which) { return 1; }
|
||||
|
||||
/// tolerance for dominance check (if within this tolerance objective values are considered equal)
|
||||
static double tol() { return 1e-6; }
|
||||
|
||||
};
|
||||
|
||||
namespace dominance {
|
||||
|
||||
template <class Traits>
|
||||
inline double worst_possible_value(unsigned objective) {
|
||||
double dir = Traits::direction(objective);
|
||||
if (dir == 0.) return 0.0;
|
||||
if (dir < 0.) return std::numeric_limits<double>::infinity();
|
||||
return -std::numeric_limits<double>::infinity();
|
||||
}
|
||||
|
||||
template <class Traits>
|
||||
inline double best_possible_value(unsigned objective) {
|
||||
return -worst_possible_value<Traits>(objective);
|
||||
}
|
||||
|
||||
enum dominance_result { non_dominated_equal, first, second, non_dominated };
|
||||
|
||||
template <class FitnessDirectionTraits>
|
||||
inline dominance_result check(const std::vector<double>& p1, const std::vector<double>& p2, double tolerance) {
|
||||
|
||||
bool all_equal = true;
|
||||
bool a_better_in_one = false;
|
||||
bool b_better_in_one = false;
|
||||
|
||||
for (unsigned i = 0; i < p1.size(); ++i) {
|
||||
|
||||
double maxim = FitnessDirectionTraits::direction(i);
|
||||
double aval = maxim * p1[i];
|
||||
double bval = maxim * p2[i];
|
||||
|
||||
if ( fabs(aval-bval) > tolerance ) {
|
||||
all_equal = false;
|
||||
if (aval > bval) {
|
||||
a_better_in_one = true;
|
||||
|
||||
} else {
|
||||
b_better_in_one = true;
|
||||
}
|
||||
// check if we need to go on
|
||||
|
||||
if (a_better_in_one && b_better_in_one) return non_dominated;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (all_equal) return non_dominated_equal;
|
||||
|
||||
if (a_better_in_one) return first;
|
||||
// else b dominates a (check for non-dominance done above
|
||||
return second;
|
||||
}
|
||||
|
||||
template <class FitnessTraits>
|
||||
inline dominance_result check(const std::vector<double>& p1, const std::vector<double>& p2) {
|
||||
return check<FitnessTraits>(p1, p2, FitnessTraits::tol());
|
||||
}
|
||||
|
||||
|
||||
} // namespace dominance
|
||||
|
||||
/**
|
||||
eoMOFitness class: std::vector of doubles with overloaded comparison operators. Comparison is done
|
||||
on 'worth'. This worth needs to be set elsewhere. The template argument FitnessTraits defaults to eMOFitnessTraits, which
|
||||
|
|
@ -66,16 +132,22 @@ public :
|
|||
|
||||
typedef FitnessTraits fitness_traits;
|
||||
|
||||
eoMOFitness(double def = 0.0) : std::vector<double>(FitnessTraits::nObjectives(),def), worthValid(false) {}
|
||||
eoMOFitness() : std::vector<double>(FitnessTraits::nObjectives()), worthValid(false) { reset(); }
|
||||
|
||||
eoMOFitness(double def) : std::vector<double>(FitnessTraits::nObjectives(),def), worthValid(false) {}
|
||||
|
||||
// Ctr from a std::vector<double>
|
||||
eoMOFitness(std::vector<double> & _v) : std::vector<double>(_v), worthValid(false) {}
|
||||
|
||||
/** access to the traits characteristics (so you don't have to write
|
||||
* a lot of typedef's around
|
||||
*/
|
||||
static void setUp(unsigned _n, std::vector<bool> & _b) {FitnessTraits::setUp(_n, _b);}
|
||||
static bool maximizing(unsigned _i) { return FitnessTraits::maximizing(_i);}
|
||||
void reset() {
|
||||
|
||||
for (unsigned i = 0; i < size(); ++i) {
|
||||
this->operator[](i) = dominance::worst_possible_value<FitnessTraits>(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Make the objective 'feasible' by setting it to the best possible value
|
||||
void setFeasible(unsigned objective) { this->operator[](objective) = dominance::best_possible_value<FitnessTraits>(objective); }
|
||||
|
||||
void setWorth(double worth_) {
|
||||
worth = worth_;
|
||||
|
|
@ -91,35 +163,30 @@ public :
|
|||
|
||||
bool validWorth() const { return worthValid; }
|
||||
void invalidateWorth() { worthValid = false; }
|
||||
|
||||
/// Check on dominance: returns 0 if non-dominated, 1 if this dominates other, -1 if other dominates this
|
||||
int check_dominance(const eoMOFitness<FitnessTraits>& _other) const
|
||||
{
|
||||
dominance::dominance_result dom = dominance::check<FitnessTraits>(*this, _other);
|
||||
|
||||
/// Partial order based on Pareto-dominance
|
||||
//bool operator<(const eoMOFitness<FitnessTraits>& _other) const
|
||||
return dom == dominance::first? 1 : (dom == dominance::second? -1 : 0);
|
||||
}
|
||||
|
||||
/// normalized fitness: all maximizing, removed the irrelevant ones
|
||||
std::vector<double> normalized() const {
|
||||
std::vector<double> f;
|
||||
|
||||
for (unsigned j = 0; j < FitnessTraits::nObjectives(); ++j) {
|
||||
if (FitnessTraits::direction(j) != 0) f.push_back( FitnessTraits::direction(j) * this->operator[](j));
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
/// returns true if this dominates other
|
||||
bool dominates(const eoMOFitness<FitnessTraits>& _other) const
|
||||
{
|
||||
bool dom = false;
|
||||
|
||||
const std::vector<double>& performance = *this;
|
||||
const std::vector<double>& otherperformance = _other;
|
||||
|
||||
for (unsigned i = 0; i < FitnessTraits::nObjectives(); ++i)
|
||||
{
|
||||
double maxim = FitnessTraits::maximizing(i);
|
||||
double aval = maxim * performance[i];
|
||||
double bval = maxim * otherperformance[i];
|
||||
|
||||
if (fabs(aval-bval)>FitnessTraits::tol)
|
||||
{
|
||||
if (aval < bval)
|
||||
{
|
||||
return false; // cannot dominate
|
||||
}
|
||||
// else aval > bval
|
||||
dom = true; // for the moment: goto next objective
|
||||
}
|
||||
//else they're equal in this objective, goto next
|
||||
}
|
||||
|
||||
return dom;
|
||||
return check_dominance(_other) == 1;
|
||||
}
|
||||
|
||||
/// compare *not* on dominance, but on worth
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
namespace nsga2a {
|
||||
|
||||
double distance(const std::vector<double>& f1, const std::vector<double>& f2, const std::vector<double>& range) {
|
||||
double calc_distance(const std::vector<double>& f1, const std::vector<double>& f2) {
|
||||
double dist = 0;
|
||||
for (unsigned i = 0; i < f1.size(); ++i) {
|
||||
double d = (f1[i] - f2[i]);
|
||||
|
|
@ -40,20 +40,6 @@ for (unsigned i = 0; i < processed.size(); ++i) {
|
|||
}
|
||||
rank--;
|
||||
|
||||
// calculate ranges
|
||||
std::vector<double> mins(nDim, std::numeric_limits<double>::infinity());
|
||||
for (unsigned dim = 0; dim < boundaries.size(); ++dim) {
|
||||
for (unsigned i = 0; i < boundaries.size(); ++i) {
|
||||
mins[dim] = std::min( mins[dim], boundaries[i].fitness[dim] );
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<double> range(nDim);
|
||||
for (unsigned dim = 0; dim < nDim; ++dim) {
|
||||
range[dim] = boundaries[dim].fitness[dim] - mins[dim];
|
||||
}
|
||||
|
||||
|
||||
// clean up processed (make unique)
|
||||
sort(processed.begin(), processed.end()); // swap out last first
|
||||
for (unsigned i = 1; i < processed.size(); ++i) {
|
||||
|
|
@ -81,7 +67,7 @@ unsigned selected = 0;
|
|||
for (unsigned i = 0; i < front.size(); ++i) {
|
||||
|
||||
for (unsigned k = 0; k < boundaries.size(); ++k) {
|
||||
double d = distance( front[i].fitness, boundaries[k].fitness, range );
|
||||
double d = calc_distance( front[i].fitness, boundaries[k].fitness);
|
||||
if (d < distances[i]) {
|
||||
distances[i] = d;
|
||||
}
|
||||
|
|
@ -111,7 +97,7 @@ while (!front.empty()) {
|
|||
selected = 0;
|
||||
|
||||
for (unsigned i = 0; i < front.size(); ++i) {
|
||||
double d = distance(front[i].fitness, last.fitness, range);
|
||||
double d = calc_distance(front[i].fitness, last.fitness);
|
||||
|
||||
if (d < distances[i]) {
|
||||
distances[i] = d;
|
||||
|
|
|
|||
Reference in a new issue