From d09c216b61b1ca8f980a10342dabc3680077e139 Mon Sep 17 00:00:00 2001 From: maartenkeijzer Date: Fri, 16 Mar 2001 12:08:26 +0000 Subject: [PATCH] Ok, made an eoParetoFitness class, which meant that I could roll back a few changes in EO.h (phew). Also changed eoSelectFromWorth etc. --- eo/Makefile.am | 6 +- eo/autogen.sh | 10 +- eo/configure.in | 2 +- eo/src/EO.h | 62 +------------ eo/src/eoDominanceMap.h | 2 + eo/src/eoNDSorting.h | 142 +++++++++++++++++++++++++++-- eo/src/eoSelectFromWorth.h | 3 +- eo/src/utils/eoGnuplot1DSnapshot.h | 28 +++--- eo/test/Makefile.am | 9 +- eo/test/t-eoPareto.cpp | 58 +++++++++--- eo/win/Makefile.am | 3 +- 11 files changed, 215 insertions(+), 110 deletions(-) diff --git a/eo/Makefile.am b/eo/Makefile.am index 0dea0e13..3c2b9a8a 100644 --- a/eo/Makefile.am +++ b/eo/Makefile.am @@ -4,7 +4,7 @@ ## ############################################################################### -SUBDIRS = src test win tutorial app +SUBDIRS = src test win app tutorial #Directory for documents DOCDIR = ~/public_html/eodocs #Directory for indices -- not useful for the user @@ -17,10 +17,6 @@ EXTRA_DIST=LICENSE lib: pushd src; $(MAKE) all; popd -# The test directory should be run explicitely, to check if nothing is broken -test: test/run_tests test/Makefile - pushd test; touch run_tests; $(MAKE) all; ./run_tests; popd - # so that make doc always compiles the doc ... doc: doc/eo.cfg pushd doc; $(MAKE) doc; touch eo.cfg; popd diff --git a/eo/autogen.sh b/eo/autogen.sh index 257ad1b6..c65b0998 100755 --- a/eo/autogen.sh +++ b/eo/autogen.sh @@ -56,8 +56,8 @@ echo echo "Now type 'make' to compile $PROG." echo "And if you have Doxygen installed, type 'make doc' to generate $PROG documentation." echo -echo "WARNING: Compiling all test programs can take some time." -echo "But you don't have to: you can simply type" -echo " 'make lib'" -echo "and then go in your application dir (or in the tutorial dir)" -echo "and there type 'make'" +#echo "WARNING: Compiling all test programs can take some time." +#echo "But you don't have to: you can simply type" +#echo " 'make lib'" +#echo "and then go in your application dir (or in the tutorial dir)" +#echo "and there type 'make'" diff --git a/eo/configure.in b/eo/configure.in index c87744d8..8bb18d42 100644 --- a/eo/configure.in +++ b/eo/configure.in @@ -2,7 +2,7 @@ dnl Process this file with autoconf to produce a configure script. AC_INIT(src/eo) dnl Change the version number here -AM_INIT_AUTOMAKE(eo, 0.9.11) +AM_INIT_AUTOMAKE(eo, 0.9.2) dnl Checks for maintainer mode AM_MAINTAINER_MODE diff --git a/eo/src/EO.h b/eo/src/EO.h index 4dba3fb0..a6d32d77 100644 --- a/eo/src/EO.h +++ b/eo/src/EO.h @@ -3,7 +3,7 @@ //----------------------------------------------------------------------------- // EO.h // (c) GeNeura Team 1998 -/* +/* 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 @@ -31,61 +31,6 @@ #include // eoObject #include // eoPersistent -/// Functions for reading and writing non-scalar fitnesses, should probably go to seperate file -template -ostream& print_fitness(ostream& _os, const std::vector& vec) -{ - _os << vec.size() << ' '; - std::copy(vec.begin(), vec.end(), ostream_iterator(_os)); - return _os; -} - -/// Functions for reading and writing non-scalar fitnesses, should probably go to seperate file -template -ostream& print_fitness(ostream& _os, const std::pair& pair) -{ - return _os << pair.first << ' ' << pair.second; -} - -/// Functions for reading and writing non-scalar fitnesses, should probably go to seperate file -template -ostream& print_fitness(ostream& _os, const T& t) -{ // general, try operator<< - return _os << t; -} - -/// Functions for reading and writing non-scalar fitnesses, should probably go to seperate file -template -istream& read_fitness(istream& _is, vector& vec) -{ - unsigned sz; - _is >> sz; - vec.resize(sz); - for (unsigned i = 0; i < vec.size(); ++i) - { - _is >> vec[i]; - } - - return _is; -} - -/// Functions for reading and writing non-scalar fitnesses, should probably go to seperate file -template -istream& read_fitness(istream& _is, pair& pair) -{ - _is >> pair.first; - _is >> pair.second; - return _is; -} - -/// Functions for reading and writing non-scalar fitnesses, should probably go to seperate file -template -istream& read_fitness(istream& _is, T& t) -{ - _is >> t; - return _is; -} - //----------------------------------------------------------------------------- /** EO is a base class for evolvable objects, that is, the subjects of evolution. EOs have only got a fitness, which at the same time needs to be @@ -158,7 +103,7 @@ public: * @throw runtime_exception If a valid object can't be read. */ virtual void readFrom(istream& _is) { - read_fitness(_is, repFitness); + _is >> repFitness; if (_is) invalidFitness = false; else @@ -173,7 +118,7 @@ public: if (invalid()) _os << "INVALID "; else - print_fitness(_os, repFitness) << ' '; // trailing space to make reading in that much easier + _os << repFitness << ' '; // trailing space to make reading in that much easier } //@} @@ -186,4 +131,3 @@ private: //----------------------------------------------------------------------------- #endif EO_H - diff --git a/eo/src/eoDominanceMap.h b/eo/src/eoDominanceMap.h index b677bc48..aec4a415 100644 --- a/eo/src/eoDominanceMap.h +++ b/eo/src/eoDominanceMap.h @@ -62,6 +62,8 @@ class eoDominanceMap : public eoUF&, void>, public std::vect fitnesses.clear(); } + bool maximize(unsigned objective) const { return maximizes[objective]; } + /** Update or create the dominance map */ diff --git a/eo/src/eoNDSorting.h b/eo/src/eoNDSorting.h index 45eea008..4b139060 100644 --- a/eo/src/eoNDSorting.h +++ b/eo/src/eoNDSorting.h @@ -32,16 +32,25 @@ #include /** - Non dominated sorting + Non dominated sorting, it *is a* vector of doubles, the integer part is the rank (to which front it belongs), + the fractional part the niching penalty or distance penalty or whatever penalty you want to squeeze into + the bits. */ template class eoNDSorting : public eoPerf2Worth { public : - eoNDSorting(eoDominanceMap& _dominanceMap, double _nicheSize) : - eoPerf2Worth(), dominanceMap(_dominanceMap), nicheSize(_nicheSize) {} + eoNDSorting(eoDominanceMap& _dominanceMap) : + eoPerf2Worth(), dominanceMap(_dominanceMap) {} + /** Pure virtual function that calculates the 'distance' for each element to the current front + Implement to create your own nondominated sorting algorithm. The size of the returned vector + should be equal to the size of the current_front. + */ + virtual vector niche_penalty(const vector& current_front, const eoPop& _pop) = 0; + + /// Do the work void operator()(const eoPop& _pop) { dominanceMap(_pop); @@ -83,20 +92,20 @@ class eoNDSorting : public eoPerf2Worth } // Now we have the indices to the current front in current_front, do the niching + vector niche_count = niche_penalty(current_front, _pop); - // As I don't have my reference text with me some homespun savagery + if (niche_count.size() != current_front.size()) + { + throw logic_error("eoNDSorting: niche and front should have the same size"); + } - ranks = dominanceMap.sum_dominants(); // how many do you dominate - - double max_rank = *std::max_element(ranks.begin(), ranks.end()); + double max_niche = *max_element(niche_count.begin(), niche_count.end()); for (unsigned i = 0; i < current_front.size(); ++i) { - // punish the ones that dominate the most individuals (sounds strange huh?) - value()[current_front[i]] = dominance_level + ranks[i] / (max_rank + 1); + value()[current_front[i]] = dominance_level + niche_count[i] / (max_niche + 1); } - dominance_level++; // go to the next front } @@ -110,10 +119,123 @@ class eoNDSorting : public eoPerf2Worth } + const eoDominanceMap& map() const; + private : eoDominanceMap& dominanceMap; +}; + +/** + The original Non Dominated Sorting algorithm from Srinivas and Deb +*/ +template +class eoNDSorting_I : public eoNDSorting +{ +public : + eoNDSorting_I(eoDominanceMap& _map, double _nicheSize) : eoNDSorting(_map), nicheSize(_nicheSize) {} + + vector niche_penalty(const vector& current_front, const eoPop& _pop) + { + vector niche_count(current_front.size(), 0.); + + for (unsigned i = 0; i < current_front.size(); ++i) + { // calculate whether the other points lie within the nice + for (unsigned j = 0; j < current_front.size(); ++j) + { + if (i == j) + continue; + + double dist = 0.0; + + for (unsigned k = 0; k < _pop[current_front[j]].fitness().size(); ++k) + { + double d = _pop[current_front[i]].fitness()[k] - _pop[current_front[j]].fitness()[k]; + dist += d*d; + } + + if (dist < nicheSize) + { + niche_count[i] += 1.0 - pow(dist / nicheSize,2.); + } + } + } + + return niche_count; + } + + private : + double nicheSize; }; +/** + 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_vector will give the right order + + The crowding distance is calculated as the sum of the distances to the nearest neighbours. As we need to return the + penalty value, we have to invert that and invert it again in the base class, but such is life, sigh +*/ +template +class eoNDSorting_II : public eoNDSorting +{ + public: + eoNDSorting_II(eoDominanceMap& _map) : eoNDSorting(_map) {} + + 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; + } + }; + + vector niche_penalty(const vector& _cf, const eoPop& _pop) + { + vector niche_count(_cf.size(), 0.); + + unsigned nObjectives = _pop[_cf[0]].fitness().size(); + + for (unsigned o = 0; o < nObjectives; ++o) + { + + vector > performance(_cf.size()); + for (unsigned i =0; i < _cf.size(); ++i) + { + performance[i].first = _pop[_cf[i]].fitness()[o]; + performance[i].second = i; + } + + sort(performance.begin(), performance.end(), compare_nodes()); // a lambda operator would've been nice here + + vector nc(niche_count.size(), 0.0); + + for (unsigned i = 1; i < _cf.size()-1; ++i) + { // and yet another level of indirection + nc[performance[i].second] = performance[i+1].first - performance[i-1].first; + } + + double max_dist = *max_element(nc.begin(), nc.end()); + + // set boundary penalty at 0 (so it will get chosen over all the others + nc[performance[0].second] = 0; + nc[performance.back().second] = 0; + + for (unsigned i = 0; i < nc.size(); ++i) + { + niche_count[i] += (max_dist + 1) - nc[i]; + } + } + + return niche_count; + } + + +}; + #endif \ No newline at end of file diff --git a/eo/src/eoSelectFromWorth.h b/eo/src/eoSelectFromWorth.h index 622f4bb9..45751f3a 100644 --- a/eo/src/eoSelectFromWorth.h +++ b/eo/src/eoSelectFromWorth.h @@ -74,8 +74,7 @@ protected: eoPerf2Worth & perf2Worth; #ifndef NDEBUG - vector fitness; // for debugging purposes, to check that the perf2worth and pop are in sync - + std::vector fitness; void check_sync(unsigned index, const EOT& _eo) { if (fitness[index] != _eo.fitness()) diff --git a/eo/src/utils/eoGnuplot1DSnapshot.h b/eo/src/utils/eoGnuplot1DSnapshot.h index d8e575a5..efb9c323 100644 --- a/eo/src/utils/eoGnuplot1DSnapshot.h +++ b/eo/src/utils/eoGnuplot1DSnapshot.h @@ -55,18 +55,20 @@ class eoGnuplot1DSnapshot: public eoFileSnapshot, public eoGnuplot { public: // Ctor - eoGnuplot1DSnapshot(std::string _dirname, unsigned _frequency = 1, - std::string _filename = "gen", std::string _delim = " ") : - eoFileSnapshot(_dirname, _frequency, _filename, _delim), - eoGnuplot(_filename,"set data style points") + eoGnuplot1DSnapshot(std::string _dirname, unsigned _frequency = 1, + std::string _filename = "gen", std::string _delim = " ") : + eoFileSnapshot(_dirname, _frequency, _filename, _delim), + eoGnuplot(_filename,"set data style points"), + pointSize(5) {} // Ctor - eoGnuplot1DSnapshot(eoFileSnapshot & _fSnapshot) : - eoFileSnapshot(_fSnapshot), - eoGnuplot(_fSnapshot.baseFileName(),"set data style points") + eoGnuplot1DSnapshot(eoFileSnapshot & _fSnapshot) : + eoFileSnapshot(_fSnapshot), + eoGnuplot(_fSnapshot.baseFileName(),"set data style points"), + pointSize(5) {} - + // Dtor virtual ~eoGnuplot1DSnapshot(){} @@ -75,10 +77,12 @@ class eoGnuplot1DSnapshot: public eoFileSnapshot, public eoGnuplot /// Class name. virtual string className() const { return "eoGnuplot1DSnapshot"; } -private: + unsigned pointSize; +private: + }; -// the following should be placed in a separate eoGnuplot1DMonitor.cpp +// the following should be placed in a separate eoGnuplot1DMonitor.cpp //////////////////////////////////////////////////////////// inline eoMonitor& eoGnuplot1DSnapshot::operator() (void) @@ -96,12 +100,12 @@ inline eoMonitor& eoGnuplot1DSnapshot::operator() (void) os << "plot"; os << " '" << getFileName().c_str() << - "' notitle with points ps 5" ; + "' notitle with points ps " << pointSize ; os << "\n"; os << '\0'; PipeComSend( gpCom, buff ); return (*this); } - + #endif _eoGnuplot1DSnapshot_H diff --git a/eo/test/Makefile.am b/eo/test/Makefile.am index 692bf4d0..55783992 100644 --- a/eo/test/Makefile.am +++ b/eo/test/Makefile.am @@ -13,9 +13,9 @@ LDADDS = $(top_builddir)/src/utils/libeoutils.a $(top_builddir)/src/libeo.a CXXFLAGS = -g -Wall ############################################################################### -check_PROGRAMS = t-eoPareto t-eofitness t-eoRandom t-eobin t-eoStateAndParser t-eoCheckpointing t-eoSSGA \ +check_PROGRAMS = t-eoParetoFitness t-eoPareto t-eofitness t-eoRandom t-eobin t-eoStateAndParser t-eoCheckpointing t-eoSSGA \ t-eoExternalEO t-eoSymreg t-eo t-eoReplacement t-eoSelect t-eoGenOp t-eoGA t-eoVector -TESTS=run_tests t-eoVector t-eoRandom t-eoSSGA t-eoPareto +TESTS=run_tests t-eoVector t-eoRandom t-eoSSGA t-eoPareto t-eoParetoFitness # removing temporarily t-eoESFull #noinst_PROGRAMS = t-eofitness t-eobin t-eoStateAndParser t-eoCheckpointing t-eoExternalEO t-eoESFull t-eoSymreg t-eo t-eoReplacement t-eoSelect t-eoGenOp t-eoGA @@ -128,3 +128,8 @@ t_eoPareto_LDFLAGS = -lm t_eoPareto_LDADD = $(LDADDS) ############################################################################### +t_eoParetoFitness_SOURCES = t-eoParetoFitness.cpp +t_eoParetoFitness_DEPENDENCIES = $(DEPS) $(top_builddir)/src/ga/libga.a +t_eoParetoFitness_LDFLAGS = -lm +t_eoParetoFitness_LDADD = $(LDADDS) +############################################################################### diff --git a/eo/test/t-eoPareto.cpp b/eo/test/t-eoPareto.cpp index f646e592..08a36732 100644 --- a/eo/test/t-eoPareto.cpp +++ b/eo/test/t-eoPareto.cpp @@ -3,20 +3,42 @@ //#include #include +#include using namespace std; -typedef vector fitness_type; + +class MinimizingFitnessTraits : public eoParetoFitnessTraits +{ + public : + static bool maximizing(int) { return false; } +}; + +typedef eoParetoFitness fitness_type; + +const unsigned chromsize=3; +const double minval = -5; +const double maxval = 5; struct eoDouble : public EO { - double value; + double value[chromsize]; }; class Mutate : public eoMonOp { bool operator()(eoDouble& _eo) { - _eo.value += rng.normal() * 0.1 * _eo.value; + for (unsigned i = 0; i < chromsize; ++i) + { + if (rng.flip(1./10.)) + _eo.value[i] += rng.normal() * 0.05 * _eo.value[i]; + + if (_eo.value[i] < minval) + _eo.value[i] = minval; + else if (_eo.value[i] > maxval) + _eo.value[i] = maxval; + } + return true; } }; @@ -25,10 +47,18 @@ class Eval : public eoEvalFunc { void operator()(eoDouble& _eo) { - double v = _eo.value; - fitness_type f(2); - f[1] = v * v; - f[0] = (v - 1.) * (v - 1.); + 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); } @@ -38,7 +68,10 @@ class Init : public eoInit { void operator()(eoDouble& _eo) { - _eo.value = rng.normal() * 10.; + _eo.value[0] = rng.uniform(); + + for (unsigned i = 1; i < chromsize; ++i) + _eo.value[i] = rng.uniform() * 10. - 5; _eo.invalidate(); } }; @@ -51,8 +84,8 @@ void the_main() Eval eval; Mutate mutate; - unsigned num_gen = 10; - unsigned pop_size = 50; + unsigned num_gen = 500; + unsigned pop_size = 100; eoPop pop(pop_size, init); vector maximizes(2, false); // minimize both objectives @@ -62,7 +95,8 @@ void the_main() // Pareto ranking needs a dominance map //eoParetoRanking perf2worth(dominance); - eoNDSorting perf2worth(dominance, 0.0); + //eoNDSorting_I perf2worth(dominance, 0.5); + eoNDSorting_II perf2worth(dominance); // Three selectors eoDetTournamentWorthSelect select1(perf2worth, 3); @@ -78,7 +112,7 @@ void the_main() eoGeneralBreeder breeder2(select2, opsel); eoGeneralBreeder breeder3(select3, opsel); - // Comma replacement + // replacement eoCommaReplacement replace; unsigned long generation = 0; diff --git a/eo/win/Makefile.am b/eo/win/Makefile.am index eab6c1f0..33d08e38 100644 --- a/eo/win/Makefile.am +++ b/eo/win/Makefile.am @@ -1,5 +1,4 @@ -EXTRA_DIST=eo_win.dsw eo.dsp esfull.dsp t_eobin.dsp t_eoCheckpointing.dsp t_eofitness.dsp \ - t_eoFunctor.dsp t_externalEO.dsp t_StateAndParser.dsp t_eoSymreg.dsp +EXTRA_DIST=eo.dsw eo.dsp