From 9a2b0a29249b725c4247f2ef2706b600afcbf37f Mon Sep 17 00:00:00 2001 From: Alessandro Sidero <75628365+Alessandro624@users.noreply.github.com> Date: Tue, 15 Apr 2025 14:58:40 +0200 Subject: [PATCH] fix(eoRanking): add validation with assertions --- eo/src/eoRanking.h | 216 ++++++++++++--------------------------------- 1 file changed, 57 insertions(+), 159 deletions(-) diff --git a/eo/src/eoRanking.h b/eo/src/eoRanking.h index ef06d6763..c1c3d1e74 100644 --- a/eo/src/eoRanking.h +++ b/eo/src/eoRanking.h @@ -40,175 +40,73 @@ template class eoRanking : public eoPerf2Worth // false: do not cache fitness { public: - using eoPerf2Worth::value; + using eoPerf2Worth::value; - /* Ctor: - @param _p selective pressure (in (1,2] - @param _e exponent (1 == linear) - */ - eoRanking(double _p = 2.0, double _e = 1.0) : pressure(_p), exponent(_e) {} - - /* helper function: finds index in _pop of _eo, an EOT * */ - int lookfor(const EOT *_eo, const eoPop &_pop) - { - typename eoPop::const_iterator it; - for (it = _pop.begin(); it < _pop.end(); it++) + /* Ctor: + @param _p selective pressure (in (1,2] + @param _e exponent (1 == linear) + */ + eoRanking(double _p = 2.0, double _e = 1.0) : pressure(_p), exponent(_e) { - if (_eo == &(*it)) - return it - _pop.begin(); + assert(1 < pressure and exponent <= 2); } - throw eoException("Not found in eoLinearRanking"); - } - /* COmputes the ranked fitness: fitnesses range in [m,M] - with m=2-pressure/popSize and M=pressure/popSize. - in between, the progression depstd::ends on exponent (linear if 1). - */ - virtual void operator()(const eoPop &_pop) - { - std::vector rank; - _pop.sort(rank); - unsigned pSize = _pop.size(); - unsigned int pSizeMinusOne = pSize - 1; - - if (pSize <= 1) - throw eoPopSizeException(pSize, "cannot do ranking with population of size <= 1"); - - // value() refers to the std::vector of worthes (we're in an eoParamvalue) - value().resize(pSize); - - double beta = (2 - pressure) / pSize; - if (exponent == 1.0) // no need for exponetial then + /* helper function: finds index in _pop of _eo, an EOT * */ + int lookfor(const EOT *_eo, const eoPop &_pop) { - double alpha = (2 * pressure - 2) / (pSize * pSizeMinusOne); - for (unsigned i = 0; i < pSize; i++) - { - int which = lookfor(rank[i], _pop); - value()[which] = alpha * (pSize - i) + beta; // worst -> 1/[P(P-1)/2] - } + typename eoPop::const_iterator it; + for (it = _pop.begin(); it < _pop.end(); it++) + { + if (_eo == &(*it)) + return it - _pop.begin(); + } + throw eoException("Not found in eoLinearRanking"); } - else // exponent != 1 + + /* COmputes the ranked fitness: fitnesses range in [m,M] + with m=2-pressure/popSize and M=pressure/popSize. + in between, the progression depstd::ends on exponent (linear if 1). + */ + virtual void operator()(const eoPop &_pop) { - double gamma = (2 * pressure - 2) / pSize; - for (unsigned i = 0; i < pSize; i++) - { - int which = lookfor(rank[i], _pop); - // value in in [0,1] - double tmp = ((double)(pSize - i)) / pSize; - // to the exponent, and back to [m,M] - value()[which] = gamma * pow(tmp, exponent) + beta; - } + std::vector rank; + _pop.sort(rank); + unsigned pSize = _pop.size(); + unsigned int pSizeMinusOne = pSize - 1; + + if (pSize <= 1) + throw eoPopSizeException(pSize, "cannot do ranking with population of size <= 1"); + + // value() refers to the std::vector of worthes (we're in an eoParamvalue) + value().resize(pSize); + + double beta = (2 - pressure) / pSize; + if (exponent == 1.0) // no need for exponetial then + { + double alpha = (2 * pressure - 2) / (pSize * pSizeMinusOne); + for (unsigned i = 0; i < pSize; i++) + { + int which = lookfor(rank[i], _pop); + value()[which] = alpha * (pSize - i) + beta; // worst -> 1/[P(P-1)/2] + } + } + else // exponent != 1 + { + double gamma = (2 * pressure - 2) / pSize; + for (unsigned i = 0; i < pSize; i++) + { + int which = lookfor(rank[i], _pop); + // value in in [0,1] + double tmp = ((double)(pSize - i)) / pSize; + // to the exponent, and back to [m,M] + value()[which] = gamma * pow(tmp, exponent) + beta; + } + } } - } private: - double pressure; // selective pressure - double exponent; -}; - -/** - * @class eoRankingCached - * @brief Cached version of eoRanking that stores precomputed values for better performance - * - * This class implements the same ranking algorithm as eoRanking but adds a caching layer - * that stores frequently used values when the population size remains constant between - * calls. This optimization is particularly useful in steady-state evolution where the - * population size typically doesn't change between selection operations. - * - * The caching mechanism stores: - * - Population size related values (pSize, pSizeMinusOne) - * - Precomputed coefficients (alpha, beta, gamma) - * - * Note: This optimization should only be used when the population size remains constant - * between calls to the operator. For dynamic population sizes, use the standard eoRanking. - * - * @ingroup Selectors - */ -template -class eoRankingCached : public eoPerf2Worth -{ -public: - using eoPerf2Worth::value; - - /* Ctor: - @param _p selective pressure (in (1,2] - @param _e exponent (1 == linear) - */ - eoRankingCached(double _p = 2.0, double _e = 1.0) - : pressure(_p), exponent(_e), cached_pSize(0) {} - - /* - Computes the ranked fitness with caching optimization - Fitnesses range in [m,M] where: - - m = 2-pressure/popSize - - M = pressure/popSize - The progression between m and M depends on the exponent (linear when exponent=1) - - @param _pop The population to rank - */ - virtual void operator()(const eoPop &_pop) - { - unsigned pSize = _pop.size(); - - if (pSize <= 1) - throw eoPopSizeException(pSize, "cannot do ranking with population of size <= 1"); - - // value() refers to the std::vector of worthes (we're in an eoParamvalue) - value().resize(pSize); - - // Cache population-size dependent values only when population size changes - if (pSize != cached_pSize) - { - cached_pSize = pSize; - cached_pSizeMinusOne = pSize - 1; - cached_beta = (2 - pressure) / pSize; - cached_gamma = (2 * pressure - 2) / pSize; - cached_alpha = (2 * pressure - 2) / (pSize * cached_pSizeMinusOne); - } - - std::vector rank; - _pop.sort(rank); - - // map of indices for the population - std::unordered_map indexMap; - for (unsigned i = 0; i < pSize; ++i) - { - indexMap[&_pop[i]] = i; - } - - if (exponent == 1.0) // no need for exponetial then (linear case) - { - for (unsigned i = 0; i < pSize; i++) - { - const EOT *indiv = rank[i]; - int which = indexMap[indiv]; - value()[which] = cached_alpha * (pSize - i) + cached_beta; - } - } - else // non-linear case (exponent != 1) - { - for (unsigned i = 0; i < pSize; i++) - { - const EOT *indiv = rank[i]; - int which = indexMap[indiv]; - // value in in [0,1] - double tmp = ((double)(pSize - i)) / pSize; - // to the exponent, and back to [m,M] - value()[which] = cached_gamma * pow(tmp, exponent) + cached_beta; - } - } - } - -private: - double pressure; // selective pressure (1 < pressure <= 2) - double exponent; // exponent (1 = linear) - - // Cached values (recomputed only when population size changes) - unsigned cached_pSize; // last seen population size - unsigned cached_pSizeMinusOne; // pSize - 1 - double cached_alpha; // linear scaling coefficient - double cached_beta; // base value coefficient - double cached_gamma; // non-linear scaling coefficient + double pressure; // selective pressure + double exponent; }; #endif