From f93327a041ce43a9ddbc71ca8408e7b908383d5b Mon Sep 17 00:00:00 2001 From: maartenkeijzer Date: Mon, 3 Sep 2007 14:37:27 +0000 Subject: [PATCH] several fixes --- eo/src/moo/Makefile.am | 6 +- eo/src/moo/eoFrontSorter.cpp | 33 +++++---- eo/src/moo/eoFrontSorter.h | 6 +- eo/src/moo/eoMOFitness.h | 22 +++--- eo/src/moo/eoNSGA_II_Eval.cpp | 77 ++++++++++++++++++++ eo/src/moo/eoNSGA_II_Eval.h | 94 ++++-------------------- eo/src/moo/eoNSGA_IIa_Eval.cpp | 129 +++++++++++++++++++++++++++++++++ eo/src/moo/eoNSGA_IIa_Eval.h | 125 +++++--------------------------- 8 files changed, 278 insertions(+), 214 deletions(-) create mode 100644 eo/src/moo/eoNSGA_II_Eval.cpp create mode 100644 eo/src/moo/eoNSGA_IIa_Eval.cpp diff --git a/eo/src/moo/Makefile.am b/eo/src/moo/Makefile.am index a9f00c73..8a94c342 100644 --- a/eo/src/moo/Makefile.am +++ b/eo/src/moo/Makefile.am @@ -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 diff --git a/eo/src/moo/eoFrontSorter.cpp b/eo/src/moo/eoFrontSorter.cpp index 62de42c8..3afc762a 100644 --- a/eo/src/moo/eoFrontSorter.cpp +++ b/eo/src/moo/eoFrontSorter.cpp @@ -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& fitness, std::vector< std::vector >& front) +void one_objective(std::vector& fitness, std::vector< std::vector >& 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& fitness, std::vector< std::vector& fitness, std::vector< std::vector >& front) +void two_objectives(std::vector& fitness, std::vector< std::vector >& 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& fitness, std::vector< std::vector< for (unsigned i = 0; i < fitness.size(); ++i) { // find front through binary search - vector::iterator it = upper_bound( front_leader.begin(), front_leader.end(), fitness[i], CompareOn(1)); + vector::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& 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& fitness, std::vector< std::vector >& front) { +void m_objectives(std::vector& fitness, std::vector< std::vector >& front, double tol) { unsigned i; std::vector > S(fitness.size()); // which individuals does guy i dominate @@ -85,11 +88,11 @@ void m_objectives(std::vector& fitness, std::vector< std::vector& fitness, std::vector< std::vector& fitness, std::vector< std::vector >& front_indices) { +void front_sorter_impl(std::vector& fitness, std::vector< std::vector >& 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); } } } diff --git a/eo/src/moo/eoFrontSorter.h b/eo/src/moo/eoFrontSorter.h index 60a4c1e6..9d9feb8e 100644 --- a/eo/src/moo/eoFrontSorter.h +++ b/eo/src/moo/eoFrontSorter.h @@ -43,7 +43,7 @@ namespace detail { FitnessInfo(const std::vector& fitness_, unsigned index_) : fitness(fitness_), index(index_) {} }; - extern void front_sorter_impl(std::vector& fitness, std::vector< std::vector >& fronts); + extern void front_sorter_impl(std::vector& fitness, std::vector< std::vector >& fronts, double tol); } // namespace detail @@ -75,7 +75,7 @@ class eoFrontSorter : public eoUF< const eoPop&, 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&, 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; } diff --git a/eo/src/moo/eoMOFitness.h b/eo/src/moo/eoMOFitness.h index 9ab29383..659a32ee 100644 --- a/eo/src/moo/eoMOFitness.h +++ b/eo/src/moo/eoMOFitness.h @@ -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 eoMOFitness : public std::vector { 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(FitnessTraits::nObjectives(),def), validWorth(false) {} + eoMOFitness(double def = 0.0) : std::vector(FitnessTraits::nObjectives(),def), worthValid(false) {} // Ctr from a std::vector - eoMOFitness(std::vector & _v) : std::vector(_v), validWorth(false) {} + eoMOFitness(std::vector & _v) : std::vector(_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& _other) const bool dominates(const eoMOFitness& _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; } diff --git a/eo/src/moo/eoNSGA_II_Eval.cpp b/eo/src/moo/eoNSGA_II_Eval.cpp new file mode 100644 index 00000000..321bc079 --- /dev/null +++ b/eo/src/moo/eoNSGA_II_Eval.cpp @@ -0,0 +1,77 @@ +#include + +namespace nsga2 { + + typedef std::pair 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& front, unsigned rank, std::vector& worths) { + + unsigned i; + + unsigned nObjectives = front[0].fitness.size(); //traits::nObjectives(); //_pop[_cf[0]].fitness().size(); + + std::vector niche_distance(front.size()); + + for (unsigned o = 0; o < nObjectives; ++o) + { + + std::vector > 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 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::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::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::infinity()) { + dist = 1.0; + } else { + dist /= (1+max); + } + + unsigned idx = front[i].index; + + worths[idx] = rank + dist; + } + + } + +} diff --git a/eo/src/moo/eoNSGA_II_Eval.h b/eo/src/moo/eoNSGA_II_Eval.h index 68a4cb2f..de3e33f8 100644 --- a/eo/src/moo/eoNSGA_II_Eval.h +++ b/eo/src/moo/eoNSGA_II_Eval.h @@ -11,6 +11,11 @@ Optimization: NSGA-II KanGAL Report No. 200001 */ + +namespace nsga2 { + void assign_worths(const std::vector& front, unsigned rank, std::vector& worths); +} + template class eoNSGA_II_Eval : public eoMOEval { @@ -28,94 +33,25 @@ class eoNSGA_II_Eval : public eoMOEval for (unsigned i = 0; i < offspring.size(); ++i) pop.push_back(&offspring[i]); typename eoFrontSorter::front_t front = sorter(pop); - + + std::vector 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 sorter; private: - typedef std::pair 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& front, unsigned rank, std::vector& parents) { - typedef typename EOT::Fitness::fitness_traits traits; - unsigned i; - - unsigned nObjectives = traits::nObjectives(); //_pop[_cf[0]].fitness().size(); - - std::vector niche_distance(front.size()); - - for (unsigned o = 0; o < nObjectives; ++o) - { - - std::vector > 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 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::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::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::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 diff --git a/eo/src/moo/eoNSGA_IIa_Eval.cpp b/eo/src/moo/eoNSGA_IIa_Eval.cpp new file mode 100644 index 00000000..c7cafef9 --- /dev/null +++ b/eo/src/moo/eoNSGA_IIa_Eval.cpp @@ -0,0 +1,129 @@ + +#include + +namespace nsga2a { + +double distance(const std::vector& f1, const std::vector& f2, const std::vector& 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 front, unsigned rank, std::vector& worths) { + +unsigned nDim = front[0].fitness.size(); + +// find boundary points +std::vector 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 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 mins(nDim, std::numeric_limits::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 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 distances(front.size(), std::numeric_limits::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; +} + +} diff --git a/eo/src/moo/eoNSGA_IIa_Eval.h b/eo/src/moo/eoNSGA_IIa_Eval.h index a25a5903..0b1369b4 100644 --- a/eo/src/moo/eoNSGA_IIa_Eval.h +++ b/eo/src/moo/eoNSGA_IIa_Eval.h @@ -3,12 +3,18 @@ #include #include +#include /** @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 front, unsigned rank, std::vector& worths); +} + template class eoNSGA_IIa_Eval : public eoMOEval { @@ -24,123 +30,30 @@ class eoNSGA_IIa_Eval : public eoMOEval std::vector 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::front_t front = sorter(pop); + std::vector 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 sorter; + eoFrontSorter sorter; - double distance(const std::vector& f1, const std::vector& f2, const std::vector& 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& front, unsigned rank, std::vector& parents) { - - unsigned nDim = front[0].fitness.size(); - - // find boundary points - std::vector 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 mins(nDim, std::numeric_limits::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 range(nDim); - for (unsigned dim = 0; dim < nDim; ++dim) { - range[dim] = front[ processed[dim] ].fitness[dim] - mins[dim]; - } - - // calculate distances - std::vector distances(front.size(), std::numeric_limits::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