From d32aac22793843b045618d0f13b8c13c333745c2 Mon Sep 17 00:00:00 2001 From: Alessandro Sidero <75628365+Alessandro624@users.noreply.github.com> Date: Mon, 14 Apr 2025 21:52:53 +0200 Subject: [PATCH] feat: add eoRankingCached with better documentation --- eo/src/eoRanking.h | 112 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 95 insertions(+), 17 deletions(-) diff --git a/eo/src/eoRanking.h b/eo/src/eoRanking.h index 212074aae..1a8bfe3ed 100644 --- a/eo/src/eoRanking.h +++ b/eo/src/eoRanking.h @@ -28,7 +28,6 @@ #define eoRanking_h #include "eoPerf2Worth.h" -#include /** An instance of eoPerfFromWorth * COmputes the ranked fitness: fitnesses range in [m,M] @@ -47,10 +46,9 @@ public: @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), cached_pSize(0) {} + 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; @@ -61,13 +59,94 @@ public: } 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 + { + 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(); @@ -78,7 +157,7 @@ public: // value() refers to the std::vector of worthes (we're in an eoParamvalue) value().resize(pSize); - // Cache only if population size changed + // Cache population-size dependent values only when population size changes if (pSize != cached_pSize) { cached_pSize = pSize; @@ -96,16 +175,15 @@ public: for (size_t i = 0; i < pSize; ++i) indices[i] = i; - if (exponent == 1.0) // no need for exponetial then + if (exponent == 1.0) // no need for exponetial then (linear case) { for (unsigned i = 0; i < pSize; i++) { int which = indices[i]; - ; - value()[which] = cached_alpha * (pSize - i) + cached_beta; // worst -> 1/[P(P-1)/2] + value()[which] = cached_alpha * (pSize - i) + cached_beta; } } - else // exponent != 1 + else // non-linear case (exponent != 1) { for (unsigned i = 0; i < pSize; i++) { @@ -119,15 +197,15 @@ public: } private: - double pressure; // selective pressure - double exponent; + double pressure; // selective pressure (1 < pressure <= 2) + double exponent; // exponent (1 = linear) - // Cached values - unsigned cached_pSize; - unsigned cached_pSizeMinusOne; - double cached_alpha; - double cached_beta; - double cached_gamma; + // 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 }; #endif