| 1 | package net.bmahe.genetics4j.neat.spec.selection; | |
| 2 | ||
| 3 | import java.util.function.BiPredicate; | |
| 4 | ||
| 5 | import org.apache.commons.lang3.Validate; | |
| 6 | import org.immutables.value.Value; | |
| 7 | ||
| 8 | import net.bmahe.genetics4j.core.Individual; | |
| 9 | import net.bmahe.genetics4j.core.spec.selection.SelectionPolicy; | |
| 10 | import net.bmahe.genetics4j.core.spec.selection.Tournament; | |
| 11 | import net.bmahe.genetics4j.neat.NeatUtils; | |
| 12 | ||
| 13 | /** | |
| 14 | * Selection policy for NEAT (NeuroEvolution of Augmenting Topologies) species-based selection. | |
| 15 | * | |
| 16 | * <p>NeatSelection implements the species-based selection mechanism that is fundamental to NEAT's ability to maintain | |
| 17 | * population diversity and protect structural innovations. It organizes the population into species based on genetic | |
| 18 | * compatibility, applies fitness sharing within species, and manages reproduction allocation to prevent dominant | |
| 19 | * topologies from eliminating exploration. | |
| 20 | * | |
| 21 | * <p>Key features: | |
| 22 | * <ul> | |
| 23 | * <li><strong>Species formation</strong>: Groups genetically similar individuals using compatibility predicates</li> | |
| 24 | * <li><strong>Fitness sharing</strong>: Reduces fitness pressure within species to promote diversity</li> | |
| 25 | * <li><strong>Species preservation</strong>: Maintains minimum viable species sizes</li> | |
| 26 | * <li><strong>Reproduction allocation</strong>: Distributes offspring based on species average fitness</li> | |
| 27 | * </ul> | |
| 28 | * | |
| 29 | * <p>NEAT species-based selection process: | |
| 30 | * <ol> | |
| 31 | * <li><strong>Compatibility testing</strong>: Apply species predicate to group similar individuals</li> | |
| 32 | * <li><strong>Species assignment</strong>: Assign individuals to species based on genetic distance</li> | |
| 33 | * <li><strong>Fitness adjustment</strong>: Apply fitness sharing within each species</li> | |
| 34 | * <li><strong>Species filtering</strong>: Remove species below minimum size threshold</li> | |
| 35 | * <li><strong>Reproduction allocation</strong>: Determine offspring count per species</li> | |
| 36 | * <li><strong>Within-species selection</strong>: Select parents using specified selection policy</li> | |
| 37 | * </ol> | |
| 38 | * | |
| 39 | * <p>Species management parameters: | |
| 40 | * <ul> | |
| 41 | * <li><strong>Keep ratio</strong>: Proportion of each species to preserve for reproduction</li> | |
| 42 | * <li><strong>Minimum size</strong>: Smallest viable species size to prevent extinction</li> | |
| 43 | * <li><strong>Compatibility predicate</strong>: Function determining species membership</li> | |
| 44 | * <li><strong>Selection policy</strong>: Within-species selection strategy</li> | |
| 45 | * </ul> | |
| 46 | * | |
| 47 | * <p>Common usage patterns: | |
| 48 | * | |
| 49 | * <pre>{@code | |
| 50 | * // Default NEAT selection with standard parameters | |
| 51 | * NeatSelection<Double> defaultSelection = NeatSelection.ofDefault(); | |
| 52 | * | |
| 53 | * // Custom compatibility threshold | |
| 54 | * BiPredicate<Individual<Double>, Individual<Double>> compatibilityPredicate = (i1, | |
| 55 | * i2) -> NeatUtils.compatibilityDistance(i1.genotype(), i2.genotype(), 0, 2, 2, 1.0f) < 3.0; // Higher threshold | |
| 56 | * // = fewer, larger | |
| 57 | * // species | |
| 58 | * | |
| 59 | * NeatSelection<Double> customSelection = NeatSelection.<Double>builder() | |
| 60 | * .perSpeciesKeepRatio(0.8f) // Keep top 80% of each species | |
| 61 | * .minSpeciesSize(3) // Minimum 3 individuals per species | |
| 62 | * .speciesPredicate(compatibilityPredicate) | |
| 63 | * .speciesSelection(Tournament.of(5)) // Tournament size 5 within species | |
| 64 | * .build(); | |
| 65 | * | |
| 66 | * // Aggressive diversity preservation | |
| 67 | * NeatSelection<Double> diverseSelection = NeatSelection.of(0.95f, // Keep 95% of each species | |
| 68 | * compatibilityPredicate, | |
| 69 | * new ProportionalSelection() // Proportional selection within species | |
| 70 | * ); | |
| 71 | * }</pre> | |
| 72 | * | |
| 73 | * <p>Compatibility distance calculation: | |
| 74 | * <ul> | |
| 75 | * <li><strong>Matching genes</strong>: Genes with same innovation numbers in both individuals</li> | |
| 76 | * <li><strong>Disjoint genes</strong>: Genes in one individual within the other's innovation range</li> | |
| 77 | * <li><strong>Excess genes</strong>: Genes beyond the other individual's highest innovation number</li> | |
| 78 | * <li><strong>Weight differences</strong>: Average difference in matching gene weights</li> | |
| 79 | * </ul> | |
| 80 | * | |
| 81 | * <p>Species preservation strategies: | |
| 82 | * <ul> | |
| 83 | * <li><strong>Keep ratio</strong>: Ensures a proportion of each species survives selection pressure</li> | |
| 84 | * <li><strong>Minimum size</strong>: Prevents viable species from going extinct due to random drift</li> | |
| 85 | * <li><strong>Fitness sharing</strong>: Reduces competition between similar individuals</li> | |
| 86 | * <li><strong>Innovation protection</strong>: Gives new topologies time to optimize</li> | |
| 87 | * </ul> | |
| 88 | * | |
| 89 | * <p>Integration with genetic operators: | |
| 90 | * <ul> | |
| 91 | * <li><strong>Crossover compatibility</strong>: Species ensure genetic similarity for meaningful recombination</li> | |
| 92 | * <li><strong>Mutation guidance</strong>: Species composition can influence mutation rates</li> | |
| 93 | * <li><strong>Structural innovation</strong>: Protected evolution of different network topologies</li> | |
| 94 | * <li><strong>Population dynamics</strong>: Species formation and extinction drive exploration</li> | |
| 95 | * </ul> | |
| 96 | * | |
| 97 | * <p>Performance considerations: | |
| 98 | * <ul> | |
| 99 | * <li><strong>Compatibility caching</strong>: Distance calculations cached for efficiency</li> | |
| 100 | * <li><strong>Species reuse</strong>: Species structures maintained across generations</li> | |
| 101 | * <li><strong>Parallel evaluation</strong>: Species-based organization enables concurrent processing</li> | |
| 102 | * <li><strong>Memory efficiency</strong>: Efficient species membership tracking</li> | |
| 103 | * </ul> | |
| 104 | * | |
| 105 | * @param <T> the fitness value type (typically Double) | |
| 106 | * @see SelectionPolicy | |
| 107 | * @see NeatUtils#compatibilityDistance | |
| 108 | * @see net.bmahe.genetics4j.neat.selection.NeatSelectionPolicyHandler | |
| 109 | * @see net.bmahe.genetics4j.neat.Species | |
| 110 | */ | |
| 111 | @Value.Immutable | |
| 112 | public abstract class NeatSelection<T extends Comparable<T>> implements SelectionPolicy { | |
| 113 | ||
| 114 | /** | |
| 115 | * Returns the proportion of each species to preserve for reproduction. | |
| 116 | * | |
| 117 | * <p>This ratio determines what fraction of each species will be retained after fitness-based culling. Higher values | |
| 118 | * preserve more diversity within species but may slow convergence, while lower values increase selection pressure | |
| 119 | * but may lose beneficial genetic variations. | |
| 120 | * | |
| 121 | * <p>Typical values: | |
| 122 | * <ul> | |
| 123 | * <li><strong>0.9 (default)</strong>: Preserve top 90% of each species</li> | |
| 124 | * <li><strong>0.8-0.95</strong>: Balanced diversity preservation</li> | |
| 125 | * <li><strong>< 0.8</strong>: Aggressive selection pressure</li> | |
| 126 | * <li><strong>> 0.95</strong>: Minimal selection pressure, maximum diversity</li> | |
| 127 | * </ul> | |
| 128 | * | |
| 129 | * @return keep ratio between 0.0 and 1.0 (exclusive of 0.0, inclusive of 1.0) | |
| 130 | */ | |
| 131 | @Value.Default | |
| 132 | public float perSpeciesKeepRatio() { | |
| 133 |
2
1. perSpeciesKeepRatio : replaced float return with 0.0f for net/bmahe/genetics4j/neat/spec/selection/NeatSelection::perSpeciesKeepRatio → KILLED 2. perSpeciesKeepRatio : Substituted 0.9 with 1.0 → KILLED |
return 0.90f; |
| 134 | } | |
| 135 | ||
| 136 | /** | |
| 137 | * Returns the minimum number of individuals required to maintain a species. | |
| 138 | * | |
| 139 | * <p>Species with fewer members than this threshold will be eliminated to prevent resource waste on non-viable | |
| 140 | * populations. This helps focus evolutionary resources on species with sufficient genetic diversity to explore their | |
| 141 | * local fitness landscape effectively. | |
| 142 | * | |
| 143 | * <p>Typical values: | |
| 144 | * <ul> | |
| 145 | * <li><strong>5 (default)</strong>: Balanced viability threshold</li> | |
| 146 | * <li><strong>3-10</strong>: Reasonable range for most problems</li> | |
| 147 | * <li><strong>< 3</strong>: Very permissive, allows small species to survive</li> | |
| 148 | * <li><strong>> 10</strong>: Strict threshold, eliminates marginal species</li> | |
| 149 | * </ul> | |
| 150 | * | |
| 151 | * @return minimum species size (must be positive) | |
| 152 | */ | |
| 153 | @Value.Default | |
| 154 | public int minSpeciesSize() { | |
| 155 |
2
1. minSpeciesSize : Substituted 5 with 6 → SURVIVED 2. minSpeciesSize : replaced int return with 0 for net/bmahe/genetics4j/neat/spec/selection/NeatSelection::minSpeciesSize → KILLED |
return 5; |
| 156 | } | |
| 157 | ||
| 158 | /** | |
| 159 | * Returns the predicate used to determine species membership. | |
| 160 | * | |
| 161 | * <p>This bi-predicate takes two individuals and returns true if they should belong to the same species based on | |
| 162 | * their genetic compatibility. Typically implemented using NEAT compatibility distance with a threshold value. | |
| 163 | * | |
| 164 | * <p>Common implementations: | |
| 165 | * <ul> | |
| 166 | * <li><strong>Compatibility distance</strong>: Based on matching, disjoint, excess genes and weight differences</li> | |
| 167 | * <li><strong>Topological similarity</strong>: Based on network structure similarity</li> | |
| 168 | * <li><strong>Behavioral similarity</strong>: Based on network output patterns</li> | |
| 169 | * <li><strong>Custom metrics</strong>: Domain-specific similarity measures</li> | |
| 170 | * </ul> | |
| 171 | * | |
| 172 | * @return bi-predicate for determining species membership | |
| 173 | */ | |
| 174 | public abstract BiPredicate<Individual<T>, Individual<T>> speciesPredicate(); | |
| 175 | ||
| 176 | /** | |
| 177 | * Returns the selection policy used within each species. | |
| 178 | * | |
| 179 | * <p>After individuals are organized into species, this policy determines how parents are selected within each | |
| 180 | * species for reproduction. Common choices include tournament selection, proportional selection, or rank-based | |
| 181 | * selection. | |
| 182 | * | |
| 183 | * <p>Selection policy considerations: | |
| 184 | * <ul> | |
| 185 | * <li><strong>Tournament selection</strong>: Good balance of selection pressure and diversity</li> | |
| 186 | * <li><strong>Proportional selection</strong>: Fitness-proportionate selection within species</li> | |
| 187 | * <li><strong>Rank selection</strong>: Rank-based selection to avoid fitness scaling issues</li> | |
| 188 | * <li><strong>Elite selection</strong>: Always select best individuals within species</li> | |
| 189 | * </ul> | |
| 190 | * | |
| 191 | * @return selection policy for within-species parent selection | |
| 192 | */ | |
| 193 | public abstract SelectionPolicy speciesSelection(); | |
| 194 | ||
| 195 | @Value.Check | |
| 196 | public void check() { | |
| 197 | Validate.inclusiveBetween(0.0f, 1.0f, perSpeciesKeepRatio()); | |
| 198 | Validate.isTrue(perSpeciesKeepRatio() > 0.0f); | |
| 199 | } | |
| 200 | ||
| 201 | public static class Builder<T extends Comparable<T>> extends ImmutableNeatSelection.Builder<T> { | |
| 202 | } | |
| 203 | ||
| 204 | public static <U extends Comparable<U>> Builder<U> builder() { | |
| 205 |
2
1. builder : replaced return value with null for net/bmahe/genetics4j/neat/spec/selection/NeatSelection::builder → KILLED 2. builder : removed call to net/bmahe/genetics4j/neat/spec/selection/NeatSelection$Builder::<init> → KILLED |
return new Builder<U>(); |
| 206 | } | |
| 207 | ||
| 208 | /** | |
| 209 | * Creates a NEAT selection policy with custom keep ratio and specified parameters. | |
| 210 | * | |
| 211 | * @param <U> the fitness value type | |
| 212 | * @param perSpeciesKeepRatio proportion of each species to preserve (0.0 < ratio <= 1.0) | |
| 213 | * @param speciesPredicate predicate for determining species membership | |
| 214 | * @param speciesSelection selection policy for within-species parent selection | |
| 215 | * @return a new NEAT selection policy with the specified parameters | |
| 216 | */ | |
| 217 | public static <U extends Comparable<U>> NeatSelection<U> of(final float perSpeciesKeepRatio, | |
| 218 | final BiPredicate<Individual<U>, Individual<U>> speciesPredicate, final SelectionPolicy speciesSelection) { | |
| 219 |
4
1. of : replaced return value with null for net/bmahe/genetics4j/neat/spec/selection/NeatSelection::of → NO_COVERAGE 2. of : removed call to net/bmahe/genetics4j/neat/spec/selection/NeatSelection$Builder::<init> → NO_COVERAGE 3. of : removed call to net/bmahe/genetics4j/neat/spec/selection/NeatSelection$Builder::perSpeciesKeepRatio → NO_COVERAGE 4. of : replaced call to net/bmahe/genetics4j/neat/spec/selection/NeatSelection$Builder::perSpeciesKeepRatio with receiver → NO_COVERAGE |
return new Builder<U>().perSpeciesKeepRatio(perSpeciesKeepRatio) |
| 220 |
2
1. of : removed call to net/bmahe/genetics4j/neat/spec/selection/NeatSelection$Builder::speciesPredicate → NO_COVERAGE 2. of : replaced call to net/bmahe/genetics4j/neat/spec/selection/NeatSelection$Builder::speciesPredicate with receiver → NO_COVERAGE |
.speciesPredicate(speciesPredicate) |
| 221 |
2
1. of : removed call to net/bmahe/genetics4j/neat/spec/selection/NeatSelection$Builder::speciesSelection → NO_COVERAGE 2. of : replaced call to net/bmahe/genetics4j/neat/spec/selection/NeatSelection$Builder::speciesSelection with receiver → NO_COVERAGE |
.speciesSelection(speciesSelection) |
| 222 |
1
1. of : removed call to net/bmahe/genetics4j/neat/spec/selection/NeatSelection$Builder::build → NO_COVERAGE |
.build(); |
| 223 | } | |
| 224 | ||
| 225 | /** | |
| 226 | * Creates a NEAT selection policy with default keep ratio and specified parameters. | |
| 227 | * | |
| 228 | * @param <U> the fitness value type | |
| 229 | * @param speciesPredicate predicate for determining species membership | |
| 230 | * @param speciesSelection selection policy for within-species parent selection | |
| 231 | * @return a new NEAT selection policy with default keep ratio (0.9) | |
| 232 | */ | |
| 233 | public static <U extends Comparable<U>> NeatSelection<U> of( | |
| 234 | final BiPredicate<Individual<U>, Individual<U>> speciesPredicate, final SelectionPolicy speciesSelection) { | |
| 235 |
4
1. of : removed call to net/bmahe/genetics4j/neat/spec/selection/NeatSelection$Builder::speciesPredicate → KILLED 2. of : removed call to net/bmahe/genetics4j/neat/spec/selection/NeatSelection$Builder::<init> → KILLED 3. of : replaced return value with null for net/bmahe/genetics4j/neat/spec/selection/NeatSelection::of → KILLED 4. of : replaced call to net/bmahe/genetics4j/neat/spec/selection/NeatSelection$Builder::speciesPredicate with receiver → KILLED |
return new Builder<U>().speciesPredicate(speciesPredicate) |
| 236 |
2
1. of : removed call to net/bmahe/genetics4j/neat/spec/selection/NeatSelection$Builder::speciesSelection → KILLED 2. of : replaced call to net/bmahe/genetics4j/neat/spec/selection/NeatSelection$Builder::speciesSelection with receiver → KILLED |
.speciesSelection(speciesSelection) |
| 237 |
1
1. of : removed call to net/bmahe/genetics4j/neat/spec/selection/NeatSelection$Builder::build → KILLED |
.build(); |
| 238 | } | |
| 239 | ||
| 240 | /** | |
| 241 | * Creates a NEAT selection policy with standard default parameters. | |
| 242 | * | |
| 243 | * <p>Default configuration: | |
| 244 | * <ul> | |
| 245 | * <li><strong>Keep ratio</strong>: 0.9 (preserve top 90% of each species)</li> | |
| 246 | * <li><strong>Minimum species size</strong>: 5 individuals</li> | |
| 247 | * <li><strong>Compatibility distance</strong>: Threshold of 1.0 with standard coefficients</li> | |
| 248 | * <li><strong>Species selection</strong>: Tournament selection with size 3</li> | |
| 249 | * </ul> | |
| 250 | * | |
| 251 | * <p>Compatibility distance uses: | |
| 252 | * <ul> | |
| 253 | * <li>Weight coefficient: 1.0</li> | |
| 254 | * <li>Excess gene coefficient: 2.0</li> | |
| 255 | * <li>Disjoint gene coefficient: 2.0</li> | |
| 256 | * <li>Distance threshold: 1.0</li> | |
| 257 | * </ul> | |
| 258 | * | |
| 259 | * @param <U> the fitness value type | |
| 260 | * @return a new NEAT selection policy with standard default parameters | |
| 261 | */ | |
| 262 | public static <U extends Comparable<U>> NeatSelection<U> ofDefault() { | |
| 263 |
2
1. ofDefault : removed call to net/bmahe/genetics4j/neat/spec/selection/NeatSelection$Builder::<init> → KILLED 2. ofDefault : replaced return value with null for net/bmahe/genetics4j/neat/spec/selection/NeatSelection::ofDefault → KILLED |
return new Builder<U>() |
| 264 |
3
1. ofDefault : Substituted 3 with 4 → SURVIVED 2. ofDefault : replaced call to net/bmahe/genetics4j/neat/spec/selection/NeatSelection$Builder::speciesPredicate with receiver → KILLED 3. ofDefault : removed call to net/bmahe/genetics4j/neat/spec/selection/NeatSelection$Builder::speciesPredicate → KILLED |
.speciesPredicate( |
| 265 |
16
1. lambda$ofDefault$0 : replaced call to net/bmahe/genetics4j/neat/NeatUtils::compatibilityDistance with argument → NO_COVERAGE 2. lambda$ofDefault$0 : Substituted 0 with 1 → NO_COVERAGE 3. lambda$ofDefault$0 : removed conditional - replaced comparison check with false → NO_COVERAGE 4. lambda$ofDefault$0 : Substituted 2.0 with 1.0 → NO_COVERAGE 5. lambda$ofDefault$0 : Substituted 1.0 with 2.0 → NO_COVERAGE 6. lambda$ofDefault$0 : removed call to net/bmahe/genetics4j/neat/NeatUtils::compatibilityDistance → NO_COVERAGE 7. lambda$ofDefault$0 : Substituted 2.0 with 1.0 → NO_COVERAGE 8. lambda$ofDefault$0 : Substituted 1.0 with 2.0 → NO_COVERAGE 9. lambda$ofDefault$0 : Substituted 1 with 0 → NO_COVERAGE 10. lambda$ofDefault$0 : Substituted 0 with 1 → NO_COVERAGE 11. lambda$ofDefault$0 : changed conditional boundary → NO_COVERAGE 12. lambda$ofDefault$0 : replaced boolean return with true for net/bmahe/genetics4j/neat/spec/selection/NeatSelection::lambda$ofDefault$0 → NO_COVERAGE 13. lambda$ofDefault$0 : removed conditional - replaced comparison check with true → NO_COVERAGE 14. lambda$ofDefault$0 : negated conditional → NO_COVERAGE 15. lambda$ofDefault$0 : removed call to net/bmahe/genetics4j/core/Individual::genotype → NO_COVERAGE 16. lambda$ofDefault$0 : removed call to net/bmahe/genetics4j/core/Individual::genotype → NO_COVERAGE |
(i1, i2) -> NeatUtils.compatibilityDistance(i1.genotype(), i2.genotype(), 0, 2, 2, 1f) < 1.0) |
| 266 |
3
1. ofDefault : removed call to net/bmahe/genetics4j/neat/spec/selection/NeatSelection$Builder::speciesSelection → KILLED 2. ofDefault : replaced call to net/bmahe/genetics4j/neat/spec/selection/NeatSelection$Builder::speciesSelection with receiver → KILLED 3. ofDefault : removed call to net/bmahe/genetics4j/core/spec/selection/Tournament::of → KILLED |
.speciesSelection(Tournament.of(3)) |
| 267 |
1
1. ofDefault : removed call to net/bmahe/genetics4j/neat/spec/selection/NeatSelection$Builder::build → KILLED |
.build(); |
| 268 | } | |
| 269 | } | |
Mutations | ||
| 133 |
1.1 2.2 |
|
| 155 |
1.1 2.2 |
|
| 205 |
1.1 2.2 |
|
| 219 |
1.1 2.2 3.3 4.4 |
|
| 220 |
1.1 2.2 |
|
| 221 |
1.1 2.2 |
|
| 222 |
1.1 |
|
| 235 |
1.1 2.2 3.3 4.4 |
|
| 236 |
1.1 2.2 |
|
| 237 |
1.1 |
|
| 263 |
1.1 2.2 |
|
| 264 |
1.1 2.2 3.3 |
|
| 265 |
1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 9.9 10.10 11.11 12.12 13.13 14.14 15.15 16.16 |
|
| 266 |
1.1 2.2 3.3 |
|
| 267 |
1.1 |