Allow scalar init of dual fitness; add a pop splitter

Scalar init of a dual fitness is dangerous, thus adds an explicit security against use of a partially initialized
object.
Use the pop splitter in the dual stat switch and in the MOEO dual fitness assignment.
This commit is contained in:
Johann Dreo 2013-06-13 09:45:29 +02:00
commit 4af7f3d1bc
2 changed files with 124 additions and 75 deletions

View file

@ -74,6 +74,25 @@ protected:
//! Flag that marks if the individual is feasible //! Flag that marks if the individual is feasible
bool _is_feasible; bool _is_feasible;
/** Flag to prevent partial initialization
*
* The reason behind the use of this flag is a bit complicated.
* Normally, we would not want to allow initialization on a scalar.
* But in MOEO, this would necessitate to re-implement most of the
* operator computing metrics, as they expect generic scalars.
*
* As this would be too much work, we use derived metric classes and
* overload them so that they initialize dual fitnesses with the
* feasibility flag. But the compiler still must compile the base
* methods, that use the scalar interface.
*
* Thus, eoDualFitness has a scalar interface, but this flag add a
* security against partial initialization. In DEBUG mode, asserts
* will fail if the feasibility has not been explicitly initialized
* at runtime.
*/
bool _feasible_init;
public: public:
//! Empty initialization //! Empty initialization
@ -82,58 +101,71 @@ public:
*/ */
eoDualFitness() : eoDualFitness() :
_value(0.0), _value(0.0),
_is_feasible(false) _is_feasible(false),
_feasible_init(false)
{} {}
//! Initialization with only the value, the fitness will be unfeasible. //! Initialization with only the value, the fitness will be unfeasible.
/*! /*!
* WARNING: this is what is used when you initialize a new fitness from a double. * WARNING: this is what is used when you initialize a new fitness from a double.
* Unfeasible by default * If you use this interface, you MUST set the feasibility BEFORE
* asking for it or the value. Or else, an assert will fail in debug mode.
*/ */
template<class T> template<class T>
eoDualFitness( T value ) : eoDualFitness( T value ) :
_value(value), _value(value),
_is_feasible(false) _is_feasible(false),
_feasible_init(false)
{ {
assert( _value == 0 );
} }
//! Copy constructor //! Copy constructor
eoDualFitness(const eoDualFitness& other) : eoDualFitness(const eoDualFitness& other) :
_value(other._value), _value(other._value),
_is_feasible(other._is_feasible) _is_feasible(other._is_feasible),
_feasible_init(true)
{} {}
//! Constructor from explicit value/feasibility //! Constructor from explicit value/feasibility
eoDualFitness(const BaseType& v, const bool& is_feasible) : eoDualFitness(const BaseType& v, const bool& is_feasible) :
_value(v), _value(v),
_is_feasible(is_feasible) _is_feasible(is_feasible),
_feasible_init(true)
{} {}
//! From a std::pair (first element is the value, second is the feasibility) //! From a std::pair (first element is the value, second is the feasibility)
eoDualFitness(const std::pair<BaseType,bool>& dual) : eoDualFitness(const std::pair<BaseType,bool>& dual) :
_value(dual.first), _value(dual.first),
_is_feasible(dual.second) _is_feasible(dual.second),
_feasible_init(true)
{} {}
// FIXME is it a good idea to include implicit conversion here?
/** Conversion operator: it permits to use a fitness instance as its scalar /** Conversion operator: it permits to use a fitness instance as its scalar
* type, if needed. For example, this is possible: * type, if needed. For example, this is possible:
* eoDualFitness<double,std::less<double> > fit; * eoDualFitness<double,std::less<double> > fit;
* double val = 1.0; * double val = 1.0;
* val = fit; * val = fit;
*/ */
operator BaseType(void) const { return _value; } operator BaseType(void) const { return _value; }
inline bool is_feasible() const inline bool is_feasible() const
{ {
assert( _feasible_init );
return _is_feasible; return _is_feasible;
} }
//! Explicitly set the feasibility. Useful if you have used previously the instantiation on a single scalar.
inline void is_feasible( bool feasible )
{
this->is_feasible( feasible );
this->_feasible_init = true;
}
inline BaseType value() const inline BaseType value() const
{ {
assert( _feasible_init );
return _value; return _value;
} }
@ -141,7 +173,7 @@ public:
eoDualFitness& operator=( const std::pair<BaseType, bool>& v ) eoDualFitness& operator=( const std::pair<BaseType, bool>& v )
{ {
this->_value = v.first; this->_value = v.first;
this->_is_feasible = v.second; this->is_feasible( v.second );
return *this; return *this;
} }
@ -151,21 +183,20 @@ public:
{ {
if (this != &other) { if (this != &other) {
this->_value = other._value; this->_value = other._value;
this->_is_feasible = other._is_feasible; this->is_feasible( other.is_feasible() );
} }
return *this; return *this;
} }
/*
//! Copy operator from a scalar //! Copy operator from a scalar
template<class T> template<class T>
eoDualFitness& operator=(const T v) eoDualFitness& operator=(const T v)
{ {
this->_value = v; this->_value = v;
this->_is_feasible = false; this->_is_feasible = false;
this->_feasible_init = false;
return *this; return *this;
} }
*/
//! Comparison that separate feasible individuals from unfeasible ones. Feasible are always better //! Comparison that separate feasible individuals from unfeasible ones. Feasible are always better
/*! /*!
@ -178,11 +209,11 @@ public:
// am I better (less, by default) than the other ? // am I better (less, by default) than the other ?
// if I'm feasible and the other is not // if I'm feasible and the other is not
if( this->_is_feasible && !other._is_feasible ) { if( this->is_feasible() && !other.is_feasible() ) {
// no, the other has a better fitness // no, the other has a better fitness
return false; return false;
} else if( !this->_is_feasible && other._is_feasible ) { } else if( !this->is_feasible() && other.is_feasible() ) {
// yes, a feasible fitness is always better than an unfeasible one // yes, a feasible fitness is always better than an unfeasible one
return true; return true;
@ -322,7 +353,7 @@ public:
friend friend
std::ostream& operator<<( std::ostream& os, const eoDualFitness<BaseType,Compare> & fitness ) std::ostream& operator<<( std::ostream& os, const eoDualFitness<BaseType,Compare> & fitness )
{ {
os << fitness._value << " " << fitness._is_feasible; os << fitness._value << " " << fitness.is_feasible();
return os; return os;
} }
@ -337,7 +368,7 @@ public:
is >> feasible; is >> feasible;
fitness._value = value; fitness._value = value;
fitness._is_feasible = feasible; fitness.is_feasible( feasible );
return is; return is;
} }
}; };
@ -355,18 +386,72 @@ template< class EOT>
bool eoIsFeasible ( const EOT & sol ) { return sol.fitness().is_feasible(); } bool eoIsFeasible ( const EOT & sol ) { return sol.fitness().is_feasible(); }
/** Separate the population into two: one with only feasible individuals, the other with unfeasible ones.
*/
template<class EOT>
class eoDualPopSplit : public eoUF<const eoPop<EOT>&, void>
{
protected:
eoPop<EOT> _pop_feasible;
eoPop<EOT> _pop_unfeasible;
public:
//! Split the pop and keep them in members
void operator()( const eoPop<EOT>& pop )
{
_pop_feasible.clear();
_pop_feasible.reserve(pop.size());
_pop_unfeasible.clear();
_pop_unfeasible.reserve(pop.size());
for( typename eoPop<EOT>::const_iterator ieot=pop.begin(), iend=pop.end(); ieot!=iend; ++ieot ) {
/*
if( ieot->invalid() ) {
eo::log << eo::errors << "ERROR: trying to access to an invalid fitness" << std::endl;
}
*/
if( ieot->fitness().is_feasible() ) {
_pop_feasible.push_back( *ieot );
} else {
_pop_unfeasible.push_back( *ieot );
}
}
}
//! Merge feasible and unfeasible populations into a new one
eoPop<EOT> merge() const
{
eoPop<EOT> merged;
merged.reserve( _pop_feasible.size() + _pop_unfeasible.size() );
std::copy( _pop_feasible.begin(), _pop_feasible.end(), std::back_inserter<eoPop<EOT> >(merged) );
std::copy( _pop_unfeasible.begin(), _pop_unfeasible.end(), std::back_inserter<eoPop<EOT> >(merged) );
return merged;
}
eoPop<EOT>& feasible() { return _pop_feasible; }
eoPop<EOT>& unfeasible() { return _pop_unfeasible; }
};
/** Embed two eoStat and call the first one on the feasible individuals and /** Embed two eoStat and call the first one on the feasible individuals and
* the second one on the unfeasible ones, merge the two resulting value in * the second one on the unfeasible ones, merge the two resulting value in
* a string, separated by a given marker. * a string, separated by a given marker.
*/ */
//template<class EOT, class T>
template<class EOT, class EOSTAT> template<class EOT, class EOSTAT>
class eoDualStatSwitch : public eoStat< EOT, std::string > class eoDualStatSwitch : public eoStat< EOT, std::string >
{ {
protected:
EOSTAT & _stat_feasible;
EOSTAT & _stat_unfeasible;
std::string _sep;
eoDualPopSplit<EOT> _pop_split;
public: public:
using eoStat<EOT,std::string>::value; using eoStat<EOT,std::string>::value;
// eoDualStatSwitch( eoStat<EOT,T> & stat_feasible, eoStat<EOT,T> & stat_unfeasible, std::string sep=" " ) :
eoDualStatSwitch( EOSTAT & stat_feasible, EOSTAT & stat_unfeasible, std::string sep=" " ) : eoDualStatSwitch( EOSTAT & stat_feasible, EOSTAT & stat_unfeasible, std::string sep=" " ) :
eoStat<EOT,std::string>( eoStat<EOT,std::string>(
"?"+sep+"?", "?"+sep+"?",
@ -379,41 +464,17 @@ public:
virtual void operator()( const eoPop<EOT> & pop ) virtual void operator()( const eoPop<EOT> & pop )
{ {
eoPop<EOT> pop_feasible; // create two separated pop in this operator
pop_feasible.reserve(pop.size()); _pop_split( pop );
eoPop<EOT> pop_unfeasible; _stat_feasible( _pop_split.feasible() );
pop_unfeasible.reserve(pop.size()); _stat_unfeasible( _pop_split.unfeasible() );
for( typename eoPop<EOT>::const_iterator ieot=pop.begin(), iend=pop.end(); ieot!=iend; ++ieot ) {
/*
if( ieot->invalid() ) {
eo::log << eo::errors << "ERROR: trying to access to an invalid fitness" << std::endl;
}
*/
if( ieot->fitness().is_feasible() ) {
pop_feasible.push_back( *ieot );
} else {
pop_unfeasible.push_back( *ieot );
}
}
_stat_feasible( pop_feasible );
_stat_unfeasible( pop_unfeasible );
std::ostringstream out; std::ostringstream out;
out << _stat_feasible.value() << _sep << _stat_unfeasible.value(); out << _stat_feasible.value() << _sep << _stat_unfeasible.value();
value() = out.str(); value() = out.str();
} }
protected:
// eoStat<EOT,T> & _stat_feasible;
// eoStat<EOT,T> & _stat_unfeasible;
EOSTAT & _stat_feasible;
EOSTAT & _stat_unfeasible;
std::string _sep;
}; };
/** @} */ /** @} */

View file

@ -1,12 +1,14 @@
#ifndef MOEOEXPBINARYINDICATORBASEDDUALFITNESSASSIGNMENT_H_
#define MOEOEXPBINARYINDICATORBASEDDUALFITNESSASSIGNMENT_H_
#include <fitness/moeoExpBinaryIndicatorBasedFitnessAssignment.h> #include <fitness/moeoExpBinaryIndicatorBasedFitnessAssignment.h>
template<class MOEOT> template<class MOEOT>
class moeoExpBinaryIndicatorBasedDualFitnessAssignment : public moeoExpBinaryIndicatorBasedFitnessAssignment<MOEOT> class moeoExpBinaryIndicatorBasedDualFitnessAssignment : public moeoExpBinaryIndicatorBasedFitnessAssignment<MOEOT>
{ {
protected: protected:
eoPop<MOEOT> _feasible_pop; eoDualPopSplit<MOEOT> _pop_split;
eoPop<MOEOT> _unfeasible_pop;
public: public:
typedef typename MOEOT::ObjectiveVector ObjectiveVector; typedef typename MOEOT::ObjectiveVector ObjectiveVector;
@ -26,20 +28,24 @@ public:
*/ */
virtual void operator()( eoPop<MOEOT>& pop ) virtual void operator()( eoPop<MOEOT>& pop )
{ {
// separate the pop in the members // separate the pop in feasible/unfeasible
split( pop ); _pop_split( pop );
eoPop<MOEOT>* ppop; eoPop<MOEOT>* ppop;
// if there is at least one feasible individual, it will supersede all the unfeasible ones // if there is at least one feasible individual,
if( _feasible_pop.size() == 0 ) { // it will supersede all the unfeasible ones
ppop = & _unfeasible_pop; if( _pop_split.feasible().size() == 0 ) {
ppop = & _pop_split.unfeasible();
} else { } else {
ppop = & _feasible_pop; ppop = & _pop_split.feasible();
} }
this->setup(*ppop); this->setup(*ppop);
this->computeValues(*ppop); this->computeValues(*ppop);
this->setFitnesses(*ppop); this->setFitnesses(*ppop); // NOTE: this alter individuals
// bring back altered individuals in the pop
pop = _pop_split.merge();
} }
@ -47,25 +53,6 @@ protected:
using moeoExpBinaryIndicatorBasedFitnessAssignment<MOEOT>::kappa; using moeoExpBinaryIndicatorBasedFitnessAssignment<MOEOT>::kappa;
//! Split up the population in two: in one pop the feasible individual, in the other the feasible ones
virtual void split( eoPop<MOEOT> & pop )
{
// clear previously used populations
_feasible_pop.clear();
_unfeasible_pop.clear();
_feasible_pop.reserve(pop.size());
_unfeasible_pop.reserve(pop.size());
for( typename eoPop<MOEOT>::iterator it=pop.begin(), end=pop.end(); it != end; ++it ) {
// The ObjectiveVector should implement "is_feasible"
if( it->objectiveVector().is_feasible() ) {
_feasible_pop.push_back( *it );
} else {
_unfeasible_pop.push_back( *it );
}
}
}
/** /**
* Compute every indicator value in values (values[i] = I(_v[i], _o)) * Compute every indicator value in values (values[i] = I(_v[i], _o))
* @param _pop the population * @param _pop the population
@ -112,3 +99,4 @@ protected:
}; };
#endif // MOEOEXPBINARYINDICATORBASEDDUALFITNESSASSIGNMENT_H_