diff --git a/eo/configure.in b/eo/configure.in index e3dd6b50..341ae7a7 100644 --- a/eo/configure.in +++ b/eo/configure.in @@ -1,8 +1,8 @@ dnl Process this file with autoconf to produce a configure script. dnl dnl Change the version number here -AC_INIT([Evolving Objects], [1.02-cvs1], [eodev-help@sourceforge.net], [eo]) -AC_PREREQ(2.53) +AC_INIT([Evolving Objects],[1.02-cvs1],[eodev-help@sourceforge.net],[eo]) +AC_PREREQ(2.60) dnl make sure we are compiling from the correct sources AC_CONFIG_SRCDIR(src/eoOp.h) @@ -14,7 +14,7 @@ AC_CANONICAL_TARGET dnl automake initialization AM_INIT_AUTOMAKE([gnu dist-bzip2 dist-zip]) -AM_CONFIG_HEADER(config.h) +AC_CONFIG_HEADERS([config.h]) dnl Checks for programs. AC_PROG_CC @@ -45,7 +45,7 @@ AC_GNUPLOT AC_TUTORIAL dnl create makefiles -AC_OUTPUT(Makefile \ +AC_CONFIG_FILES([Makefile \ app/Makefile \ app/mastermind/Makefile \ app/gprop/Makefile \ @@ -59,6 +59,7 @@ AC_OUTPUT(Makefile \ src/ga/Makefile \ src/other/Makefile \ src/utils/Makefile \ + src/moo/Makefile \ test/Makefile \ tutorial/Makefile \ tutorial/html/Makefile \ @@ -69,4 +70,5 @@ AC_OUTPUT(Makefile \ tutorial/Lesson5/Makefile \ tutorial/Templates/Makefile \ tutorial/pdf/Makefile \ - win/Makefile) + win/Makefile]) +AC_OUTPUT diff --git a/eo/src/Makefile.am b/eo/src/Makefile.am index 3ea3000f..67a75ff1 100644 --- a/eo/src/Makefile.am +++ b/eo/src/Makefile.am @@ -1,6 +1,6 @@ # Makefile.am for eo/src -SUBDIRS = es ga gp do utils other +SUBDIRS = es ga gp do utils other moo lib_LIBRARIES = libeo.a diff --git a/eo/src/moo/CMakeLists.txt b/eo/src/moo/CMakeLists.txt new file mode 100644 index 00000000..bde28898 --- /dev/null +++ b/eo/src/moo/CMakeLists.txt @@ -0,0 +1,56 @@ + +###################################################################################### +### 1) Include the sources +###################################################################################### + +INCLUDE_DIRECTORIES(${EO_SOURCE_DIR}/src) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +###################################################################################### + + +###################################################################################### +### 2) Define the ga target +###################################################################################### + +SET(EOMOO_LIB_OUTPUT_PATH ${EO_BINARY_DIR}/lib) +SET(LIBRARY_OUTPUT_PATH ${EOMOO_LIB_OUTPUT_PATH}) + +SET (EOMOO_SOURCES eoFrontSorter.cpp ) + + +ADD_LIBRARY(eomoo STATIC ${EOMOO_SOURCES}) +###################################################################################### + + + +###################################################################################### +### 3) Optionnal +###################################################################################### + +SET(EOMOO_VERSION "1.0.1") +SET_TARGET_PROPERTIES(eomoo PROPERTIES VERSION "${EOMOO_VERSION}") + +###################################################################################### + + + +###################################################################################### +### 4) Windows advanced config - especially for Microsoft Visual Studio 8 +###################################################################################### + + IF(CMAKE_CXX_COMPILER MATCHES cl) + IF(NOT WITH_SHARED_LIBS) + IF(CMAKE_GENERATOR STREQUAL "Visual Studio 8 2005") + SET(CMAKE_CXX_FLAGS "/nologo /W3 /Gy") + SET(CMAKE_CXX_FLAGS_DEBUG "/MTd /Z7 /Od") + SET(CMAKE_CXX_FLAGS_RELEASE "/MT /O2") + SET(CMAKE_CXX_FLAGS_MINSIZEREL "/MT /O2") + SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/MTd /Z7 /Od") + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:CONSOLE") + + ENDIF(CMAKE_GENERATOR STREQUAL "Visual Studio 8 2005") + ENDIF(NOT WITH_SHARED_LIBS) + ENDIF(CMAKE_CXX_COMPILER MATCHES cl) +###################################################################################### + diff --git a/eo/src/moo/Makefile.am b/eo/src/moo/Makefile.am new file mode 100644 index 00000000..a9f00c73 --- /dev/null +++ b/eo/src/moo/Makefile.am @@ -0,0 +1,9 @@ + +lib_LIBRARIES = libeomoo.a + +libeomoo_a_SOURCES = eoFrontSorter.cpp + +pkginclude_HEADERS = eoFrontSorter.h \ + eoMOFitness.h + +AM_CXXFLAGS = -I$(top_srcdir)/src diff --git a/eo/src/moo/eoFrontSorter.cpp b/eo/src/moo/eoFrontSorter.cpp new file mode 100644 index 00000000..62de42c8 --- /dev/null +++ b/eo/src/moo/eoFrontSorter.cpp @@ -0,0 +1,156 @@ +#include + +using namespace std; + +namespace detail { + +namespace { + struct CompareOn { + unsigned dim; + + CompareOn(unsigned d) : dim(d) {} + + bool operator()(const FitnessInfo& a, const FitnessInfo& b) { + return a.fitness[dim] > b.fitness[dim]; + } + + }; +} // end anonymous namespace + +void one_objective(std::vector& fitness, std::vector< std::vector >& front) +{ + std::sort(fitness.begin(), fitness.end(), CompareOn(0)); + + front.clear(); + front.resize(1); + front[0].push_back(fitness[0]); + for (unsigned i = 1; i < fitness.size(); ++i) { + if (fitness[i].fitness[0] < fitness[i-1].fitness[0]) { // keep clones in same front + front.push_back( std::vector() ); + } + + front.back().push_back( fitness[i] ); + } +} + + +/** + * Optimization for two objectives. Makes the algorithm run in + * complexity O(n log n) where n is the population size + */ + + +void two_objectives(std::vector& fitness, std::vector< std::vector >& front) +{ + std::sort(fitness.begin(), fitness.end(), CompareOn(0)); + + front.clear(); + + std::vector front_leader; + + 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)); + + if (it == front_leader.end()) { + front_leader.push_back(fitness[i]); + front.push_back( vector(1, fitness[i]) ); + } else { + *it = fitness[i]; // new 'best of front in second dimension' + front[ it - front_leader.begin() ].push_back(fitness[i]); // add to front of nth dominated solutions + } + } +} + +bool dominates(const FitnessInfo& a, const FitnessInfo& b) { + bool better_on_one = false; + + for (unsigned i = 0; i < a.fitness.size(); ++i) { + 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; + } + + return better_on_one; +} + +void m_objectives(std::vector& fitness, std::vector< std::vector >& front) { + unsigned i; + + std::vector > S(fitness.size()); // which individuals does guy i dominate + std::vector n(fitness.size()); // how many individuals dominate guy i + + unsigned j; + for (i = 0; i < fitness.size(); ++i) + { + for (j = 0; j < fitness.size(); ++j) + { + if ( dominates(fitness[i], fitness[j]) ) + { // i dominates j + S[i].push_back(j); // add j to i's domination list + } + else if (dominates(fitness[j], fitness[i])) + { // j dominates i, increment count for i, add i to the domination list of j + n[i]++; + } + } + } + + front.clear(); + front.resize(1); + // get the first front out + for (i = 0; i < fitness.size(); ++i) + { + if (n[i] == 0) + { + front.back().push_back( fitness[i] ); + } + } + + while (!front.back().empty()) + { + front.push_back(vector()); + vector& last_front = front[front.size()-2]; + + // Calculate which individuals are in the next front; + + for (i = 0; i < last_front.size(); ++i) + { + for (j = 0; j < S[ last_front[i].index ].size(); ++j) + { + unsigned dominated_individual = S[ last_front[i].index ][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 + { + front.back().push_back( fitness[dominated_individual] ); + } + } + } + } + + front.pop_back(); // last front is empty; +} + +void front_sorter_impl(std::vector& fitness, std::vector< std::vector >& front_indices) { + switch (fitness[0].fitness.size()) + { + case 1: + { + one_objective(fitness, front_indices); + return; + } + case 2: + { + two_objectives(fitness, front_indices); + return; + } + default : + { + m_objectives(fitness, front_indices); + } + } +} + +} // namespace detail + diff --git a/eo/src/moo/eoFrontSorter.h b/eo/src/moo/eoFrontSorter.h new file mode 100644 index 00000000..d96a3e90 --- /dev/null +++ b/eo/src/moo/eoFrontSorter.h @@ -0,0 +1,85 @@ +/** -*- mode: c++; c-indent-level: 4; c++-member-init-indent: 8; comment-column: 35; -*- + + ----------------------------------------------------------------------------- + eoNDSorting.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 + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Contact: todos@geneura.ugr.es, http://geneura.ugr.es + Marc.Schoenauer@polytechnique.fr + mkeijzer@dhi.dk + */ +//----------------------------------------------------------------------------- + +#ifndef eoFrontSorter_h +#define eoFrontSorter_h + +#include +#include +#include +#include + +namespace detail { +// small helper structs to store the multi-objective information. To be used in the implementation + + struct FitnessInfo { + std::vector fitness; // preprocessed fitness -> all maximizing + unsigned index; // index into population + + FitnessInfo() {} + FitnessInfo(const std::vector& fitness_, unsigned index_) : fitness(fitness_), index(index_) {} + }; + + extern void front_sorter_impl(std::vector& fitness, std::vector< std::vector >& fronts); + +} // namespace detail + +/** + * Reassembles population into a set of fronts; +*/ +template +class eoFrontSorter : public eoUF< const eoPop&, const std::vector< std::vector >& > +{ + + std::vector fitness; + std::vector< std::vector > fronts; + + public : + + typedef typename EOT::Fitness::fitness_traits Traits; + + typedef std::vector< std::vector > front_t; + + const std::vector >& operator()(const eoPop& _pop) + { + fitness.resize(_pop.size()); + for (unsigned i = 0; i < _pop.size(); ++i) { + std::vector 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); + } + + detail::front_sorter_impl(fitness, fronts); + + return fronts; + } +}; + + +#endif diff --git a/eo/src/moo/eoMOFitness.h b/eo/src/moo/eoMOFitness.h new file mode 100644 index 00000000..9ab29383 --- /dev/null +++ b/eo/src/moo/eoMOFitness.h @@ -0,0 +1,174 @@ +// -*- mode: c++; c-indent-level: 4; c++-member-init-indent: 8; comment-column: 35; -*- + +//----------------------------------------------------------------------------- +// eoMOFitness.h +// (c) Maarten Keijzer and Marc Schoenauer, 2007 +/* + 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 + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Contact: mak@dhi.dk + Marc.Schoenauer@inria.fr + */ +//----------------------------------------------------------------------------- + +#ifndef _eoMOFitness_h +#define _eoMOFitness_h + +#include +#include +#include +#include + + +/** + * eoMOFitnessTraits: a traits class to specify + * the number of objectives and which one are maximizing or not + * See test/t-eoParetoFitness for its use. + * + * If you define your own, make sure you make the functions static! +*/ +class eoMOFitnessTraits +{ + public : + + 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 +}; + +/** + 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 + can be replaces at will by any other class that implements the static functions defined therein. + + Note that the comparison defines a partial order, so that + !(a < b) && !(b +class eoMOFitness : public std::vector +{ + double worth; // used for sorting and selection, by definition, bigger is better + bool validWorth; +public : + + typedef FitnessTraits fitness_traits; + + eoMOFitness(double def = 0.0) : std::vector(FitnessTraits::nObjectives(),def), validWorth(false) {} + + // Ctr from a std::vector + eoMOFitness(std::vector & _v) : std::vector(_v), validWorth(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 & _b) {FitnessTraits::setUp(_n, _b);} + static bool maximizing(unsigned _i) { return FitnessTraits::maximizing(_i);} + + void setWorth(double worth_) { + worth = worth_; + validWorth = true; + } + + double getWorth() const { + if (!validWorth) { + throw std::runtime_error("invalid worth"); + } + return worth; + } + + /// Partial order based on Pareto-dominance + //bool operator<(const eoMOFitness& _other) const + bool dominates(const eoMOFitness& _other) const + { + bool dom = false; + + const std::vector& performance = *this; + const std::vector& 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 (aval != bval) + { + 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; + } + + /// compare *not* on dominance, but on worth + bool operator<(const eoMOFitness& _other) const + { + return getWorth() > _other.getWorth(); + } + + bool operator>(const eoMOFitness& _other) const + { + return _other < *this; + } + + bool operator<=(const eoMOFitness& _other) const + { + return getWorth() >= _other.getWorth(); + } + + bool operator>=(const eoMOFitness& _other) const + { + return _other <= *this; + } + + bool operator==(const eoMOFitness& _other) const + { // check if they're all within tolerance + return getWorth() == _other.getWorth(); + } + + bool operator!=(const eoMOFitness& _other) const + { return ! operator==(_other); } + + + friend std::ostream& operator<<(std::ostream& os, const eoMOFitness& fitness) + { + for (unsigned i = 0; i < fitness.size(); ++i) + { + os << fitness[i] << ' '; + } + os << fitness.validWorth << ' ' << fitness.worth; + return os; + } + + friend std::istream& operator>>(std::istream& is, eoMOFitness& fitness) + { + fitness = eoMOFitness(); + for (unsigned i = 0; i < fitness.size(); ++i) + { + is >> fitness[i]; + } + is >> fitness.validWorth >> fitness.worth; + return is; + } + +}; + +#endif diff --git a/eo/src/moo/eoNSGA_II_Replacement.h b/eo/src/moo/eoNSGA_II_Replacement.h new file mode 100644 index 00000000..1cafbb81 --- /dev/null +++ b/eo/src/moo/eoNSGA_II_Replacement.h @@ -0,0 +1,123 @@ +#ifndef eoNSGA_II_Replacement_h +#define eoNSGA_II_Replacement_h + +#include +#include + +/** @brief Fast Elitist Non-Dominant Sorting Genetic Algorithm + + Adapted from Deb, Agrawal, Pratab and Meyarivan: A Fast Elitist + Non-Dominant Sorting Genetic Algorithm for MultiObjective + Optimization: NSGA-II KanGAL Report No. 200001 + + Note that this class does not do the sorting per se, but the sorting + of it worth_std::vector will give the right order + +*/ +template +class eoNSGA_II_Replacement : public eoReplacement +{ + public: + + void operator()(eoPop& parents, eoPop& offspring) { + unsigned origSize = parents.size(); + + std::copy(offspring.begin(), offspring.end(), std::back_inserter(parents)); + + typename eoFrontSorter::front_t front = sorter(parents); + + for (unsigned i = 0; i < front.size(); ++i) { + assign_worths(front[i], front.size() - i, parents); + } + + // sort on worth (assuming eoMOFitness) + std::sort(parents.begin(), parents.end()); + + // truncate + parents.resize(origSize); + } + + + 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; + } + }; + + /// _cf points into the elements that consist of the current front + void assign_worths(const std::vector& front, unsigned rank, eoPop& 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_Replacement.h b/eo/src/moo/eoNSGA_IIa_Replacement.h new file mode 100644 index 00000000..b1edb425 --- /dev/null +++ b/eo/src/moo/eoNSGA_IIa_Replacement.h @@ -0,0 +1,150 @@ +#ifndef eoNSGA_IIa_Replacement_h +#define eoNSGA_IIa_Replacement_h + +#include +#include + +/** @brief Fast Elitist Non-Dominant Sorting Genetic Algorithm + + Adapted from Deb, Agrawal, Pratab and Meyarivan: A Fast Elitist + Non-Dominant Sorting Genetic Algorithm for MultiObjective + Optimization: NSGA-II KanGAL Report No. 200001 + + Note that this class does not do the sorting per se, but the sorting + of it worth_std::vector will give the right order + +*/ +template +class eoNSGA_IIa_Replacement : public eoReplacement +{ + public: + + void operator()(eoPop& parents, eoPop& offspring) { + unsigned origSize = parents.size(); + + std::copy(offspring.begin(), offspring.end(), std::back_inserter(parents)); + + typename eoFrontSorter::front_t front = sorter(parents); + + unsigned rank = parents.size(); + for (unsigned i = 0; i < front.size(); ++i) { + rank = assign_worths(front[i], rank, parents); + } + + // sort on worth (assuming eoMOFitness) + std::sort(parents.begin(), parents.end()); + + // truncate + parents.resize(origSize); + } + + private: + + 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, eoPop& 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; + } +}; + +#endif diff --git a/eo/src/moo/eoNSGA_I_Replacement.h b/eo/src/moo/eoNSGA_I_Replacement.h new file mode 100644 index 00000000..22dab6a7 --- /dev/null +++ b/eo/src/moo/eoNSGA_I_Replacement.h @@ -0,0 +1,75 @@ +#ifndef __EONDSorting_I_h +#define __EONDSorting_I_h + +#include + +/** + The original Non Dominated Sorting algorithm from Srinivas and Deb +*/ +template +class eoNSGA_I_Replacement : public eoReplacement +{ +public : + eoNSGA_I_Replacement(double _nicheSize) : nicheSize(_nicheSize) {} + + void operator()(eoPop& parents, eoPop& offspring) { + + unsigned origSize = parents.size(); + + std::copy(offspring.begin(), offspring.end(), std::back_inserter(parents)); + + typename eoFrontSorter::front_t front = sorter(parents); + + for (unsigned i = 0; i < front.size(); ++i) { + assign_worths(front[i], front.size() - i, parents); + } + + // sort on worth (assuming eoMOFitness) + std::sort(parents.begin(), parents.end()); + + // truncate + parents.resize(origSize); + } + + private: + void assign_worths(const std::vector& front, unsigned rank, eoPop& parents) { + + for (unsigned i = 0; i < front.size(); ++i) + { // calculate whether the other points lie within the nice + double niche_count = 0; + + for (unsigned j = 0; j < front.size(); ++j) + { + if (i == j) + continue; + + double dist = 0.0; + + for (unsigned k = 0; k < front[j].fitness.size(); ++k) + { + double d = front[i].fitness[k] - front[j].fitness[k]; + dist += d*d; + } + + if (dist < nicheSize) + { + niche_count += 1.0 - pow(dist / nicheSize,2.); + } + } + + unsigned idx = front[i].index; + typename EOT::Fitness f = parents[idx].fitness(); + f.setWorth(rank + niche_count); + parents[ idx ].fitness(f); + } + } + + private : + + double nicheSize; + eoFrontSorter sorter; +}; + +#endif + + diff --git a/eo/test/Makefile.am b/eo/test/Makefile.am index 9d13b2ce..87a19a0c 100644 --- a/eo/test/Makefile.am +++ b/eo/test/Makefile.am @@ -3,12 +3,12 @@ ## Makefile.am for eo/test ## ############################################################################### - LIBEO = $(top_builddir)/src/libeo.a LIBEOUTILS = $(top_builddir)/src/utils/libeoutils.a LIBES = $(top_builddir)/src/es/libes.a LIBGA = $(top_builddir)/src/ga/libga.a LIBCMA = $(top_builddir)/src/es/libcma.a +LIBMOO = $(top_builddir)/src/moo/libeomoo.a # PLEASE don't break the line (see create_batch.sh) check_PROGRAMS = t-eoParetoFitness \ @@ -41,7 +41,9 @@ check_PROGRAMS = t-eoParetoFitness \ t-eoCMAES \ t-eoSecondsElapsedContinue\ t-eoRNG \ - t-eoEasyPSO + t-eoEasyPSO \ + t-eoNSGA \ + t-eoFrontSorter TESTS = $(check_PROGRAMS) \ run_tests # This script can be used to check command-line arguments @@ -49,7 +51,7 @@ TESTS = $(check_PROGRAMS) \ noinst_HEADERS = binary_value.h real_value.h RoyalRoad.h -DEPS = $(LIBGA) $(LIBES) $(LIBCMA) $(LIBEOUTILS) $(LIBEO) +DEPS = $(LIBGA) $(LIBES) $(LIBCMA) $(LIBEOUTILS) $(LIBEO) $(LIBMOO) INCLUDES = -I$(top_srcdir)/src -I$(srcdir)/../src LIBS = $(DEPS) @@ -96,3 +98,6 @@ t_eoCMAES_SOURCES = t-eoCMAES.cpp t_eoRNG_SOURCES = t-eoRNG.cpp t_eoSecondsElapsedContinue_SOURCES = t-eoSecondsElapsedContinue.cpp t_eoEasyPSO_SOURCES = t-eoEasyPSO.cpp +t_eoNSGA_SOURCES = t-eoNSGA.cpp +t_eoFrontSorter_SOURCES = t-eoFrontSorter.cpp + diff --git a/eo/test/t-eoNSGA.cpp b/eo/test/t-eoNSGA.cpp new file mode 100644 index 00000000..79dff937 --- /dev/null +++ b/eo/test/t-eoNSGA.cpp @@ -0,0 +1,205 @@ + +#include + +#include +#include +#include +#include + +using namespace std; + +// Look: overloading the maximization without overhead (thing can be inlined) +class MinimizingFitnessTraits : public eoMOFitnessTraits +{ + public : + static double maximizing(int) { return -1; } +}; + +typedef eoMOFitness fitness_type; + +const unsigned chromsize=5; +const double minval = -15; +const double maxval = 15; + +struct eoDouble : public EO +{ + double value[chromsize]; +}; + +class Mutate : public eoMonOp +{ + bool operator()(eoDouble& _eo) + { + for (unsigned i = 0; i < chromsize; ++i) + { + if (rng.flip(1./chromsize)) + _eo.value[i] += rng.normal() * 0.1 * _eo.value[i]; + + if (_eo.value[i] < minval) + _eo.value[i] = minval; + else if (_eo.value[i] > maxval) + _eo.value[i] = maxval; + } + + return true; + } +}; + +class Eval : public eoEvalFunc +{ + void operator()(eoDouble& _eo) + { + vector x(_eo.value, _eo.value + chromsize); + fitness_type f; + + for (unsigned i = 0; i < chromsize; ++i) + { + if (i < chromsize-1) + { + f[0] += -10.0 * exp(-0.2 * sqrt(x[i]*x[i] + x[i+1]*x[i+1])); + } + + f[1] += pow(fabs(x[i]), 0.8) + 5 * pow(sin(x[i]),3.); + } + + _eo.fitness(f); + } +}; + +class Eval2 : public eoEvalFunc +{ + void operator()(eoDouble& _eo) + { + vector x(_eo.value, _eo.value + chromsize); + fitness_type f; + + for (unsigned i = 0; i < chromsize; ++i) + { + f[0] += x[i] * x[i]; + } + + f[1] = + 3 * x[0] + 2 * x[1] + - x[2]/3 + 0.01*pow(x[3] - x[4], 3); + + _eo.fitness(f); + } +}; + +class Eval3 : public eoEvalFunc +{ + void operator()(eoDouble& _eo) + { + double x = _eo.value[0]; + fitness_type f; + + f[0] = x * x; + double y = x-10; + f[1] = y * y; + + _eo.fitness(f); + } +}; + +class Init : public eoInit +{ + void operator()(eoDouble& _eo) + { + _eo.value[0] = rng.uniform(); + + double range = maxval - minval; + + for (unsigned i = 1; i < chromsize; ++i) + _eo.value[i] = rng.uniform() * range + minval; + _eo.invalidate(); + } +}; + +// Test pareto dominance and perf2worth, and while you're at it, test the eoGnuPlot monitor as well +void the_main(int argc, char* argv[]) +{ + Init init; + Eval eval; + Mutate mutate; + + eoParser parser(argc, argv); + eoState state; + + unsigned num_gen = parser.createParam(unsigned(50), "num_gen", "number of generations to run", 'g').value(); + unsigned pop_size = parser.createParam(unsigned(100), "pop_size", "population size", 'p').value(); + bool use_nsga1 = parser.createParam(false, "nsga1", "Use nsga 1").value(); + + eoPop pop(pop_size, init); + + // binary tournament selection + eoDetTournamentSelect select; + + // One general operator + eoProportionalOp opsel; + opsel.add(mutate, 1.0); + + // the breeder + eoGeneralBreeder breeder(select, opsel); + + // replacement + eoNSGA_IIa_Replacement nsga1; + eoNSGA_II_Replacement nsga2; + eoReplacement& replace = use_nsga1 ? static_cast&>(nsga1) : static_cast&>(nsga2); + + unsigned long generation = 0; + eoGenContinue gen(num_gen, generation); + eoCheckPoint cp(gen); + + eoMOFitnessStat fitness0(0, "FirstObjective"); + eoMOFitnessStat fitness1(1, "SecondObjective"); + + cp.add(fitness0); + cp.add(fitness1); + + eoGnuplot1DSnapshot snapshot("pareto"); + //snapshot.with(eoGnuplot::Points(3)); + + cp.add(snapshot); + + snapshot.add(fitness0); + snapshot.add(fitness1); + + // the algo + eoEasyEA ea(cp, eval, breeder, replace); + + if (parser.userNeedsHelp()) + { + parser.printHelp(std::cout); + return; + } + + apply(eval, pop); + + eoPop nothing; + replace(pop, nothing); // calculates worths + + ea(pop); +} + + +int main(int argc, char* argv[]) +{ + the_main(argc, argv); + return 0; + try + { + the_main(argc, argv); + } + catch (std::exception& e) + { + std::cout << "Exception thrown: " << e.what() << std::endl; + throw e; // make sure it does not pass the test + } +} + + + +// Local Variables: +// mode: C++ +// c-file-style: "Stroustrup" +// End: