1 package net.bmahe.genetics4j.neat.mutation.chromosome;
2
3 import java.util.ArrayList;
4 import java.util.HashSet;
5 import java.util.List;
6 import java.util.Objects;
7 import java.util.Set;
8 import java.util.random.RandomGenerator;
9 import java.util.stream.Stream;
10
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.mutation.chromosome.ChromosomeMutationHandler;
17 import net.bmahe.genetics4j.core.spec.chromosome.ChromosomeSpec;
18 import net.bmahe.genetics4j.core.spec.mutation.MutationPolicy;
19 import net.bmahe.genetics4j.neat.Connection;
20 import net.bmahe.genetics4j.neat.chromosomes.NeatChromosome;
21 import net.bmahe.genetics4j.neat.spec.NeatChromosomeSpec;
22 import net.bmahe.genetics4j.neat.spec.mutation.DeleteNode;
23
24 public class NeatChromosomeDeleteNodeMutationHandler implements ChromosomeMutationHandler<NeatChromosome> {
25
26 public static final Logger logger = LogManager.getLogger(NeatChromosomeDeleteNodeMutationHandler.class);
27
28 private final RandomGenerator randomGenerator;
29
30 public NeatChromosomeDeleteNodeMutationHandler(final RandomGenerator _randomGenerator) {
31 Objects.requireNonNull(_randomGenerator);
32
33 this.randomGenerator = _randomGenerator;
34 }
35
36 @Override
37 public boolean canHandle(final MutationPolicy mutationPolicy, final ChromosomeSpec chromosome) {
38 Objects.requireNonNull(mutationPolicy);
39 Objects.requireNonNull(chromosome);
40
41 return mutationPolicy instanceof DeleteNode && chromosome instanceof NeatChromosomeSpec;
42 }
43
44 @Override
45 public NeatChromosome mutate(final MutationPolicy mutationPolicy, final Chromosome chromosome) {
46 Objects.requireNonNull(mutationPolicy);
47 Objects.requireNonNull(chromosome);
48 Validate.isInstanceOf(DeleteNode.class, mutationPolicy);
49 Validate.isInstanceOf(NeatChromosome.class, chromosome);
50
51 final var neatChromosome = (NeatChromosome) chromosome;
52 final var minValue = neatChromosome.getMinWeightValue();
53 final var maxValue = neatChromosome.getMaxWeightValue();
54
55 final var oldConnections = neatChromosome.getConnections();
56
57 final Set<Integer> inoutNodes = new HashSet<>();
58 inoutNodes.addAll(neatChromosome.getInputNodeIds());
59 inoutNodes.addAll(neatChromosome.getOutputNodeIds());
60
61 final List<Integer> allNodeValues = neatChromosome.getConnections()
62 .stream()
63 .flatMap(connection -> Stream.of(connection.fromNodeIndex(), connection.toNodeIndex()))
64 .filter(nodeIndex -> inoutNodes.contains(nodeIndex) == false)
65 .toList();
66
67 final Set<Integer> nodeValues = Set.copyOf(allNodeValues);
68
69 final List<Connection> newConnections = switch (nodeValues.size()) {
70 case 0 -> new ArrayList<>(oldConnections);
71 default -> {
72 final int nodeIndexToRemove = nodeValues.size() > 1 ? randomGenerator.nextInt(nodeValues.size() - 1) : 0;
73
74 final int nodeValueToRemove = nodeValues.stream().skip(nodeIndexToRemove).findFirst().get();
75
76 yield oldConnections.stream()
77 .filter(
78 connection -> connection.fromNodeIndex() != nodeValueToRemove
79 && connection.toNodeIndex() != nodeValueToRemove)
80 .toList();
81 }
82 };
83
84 return new NeatChromosome(neatChromosome.getNodeLayout(), minValue, maxValue, newConnections);
85 }
86 }