1 | package net.bmahe.genetics4j.neat.chromosomes.factory; | |
2 | ||
3 | import java.util.ArrayList; | |
4 | import java.util.List; | |
5 | import java.util.random.RandomGenerator; | |
6 | ||
7 | import org.apache.commons.lang3.Validate; | |
8 | ||
9 | import net.bmahe.genetics4j.core.chromosomes.factory.ChromosomeFactory; | |
10 | import net.bmahe.genetics4j.core.spec.chromosome.ChromosomeSpec; | |
11 | import net.bmahe.genetics4j.neat.Connection; | |
12 | import net.bmahe.genetics4j.neat.InnovationManager; | |
13 | import net.bmahe.genetics4j.neat.chromosomes.NeatChromosome; | |
14 | import net.bmahe.genetics4j.neat.spec.NeatChromosomeSpec; | |
15 | ||
16 | /** | |
17 | * Factory for creating fully-connected initial NEAT (NeuroEvolution of Augmenting Topologies) chromosomes. | |
18 | * | |
19 | * <p>NeatConnectedChromosomeFactory generates initial neural network chromosomes with direct connections between all | |
20 | * input and output nodes. This provides a minimal starting topology that ensures all inputs can influence all outputs, | |
21 | * creating a foundation for structural evolution through the NEAT algorithm. | |
22 | * | |
23 | * <p>Generated network characteristics: | |
24 | * <ul> | |
25 | * <li><strong>Full connectivity</strong>: Every input node connected to every output node</li> | |
26 | * <li><strong>No hidden nodes</strong>: Initial networks contain only input and output layers</li> | |
27 | * <li><strong>Random weights</strong>: Connection weights uniformly distributed within specified bounds</li> | |
28 | * <li><strong>Innovation tracking</strong>: All connections assigned unique innovation numbers</li> | |
29 | * </ul> | |
30 | * | |
31 | * <p>Network topology structure: | |
32 | * <ul> | |
33 | * <li><strong>Input layer</strong>: Nodes 0 to (numInputs - 1)</li> | |
34 | * <li><strong>Output layer</strong>: Nodes numInputs to (numInputs + numOutputs - 1)</li> | |
35 | * <li><strong>Connections</strong>: numInputs × numOutputs fully-connected bipartite graph</li> | |
36 | * <li><strong>Enabled state</strong>: All initial connections are enabled</li> | |
37 | * </ul> | |
38 | * | |
39 | * <p>Common usage patterns: | |
40 | * | |
41 | * <pre>{@code | |
42 | * // Create factory with innovation manager | |
43 | * RandomGenerator randomGen = RandomGenerator.getDefault(); | |
44 | * InnovationManager innovationManager = new InnovationManager(); | |
45 | * NeatConnectedChromosomeFactory factory = new NeatConnectedChromosomeFactory(randomGen, innovationManager); | |
46 | * | |
47 | * // Define network specification | |
48 | * NeatChromosomeSpec spec = NeatChromosomeSpec.of(3, // 3 input nodes | |
49 | * 2, // 2 output nodes | |
50 | * -1.0f, // minimum weight | |
51 | * 1.0f // maximum weight | |
52 | * ); | |
53 | * | |
54 | * // Generate initial chromosome | |
55 | * NeatChromosome chromosome = factory.generate(spec); | |
56 | * | |
57 | * // Result: 3×2 = 6 connections with random weights | |
58 | * // Connections: (0→3), (0→4), (1→3), (1→4), (2→3), (2→4) | |
59 | * }</pre> | |
60 | * | |
61 | * <p>Integration with NEAT evolution: | |
62 | * <ul> | |
63 | * <li><strong>Population initialization</strong>: Creates diverse initial population with same topology</li> | |
64 | * <li><strong>Weight diversity</strong>: Random weights provide behavioral variation</li> | |
65 | * <li><strong>Structural foundation</strong>: Minimal topology allows maximum structural exploration</li> | |
66 | * <li><strong>Innovation consistency</strong>: Same connection types get same innovation numbers across population</li> | |
67 | * </ul> | |
68 | * | |
69 | * <p>Innovation number management: | |
70 | * <ul> | |
71 | * <li><strong>Deterministic assignment</strong>: Same input-output pairs get same innovation numbers</li> | |
72 | * <li><strong>Population consistency</strong>: All individuals use same innovation numbers for same connections</li> | |
73 | * <li><strong>Crossover compatibility</strong>: Enables meaningful genetic recombination from generation 0</li> | |
74 | * <li><strong>Historical tracking</strong>: Foundation for tracking structural evolution</li> | |
75 | * </ul> | |
76 | * | |
77 | * <p>Weight initialization strategy: | |
78 | * <ul> | |
79 | * <li><strong>Uniform distribution</strong>: Weights uniformly sampled from [minWeight, maxWeight]</li> | |
80 | * <li><strong>Behavioral diversity</strong>: Different weight combinations create different behaviors</li> | |
81 | * <li><strong>Network stability</strong>: Bounded weights prevent extreme activation values</li> | |
82 | * <li><strong>Evolution readiness</strong>: Initial weights suitable for gradient-based optimization</li> | |
83 | * </ul> | |
84 | * | |
85 | * <p>Performance considerations: | |
86 | * <ul> | |
87 | * <li><strong>Linear time complexity</strong>: O(numInputs × numOutputs) generation time</li> | |
88 | * <li><strong>Memory efficiency</strong>: Minimal memory allocation during generation</li> | |
89 | * <li><strong>Innovation caching</strong>: InnovationManager provides O(1) innovation number lookup</li> | |
90 | * <li><strong>Thread safety</strong>: Safe for concurrent chromosome generation</li> | |
91 | * </ul> | |
92 | * | |
93 | * @see NeatChromosome | |
94 | * @see NeatChromosomeSpec | |
95 | * @see InnovationManager | |
96 | * @see ChromosomeFactory | |
97 | */ | |
98 | public class NeatConnectedChromosomeFactory implements ChromosomeFactory<NeatChromosome> { | |
99 | ||
100 | private final RandomGenerator randomGenerator; | |
101 | private final InnovationManager innovationManager; | |
102 | ||
103 | /** | |
104 | * Constructs a new connected chromosome factory with the specified components. | |
105 | * | |
106 | * <p>The random generator is used for weight initialization, providing behavioral diversity in the initial | |
107 | * population. The innovation manager ensures consistent innovation number assignment across all generated | |
108 | * chromosomes. | |
109 | * | |
110 | * @param _randomGenerator random number generator for weight initialization | |
111 | * @param _innovationManager innovation manager for tracking structural innovations | |
112 | * @throws IllegalArgumentException if randomGenerator or innovationManager is null | |
113 | */ | |
114 | public NeatConnectedChromosomeFactory(final RandomGenerator _randomGenerator, | |
115 | final InnovationManager _innovationManager) { | |
116 | Validate.notNull(_randomGenerator); | |
117 | Validate.notNull(_innovationManager); | |
118 | ||
119 |
1
1. <init> : Removed assignment to member variable randomGenerator → KILLED |
this.randomGenerator = _randomGenerator; |
120 |
1
1. <init> : Removed assignment to member variable innovationManager → KILLED |
this.innovationManager = _innovationManager; |
121 | } | |
122 | ||
123 | /** | |
124 | * Determines whether this factory can generate chromosomes for the given specification. | |
125 | * | |
126 | * <p>This factory specifically handles NeatChromosomeSpec specifications, which define the input/output structure | |
127 | * and weight bounds for NEAT neural networks. | |
128 | * | |
129 | * @param chromosomeSpec the chromosome specification to check | |
130 | * @return true if chromosomeSpec is a NeatChromosomeSpec, false otherwise | |
131 | * @throws IllegalArgumentException if chromosomeSpec is null | |
132 | */ | |
133 | @Override | |
134 | public boolean canHandle(final ChromosomeSpec chromosomeSpec) { | |
135 | Validate.notNull(chromosomeSpec); | |
136 | ||
137 |
2
1. canHandle : replaced boolean return with false for net/bmahe/genetics4j/neat/chromosomes/factory/NeatConnectedChromosomeFactory::canHandle → KILLED 2. canHandle : replaced boolean return with true for net/bmahe/genetics4j/neat/chromosomes/factory/NeatConnectedChromosomeFactory::canHandle → KILLED |
return chromosomeSpec instanceof NeatChromosomeSpec; |
138 | } | |
139 | ||
140 | /** | |
141 | * Generates a fully-connected NEAT chromosome based on the given specification. | |
142 | * | |
143 | * <p>This method creates a neural network chromosome with direct connections between all input and output nodes. | |
144 | * Each connection is initialized with a random weight within the specified bounds and assigned a unique innovation | |
145 | * number for genetic tracking. | |
146 | * | |
147 | * <p>Generation process: | |
148 | * <ol> | |
149 | * <li>Extract network parameters from the chromosome specification</li> | |
150 | * <li>Create connections between all input-output node pairs</li> | |
151 | * <li>Assign innovation numbers to each connection type</li> | |
152 | * <li>Initialize connection weights randomly within bounds</li> | |
153 | * <li>Enable all connections for immediate network functionality</li> | |
154 | * <li>Construct and return the complete chromosome</li> | |
155 | * </ol> | |
156 | * | |
157 | * @param chromosomeSpec the NEAT chromosome specification defining network structure | |
158 | * @return a new fully-connected NEAT chromosome | |
159 | * @throws IllegalArgumentException if chromosomeSpec is null or not a NeatChromosomeSpec | |
160 | */ | |
161 | @Override | |
162 | public NeatChromosome generate(final ChromosomeSpec chromosomeSpec) { | |
163 | Validate.notNull(chromosomeSpec); | |
164 | Validate.isInstanceOf(NeatChromosomeSpec.class, chromosomeSpec); | |
165 | ||
166 | final NeatChromosomeSpec neatChromosomeSpec = (NeatChromosomeSpec) chromosomeSpec; | |
167 |
1
1. generate : removed call to net/bmahe/genetics4j/neat/spec/NeatChromosomeSpec::numInputs → KILLED |
final int numInputs = neatChromosomeSpec.numInputs(); |
168 |
1
1. generate : removed call to net/bmahe/genetics4j/neat/spec/NeatChromosomeSpec::numOutputs → KILLED |
final int numOutputs = neatChromosomeSpec.numOutputs(); |
169 |
1
1. generate : removed call to net/bmahe/genetics4j/neat/spec/NeatChromosomeSpec::minWeightValue → KILLED |
float minWeightValue = neatChromosomeSpec.minWeightValue(); |
170 |
1
1. generate : removed call to net/bmahe/genetics4j/neat/spec/NeatChromosomeSpec::maxWeightValue → KILLED |
float maxWeightValue = neatChromosomeSpec.maxWeightValue(); |
171 | ||
172 |
1
1. generate : removed call to java/util/ArrayList::<init> → KILLED |
final List<Connection> connections = new ArrayList<>(); |
173 |
5
1. generate : changed conditional boundary → KILLED 2. generate : Substituted 0 with 1 → KILLED 3. generate : removed conditional - replaced comparison check with false → KILLED 4. generate : negated conditional → KILLED 5. generate : removed conditional - replaced comparison check with true → KILLED |
for (int inputIndex = 0; inputIndex < numInputs; inputIndex++) { |
174 |
5
1. generate : removed conditional - replaced comparison check with true → TIMED_OUT 2. generate : Replaced integer addition with subtraction → KILLED 3. generate : removed conditional - replaced comparison check with false → KILLED 4. generate : negated conditional → KILLED 5. generate : changed conditional boundary → KILLED |
for (int outputIndex = numInputs; outputIndex < numInputs + numOutputs; outputIndex++) { |
175 | ||
176 |
2
1. generate : replaced call to net/bmahe/genetics4j/neat/InnovationManager::computeNewId with argument → KILLED 2. generate : removed call to net/bmahe/genetics4j/neat/InnovationManager::computeNewId → KILLED |
final int innovation = innovationManager.computeNewId(inputIndex, outputIndex); |
177 |
1
1. generate : removed call to net/bmahe/genetics4j/neat/Connection::builder → KILLED |
final Connection connection = Connection.builder() |
178 |
2
1. generate : removed call to net/bmahe/genetics4j/neat/Connection$Builder::fromNodeIndex → KILLED 2. generate : replaced call to net/bmahe/genetics4j/neat/Connection$Builder::fromNodeIndex with receiver → KILLED |
.fromNodeIndex(inputIndex) |
179 |
2
1. generate : removed call to net/bmahe/genetics4j/neat/Connection$Builder::toNodeIndex → KILLED 2. generate : replaced call to net/bmahe/genetics4j/neat/Connection$Builder::toNodeIndex with receiver → KILLED |
.toNodeIndex(outputIndex) |
180 |
3
1. generate : Substituted 1 with 0 → KILLED 2. generate : removed call to net/bmahe/genetics4j/neat/Connection$Builder::innovation → KILLED 3. generate : replaced call to net/bmahe/genetics4j/neat/Connection$Builder::innovation with receiver → KILLED |
.innovation(innovation) |
181 |
2
1. generate : removed call to net/bmahe/genetics4j/neat/Connection$Builder::isEnabled → KILLED 2. generate : replaced call to net/bmahe/genetics4j/neat/Connection$Builder::isEnabled with receiver → KILLED |
.isEnabled(true) |
182 |
4
1. generate : removed call to net/bmahe/genetics4j/neat/Connection$Builder::weight → KILLED 2. generate : replaced call to java/util/random/RandomGenerator::nextFloat with argument → KILLED 3. generate : removed call to java/util/random/RandomGenerator::nextFloat → KILLED 4. generate : replaced call to net/bmahe/genetics4j/neat/Connection$Builder::weight with receiver → KILLED |
.weight(randomGenerator.nextFloat(minWeightValue, maxWeightValue)) |
183 |
1
1. generate : removed call to net/bmahe/genetics4j/neat/Connection$Builder::build → KILLED |
.build(); |
184 |
1
1. generate : removed call to java/util/List::add → KILLED |
connections.add(connection); |
185 | } | |
186 | } | |
187 | ||
188 |
2
1. generate : replaced return value with null for net/bmahe/genetics4j/neat/chromosomes/factory/NeatConnectedChromosomeFactory::generate → KILLED 2. generate : removed call to net/bmahe/genetics4j/neat/chromosomes/NeatChromosome::<init> → KILLED |
return new NeatChromosome(numInputs, numOutputs, minWeightValue, maxWeightValue, connections); |
189 | } | |
190 | } | |
Mutations | ||
119 |
1.1 |
|
120 |
1.1 |
|
137 |
1.1 2.2 |
|
167 |
1.1 |
|
168 |
1.1 |
|
169 |
1.1 |
|
170 |
1.1 |
|
172 |
1.1 |
|
173 |
1.1 2.2 3.3 4.4 5.5 |
|
174 |
1.1 2.2 3.3 4.4 5.5 |
|
176 |
1.1 2.2 |
|
177 |
1.1 |
|
178 |
1.1 2.2 |
|
179 |
1.1 2.2 |
|
180 |
1.1 2.2 3.3 |
|
181 |
1.1 2.2 |
|
182 |
1.1 2.2 3.3 4.4 |
|
183 |
1.1 |
|
184 |
1.1 |
|
188 |
1.1 2.2 |