View Javadoc
1   package net.bmahe.genetics4j.neat.mutation;
2   
3   import java.util.Objects;
4   import java.util.random.RandomGenerator;
5   
6   import net.bmahe.genetics4j.core.chromosomes.Chromosome;
7   import net.bmahe.genetics4j.core.mutation.GenericMutatorImpl;
8   import net.bmahe.genetics4j.core.mutation.MutationPolicyHandler;
9   import net.bmahe.genetics4j.core.mutation.MutationPolicyHandlerResolver;
10  import net.bmahe.genetics4j.core.mutation.Mutator;
11  import net.bmahe.genetics4j.core.mutation.chromosome.ChromosomeMutationHandler;
12  import net.bmahe.genetics4j.core.spec.AbstractEAConfiguration;
13  import net.bmahe.genetics4j.core.spec.AbstractEAExecutionContext;
14  import net.bmahe.genetics4j.core.spec.mutation.MutationPolicy;
15  import net.bmahe.genetics4j.core.util.ChromosomeResolverUtils;
16  import net.bmahe.genetics4j.neat.spec.mutation.AddNode;
17  
18  /**
19   * Mutation policy handler for NEAT (NeuroEvolution of Augmenting Topologies) add-node mutations.
20   * 
21   * <p>AddNodePolicyHandler manages the structural mutation that adds new hidden nodes to NEAT neural networks by
22   * splitting existing connections. This is one of the most important structural mutations in NEAT as it enables the
23   * evolution of increasingly complex network topologies.
24   * 
25   * <p>Add-node mutation process:
26   * <ol>
27   * <li><strong>Connection selection</strong>: Choose an existing enabled connection to split</li>
28   * <li><strong>Connection disabling</strong>: Disable the original connection</li>
29   * <li><strong>Node creation</strong>: Create a new hidden node between the connection endpoints</li>
30   * <li><strong>Connection replacement</strong>: Create two new connections through the new node</li>
31   * <li><strong>Innovation tracking</strong>: Assign innovation numbers to new connections</li>
32   * <li><strong>Weight preservation</strong>: Set weights to preserve network function</li>
33   * </ol>
34   * 
35   * <p>Key characteristics:
36   * <ul>
37   * <li><strong>Topology complexity</strong>: Increases network depth and node count</li>
38   * <li><strong>Function preservation</strong>: Maintains network behavior through careful weight setting</li>
39   * <li><strong>Innovation tracking</strong>: New connections receive unique innovation numbers</li>
40   * <li><strong>Gradual growth</strong>: Incrementally increases network complexity</li>
41   * </ul>
42   * 
43   * <p>Network transformation:
44   * <ul>
45   * <li><strong>Before</strong>: Direct connection A → B with weight W</li>
46   * <li><strong>After</strong>: Path A → NewNode → B with weights W₁ and W₂</li>
47   * <li><strong>Weight strategy</strong>: Often W₁ = 1.0, W₂ = W to preserve function</li>
48   * <li><strong>Node placement</strong>: New node gets next available index</li>
49   * </ul>
50   * 
51   * <p>Common usage patterns:
52   * 
53   * <pre>{@code
54   * // Create add-node mutation policy
55   * AddNode addNodePolicy = AddNode.of(0.05); // 5% mutation rate
56   * 
57   * // Create policy handler
58   * RandomGenerator randomGen = RandomGenerator.getDefault();
59   * AddNodePolicyHandler<Double> handler = new AddNodePolicyHandler<>(randomGen);
60   * 
61   * // Check if handler can process the policy
62   * boolean canHandle = handler.canHandle(resolver, addNodePolicy);
63   * 
64   * // Create mutator for the policy
65   * Mutator mutator = handler.createMutator(executionContext, configuration, resolver, addNodePolicy);
66   * 
67   * // Apply mutation to population
68   * List<Individual<Double>> mutatedPopulation = mutator.mutate(configuration, population);
69   * }</pre>
70   * 
71   * <p>Integration with NEAT algorithm:
72   * <ul>
73   * <li><strong>Innovation management</strong>: Coordinates with InnovationManager for new connections</li>
74   * <li><strong>Chromosome mutation</strong>: Delegates to NeatChromosomeAddNodeMutationHandler</li>
75   * <li><strong>Population evolution</strong>: Applied based on configured mutation probability</li>
76   * <li><strong>Complexity growth</strong>: Primary mechanism for increasing network complexity</li>
77   * </ul>
78   * 
79   * <p>Structural impact:
80   * <ul>
81   * <li><strong>Hidden layer growth</strong>: Creates new hidden nodes that can form layers</li>
82   * <li><strong>Computational depth</strong>: Increases potential computational complexity</li>
83   * <li><strong>Feature detection</strong>: New nodes can detect intermediate features</li>
84   * <li><strong>Representation power</strong>: Enhances network's representational capacity</li>
85   * </ul>
86   * 
87   * <p>Performance considerations:
88   * <ul>
89   * <li><strong>Conservative application</strong>: Typically applied less frequently than weight mutations</li>
90   * <li><strong>Innovation caching</strong>: Leverages InnovationManager for efficient tracking</li>
91   * <li><strong>Memory efficiency</strong>: Minimal allocation during mutation operations</li>
92   * <li><strong>Function preservation</strong>: Weight setting strategies maintain network behavior</li>
93   * </ul>
94   * 
95   * @param <T> the fitness value type (typically Double)
96   * @see AddNode
97   * @see net.bmahe.genetics4j.neat.mutation.chromosome.NeatChromosomeAddNodeMutationHandler
98   * @see MutationPolicyHandler
99   * @see InnovationManager
100  */
101 public class AddNodePolicyHandler<T extends Comparable<T>> implements MutationPolicyHandler<T> {
102 
103 	private final RandomGenerator randomGenerator;
104 
105 	/**
106 	 * Constructs a new add-node policy handler with the specified random generator.
107 	 * 
108 	 * <p>The random generator is used for stochastic decisions during mutation application, including selection of
109 	 * individuals to mutate and selection of connections to split.
110 	 * 
111 	 * @param _randomGenerator random number generator for stochastic mutation operations
112 	 * @throws IllegalArgumentException if randomGenerator is null
113 	 */
114 	public AddNodePolicyHandler(final RandomGenerator _randomGenerator) {
115 		Objects.requireNonNull(_randomGenerator);
116 
117 		this.randomGenerator = _randomGenerator;
118 	}
119 
120 	/**
121 	 * Determines whether this handler can process the given mutation policy.
122 	 * 
123 	 * <p>This handler specifically processes AddNode mutation policies, which configure the parameters for adding new
124 	 * hidden nodes to NEAT neural networks.
125 	 * 
126 	 * @param mutationPolicyHandlerResolver resolver for nested mutation policies
127 	 * @param mutationPolicy                the mutation policy to check
128 	 * @return true if the policy is an AddNode instance, false otherwise
129 	 * @throws IllegalArgumentException if any parameter is null
130 	 */
131 	@Override
132 	public boolean canHandle(final MutationPolicyHandlerResolver<T> mutationPolicyHandlerResolver,
133 			final MutationPolicy mutationPolicy) {
134 		Objects.requireNonNull(mutationPolicyHandlerResolver);
135 		Objects.requireNonNull(mutationPolicy);
136 
137 		return mutationPolicy instanceof AddNode;
138 	}
139 
140 	/**
141 	 * Creates a concrete mutator for add-node mutations.
142 	 * 
143 	 * <p>This method resolves the appropriate chromosome mutation handlers for NEAT chromosomes and creates a generic
144 	 * mutator that applies add-node mutations according to the specified policy parameters.
145 	 * 
146 	 * <p>Mutator creation process:
147 	 * <ol>
148 	 * <li>Extract population mutation probability from the policy</li>
149 	 * <li>Resolve chromosome-specific mutation handlers</li>
150 	 * <li>Create generic mutator with resolved components</li>
151 	 * <li>Return configured mutator ready for population application</li>
152 	 * </ol>
153 	 * 
154 	 * @param eaExecutionContext            execution context containing NEAT-specific components
155 	 * @param eaConfiguration               evolutionary algorithm configuration
156 	 * @param mutationPolicyHandlerResolver resolver for chromosome mutation handlers
157 	 * @param mutationPolicy                the add-node mutation policy
158 	 * @return a configured mutator for applying add-node mutations
159 	 * @throws IllegalArgumentException if any parameter is null
160 	 */
161 	@Override
162 	public Mutator createMutator(final AbstractEAExecutionContext<T> eaExecutionContext,
163 			final AbstractEAConfiguration<T> eaConfiguration,
164 			final MutationPolicyHandlerResolver<T> mutationPolicyHandlerResolver, MutationPolicy mutationPolicy) {
165 		Objects.requireNonNull(eaExecutionContext);
166 		Objects.requireNonNull(eaConfiguration);
167 		Objects.requireNonNull(mutationPolicy);
168 		Objects.requireNonNull(mutationPolicyHandlerResolver);
169 
170 		final AddNode addNodeMutationPolicy = (AddNode) mutationPolicy;
171 		final double populationMutationProbability = addNodeMutationPolicy.populationMutationProbability();
172 
173 		final ChromosomeMutationHandler<? extends Chromosome>[] chromosomeMutationHandlers = ChromosomeResolverUtils
174 				.resolveChromosomeMutationHandlers(eaExecutionContext, eaConfiguration, mutationPolicy);
175 
176 		return new GenericMutatorImpl(randomGenerator,
177 				chromosomeMutationHandlers,
178 				mutationPolicy,
179 				populationMutationProbability);
180 	}
181 }