1 package net.bmahe.genetics4j.neat.mutation.chromosome;
2
3 import java.util.List;
4 import java.util.Objects;
5 import java.util.function.Supplier;
6 import java.util.random.RandomGenerator;
7
8 import org.apache.commons.lang3.Validate;
9 import org.apache.logging.log4j.LogManager;
10 import org.apache.logging.log4j.Logger;
11
12 import net.bmahe.genetics4j.core.chromosomes.Chromosome;
13 import net.bmahe.genetics4j.core.mutation.chromosome.ChromosomeMutationHandler;
14 import net.bmahe.genetics4j.core.spec.chromosome.ChromosomeSpec;
15 import net.bmahe.genetics4j.core.spec.mutation.MutationPolicy;
16 import net.bmahe.genetics4j.core.spec.statistics.distributions.Distribution;
17 import net.bmahe.genetics4j.core.util.DistributionUtils;
18 import net.bmahe.genetics4j.neat.Connection;
19 import net.bmahe.genetics4j.neat.chromosomes.NeatChromosome;
20 import net.bmahe.genetics4j.neat.spec.NeatChromosomeSpec;
21 import net.bmahe.genetics4j.neat.spec.mutation.NeatConnectionWeight;
22
23 public class NeatChromosomeConnectionWeightMutationHandler implements ChromosomeMutationHandler<NeatChromosome> {
24
25 public static final Logger logger = LogManager.getLogger(NeatChromosomeConnectionWeightMutationHandler.class);
26
27 private final RandomGenerator randomGenerator;
28
29 public NeatChromosomeConnectionWeightMutationHandler(final RandomGenerator _randomGenerator) {
30 Objects.requireNonNull(_randomGenerator);
31
32 this.randomGenerator = _randomGenerator;
33 }
34
35 @Override
36 public boolean canHandle(final MutationPolicy mutationPolicy, final ChromosomeSpec chromosome) {
37 Objects.requireNonNull(mutationPolicy);
38 Objects.requireNonNull(chromosome);
39
40 return mutationPolicy instanceof NeatConnectionWeight && chromosome instanceof NeatChromosomeSpec;
41 }
42
43 protected float perturbateWeight(final float weight, final float disturbance, final float minValue,
44 final float maxValue) {
45 Validate.isTrue(minValue <= maxValue);
46
47 float newWeight = weight + disturbance;
48 if (newWeight > maxValue) {
49 newWeight = maxValue;
50 } else if (newWeight < minValue) {
51 newWeight = minValue;
52 }
53
54 return newWeight;
55 }
56
57 protected Connection mutateConnection(final Connection connection, final double perturbationRatio,
58 final Supplier<Float> distributionValueSupplier, final Supplier<Float> distributionNewValueSupplier,
59 final float minValue, final float maxValue) {
60
61 final var connectionBuilder = Connection.builder().from(connection);
62
63 float newWeight = connection.weight();
64 if (randomGenerator.nextDouble() < perturbationRatio) {
65 final float disturbance = distributionValueSupplier.get();
66 newWeight = perturbateWeight(newWeight, disturbance, minValue, maxValue);
67 } else {
68 newWeight = distributionNewValueSupplier.get();
69 }
70 connectionBuilder.weight(newWeight);
71 return connectionBuilder.build();
72 }
73
74 @Override
75 public NeatChromosome mutate(final MutationPolicy mutationPolicy, final Chromosome chromosome) {
76 Objects.requireNonNull(mutationPolicy);
77 Objects.requireNonNull(chromosome);
78 Validate.isInstanceOf(NeatConnectionWeight.class, mutationPolicy);
79 Validate.isInstanceOf(NeatChromosome.class, chromosome);
80
81 final var neatChromosome = (NeatChromosome) chromosome;
82 final var minValue = neatChromosome.getMinWeightValue();
83 final var maxValue = neatChromosome.getMaxWeightValue();
84
85 final var neatConnectionWeight = (NeatConnectionWeight) mutationPolicy;
86 final Distribution perturbationDistribution = neatConnectionWeight.perturbationDistribution();
87 final double perturbationRatio = neatConnectionWeight.perturbationRatio();
88 final Distribution newValuesDistribution = neatConnectionWeight.newValuesDistribution();
89
90 final Supplier<Float> distributionValueSupplier = DistributionUtils
91 .distributionFloatValueSupplier(randomGenerator, minValue, maxValue, perturbationDistribution);
92 final Supplier<Float> distributionNewValueSupplier = DistributionUtils
93 .distributionFloatValueSupplier(randomGenerator, minValue, maxValue, newValuesDistribution);
94
95 final var oldConnections = neatChromosome.getConnections();
96 final List<Connection> newConnections = oldConnections.stream()
97 .map(
98 connection -> mutateConnection(
99 connection,
100 perturbationRatio,
101 distributionValueSupplier,
102 distributionNewValueSupplier,
103 minValue,
104 maxValue))
105 .toList();
106
107 return new NeatChromosome(neatChromosome.getNodeLayout(), minValue, maxValue, newConnections);
108 }
109 }