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 | |
17 | * ability to maintain population diversity and protect structural innovations. It organizes the | |
18 | * population into species based on genetic compatibility, applies fitness sharing within species, | |
19 | * and manages reproduction allocation to prevent dominant 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 | * <pre>{@code | |
49 | * // Default NEAT selection with standard parameters | |
50 | * NeatSelection<Double> defaultSelection = NeatSelection.ofDefault(); | |
51 | * | |
52 | * // Custom compatibility threshold | |
53 | * BiPredicate<Individual<Double>, Individual<Double>> compatibilityPredicate = | |
54 | * (i1, i2) -> NeatUtils.compatibilityDistance( | |
55 | * i1.genotype(), i2.genotype(), 0, 2, 2, 1.0f | |
56 | * ) < 3.0; // Higher threshold = fewer, larger species | |
57 | * | |
58 | * NeatSelection<Double> customSelection = NeatSelection.<Double>builder() | |
59 | * .perSpeciesKeepRatio(0.8f) // Keep top 80% of each species | |
60 | * .minSpeciesSize(3) // Minimum 3 individuals per species | |
61 | * .speciesPredicate(compatibilityPredicate) | |
62 | * .speciesSelection(Tournament.of(5)) // Tournament size 5 within species | |
63 | * .build(); | |
64 | * | |
65 | * // Aggressive diversity preservation | |
66 | * NeatSelection<Double> diverseSelection = NeatSelection.of( | |
67 | * 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 | |
118 | * after fitness-based culling. Higher values preserve more diversity within | |
119 | * species but may slow convergence, while lower values increase selection | |
120 | * pressure but may lose beneficial genetic variations. | |
121 | * | |
122 | * <p>Typical values: | |
123 | * <ul> | |
124 | * <li><strong>0.9 (default)</strong>: Preserve top 90% of each species</li> | |
125 | * <li><strong>0.8-0.95</strong>: Balanced diversity preservation</li> | |
126 | * <li><strong>< 0.8</strong>: Aggressive selection pressure</li> | |
127 | * <li><strong>> 0.95</strong>: Minimal selection pressure, maximum diversity</li> | |
128 | * </ul> | |
129 | * | |
130 | * @return keep ratio between 0.0 and 1.0 (exclusive of 0.0, inclusive of 1.0) | |
131 | */ | |
132 | @Value.Default | |
133 | public float perSpeciesKeepRatio() { | |
134 |
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; |
135 | } | |
136 | ||
137 | /** | |
138 | * Returns the minimum number of individuals required to maintain a species. | |
139 | * | |
140 | * <p>Species with fewer members than this threshold will be eliminated to | |
141 | * prevent resource waste on non-viable populations. This helps focus | |
142 | * evolutionary resources on species with sufficient genetic diversity | |
143 | * to explore their local fitness landscape effectively. | |
144 | * | |
145 | * <p>Typical values: | |
146 | * <ul> | |
147 | * <li><strong>5 (default)</strong>: Balanced viability threshold</li> | |
148 | * <li><strong>3-10</strong>: Reasonable range for most problems</li> | |
149 | * <li><strong>< 3</strong>: Very permissive, allows small species to survive</li> | |
150 | * <li><strong>> 10</strong>: Strict threshold, eliminates marginal species</li> | |
151 | * </ul> | |
152 | * | |
153 | * @return minimum species size (must be positive) | |
154 | */ | |
155 | @Value.Default | |
156 | public int minSpeciesSize() { | |
157 |
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; |
158 | } | |
159 | ||
160 | /** | |
161 | * Returns the predicate used to determine species membership. | |
162 | * | |
163 | * <p>This bi-predicate takes two individuals and returns true if they should | |
164 | * belong to the same species based on their genetic compatibility. Typically | |
165 | * implemented using NEAT compatibility distance with a threshold value. | |
166 | * | |
167 | * <p>Common implementations: | |
168 | * <ul> | |
169 | * <li><strong>Compatibility distance</strong>: Based on matching, disjoint, excess genes and weight differences</li> | |
170 | * <li><strong>Topological similarity</strong>: Based on network structure similarity</li> | |
171 | * <li><strong>Behavioral similarity</strong>: Based on network output patterns</li> | |
172 | * <li><strong>Custom metrics</strong>: Domain-specific similarity measures</li> | |
173 | * </ul> | |
174 | * | |
175 | * @return bi-predicate for determining species membership | |
176 | */ | |
177 | public abstract BiPredicate<Individual<T>, Individual<T>> speciesPredicate(); | |
178 | ||
179 | /** | |
180 | * Returns the selection policy used within each species. | |
181 | * | |
182 | * <p>After individuals are organized into species, this policy determines | |
183 | * how parents are selected within each species for reproduction. Common | |
184 | * choices include tournament selection, proportional selection, or rank-based | |
185 | * selection. | |
186 | * | |
187 | * <p>Selection policy considerations: | |
188 | * <ul> | |
189 | * <li><strong>Tournament selection</strong>: Good balance of selection pressure and diversity</li> | |
190 | * <li><strong>Proportional selection</strong>: Fitness-proportionate selection within species</li> | |
191 | * <li><strong>Rank selection</strong>: Rank-based selection to avoid fitness scaling issues</li> | |
192 | * <li><strong>Elite selection</strong>: Always select best individuals within species</li> | |
193 | * </ul> | |
194 | * | |
195 | * @return selection policy for within-species parent selection | |
196 | */ | |
197 | public abstract SelectionPolicy speciesSelection(); | |
198 | ||
199 | @Value.Check | |
200 | public void check() { | |
201 | Validate.inclusiveBetween(0.0f, 1.0f, perSpeciesKeepRatio()); | |
202 | Validate.isTrue(perSpeciesKeepRatio() > 0.0f); | |
203 | } | |
204 | ||
205 | public static class Builder<T extends Comparable<T>> extends ImmutableNeatSelection.Builder<T> { | |
206 | } | |
207 | ||
208 | public static <U extends Comparable<U>> Builder<U> builder() { | |
209 |
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>(); |
210 | } | |
211 | ||
212 | /** | |
213 | * Creates a NEAT selection policy with custom keep ratio and specified parameters. | |
214 | * | |
215 | * @param <U> the fitness value type | |
216 | * @param perSpeciesKeepRatio proportion of each species to preserve (0.0 < ratio <= 1.0) | |
217 | * @param speciesPredicate predicate for determining species membership | |
218 | * @param speciesSelection selection policy for within-species parent selection | |
219 | * @return a new NEAT selection policy with the specified parameters | |
220 | */ | |
221 | public static <U extends Comparable<U>> NeatSelection<U> of(final float perSpeciesKeepRatio, | |
222 | final BiPredicate<Individual<U>, Individual<U>> speciesPredicate, final SelectionPolicy speciesSelection) { | |
223 |
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) |
224 |
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) |
225 |
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) |
226 |
1
1. of : removed call to net/bmahe/genetics4j/neat/spec/selection/NeatSelection$Builder::build → NO_COVERAGE |
.build(); |
227 | } | |
228 | ||
229 | /** | |
230 | * Creates a NEAT selection policy with default keep ratio and specified parameters. | |
231 | * | |
232 | * @param <U> the fitness value type | |
233 | * @param speciesPredicate predicate for determining species membership | |
234 | * @param speciesSelection selection policy for within-species parent selection | |
235 | * @return a new NEAT selection policy with default keep ratio (0.9) | |
236 | */ | |
237 | public static <U extends Comparable<U>> NeatSelection<U> of( | |
238 | final BiPredicate<Individual<U>, Individual<U>> speciesPredicate, final SelectionPolicy speciesSelection) { | |
239 |
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) |
240 |
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) |
241 |
1
1. of : removed call to net/bmahe/genetics4j/neat/spec/selection/NeatSelection$Builder::build → KILLED |
.build(); |
242 | } | |
243 | ||
244 | /** | |
245 | * Creates a NEAT selection policy with standard default parameters. | |
246 | * | |
247 | * <p>Default configuration: | |
248 | * <ul> | |
249 | * <li><strong>Keep ratio</strong>: 0.9 (preserve top 90% of each species)</li> | |
250 | * <li><strong>Minimum species size</strong>: 5 individuals</li> | |
251 | * <li><strong>Compatibility distance</strong>: Threshold of 1.0 with standard coefficients</li> | |
252 | * <li><strong>Species selection</strong>: Tournament selection with size 3</li> | |
253 | * </ul> | |
254 | * | |
255 | * <p>Compatibility distance uses: | |
256 | * <ul> | |
257 | * <li>Weight coefficient: 1.0</li> | |
258 | * <li>Excess gene coefficient: 2.0</li> | |
259 | * <li>Disjoint gene coefficient: 2.0</li> | |
260 | * <li>Distance threshold: 1.0</li> | |
261 | * </ul> | |
262 | * | |
263 | * @param <U> the fitness value type | |
264 | * @return a new NEAT selection policy with standard default parameters | |
265 | */ | |
266 | public static <U extends Comparable<U>> NeatSelection<U> ofDefault() { | |
267 |
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>() |
268 |
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( |
269 |
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) |
270 |
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)) |
271 |
1
1. ofDefault : removed call to net/bmahe/genetics4j/neat/spec/selection/NeatSelection$Builder::build → KILLED |
.build(); |
272 | } | |
273 | } | |
Mutations | ||
134 |
1.1 2.2 |
|
157 |
1.1 2.2 |
|
209 |
1.1 2.2 |
|
223 |
1.1 2.2 3.3 4.4 |
|
224 |
1.1 2.2 |
|
225 |
1.1 2.2 |
|
226 |
1.1 |
|
239 |
1.1 2.2 3.3 4.4 |
|
240 |
1.1 2.2 |
|
241 |
1.1 |
|
267 |
1.1 2.2 |
|
268 |
1.1 2.2 3.3 |
|
269 |
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 |
|
270 |
1.1 2.2 3.3 |
|
271 |
1.1 |