1 package net.bmahe.genetics4j.core;
2
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.List;
6 import java.util.Objects;
7 import java.util.Optional;
8 import java.util.stream.IntStream;
9
10 import org.apache.commons.collections4.CollectionUtils;
11 import org.apache.commons.lang3.Validate;
12 import org.apache.logging.log4j.LogManager;
13 import org.apache.logging.log4j.Logger;
14
15 import net.bmahe.genetics4j.core.chromosomes.Chromosome;
16 import net.bmahe.genetics4j.core.chromosomes.factory.ChromosomeFactoryProvider;
17 import net.bmahe.genetics4j.core.combination.ChromosomeCombinator;
18 import net.bmahe.genetics4j.core.combination.GenotypeCombinator;
19 import net.bmahe.genetics4j.core.evaluation.FitnessEvaluator;
20 import net.bmahe.genetics4j.core.evolutionlisteners.EvolutionListener;
21 import net.bmahe.genetics4j.core.mutation.Mutator;
22 import net.bmahe.genetics4j.core.replacement.ReplacementStrategyImplementor;
23 import net.bmahe.genetics4j.core.selection.Selector;
24 import net.bmahe.genetics4j.core.spec.AbstractEAConfiguration;
25 import net.bmahe.genetics4j.core.spec.AbstractEAExecutionContext;
26 import net.bmahe.genetics4j.core.spec.EvolutionResult;
27 import net.bmahe.genetics4j.core.spec.ImmutableEvolutionResult;
28 import net.bmahe.genetics4j.core.spec.PostEvaluationProcessor;
29 import net.bmahe.genetics4j.core.termination.Termination;
30 import net.bmahe.genetics4j.core.util.GenotypeGenerator;
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113 public class EASystem<T extends Comparable<T>> {
114 public static final Logger logger = LogManager.getLogger(EASystem.class);
115
116 private final GenotypeGenerator<T> genotypeGenerator;
117 private final FitnessEvaluator<T> fitnessEvaluator;
118 private final AbstractEAConfiguration<T> eaConfiguration;
119 private final AbstractEAExecutionContext<T> eaExecutionContext;
120 private final int populationSize;
121
122 private final List<ChromosomeCombinator<T>> chromosomeCombinators;
123 private final ChromosomeFactoryProvider chromosomeFactoryProvider;
124
125 private final ReplacementStrategyImplementor<T> replacementStrategyImplementor;
126
127 private final List<Mutator> mutators;
128
129 private final double offspringRatio;
130
131 private final Selector<T> parentSelector;
132
133 public EASystem(final AbstractEAConfiguration<T> _eaConfiguration,
134 final long _populationSize,
135 final List<ChromosomeCombinator<T>> _chromosomeCombinators,
136 final double _offspringRatio,
137 final Selector<T> _parentSelectionPolicyHandler,
138 final List<Mutator> _mutators,
139 final ReplacementStrategyImplementor<T> _replacementStrategyImplementor,
140 final AbstractEAExecutionContext<T> _eaExecutionContext,
141 final FitnessEvaluator<T> _fitnessEvaluator) {
142 Objects.requireNonNull(_eaConfiguration);
143 Validate.isTrue(_populationSize > 0);
144 Objects.requireNonNull(_chromosomeCombinators);
145 Validate.isTrue(_chromosomeCombinators.size() == _eaConfiguration.numChromosomes());
146 Validate.inclusiveBetween(0.0, 1.0, _offspringRatio);
147 Objects.requireNonNull(_parentSelectionPolicyHandler);
148 Objects.requireNonNull(_replacementStrategyImplementor);
149 Objects.requireNonNull(_eaExecutionContext);
150 Objects.requireNonNull(_fitnessEvaluator);
151
152 this.eaConfiguration = _eaConfiguration;
153 this.eaExecutionContext = _eaExecutionContext;
154 this.populationSize = (int) _populationSize;
155 this.chromosomeCombinators = _chromosomeCombinators;
156 this.offspringRatio = _offspringRatio;
157 this.mutators = _mutators;
158 this.chromosomeFactoryProvider = _eaExecutionContext.chromosomeFactoryProvider();
159 this.fitnessEvaluator = _fitnessEvaluator;
160
161 parentSelector = _parentSelectionPolicyHandler;
162
163 this.replacementStrategyImplementor = _replacementStrategyImplementor;
164 this.genotypeGenerator = new GenotypeGenerator<>(chromosomeFactoryProvider, eaConfiguration);
165 }
166
167 private List<T> evaluate(final long generation, final List<Genotype> population) {
168 Validate.isTrue(generation >= 0);
169 Objects.requireNonNull(population);
170 Validate.isTrue(population.isEmpty() == false);
171
172 logger.debug("Evaluating population of size {}", population.size());
173 final List<T> fitnesses = fitnessEvaluator.evaluate(generation, population);
174
175 logger.debug("Done evaluating population of size {}", population.size());
176 return fitnesses;
177 }
178
179 private List<Genotype> initializePopulation() {
180 final int initialPopulationSize = eaExecutionContext.populationSize();
181 logger.info("Generating initial population of {} individuals", initialPopulationSize);
182
183 final List<Genotype> genotypes = new ArrayList<>(initialPopulationSize);
184
185 final var seedPopulation = eaConfiguration.seedPopulation();
186 if (CollectionUtils.isNotEmpty(seedPopulation)) {
187 genotypes.addAll(seedPopulation);
188 }
189 if (genotypes.size() < initialPopulationSize) {
190 final var missingInitialIndividualCount = initialPopulationSize - genotypes.size();
191 logger.info(
192 "{} seed individual(s) added and generating {} individuals to reach the target of {} initial population size",
193 genotypes.size(),
194 missingInitialIndividualCount,
195 initialPopulationSize);
196
197 final var extraIndividuals = genotypeGenerator.generateGenotypes(missingInitialIndividualCount);
198 genotypes.addAll(extraIndividuals);
199 }
200
201 return genotypes;
202 }
203
204 private List<Genotype> mutateGenotypes(final long generation, final List<Genotype> genotypes) {
205 Validate.isTrue(generation >= 0);
206 Objects.requireNonNull(genotypes);
207
208 return genotypes.stream().map(child -> {
209 Genotype mutatedChild = child;
210
211 for (final Mutator mutator : mutators) {
212 mutatedChild = mutator.mutate(generation, mutatedChild);
213 }
214
215 return mutatedChild;
216 }).toList();
217 }
218
219 private List<Genotype> combineParents(final Population<T> parents, final GenotypeCombinator genotypeCombinator) {
220 Objects.requireNonNull(parents);
221 Objects.requireNonNull(genotypeCombinator);
222
223 return IntStream.range(0, parents.size() / 2).parallel().boxed().flatMap(baseIndex -> {
224 final int firstParentIndex = baseIndex * 2;
225 final Genotype firstParent = parents.getGenotype(firstParentIndex);
226 final T firstParentFitness = parents.getFitness(firstParentIndex);
227
228 final int secondParentIndex = firstParentIndex + 1;
229 final Genotype secondParent = parents.getGenotype(secondParentIndex);
230 final T secondParentFitness = parents.getFitness(secondParentIndex);
231
232 final List<List<Chromosome>> chromosomes = new ArrayList<>();
233 for (int chromosomeIndex = 0; chromosomeIndex < eaConfiguration.numChromosomes(); chromosomeIndex++) {
234
235 final Chromosome firstChromosome = firstParent.getChromosome(chromosomeIndex);
236 final Chromosome secondChromosome = secondParent.getChromosome(chromosomeIndex);
237
238 final List<Chromosome> combinedChromosomes = chromosomeCombinators.get(chromosomeIndex)
239 .combine(eaConfiguration, firstChromosome, firstParentFitness, secondChromosome, secondParentFitness);
240
241 chromosomes.add(combinedChromosomes);
242 logger.trace("Combining {} with {} ---> {}", firstChromosome, secondChromosome, combinedChromosomes);
243 }
244
245 final List<Genotype> offsprings = genotypeCombinator.combine(eaConfiguration, chromosomes);
246 return offsprings.stream();
247 }).toList();
248 }
249
250
251
252
253
254
255
256
257
258 private List<Genotype> createBasicOffsprings(final long generation, final Population<T> population,
259 final int offspringsNeeded) {
260 Objects.requireNonNull(population);
261 Validate.isTrue(offspringsNeeded > 0);
262
263 final GenotypeCombinator genotypeCombinator = eaConfiguration.genotypeCombinator();
264
265 final int parentsNeeded = (int) (offspringsNeeded * 2);
266 logger.info("Selecting {} parents as we expect to generate {} offsprings", parentsNeeded, offspringsNeeded);
267 final Population<T> selectedParents = parentSelector.select(
268 eaConfiguration,
269 generation,
270 parentsNeeded,
271 population.getAllGenotypes(),
272 population.getAllFitnesses());
273 logger.trace("Selected parents: {}", selectedParents);
274 Validate.isTrue(selectedParents.size() % 2 == 0);
275
276 logger.info("Combining {} parents into offsprings", selectedParents.size());
277 return combineParents(selectedParents, genotypeCombinator);
278 }
279
280
281
282
283
284
285 public AbstractEAConfiguration<T> getEAConfiguration() {
286 return eaConfiguration;
287 }
288
289
290
291
292
293
294 public long getPopulationSize() {
295 return populationSize;
296 }
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315 public List<Genotype> createOffsprings(final long generation, final Population<T> population,
316 final int offspringsNeeded) {
317 Objects.requireNonNull(population);
318 Validate.isTrue(offspringsNeeded > 0);
319
320 final List<Genotype> offpsrings = createBasicOffsprings(generation, population, offspringsNeeded);
321 logger.info("Generated {} offsprings", offpsrings.size());
322
323 logger.info("Mutating offsprigns");
324 return mutateGenotypes(generation, offpsrings);
325 }
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346 public EvolutionResult<T> evolve() {
347 final Termination<T> termination = eaConfiguration.termination();
348
349 logger.info("Starting evolution");
350
351 final Optional<PostEvaluationProcessor<T>> postEvaluationProcessor = eaConfiguration.postEvaluationProcessor();
352 final List<EvolutionListener<T>> evolutionListeners = eaExecutionContext.evolutionListeners();
353
354 fitnessEvaluator.preEvaluation();
355 evolutionListeners.forEach(evolutionListener -> evolutionListener.preEvaluation(eaConfiguration));
356
357 long generation = 0;
358 final List<Genotype> genotypes = initializePopulation();
359
360 logger.info("Evaluating initial population");
361 final List<T> fitnessScore = evaluate(generation, genotypes);
362 final Population<T> initialPopulation = Population.of(genotypes, fitnessScore);
363
364 Population<T> population = postEvaluationProcessor.map(pep -> pep.apply(0, initialPopulation))
365 .orElseGet(() -> initialPopulation);
366
367 while (termination
368 .isDone(eaConfiguration, generation, population.getAllGenotypes(), population.getAllFitnesses()) == false) {
369 final long currentGeneration = generation;
370 logger.info("Going through evolution of generation {}", currentGeneration);
371
372 for (final EvolutionListener<T> evolutionListener : evolutionListeners) {
373 evolutionListener
374 .onEvolution(currentGeneration, population.getAllGenotypes(), population.getAllFitnesses(), false);
375 }
376
377 final int offspringsNeeded = (int) (populationSize * offspringRatio);
378 final List<Genotype> offsprings = createOffsprings(currentGeneration, population, offspringsNeeded);
379
380 logger.info("Evaluating offsprings");
381 final List<T> offspringScores = evaluate(currentGeneration, offsprings);
382
383 final Population<T> childrenPopulation = postEvaluationProcessor
384 .map(pep -> pep.apply(currentGeneration, Population.of(offsprings, offspringScores)))
385 .orElseGet(() -> Population.of(offsprings, offspringScores));
386
387 logger.info("Executing replacement strategy");
388 final int nextGenerationPopulationSize = eaExecutionContext.populationSize();
389 final Population<T> newPopulation = replacementStrategyImplementor.select(
390 eaConfiguration,
391 currentGeneration,
392 nextGenerationPopulationSize,
393 population.getAllGenotypes(),
394 population.getAllFitnesses(),
395 childrenPopulation.getAllGenotypes(),
396 childrenPopulation.getAllFitnesses());
397
398 if (newPopulation.size() < nextGenerationPopulationSize) {
399 logger.info("New population only has {} members. Generating more individuals", newPopulation.size());
400 final List<Genotype> additionalIndividuals = genotypeGenerator
401 .generateGenotypes(nextGenerationPopulationSize - newPopulation.size());
402 logger.debug("Number of generated individuals: {}", additionalIndividuals.size());
403
404 if (additionalIndividuals.isEmpty() == false) {
405
406 final List<T> additionalFitness = evaluate(currentGeneration, additionalIndividuals);
407 newPopulation.addAll(Population.of(additionalIndividuals, additionalFitness));
408 }
409 }
410
411 if (logger.isTraceEnabled()) {
412 logger.trace("[Generation {}] New population: {}", currentGeneration, Arrays.asList(newPopulation));
413 }
414 population = newPopulation;
415 generation++;
416 }
417
418 logger.info("Evolution has terminated");
419
420
421 for (final EvolutionListener<T> evolutionListener : evolutionListeners) {
422 evolutionListener.onEvolution(generation, population.getAllGenotypes(), population.getAllFitnesses(), true);
423 evolutionListener.postEvaluation(eaConfiguration);
424 }
425
426 fitnessEvaluator.postEvaluation();
427
428 return ImmutableEvolutionResult
429 .of(eaConfiguration, generation, population.getAllGenotypes(), population.getAllFitnesses());
430 }
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452 public List<T> evaluateOnce(final long generation, final List<Genotype> genotypes) {
453 Validate.isTrue(generation >= 0);
454 Objects.requireNonNull(genotypes);
455 Validate.isTrue(genotypes.isEmpty() == false);
456
457 final List<EvolutionListener<T>> evolutionListeners = eaExecutionContext.evolutionListeners();
458
459 fitnessEvaluator.preEvaluation();
460 evolutionListeners.forEach(evolutionListener -> evolutionListener.preEvaluation(eaConfiguration));
461 final var fitness = evaluate(generation, genotypes);
462
463 final var population = Population.of(genotypes, fitness);
464 for (final EvolutionListener<T> evolutionListener : eaExecutionContext.evolutionListeners()) {
465 evolutionListener.onEvolution(generation, population.getAllGenotypes(), population.getAllFitnesses(), true);
466 }
467
468 evolutionListeners.forEach(evolutionListener -> evolutionListener.postEvaluation(eaConfiguration));
469 fitnessEvaluator.postEvaluation();
470
471 return fitness;
472 }
473 }