View Javadoc
1   package net.bmahe.genetics4j.neat.mutation.chromosome;
2   
3   import java.util.ArrayList;
4   import java.util.List;
5   import java.util.Objects;
6   import java.util.random.RandomGenerator;
7   
8   import net.bmahe.genetics4j.neat.Connection;
9   import net.bmahe.genetics4j.neat.InnovationManager;
10  import net.bmahe.genetics4j.neat.NodeIdManagerRegistry;
11  import net.bmahe.genetics4j.neat.chromosomes.NeatChromosome;
12  import net.bmahe.genetics4j.neat.spec.mutation.AddNode;
13  
14  public class NeatChromosomeAddNodeMutationHandler extends AbstractNeatChromosomeConnectionMutationHandler<AddNode> {
15  
16  	private final RandomGenerator randomGenerator;
17  	private final InnovationManager innovationManager;
18  	private final NodeIdManagerRegistry nodeIdManagerRegistry;
19  
20  	public NeatChromosomeAddNodeMutationHandler(final RandomGenerator randomGenerator,
21  			final InnovationManager innovationManager) {
22  		this(randomGenerator, innovationManager, new NodeIdManagerRegistry());
23  	}
24  
25  	public NeatChromosomeAddNodeMutationHandler(final RandomGenerator _randomGenerator,
26  			final InnovationManager _innovationManager,
27  			final NodeIdManagerRegistry _nodeIdManagerRegistry) {
28  		super(AddNode.class, _randomGenerator);
29  		Objects.requireNonNull(_randomGenerator);
30  		Objects.requireNonNull(_innovationManager);
31  		Objects.requireNonNull(_nodeIdManagerRegistry);
32  
33  		this.randomGenerator = _randomGenerator;
34  		this.innovationManager = _innovationManager;
35  		this.nodeIdManagerRegistry = _nodeIdManagerRegistry;
36  	}
37  
38  	@Override
39  	protected List<Connection> mutateConnection(final AddNode mutationPolicy, final NeatChromosome neatChromosome,
40  			final Connection oldConnection, final int i) {
41  
42  		/*
43  		 * Add-node mutations split enabled connections only. A disabled connection may already have been split, so
44  		 * splitting it again could recreate child links that use the same stable hidden-node ID.
45  		 */
46  		if (oldConnection.isEnabled() == false) {
47  			return List.of(Connection.copyOf(oldConnection));
48  		}
49  
50  		final List<Connection> connections = new ArrayList<>();
51  
52  		final var disabledConnection = Connection.builder().from(oldConnection).isEnabled(false).build();
53  
54  		final var nodeIdManager = nodeIdManagerRegistry.managerFor(neatChromosome.getNodeLayout());
55  		nodeIdManager.registerExistingNodeIds(
56  				neatChromosome.getConnections()
57  						.stream()
58  						.flatMap(
59  								connection -> java.util.stream.Stream.of(connection.fromNodeIndex(), connection.toNodeIndex()))
60  						.toList());
61  		final int newNodeValue = nodeIdManager.nodeIdForSplit(oldConnection.innovation());
62  
63  		connections.add(disabledConnection);
64  
65  		final boolean firstConnectionAlreadyExists = neatChromosome.getConnections()
66  				.stream()
67  				.anyMatch(
68  						connection -> connection.fromNodeIndex() == oldConnection.fromNodeIndex()
69  								&& connection.toNodeIndex() == newNodeValue);
70  		if (firstConnectionAlreadyExists == false) {
71  			final int firstInnovation = innovationManager.computeNewId(oldConnection.fromNodeIndex(), newNodeValue);
72  			final var firstConnection = Connection.builder()
73  					.from(oldConnection)
74  					.weight(1.0f)
75  					.toNodeIndex(newNodeValue)
76  					.innovation(firstInnovation)
77  					.build();
78  			connections.add(firstConnection);
79  		}
80  
81  		final boolean secondConnectionAlreadyExists = neatChromosome.getConnections()
82  				.stream()
83  				.anyMatch(
84  						connection -> connection.fromNodeIndex() == newNodeValue
85  								&& connection.toNodeIndex() == oldConnection.toNodeIndex());
86  		if (secondConnectionAlreadyExists == false) {
87  			final int secondInnovation = innovationManager.computeNewId(newNodeValue, oldConnection.toNodeIndex());
88  			final var secondConnection = Connection.builder()
89  					.from(oldConnection)
90  					.fromNodeIndex(newNodeValue)
91  					.innovation(secondInnovation)
92  					.build();
93  			connections.add(secondConnection);
94  		}
95  
96  		return connections;
97  	}
98  
99  }