This repository has been archived on 2026-03-28. You can view files and clone it, but you cannot make any changes to its state, such as pushing and creating new issues, pull requests or comments.
eodev/eo/src/moo/eoMOFitness.h
2007-09-10 10:30:53 +00:00

298 lines
9.3 KiB
C++

// -*- 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>
#include <limits>
/**
* eoMOFitnessTraits: a traits class to specify
* the number of objectives and which one are direction or not
* See test/t-eoParetoFitness for its use.
*
* If you define your own, make sure you make the functions static!
*/
class eoMOFitnessTraits
{
public :
/// Number of Objectives
static unsigned nObjectives() { return 2; }
/// by default: all are maximizing, zero will lead to ignored fitness, negative to minimization for that objective
static double direction(unsigned which) { return 1; }
/// tolerance for dominance check (if within this tolerance objective values are considered equal)
static double tol() { return 1e-6; }
};
namespace dominance {
template <class Traits>
inline double worst_possible_value(unsigned objective) {
double dir = Traits::direction(objective);
if (dir == 0.) return 0.0;
if (dir < 0.) return std::numeric_limits<double>::infinity();
return -std::numeric_limits<double>::infinity();
}
template <class Traits>
inline double best_possible_value(unsigned objective) {
return -worst_possible_value<Traits>(objective);
}
enum dominance_result { non_dominated_equal, first, second, non_dominated };
template <class FitnessDirectionTraits>
inline dominance_result check(const std::vector<double>& p1, const std::vector<double>& p2, double tolerance) {
bool all_equal = true;
bool a_better_in_one = false;
bool b_better_in_one = false;
for (unsigned i = 0; i < p1.size(); ++i) {
double maxim = FitnessDirectionTraits::direction(i);
double aval = maxim * p1[i];
double bval = maxim * p2[i];
if ( fabs(aval-bval) > tolerance ) {
all_equal = false;
if (aval > bval) {
a_better_in_one = true;
} else {
b_better_in_one = true;
}
// check if we need to go on
if (a_better_in_one && b_better_in_one) return non_dominated;
}
}
if (all_equal) return non_dominated_equal;
if (a_better_in_one) return first;
// else b dominates a (check for non-dominance done above
return second;
}
template <class IntType>
inline dominance_result check_discrete(const std::vector<IntType>& a, const std::vector<IntType>& b) {
bool all_equal = true;
bool a_better_in_one = false;
bool b_better_in_one = false;
for (unsigned i = 0; i < a.size(); ++i) {
if ( a[i] != b[i] ) {
all_equal = false;
if (a[i] > b[i]) {
a_better_in_one = true;
} else {
b_better_in_one = true;
}
// check if we need to go on
if (a_better_in_one && b_better_in_one) return non_dominated;
}
}
if (all_equal) return non_dominated_equal;
if (a_better_in_one) return first;
// else b dominates a (check for non-dominance done above
return second;
}
template <class FitnessTraits>
inline dominance_result check(const std::vector<double>& p1, const std::vector<double>& p2) {
return check<FitnessTraits>(p1, p2, FitnessTraits::tol());
}
} // namespace dominance
/**
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 worthValid;
public :
typedef FitnessTraits fitness_traits;
eoMOFitness() : std::vector<double>(FitnessTraits::nObjectives()), worthValid(false) { reset(); }
explicit eoMOFitness(double def) : std::vector<double>(FitnessTraits::nObjectives(),def), worthValid(false) {}
// Ctr from a std::vector<double>
explicit eoMOFitness(std::vector<double> & _v) : std::vector<double>(_v), worthValid(false) {}
virtual ~eoMOFitness() {} // in case someone wants to subclass
eoMOFitness(const eoMOFitness<FitnessTraits>& org) { operator=(org); }
eoMOFitness<FitnessTraits>& operator=(const eoMOFitness<FitnessTraits>& org) {
std::vector<double>::operator=(org);
worth = org.worth;
worthValid = org.worthValid;
return *this;
}
void reset() {
for (unsigned i = 0; i < size(); ++i) {
this->operator[](i) = dominance::worst_possible_value<FitnessTraits>(i);
}
}
// Make the objective 'feasible' by setting it to the best possible value
void setFeasible(unsigned objective) { this->operator[](objective) = dominance::best_possible_value<FitnessTraits>(objective); }
void setWorth(double worth_) {
worth = worth_;
worthValid = true;
}
double getWorth() const {
if (!worthValid) {
throw std::runtime_error("invalid worth");
}
return worth;
}
bool validWorth() const { return worthValid; }
void invalidateWorth() { worthValid = false; }
/// Check on dominance: returns 0 if non-dominated, 1 if this dominates other, -1 if other dominates this
int check_dominance(const eoMOFitness<FitnessTraits>& _other) const
{
dominance::dominance_result dom = dominance::check<FitnessTraits>(*this, _other);
return dom == dominance::first? 1 : (dom == dominance::second? -1 : 0);
}
/// normalized fitness: all maximizing, removed the irrelevant ones
std::vector<double> normalized() const {
std::vector<double> f;
for (unsigned j = 0; j < FitnessTraits::nObjectives(); ++j) {
if (FitnessTraits::direction(j) != 0) {
f.push_back( FitnessTraits::direction(j) * this->operator[](j));
}
}
return f;
}
/// returns true if this dominates other
bool dominates(const eoMOFitness<FitnessTraits>& _other) const
{
return check_dominance(_other) == 1;
}
/// 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.worthValid << ' ' << 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.worthValid >> fitness.worth;
return is;
}
};
namespace dominance {
template <class FitnessTraits>
inline dominance_result check(const eoMOFitness<FitnessTraits>& p1, const eoMOFitness<FitnessTraits>& p2) {
return check<FitnessTraits>(p1, p2, FitnessTraits::tol());
}
}
#endif