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 |