added new multi-objective support
This commit is contained in:
parent
283026ed5c
commit
553586d55f
12 changed files with 1049 additions and 9 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
56
eo/src/moo/CMakeLists.txt
Normal file
56
eo/src/moo/CMakeLists.txt
Normal file
|
|
@ -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)
|
||||
######################################################################################
|
||||
|
||||
9
eo/src/moo/Makefile.am
Normal file
9
eo/src/moo/Makefile.am
Normal file
|
|
@ -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
|
||||
156
eo/src/moo/eoFrontSorter.cpp
Normal file
156
eo/src/moo/eoFrontSorter.cpp
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
#include <moo/eoFrontSorter.h>
|
||||
|
||||
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<FitnessInfo>& fitness, std::vector< std::vector<FitnessInfo> >& 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<FitnessInfo>() );
|
||||
}
|
||||
|
||||
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<FitnessInfo>& fitness, std::vector< std::vector<FitnessInfo> >& front)
|
||||
{
|
||||
std::sort(fitness.begin(), fitness.end(), CompareOn(0));
|
||||
|
||||
front.clear();
|
||||
|
||||
std::vector<FitnessInfo> front_leader;
|
||||
|
||||
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));
|
||||
|
||||
if (it == front_leader.end()) {
|
||||
front_leader.push_back(fitness[i]);
|
||||
front.push_back( vector<FitnessInfo>(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<FitnessInfo>& fitness, std::vector< std::vector<FitnessInfo> >& front) {
|
||||
unsigned i;
|
||||
|
||||
std::vector<std::vector<unsigned> > S(fitness.size()); // which individuals does guy i dominate
|
||||
std::vector<unsigned> 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<FitnessInfo>());
|
||||
vector<FitnessInfo>& 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<FitnessInfo>& fitness, std::vector< std::vector<FitnessInfo> >& 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
|
||||
|
||||
85
eo/src/moo/eoFrontSorter.h
Normal file
85
eo/src/moo/eoFrontSorter.h
Normal file
|
|
@ -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 <EO.h>
|
||||
#include <algorithm>
|
||||
#include <eoPop.h>
|
||||
#include <cassert>
|
||||
|
||||
namespace detail {
|
||||
// small helper structs to store the multi-objective information. To be used in the implementation
|
||||
|
||||
struct FitnessInfo {
|
||||
std::vector<double> fitness; // preprocessed fitness -> all maximizing
|
||||
unsigned index; // index into population
|
||||
|
||||
FitnessInfo() {}
|
||||
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);
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* Reassembles population into a set of fronts;
|
||||
*/
|
||||
template <class EOT>
|
||||
class eoFrontSorter : public eoUF< const eoPop<EOT>&, const std::vector< std::vector<detail::FitnessInfo> >& >
|
||||
{
|
||||
|
||||
std::vector<detail::FitnessInfo > fitness;
|
||||
std::vector< std::vector<detail::FitnessInfo> > fronts;
|
||||
|
||||
public :
|
||||
|
||||
typedef typename EOT::Fitness::fitness_traits Traits;
|
||||
|
||||
typedef std::vector< std::vector<detail::FitnessInfo> > front_t;
|
||||
|
||||
const std::vector<std::vector<detail::FitnessInfo> >& operator()(const eoPop<EOT>& _pop)
|
||||
{
|
||||
fitness.resize(_pop.size());
|
||||
for (unsigned i = 0; i < _pop.size(); ++i) {
|
||||
std::vector<double> 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
|
||||
174
eo/src/moo/eoMOFitness.h
Normal file
174
eo/src/moo/eoMOFitness.h
Normal file
|
|
@ -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 <math.h>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
/**
|
||||
* 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 <a) does not neccessarily imply that (a==b)
|
||||
The other way around does hold.
|
||||
*/
|
||||
template <class FitnessTraits = eoMOFitnessTraits>
|
||||
class eoMOFitness : public std::vector<double>
|
||||
{
|
||||
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<double>(FitnessTraits::nObjectives(),def), validWorth(false) {}
|
||||
|
||||
// Ctr from a std::vector<double>
|
||||
eoMOFitness(std::vector<double> & _v) : std::vector<double>(_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<bool> & _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<FitnessTraits>& _other) const
|
||||
bool dominates(const eoMOFitness<FitnessTraits>& _other) const
|
||||
{
|
||||
bool dom = false;
|
||||
|
||||
const std::vector<double>& performance = *this;
|
||||
const std::vector<double>& 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<FitnessTraits>& _other) const
|
||||
{
|
||||
return getWorth() > _other.getWorth();
|
||||
}
|
||||
|
||||
bool operator>(const eoMOFitness<FitnessTraits>& _other) const
|
||||
{
|
||||
return _other < *this;
|
||||
}
|
||||
|
||||
bool operator<=(const eoMOFitness<FitnessTraits>& _other) const
|
||||
{
|
||||
return getWorth() >= _other.getWorth();
|
||||
}
|
||||
|
||||
bool operator>=(const eoMOFitness<FitnessTraits>& _other) const
|
||||
{
|
||||
return _other <= *this;
|
||||
}
|
||||
|
||||
bool operator==(const eoMOFitness<FitnessTraits>& _other) const
|
||||
{ // check if they're all within tolerance
|
||||
return getWorth() == _other.getWorth();
|
||||
}
|
||||
|
||||
bool operator!=(const eoMOFitness<FitnessTraits>& _other) const
|
||||
{ return ! operator==(_other); }
|
||||
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const eoMOFitness<FitnessTraits>& 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<FitnessTraits>& fitness)
|
||||
{
|
||||
fitness = eoMOFitness<FitnessTraits>();
|
||||
for (unsigned i = 0; i < fitness.size(); ++i)
|
||||
{
|
||||
is >> fitness[i];
|
||||
}
|
||||
is >> fitness.validWorth >> fitness.worth;
|
||||
return is;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
123
eo/src/moo/eoNSGA_II_Replacement.h
Normal file
123
eo/src/moo/eoNSGA_II_Replacement.h
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
#ifndef eoNSGA_II_Replacement_h
|
||||
#define eoNSGA_II_Replacement_h
|
||||
|
||||
#include <moo/eoFrontSorter.h>
|
||||
#include <eoReplacement.h>
|
||||
|
||||
/** @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 EOT>
|
||||
class eoNSGA_II_Replacement : public eoReplacement<EOT>
|
||||
{
|
||||
public:
|
||||
|
||||
void operator()(eoPop<EOT>& parents, eoPop<EOT>& offspring) {
|
||||
unsigned origSize = parents.size();
|
||||
|
||||
std::copy(offspring.begin(), offspring.end(), std::back_inserter(parents));
|
||||
|
||||
typename eoFrontSorter<EOT>::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<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;
|
||||
}
|
||||
};
|
||||
|
||||
/// _cf points into the elements that consist of the current front
|
||||
void assign_worths(const std::vector<detail::FitnessInfo>& front, unsigned rank, eoPop<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
|
||||
150
eo/src/moo/eoNSGA_IIa_Replacement.h
Normal file
150
eo/src/moo/eoNSGA_IIa_Replacement.h
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
#ifndef eoNSGA_IIa_Replacement_h
|
||||
#define eoNSGA_IIa_Replacement_h
|
||||
|
||||
#include <moo/eoFrontSorter.h>
|
||||
#include <eoReplacement.h>
|
||||
|
||||
/** @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 EOT>
|
||||
class eoNSGA_IIa_Replacement : public eoReplacement<EOT>
|
||||
{
|
||||
public:
|
||||
|
||||
void operator()(eoPop<EOT>& parents, eoPop<EOT>& offspring) {
|
||||
unsigned origSize = parents.size();
|
||||
|
||||
std::copy(offspring.begin(), offspring.end(), std::back_inserter(parents));
|
||||
|
||||
typename eoFrontSorter<EOT>::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<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, eoPop<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;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
75
eo/src/moo/eoNSGA_I_Replacement.h
Normal file
75
eo/src/moo/eoNSGA_I_Replacement.h
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
#ifndef __EONDSorting_I_h
|
||||
#define __EONDSorting_I_h
|
||||
|
||||
#include <moo/eoFrontSorter.h>
|
||||
|
||||
/**
|
||||
The original Non Dominated Sorting algorithm from Srinivas and Deb
|
||||
*/
|
||||
template <class EOT>
|
||||
class eoNSGA_I_Replacement : public eoReplacement<EOT>
|
||||
{
|
||||
public :
|
||||
eoNSGA_I_Replacement(double _nicheSize) : nicheSize(_nicheSize) {}
|
||||
|
||||
void operator()(eoPop<EOT>& parents, eoPop<EOT>& offspring) {
|
||||
|
||||
unsigned origSize = parents.size();
|
||||
|
||||
std::copy(offspring.begin(), offspring.end(), std::back_inserter(parents));
|
||||
|
||||
typename eoFrontSorter<EOT>::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<detail::FitnessInfo>& front, unsigned rank, eoPop<EOT>& 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<EOT> sorter;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
205
eo/test/t-eoNSGA.cpp
Normal file
205
eo/test/t-eoNSGA.cpp
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
|
||||
#include <eo>
|
||||
|
||||
#include <moo/eoMOFitness.h>
|
||||
#include <moo/eoNSGA_I_Replacement.h>
|
||||
#include <moo/eoNSGA_II_Replacement.h>
|
||||
#include <moo/eoNSGA_IIa_Replacement.h>
|
||||
|
||||
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<MinimizingFitnessTraits> fitness_type;
|
||||
|
||||
const unsigned chromsize=5;
|
||||
const double minval = -15;
|
||||
const double maxval = 15;
|
||||
|
||||
struct eoDouble : public EO<fitness_type>
|
||||
{
|
||||
double value[chromsize];
|
||||
};
|
||||
|
||||
class Mutate : public eoMonOp<eoDouble>
|
||||
{
|
||||
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<eoDouble>
|
||||
{
|
||||
void operator()(eoDouble& _eo)
|
||||
{
|
||||
vector<double> 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<eoDouble>
|
||||
{
|
||||
void operator()(eoDouble& _eo)
|
||||
{
|
||||
vector<double> 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<eoDouble>
|
||||
{
|
||||
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<eoDouble>
|
||||
{
|
||||
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<eoDouble> pop(pop_size, init);
|
||||
|
||||
// binary tournament selection
|
||||
eoDetTournamentSelect<eoDouble> select;
|
||||
|
||||
// One general operator
|
||||
eoProportionalOp<eoDouble> opsel;
|
||||
opsel.add(mutate, 1.0);
|
||||
|
||||
// the breeder
|
||||
eoGeneralBreeder<eoDouble> breeder(select, opsel);
|
||||
|
||||
// replacement
|
||||
eoNSGA_IIa_Replacement<eoDouble> nsga1;
|
||||
eoNSGA_II_Replacement<eoDouble> nsga2;
|
||||
eoReplacement<eoDouble>& replace = use_nsga1 ? static_cast<eoReplacement<eoDouble>&>(nsga1) : static_cast<eoReplacement<eoDouble>&>(nsga2);
|
||||
|
||||
unsigned long generation = 0;
|
||||
eoGenContinue<eoDouble> gen(num_gen, generation);
|
||||
eoCheckPoint<eoDouble> cp(gen);
|
||||
|
||||
eoMOFitnessStat<eoDouble> fitness0(0, "FirstObjective");
|
||||
eoMOFitnessStat<eoDouble> 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<eoDouble> ea(cp, eval, breeder, replace);
|
||||
|
||||
if (parser.userNeedsHelp())
|
||||
{
|
||||
parser.printHelp(std::cout);
|
||||
return;
|
||||
}
|
||||
|
||||
apply<eoDouble>(eval, pop);
|
||||
|
||||
eoPop<eoDouble> 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:
|
||||
Reference in a new issue