From 4af7f3d1bc873677aee9c3377a4df679c9e9f5b7 Mon Sep 17 00:00:00 2001 From: Johann Dreo Date: Thu, 13 Jun 2013 09:45:29 +0200 Subject: [PATCH] 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. --- eo/src/eoDualFitness.h | 155 ++++++++++++------ ...inaryIndicatorBasedDualFitnessAssignment.h | 44 ++--- 2 files changed, 124 insertions(+), 75 deletions(-) diff --git a/eo/src/eoDualFitness.h b/eo/src/eoDualFitness.h index 59d6f6d05..da70e760a 100644 --- a/eo/src/eoDualFitness.h +++ b/eo/src/eoDualFitness.h @@ -74,6 +74,25 @@ protected: //! Flag that marks if the individual 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: //! Empty initialization @@ -82,58 +101,71 @@ public: */ eoDualFitness() : _value(0.0), - _is_feasible(false) + _is_feasible(false), + _feasible_init(false) {} //! 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. - * 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 eoDualFitness( T value ) : _value(value), - _is_feasible(false) + _is_feasible(false), + _feasible_init(false) { - assert( _value == 0 ); } //! Copy constructor eoDualFitness(const eoDualFitness& other) : _value(other._value), - _is_feasible(other._is_feasible) + _is_feasible(other._is_feasible), + _feasible_init(true) {} //! Constructor from explicit value/feasibility eoDualFitness(const BaseType& v, const bool& is_feasible) : _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) eoDualFitness(const std::pair& dual) : _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 * type, if needed. For example, this is possible: * eoDualFitness > fit; * double val = 1.0; * val = fit; */ - operator BaseType(void) const { return _value; } + operator BaseType(void) const { return _value; } inline bool is_feasible() const { + assert( _feasible_init ); 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 { + assert( _feasible_init ); return _value; } @@ -141,7 +173,7 @@ public: eoDualFitness& operator=( const std::pair& v ) { this->_value = v.first; - this->_is_feasible = v.second; + this->is_feasible( v.second ); return *this; } @@ -151,21 +183,20 @@ public: { if (this != &other) { this->_value = other._value; - this->_is_feasible = other._is_feasible; + this->is_feasible( other.is_feasible() ); } return *this; } - /* //! Copy operator from a scalar template eoDualFitness& operator=(const T v) { this->_value = v; this->_is_feasible = false; + this->_feasible_init = false; return *this; } - */ //! 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 ? // 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 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 return true; @@ -322,7 +353,7 @@ public: friend std::ostream& operator<<( std::ostream& os, const eoDualFitness & fitness ) { - os << fitness._value << " " << fitness._is_feasible; + os << fitness._value << " " << fitness.is_feasible(); return os; } @@ -337,7 +368,7 @@ public: is >> feasible; fitness._value = value; - fitness._is_feasible = feasible; + fitness.is_feasible( feasible ); return is; } }; @@ -355,18 +386,72 @@ template< class EOT> 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 eoDualPopSplit : public eoUF&, void> +{ +protected: + eoPop _pop_feasible; + eoPop _pop_unfeasible; + +public: + //! Split the pop and keep them in members + void operator()( const eoPop& pop ) + { + _pop_feasible.clear(); + _pop_feasible.reserve(pop.size()); + + _pop_unfeasible.clear(); + _pop_unfeasible.reserve(pop.size()); + + for( typename eoPop::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 merge() const + { + eoPop merged; + merged.reserve( _pop_feasible.size() + _pop_unfeasible.size() ); + std::copy( _pop_feasible.begin(), _pop_feasible.end(), std::back_inserter >(merged) ); + std::copy( _pop_unfeasible.begin(), _pop_unfeasible.end(), std::back_inserter >(merged) ); + return merged; + } + + eoPop& feasible() { return _pop_feasible; } + eoPop& unfeasible() { return _pop_unfeasible; } +}; + + /** 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 * a string, separated by a given marker. */ -//template template class eoDualStatSwitch : public eoStat< EOT, std::string > { +protected: + EOSTAT & _stat_feasible; + EOSTAT & _stat_unfeasible; + + std::string _sep; + + eoDualPopSplit _pop_split; + public: using eoStat::value; -// eoDualStatSwitch( eoStat & stat_feasible, eoStat & stat_unfeasible, std::string sep=" " ) : eoDualStatSwitch( EOSTAT & stat_feasible, EOSTAT & stat_unfeasible, std::string sep=" " ) : eoStat( "?"+sep+"?", @@ -379,41 +464,17 @@ public: virtual void operator()( const eoPop & pop ) { - eoPop pop_feasible; - pop_feasible.reserve(pop.size()); + // create two separated pop in this operator + _pop_split( pop ); - eoPop pop_unfeasible; - pop_unfeasible.reserve(pop.size()); - - for( typename eoPop::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 ); + _stat_feasible( _pop_split.feasible() ); + _stat_unfeasible( _pop_split.unfeasible() ); std::ostringstream out; out << _stat_feasible.value() << _sep << _stat_unfeasible.value(); value() = out.str(); } - -protected: -// eoStat & _stat_feasible; -// eoStat & _stat_unfeasible; - EOSTAT & _stat_feasible; - EOSTAT & _stat_unfeasible; - - std::string _sep; }; /** @} */ diff --git a/moeo/src/fitness/moeoExpBinaryIndicatorBasedDualFitnessAssignment.h b/moeo/src/fitness/moeoExpBinaryIndicatorBasedDualFitnessAssignment.h index 793c6191a..0fce27148 100644 --- a/moeo/src/fitness/moeoExpBinaryIndicatorBasedDualFitnessAssignment.h +++ b/moeo/src/fitness/moeoExpBinaryIndicatorBasedDualFitnessAssignment.h @@ -1,12 +1,14 @@ +#ifndef MOEOEXPBINARYINDICATORBASEDDUALFITNESSASSIGNMENT_H_ +#define MOEOEXPBINARYINDICATORBASEDDUALFITNESSASSIGNMENT_H_ + #include template class moeoExpBinaryIndicatorBasedDualFitnessAssignment : public moeoExpBinaryIndicatorBasedFitnessAssignment { protected: - eoPop _feasible_pop; - eoPop _unfeasible_pop; + eoDualPopSplit _pop_split; public: typedef typename MOEOT::ObjectiveVector ObjectiveVector; @@ -26,20 +28,24 @@ public: */ virtual void operator()( eoPop& pop ) { - // separate the pop in the members - split( pop ); + // separate the pop in feasible/unfeasible + _pop_split( pop ); eoPop* ppop; - // if there is at least one feasible individual, it will supersede all the unfeasible ones - if( _feasible_pop.size() == 0 ) { - ppop = & _unfeasible_pop; + // if there is at least one feasible individual, + // it will supersede all the unfeasible ones + if( _pop_split.feasible().size() == 0 ) { + ppop = & _pop_split.unfeasible(); } else { - ppop = & _feasible_pop; + ppop = & _pop_split.feasible(); } this->setup(*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::kappa; - //! Split up the population in two: in one pop the feasible individual, in the other the feasible ones - virtual void split( eoPop & pop ) - { - // clear previously used populations - _feasible_pop.clear(); - _unfeasible_pop.clear(); - _feasible_pop.reserve(pop.size()); - _unfeasible_pop.reserve(pop.size()); - - for( typename eoPop::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)) * @param _pop the population @@ -112,3 +99,4 @@ protected: }; +#endif // MOEOEXPBINARYINDICATORBASEDDUALFITNESSASSIGNMENT_H_