several fixes

This commit is contained in:
maartenkeijzer 2007-09-03 14:37:27 +00:00
commit f93327a041
8 changed files with 277 additions and 213 deletions

View file

@ -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

View file

@ -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);
}
}
}

View file

@ -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;
}

View file

@ -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;
}

View 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;
}
}
}

View file

@ -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

View 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;
}
}

View file

@ -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