several fixes
This commit is contained in:
parent
f2f65d7ed4
commit
f93327a041
8 changed files with 277 additions and 213 deletions
|
|
@ -1,9 +1,11 @@
|
|||
|
||||
lib_LIBRARIES = libeomoo.a
|
||||
|
||||
libeomoo_a_SOURCES = eoFrontSorter.cpp
|
||||
libeomoo_a_SOURCES = eoFrontSorter.cpp \
|
||||
eoNSGA_IIa_Eval.cpp \
|
||||
eoNSGA_II_Eval.cpp
|
||||
|
||||
pkginclude_HEADERS = eoFrontSorter.h \
|
||||
eoMOFitness.h
|
||||
eoMOFitness.h
|
||||
|
||||
AM_CXXFLAGS = -I$(top_srcdir)/src
|
||||
|
|
|
|||
|
|
@ -7,19 +7,20 @@ namespace detail {
|
|||
namespace {
|
||||
struct CompareOn {
|
||||
unsigned dim;
|
||||
double tol;
|
||||
|
||||
CompareOn(unsigned d) : dim(d) {}
|
||||
CompareOn(unsigned d, double t) : dim(d), tol(t) {}
|
||||
|
||||
bool operator()(const FitnessInfo& a, const FitnessInfo& b) {
|
||||
return a.fitness[dim] > b.fitness[dim];
|
||||
return a.fitness[dim] > b.fitness[dim] && fabs(a.fitness[dim] - b.fitness[dim]) > tol;
|
||||
}
|
||||
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
void one_objective(std::vector<FitnessInfo>& fitness, std::vector< std::vector<FitnessInfo> >& front)
|
||||
void one_objective(std::vector<FitnessInfo>& fitness, std::vector< std::vector<FitnessInfo> >& front, double tol)
|
||||
{
|
||||
std::sort(fitness.begin(), fitness.end(), CompareOn(0));
|
||||
std::sort(fitness.begin(), fitness.end(), CompareOn(0, tol));
|
||||
|
||||
front.clear();
|
||||
front.resize(1);
|
||||
|
|
@ -40,9 +41,9 @@ void one_objective(std::vector<FitnessInfo>& fitness, std::vector< std::vector<F
|
|||
*/
|
||||
|
||||
|
||||
void two_objectives(std::vector<FitnessInfo>& fitness, std::vector< std::vector<FitnessInfo> >& front)
|
||||
void two_objectives(std::vector<FitnessInfo>& fitness, std::vector< std::vector<FitnessInfo> >& front, double tol)
|
||||
{
|
||||
std::sort(fitness.begin(), fitness.end(), CompareOn(0));
|
||||
std::sort(fitness.begin(), fitness.end(), CompareOn(0, tol));
|
||||
|
||||
front.clear();
|
||||
|
||||
|
|
@ -51,7 +52,7 @@ void two_objectives(std::vector<FitnessInfo>& fitness, std::vector< std::vector<
|
|||
for (unsigned i = 0; i < fitness.size(); ++i) {
|
||||
|
||||
// find front through binary search
|
||||
vector<FitnessInfo>::iterator it = upper_bound( front_leader.begin(), front_leader.end(), fitness[i], CompareOn(1));
|
||||
vector<FitnessInfo>::iterator it = upper_bound( front_leader.begin(), front_leader.end(), fitness[i], CompareOn(1, tol));
|
||||
|
||||
if (it == front_leader.end()) {
|
||||
front_leader.push_back(fitness[i]);
|
||||
|
|
@ -63,10 +64,12 @@ void two_objectives(std::vector<FitnessInfo>& fitness, std::vector< std::vector<
|
|||
}
|
||||
}
|
||||
|
||||
bool dominates(const FitnessInfo& a, const FitnessInfo& b) {
|
||||
bool dominates(const FitnessInfo& a, const FitnessInfo& b, double tol) {
|
||||
bool better_on_one = false;
|
||||
|
||||
for (unsigned i = 0; i < a.fitness.size(); ++i) {
|
||||
if (fabs(a.fitness[i] - b.fitness[i]) < tol) continue;
|
||||
|
||||
if (a.fitness[i] < b.fitness[i]) return false; // worse on at least one other objective
|
||||
if (a.fitness[i] > b.fitness[i]) better_on_one = true;
|
||||
}
|
||||
|
|
@ -74,7 +77,7 @@ bool dominates(const FitnessInfo& a, const FitnessInfo& b) {
|
|||
return better_on_one;
|
||||
}
|
||||
|
||||
void m_objectives(std::vector<FitnessInfo>& fitness, std::vector< std::vector<FitnessInfo> >& front) {
|
||||
void m_objectives(std::vector<FitnessInfo>& fitness, std::vector< std::vector<FitnessInfo> >& front, double tol) {
|
||||
unsigned i;
|
||||
|
||||
std::vector<std::vector<unsigned> > S(fitness.size()); // which individuals does guy i dominate
|
||||
|
|
@ -85,11 +88,11 @@ void m_objectives(std::vector<FitnessInfo>& fitness, std::vector< std::vector<Fi
|
|||
{
|
||||
for (j = 0; j < fitness.size(); ++j)
|
||||
{
|
||||
if ( dominates(fitness[i], fitness[j]) )
|
||||
if ( dominates(fitness[i], fitness[j], tol) )
|
||||
{ // i dominates j
|
||||
S[i].push_back(j); // add j to i's domination list
|
||||
}
|
||||
else if (dominates(fitness[j], fitness[i]))
|
||||
else if (dominates(fitness[j], fitness[i], tol))
|
||||
{ // j dominates i, increment count for i, add i to the domination list of j
|
||||
n[i]++;
|
||||
}
|
||||
|
|
@ -132,22 +135,22 @@ void m_objectives(std::vector<FitnessInfo>& fitness, std::vector< std::vector<Fi
|
|||
front.pop_back(); // last front is empty;
|
||||
}
|
||||
|
||||
void front_sorter_impl(std::vector<FitnessInfo>& fitness, std::vector< std::vector<FitnessInfo> >& front_indices) {
|
||||
void front_sorter_impl(std::vector<FitnessInfo>& fitness, std::vector< std::vector<FitnessInfo> >& front_indices, double tol) {
|
||||
switch (fitness[0].fitness.size())
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
one_objective(fitness, front_indices);
|
||||
one_objective(fitness, front_indices, tol);
|
||||
return;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
two_objectives(fitness, front_indices);
|
||||
two_objectives(fitness, front_indices, tol);
|
||||
return;
|
||||
}
|
||||
default :
|
||||
{
|
||||
m_objectives(fitness, front_indices);
|
||||
m_objectives(fitness, front_indices, tol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ namespace detail {
|
|||
FitnessInfo(const std::vector<double>& fitness_, unsigned index_) : fitness(fitness_), index(index_) {}
|
||||
};
|
||||
|
||||
extern void front_sorter_impl(std::vector<FitnessInfo>& fitness, std::vector< std::vector<FitnessInfo> >& fronts);
|
||||
extern void front_sorter_impl(std::vector<FitnessInfo>& fitness, std::vector< std::vector<FitnessInfo> >& fronts, double tol);
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
|
@ -75,7 +75,7 @@ class eoFrontSorter : public eoUF< const eoPop<EOT>&, const std::vector< std::ve
|
|||
fitness[i] = detail::FitnessInfo(f, i);
|
||||
}
|
||||
|
||||
detail::front_sorter_impl(fitness, fronts);
|
||||
detail::front_sorter_impl(fitness, fronts, Traits::tol());
|
||||
|
||||
return fronts;
|
||||
}
|
||||
|
|
@ -92,7 +92,7 @@ class eoFrontSorter : public eoUF< const eoPop<EOT>&, const std::vector< std::ve
|
|||
fitness[i] = detail::FitnessInfo(f, i);
|
||||
}
|
||||
|
||||
detail::front_sorter_impl(fitness, fronts);
|
||||
detail::front_sorter_impl(fitness, fronts, Traits::tol());
|
||||
|
||||
return fronts;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ class eoMOFitnessTraits
|
|||
|
||||
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
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -60,15 +61,15 @@ template <class FitnessTraits = eoMOFitnessTraits>
|
|||
class eoMOFitness : public std::vector<double>
|
||||
{
|
||||
double worth; // used for sorting and selection, by definition, bigger is better
|
||||
bool validWorth;
|
||||
bool worthValid;
|
||||
public :
|
||||
|
||||
typedef FitnessTraits fitness_traits;
|
||||
|
||||
eoMOFitness(double def = 0.0) : std::vector<double>(FitnessTraits::nObjectives(),def), validWorth(false) {}
|
||||
eoMOFitness(double def = 0.0) : std::vector<double>(FitnessTraits::nObjectives(),def), worthValid(false) {}
|
||||
|
||||
// Ctr from a std::vector<double>
|
||||
eoMOFitness(std::vector<double> & _v) : std::vector<double>(_v), validWorth(false) {}
|
||||
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
|
||||
|
|
@ -78,16 +79,19 @@ public :
|
|||
|
||||
void setWorth(double worth_) {
|
||||
worth = worth_;
|
||||
validWorth = true;
|
||||
worthValid = true;
|
||||
}
|
||||
|
||||
double getWorth() const {
|
||||
if (!validWorth) {
|
||||
if (!worthValid) {
|
||||
throw std::runtime_error("invalid worth");
|
||||
}
|
||||
return worth;
|
||||
}
|
||||
|
||||
bool validWorth() const { return worthValid; }
|
||||
void invalidateWorth() { worthValid = false; }
|
||||
|
||||
/// Partial order based on Pareto-dominance
|
||||
//bool operator<(const eoMOFitness<FitnessTraits>& _other) const
|
||||
bool dominates(const eoMOFitness<FitnessTraits>& _other) const
|
||||
|
|
@ -103,13 +107,13 @@ public :
|
|||
double aval = maxim * performance[i];
|
||||
double bval = maxim * otherperformance[i];
|
||||
|
||||
if (aval != bval)
|
||||
if (fabs(aval-bval)>FitnessTraits::tol)
|
||||
{
|
||||
if (aval < bval)
|
||||
{
|
||||
return false; // cannot dominate
|
||||
}
|
||||
// else aval < bval
|
||||
// else aval > bval
|
||||
dom = true; // for the moment: goto next objective
|
||||
}
|
||||
//else they're equal in this objective, goto next
|
||||
|
|
@ -154,7 +158,7 @@ public :
|
|||
{
|
||||
os << fitness[i] << ' ';
|
||||
}
|
||||
os << fitness.validWorth << ' ' << fitness.worth;
|
||||
os << fitness.worthValid << ' ' << fitness.worth;
|
||||
return os;
|
||||
}
|
||||
|
||||
|
|
@ -165,7 +169,7 @@ public :
|
|||
{
|
||||
is >> fitness[i];
|
||||
}
|
||||
is >> fitness.validWorth >> fitness.worth;
|
||||
is >> fitness.worthValid >> fitness.worth;
|
||||
return is;
|
||||
}
|
||||
|
||||
|
|
|
|||
77
eo/src/moo/eoNSGA_II_Eval.cpp
Normal file
77
eo/src/moo/eoNSGA_II_Eval.cpp
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
#include <eoNSGA_II_Eval.h>
|
||||
|
||||
namespace nsga2 {
|
||||
|
||||
typedef std::pair<double, unsigned> double_index_pair;
|
||||
|
||||
class compare_nodes
|
||||
{
|
||||
public :
|
||||
bool operator()(const double_index_pair& a, const double_index_pair& b) const
|
||||
{
|
||||
return a.first < b.first;
|
||||
}
|
||||
};
|
||||
|
||||
void assign_worths(const std::vector<detail::FitnessInfo>& front, unsigned rank, std::vector<double>& worths) {
|
||||
|
||||
unsigned i;
|
||||
|
||||
unsigned nObjectives = front[0].fitness.size(); //traits::nObjectives(); //_pop[_cf[0]].fitness().size();
|
||||
|
||||
std::vector<double> niche_distance(front.size());
|
||||
|
||||
for (unsigned o = 0; o < nObjectives; ++o)
|
||||
{
|
||||
|
||||
std::vector<std::pair<double, unsigned> > performance(front.size());
|
||||
for (i =0; i < front.size(); ++i)
|
||||
{
|
||||
performance[i].first = front[i].fitness[o];
|
||||
performance[i].second = i;
|
||||
}
|
||||
|
||||
std::sort(performance.begin(), performance.end(), compare_nodes()); // a lambda operator would've been nice here
|
||||
|
||||
std::vector<double> nc(front.size(), 0.0);
|
||||
|
||||
for (i = 1; i < front.size()-1; ++i)
|
||||
{ // and yet another level of indirection
|
||||
nc[performance[i].second] = performance[i+1].first - performance[i-1].first;
|
||||
}
|
||||
|
||||
// set boundary at max_dist + 1 (so it will get chosen over all the others
|
||||
//nc[performance[0].second] += 0;
|
||||
nc[performance.back().second] += std::numeric_limits<double>::infinity(); // best on objective
|
||||
|
||||
for (i = 0; i < nc.size(); ++i)
|
||||
{
|
||||
niche_distance[i] += nc[i];
|
||||
}
|
||||
}
|
||||
|
||||
// now we've got niche_distances, scale them between (0, 1), making sure that infinities get maximum rank
|
||||
|
||||
double max = 0;
|
||||
for (unsigned i = 0; i < niche_distance[i]; ++i) {
|
||||
if (niche_distance[i] != std::numeric_limits<double>::infinity()) {
|
||||
max = std::max(max, niche_distance[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < front.size(); ++i) {
|
||||
double dist = niche_distance[i];
|
||||
if (dist == std::numeric_limits<double>::infinity()) {
|
||||
dist = 1.0;
|
||||
} else {
|
||||
dist /= (1+max);
|
||||
}
|
||||
|
||||
unsigned idx = front[i].index;
|
||||
|
||||
worths[idx] = rank + dist;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -11,6 +11,11 @@
|
|||
Optimization: NSGA-II KanGAL Report No. 200001
|
||||
|
||||
*/
|
||||
|
||||
namespace nsga2 {
|
||||
void assign_worths(const std::vector<detail::FitnessInfo>& front, unsigned rank, std::vector<double>& worths);
|
||||
}
|
||||
|
||||
template <class EOT>
|
||||
class eoNSGA_II_Eval : public eoMOEval<EOT>
|
||||
{
|
||||
|
|
@ -28,94 +33,25 @@ class eoNSGA_II_Eval : public eoMOEval<EOT>
|
|||
for (unsigned i = 0; i < offspring.size(); ++i) pop.push_back(&offspring[i]);
|
||||
|
||||
typename eoFrontSorter<EOT>::front_t front = sorter(pop);
|
||||
|
||||
|
||||
std::vector<double> worths(pop.size());
|
||||
for (unsigned i = 0; i < front.size(); ++i) {
|
||||
assign_worths(front[i], front.size() - i, pop);
|
||||
nsga2::assign_worths(front[i], front.size() - i, worths);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < pop.size(); ++i) {
|
||||
typename EOT::Fitness f = pop[i]->fitness();
|
||||
f.setWorth(worths[i]);
|
||||
pop[i]->fitness(f);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
eoFrontSorter<EOT> sorter;
|
||||
|
||||
private:
|
||||
typedef std::pair<double, unsigned> double_index_pair;
|
||||
|
||||
class compare_nodes
|
||||
{
|
||||
public :
|
||||
bool operator()(const double_index_pair& a, const double_index_pair& b) const
|
||||
{
|
||||
return a.first < b.first;
|
||||
}
|
||||
};
|
||||
|
||||
void assign_worths(const std::vector<detail::FitnessInfo>& front, unsigned rank, std::vector<EOT*>& parents) {
|
||||
|
||||
typedef typename EOT::Fitness::fitness_traits traits;
|
||||
unsigned i;
|
||||
|
||||
unsigned nObjectives = traits::nObjectives(); //_pop[_cf[0]].fitness().size();
|
||||
|
||||
std::vector<double> niche_distance(front.size());
|
||||
|
||||
for (unsigned o = 0; o < nObjectives; ++o)
|
||||
{
|
||||
|
||||
std::vector<std::pair<double, unsigned> > performance(front.size());
|
||||
for (i =0; i < front.size(); ++i)
|
||||
{
|
||||
performance[i].first = front[i].fitness[o];
|
||||
performance[i].second = i;
|
||||
}
|
||||
|
||||
std::sort(performance.begin(), performance.end(), compare_nodes()); // a lambda operator would've been nice here
|
||||
|
||||
std::vector<double> nc(front.size(), 0.0);
|
||||
|
||||
for (i = 1; i < front.size()-1; ++i)
|
||||
{ // and yet another level of indirection
|
||||
nc[performance[i].second] = performance[i+1].first - performance[i-1].first;
|
||||
}
|
||||
|
||||
// set boundary at max_dist + 1 (so it will get chosen over all the others
|
||||
//nc[performance[0].second] += 0;
|
||||
nc[performance.back().second] += std::numeric_limits<double>::infinity(); // best on objective
|
||||
|
||||
for (i = 0; i < nc.size(); ++i)
|
||||
{
|
||||
niche_distance[i] += nc[i];
|
||||
}
|
||||
}
|
||||
|
||||
// now we've got niche_distances, scale them between (0, 1), making sure that infinities get maximum rank
|
||||
|
||||
double max = 0;
|
||||
for (unsigned i = 0; i < niche_distance[i]; ++i) {
|
||||
if (niche_distance[i] != std::numeric_limits<double>::infinity()) {
|
||||
max = std::max(max, niche_distance[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < front.size(); ++i) {
|
||||
double dist = niche_distance[i];
|
||||
if (dist == std::numeric_limits<double>::infinity()) {
|
||||
dist = 1.0;
|
||||
} else {
|
||||
dist /= (1+max);
|
||||
}
|
||||
|
||||
unsigned idx = front[i].index;
|
||||
|
||||
typename EOT::Fitness f = parents[idx]->fitness();
|
||||
f.setWorth(rank + dist);
|
||||
//std::cout << "Base rank " << rank << " dist " << dist << " result " << (rank+dist) << std::endl;
|
||||
|
||||
parents[idx]->fitness(f);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
129
eo/src/moo/eoNSGA_IIa_Eval.cpp
Normal file
129
eo/src/moo/eoNSGA_IIa_Eval.cpp
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
|
||||
#include <moo/eoNSGA_IIa_Eval.h>
|
||||
|
||||
namespace nsga2a {
|
||||
|
||||
double distance(const std::vector<double>& f1, const std::vector<double>& f2, const std::vector<double>& range) {
|
||||
double dist = 0;
|
||||
for (unsigned i = 0; i < f1.size(); ++i) {
|
||||
double d = (f1[i] - f2[i]);
|
||||
dist += d*d;
|
||||
}
|
||||
return dist;
|
||||
}
|
||||
|
||||
|
||||
unsigned assign_worths(std::vector<detail::FitnessInfo> front, unsigned rank, std::vector<double>& worths) {
|
||||
|
||||
unsigned nDim = front[0].fitness.size();
|
||||
|
||||
// find boundary points
|
||||
std::vector<unsigned> processed(nDim);
|
||||
|
||||
for (unsigned i = 1; i < front.size(); ++i) {
|
||||
for (unsigned dim = 0; dim < nDim; ++dim) {
|
||||
if (front[i].fitness[dim] > front[processed[dim]].fitness[dim]) {
|
||||
processed[dim] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// assign fitness to processed and store in boundaries
|
||||
std::vector<detail::FitnessInfo> boundaries;
|
||||
for (unsigned i = 0; i < processed.size(); ++i) {
|
||||
|
||||
worths[ front[ processed[i] ].index] = rank;
|
||||
|
||||
//cout << "Boundary " << i << ' ' << front[processed[i]].index << ' ' << parents[ front[ processed[i] ].index]->fitness() << endl;
|
||||
|
||||
boundaries.push_back( front[ processed[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) {
|
||||
if (processed[i] == processed[i-1]) {
|
||||
std::swap(processed[i], processed.back());
|
||||
processed.pop_back();
|
||||
--i;
|
||||
}
|
||||
}
|
||||
// remove boundaries from front
|
||||
while (processed.size()) {
|
||||
unsigned idx = processed.back();
|
||||
//std::cout << idx << ' ' ;
|
||||
processed.pop_back();
|
||||
std::swap(front.back(), front[idx]);
|
||||
front.pop_back();
|
||||
}
|
||||
//std::cout << std::endl;
|
||||
|
||||
// calculate distances
|
||||
std::vector<double> distances(front.size(), std::numeric_limits<double>::infinity());
|
||||
|
||||
unsigned selected = 0;
|
||||
// select based on maximum distance to nearest processed point
|
||||
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 );
|
||||
if (d < distances[i]) {
|
||||
distances[i] = d;
|
||||
}
|
||||
}
|
||||
|
||||
if (distances[i] > distances[selected]) {
|
||||
selected = i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
while (!front.empty()) {
|
||||
|
||||
detail::FitnessInfo last = front[selected];
|
||||
|
||||
std::swap(front[selected], front.back());
|
||||
front.pop_back();
|
||||
|
||||
std::swap(distances[selected], distances.back());
|
||||
distances.pop_back();
|
||||
|
||||
// set worth
|
||||
worths[last.index] = rank--;
|
||||
|
||||
if (front.empty()) break;
|
||||
|
||||
selected = 0;
|
||||
|
||||
for (unsigned i = 0; i < front.size(); ++i) {
|
||||
double d = distance(front[i].fitness, last.fitness, range);
|
||||
|
||||
if (d < distances[i]) {
|
||||
distances[i] = d;
|
||||
}
|
||||
|
||||
if (distances[i] >= distances[selected]) {
|
||||
selected = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rank;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -3,12 +3,18 @@
|
|||
|
||||
#include <moo/eoFrontSorter.h>
|
||||
#include <moo/eoMOEval.h>
|
||||
#include <cassert>
|
||||
|
||||
/** @brief Fast Elitist Non-Dominant Sorting Genetic Algorithm
|
||||
|
||||
Variant of the NSGA-II, where the ranking is based on a top-down distance based mechanism ( O(n^2)! )
|
||||
|
||||
*/
|
||||
|
||||
namespace nsga2a {
|
||||
extern unsigned assign_worths(std::vector<detail::FitnessInfo> front, unsigned rank, std::vector<double>& worths);
|
||||
}
|
||||
|
||||
template <class EOT>
|
||||
class eoNSGA_IIa_Eval : public eoMOEval<EOT>
|
||||
{
|
||||
|
|
@ -24,123 +30,30 @@ class eoNSGA_IIa_Eval : public eoMOEval<EOT>
|
|||
std::vector<EOT*> pop;
|
||||
pop.reserve(parents.size() + offspring.size());
|
||||
for (unsigned i = 0; i < parents.size(); ++i) pop.push_back(&parents[i]);
|
||||
for (unsigned i = 0; i < offspring.size(); ++i) pop.push_back(&offspring[i]);
|
||||
for (unsigned i = 0; i < offspring.size(); ++i) {
|
||||
pop.push_back(&offspring[i]);
|
||||
}
|
||||
|
||||
typename eoFrontSorter<EOT>::front_t front = sorter(pop);
|
||||
|
||||
std::vector<double> worths(pop.size());
|
||||
unsigned rank = pop.size();
|
||||
for (unsigned i = 0; i < front.size(); ++i) {
|
||||
rank = assign_worths(front[i], rank, pop);
|
||||
rank = nsga2a::assign_worths(front[i], rank, worths);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < worths.size(); ++i) {
|
||||
typename EOT::Fitness f = pop[i]->fitness();
|
||||
f.setWorth(worths[i]);
|
||||
pop[i]->fitness(f);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
eoFrontSorter<EOT> sorter;
|
||||
eoFrontSorter<EOT> sorter;
|
||||
|
||||
double distance(const std::vector<double>& f1, const std::vector<double>& f2, const std::vector<double>& range) {
|
||||
double dist = 0;
|
||||
for (unsigned i = 0; i < f1.size(); ++i) {
|
||||
double d = (f1[i] - f2[i])/range[i];
|
||||
dist += d*d;
|
||||
}
|
||||
return dist;
|
||||
}
|
||||
|
||||
unsigned assign_worths(const std::vector<detail::FitnessInfo>& front, unsigned rank, std::vector<EOT*>& parents) {
|
||||
|
||||
unsigned nDim = front[0].fitness.size();
|
||||
|
||||
// find boundary points
|
||||
std::vector<unsigned> processed(nDim);
|
||||
|
||||
for (unsigned i = 1; i < front.size(); ++i) {
|
||||
for (unsigned dim = 0; dim < nDim; ++dim) {
|
||||
if (front[i].fitness[dim] > front[processed[dim]].fitness[dim]) {
|
||||
processed[dim] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// assign fitness to processed
|
||||
for (unsigned i = 0; i < processed.size(); ++i) {
|
||||
typename EOT::Fitness f = parents[ front[ processed[i] ].index]->fitness();
|
||||
f.setWorth(rank);
|
||||
parents[ front[ processed[i] ].index ]->fitness(f);
|
||||
}
|
||||
rank--;
|
||||
|
||||
// calculate ranges
|
||||
std::vector<double> mins(nDim, std::numeric_limits<double>::infinity());
|
||||
for (unsigned dim = 0; dim < nDim; ++dim) {
|
||||
for (unsigned i = 0; i < nDim; ++i) {
|
||||
mins[dim] = std::min( mins[dim], front[ processed[i] ].fitness[dim] );
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<double> range(nDim);
|
||||
for (unsigned dim = 0; dim < nDim; ++dim) {
|
||||
range[dim] = front[ processed[dim] ].fitness[dim] - mins[dim];
|
||||
}
|
||||
|
||||
// calculate distances
|
||||
std::vector<double> distances(front.size(), std::numeric_limits<double>::infinity());
|
||||
|
||||
unsigned selected = 0;
|
||||
// select based on maximum distance to nearest processed point
|
||||
for (unsigned i = 0; i < front.size(); ++i) {
|
||||
|
||||
for (unsigned k = 0; k < processed.size(); ++k) {
|
||||
|
||||
if (i==processed[k]) {
|
||||
distances[i] = -1.0;
|
||||
continue;
|
||||
}
|
||||
|
||||
double d = distance( front[i].fitness, front[ processed[k] ].fitness, range );
|
||||
|
||||
if (d < distances[i]) {
|
||||
distances[i] = d;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (distances[i] > distances[selected]) {
|
||||
selected = i;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
while (processed.size() < front.size()) {
|
||||
|
||||
// set worth
|
||||
typename EOT::Fitness f = parents[ front[selected].index ]->fitness();
|
||||
f.setWorth(rank--);
|
||||
parents[ front[selected].index ]->fitness(f);
|
||||
distances[selected] = -1;
|
||||
|
||||
processed.push_back(selected);
|
||||
|
||||
selected = 0;
|
||||
|
||||
for (unsigned i = 0; i < front.size(); ++i) {
|
||||
if (distances[i] < 0) continue;
|
||||
|
||||
double d = distance(front[i].fitness, front[processed.back()].fitness, range);
|
||||
|
||||
if (d < distances[i]) {
|
||||
distances[i] = d;
|
||||
}
|
||||
|
||||
if (distances[i] > distances[selected]) {
|
||||
selected = i;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return rank;
|
||||
}
|
||||
// implementation
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Reference in a new issue