diff --git a/docs/source/releases.md b/docs/source/releases.md index 6b6e310..a849528 100644 --- a/docs/source/releases.md +++ b/docs/source/releases.md @@ -8,6 +8,13 @@ Release notes are listed from newest to oldest. Unreleased contains changes plan No changes yet. +## PyGAD 3.9.2 + +Release Date: October 16, 2026. + +1. [Roulette wheel selection](utils.md#roulette_wheel_selection) and [stochastic universal selection](utils.md#stochastic_universal_selection) no longer fail with fitness values near the float limits. In PyGAD 3.9.1, a fitness of `-sys.float_info.max`, which some libraries give a rejected solution, made the shifted values overflow, and parent selection raised `IndexError`. The values are now scaled before they are shifted or added. `-inf` and `NaN` get no chance, solutions with `+inf` fitness share every chance equally, and a population with no finite fitness raises a clear `ValueError`. Ordinary non-negative fitness keeps exactly the same probabilities. +2. Regression tests cover the most negative float, huge values of both signs, huge positive values, infinite and missing values, and full runs with both selectors. The `pygad.utils` submodule version is `1.5.12`. + ## PyGAD 3.9.1 Release Date: October 16, 2026. diff --git a/docs/source/utils.md b/docs/source/utils.md index 6ade59c..f4e0662 100644 --- a/docs/source/utils.md +++ b/docs/source/utils.md @@ -450,7 +450,7 @@ Selects the parents using the tournament selection technique. Selects the parents using the roulette wheel selection technique. -Each solution gets a share of the wheel equal to its fitness divided by the total fitness. If any fitness value is negative, all values are first shifted by the smallest fitness, so the worst solution gets no share and the better solutions keep larger shares. For example, the fitness values `-1` and `-9` become `8` and `0`. If all values are equal and negative, every solution gets the same chance. Fitness values that are all zero or above are used without a shift, and a total of zero raises `ZeroDivisionError`. +Each solution gets a share of the wheel equal to its fitness divided by the total fitness. If any fitness value is negative, all values are first shifted by the smallest fitness, so the worst solution gets no share and the better solutions keep larger shares. For example, the fitness values `-1` and `-9` become `8` and `0`. If all values are equal and negative, every solution gets the same chance. Fitness values that are all zero or above are used without a shift, and a total of zero raises `ZeroDivisionError`. Values near the float limits, such as `-sys.float_info.max` for a rejected solution, are scaled before they are shifted or added, so nothing overflows. `-inf` and `NaN` get no chance, and when any fitness is `+inf`, those solutions share every chance equally.