1 package net.bmahe.genetics4j.core;
2
3 import java.util.ArrayList;
4 import java.util.List;
5 import java.util.Objects;
6 import java.util.Optional;
7 import java.util.concurrent.ExecutorService;
8 import java.util.concurrent.ForkJoinPool;
9 import java.util.stream.Collectors;
10
11 import net.bmahe.genetics4j.core.combination.ChromosomeCombinator;
12 import net.bmahe.genetics4j.core.combination.ChromosomeCombinatorResolver;
13 import net.bmahe.genetics4j.core.evaluation.FitnessEvaluator;
14 import net.bmahe.genetics4j.core.evaluation.FitnessEvaluatorBulkAsync;
15 import net.bmahe.genetics4j.core.evaluation.FitnessEvaluatorSync;
16 import net.bmahe.genetics4j.core.evaluation.FitnessEvaluatorVirtualThread;
17 import net.bmahe.genetics4j.core.mutation.MutationPolicyHandlerResolver;
18 import net.bmahe.genetics4j.core.mutation.Mutator;
19 import net.bmahe.genetics4j.core.replacement.ReplacementStrategyHandler;
20 import net.bmahe.genetics4j.core.selection.SelectionPolicyHandlerResolver;
21 import net.bmahe.genetics4j.core.spec.AbstractEAConfiguration;
22 import net.bmahe.genetics4j.core.spec.AbstractEAExecutionContext;
23 import net.bmahe.genetics4j.core.spec.EAConfiguration;
24 import net.bmahe.genetics4j.core.spec.EAConfigurationBulkAsync;
25 import net.bmahe.genetics4j.core.spec.EAExecutionContext;
26 import net.bmahe.genetics4j.core.spec.combination.CombinationPolicy;
27 import net.bmahe.genetics4j.core.spec.mutation.MutationPolicy;
28
29 /**
30 * Factory class providing convenient methods for creating properly configured {@link EASystem} instances.
31 *
32 * <p>EASystemFactory simplifies the complex process of assembling all the components required for evolutionary
33 * algorithms by providing pre-configured factory methods that handle the initialization and wiring of selection
34 * strategies, mutation operators, combination policies, and fitness evaluators.
35 *
36 * <p>The factory abstracts away the complexity of manually creating and configuring:
37 * <ul>
38 * <li><strong>Selection policy resolvers</strong>: Components that match selection strategies to implementations</li>
39 * <li><strong>Mutation policy resolvers</strong>: Components that handle different mutation strategies</li>
40 * <li><strong>Chromosome combinators</strong>: Components responsible for crossover operations</li>
41 * <li><strong>Replacement strategy handlers</strong>: Components managing population replacement</li>
42 * <li><strong>Fitness evaluators</strong>: Components handling fitness computation strategies</li>
43 * </ul>
44 *
45 * <p>Factory methods are organized by evaluation strategy:
46 * <ul>
47 * <li><strong>Synchronous evaluation</strong>: Traditional single-threaded or parallel evaluation using
48 * {@link EAConfiguration}</li>
49 * <li><strong>Bulk asynchronous evaluation</strong>: Batch processing for external services or GPU acceleration using
50 * {@link EAConfigurationBulkAsync}</li>
51 * <li><strong>Custom evaluation</strong>: User-provided {@link FitnessEvaluator} implementations</li>
52 * </ul>
53 *
54 * <p>Common usage patterns:
55 *
56 * <pre>{@code
57 * // Simple synchronous evaluation with default thread pool
58 * EAConfiguration<Double> config = EAConfigurationBuilder.<Double>builder()
59 * .chromosomeSpecs(chromosomeSpec)
60 * .parentSelectionPolicy(Tournament.of(3))
61 * .combinationPolicy(SinglePointCrossover.build())
62 * .mutationPolicies(List.of(RandomMutation.of(0.1)))
63 * .replacementStrategy(Elitism.builder().offspringRatio(0.8).build())
64 * .build();
65 *
66 * EAExecutionContext<Double> context = EAExecutionContextBuilder.<Double>builder()
67 * .populationSize(100)
68 * .fitness(genotype -> computeFitness(genotype))
69 * .termination(Generations.of(100))
70 * .build();
71 *
72 * EASystem<Double> eaSystem = EASystemFactory.from(config, context);
73 *
74 * // Asynchronous evaluation for expensive fitness functions
75 * EAConfigurationBulkAsync<Double> asyncConfig = EAConfigurationBulkAsyncBuilder.<Double>builder()
76 * .chromosomeSpecs(chromosomeSpec)
77 * .parentSelectionPolicy(Tournament.of(3))
78 * .combinationPolicy(SinglePointCrossover.build())
79 * .mutationPolicies(List.of(RandomMutation.of(0.1)))
80 * .replacementStrategy(Elitism.builder().offspringRatio(0.8).build())
81 * .fitnessBulkAsync(genotypes -> evaluateBatch(genotypes))
82 * .build();
83 *
84 * EASystem<Double> asyncSystem = EASystemFactory.from(asyncConfig, context);
85 *
86 * // Custom evaluation strategy
87 * FitnessEvaluator<Double> customEvaluator = new CachedFitnessEvaluator<>(baseFitness, cacheSize);
88 * EASystem<Double> customSystem = EASystemFactory.from(config, context, executorService, customEvaluator);
89 * }</pre>
90 *
91 * <p>Factory method selection guide:
92 * <ul>
93 * <li><strong>Fast fitness functions</strong>: Use synchronous methods with {@link EAConfiguration}</li>
94 * <li><strong>Expensive fitness functions</strong>: Use asynchronous methods with {@link EAConfigurationBulkAsync}</li>
95 * <li><strong>External fitness computation</strong>: Use custom evaluator methods</li>
96 * <li><strong>GPU acceleration</strong>: Use bulk async methods for batch processing</li>
97 * </ul>
98 *
99 * <p>Thread pool considerations:
100 * <ul>
101 * <li><strong>Default behavior</strong>: Uses {@link ForkJoinPool#commonPool()} for automatic parallelization</li>
102 * <li><strong>Custom thread pools</strong>: Provide specific {@link ExecutorService} for resource control</li>
103 * <li><strong>Single-threaded</strong>: Use {@link java.util.concurrent.Executors#newSingleThreadExecutor()}</li>
104 * <li><strong>Resource management</strong>: Caller responsible for shutdown of custom thread pools</li>
105 * </ul>
106 *
107 * @see EASystem
108 * @see EAConfiguration
109 * @see EAConfigurationBulkAsync
110 * @see EAExecutionContext
111 * @see FitnessEvaluator
112 */
113 public class EASystemFactory {
114
115 /**
116 * Prevents instantiation since it's a bunch of static methods
117 */
118 private EASystemFactory() {
119 }
120
121 /**
122 * Creates an {@link EASystem} with a custom fitness evaluator and explicit thread pool.
123 *
124 * <p>This is the most flexible factory method that allows complete control over all components of the evolutionary
125 * algorithm system. It assembles and wires all necessary components including selection strategies, mutation
126 * operators, chromosome combinators, and replacement strategies.
127 *
128 * <p>This method is primarily used internally by other factory methods, but can be used directly when you need to
129 * provide a custom {@link FitnessEvaluator} implementation such as cached, distributed, or specialized evaluation
130 * strategies.
131 *
132 * @param <T> the type of fitness values, must be comparable for selection operations
133 * @param eaConfiguration the evolutionary algorithm configuration specifying genetic operators and strategies
134 * @param eaExecutionContext the execution context containing population parameters and fitness functions
135 * @param fitnessEvaluator the fitness evaluator implementation to use for population evaluation
136 * @return a fully configured {@link EASystem} ready for evolution execution
137 * @throws IllegalArgumentException if any parameter is null
138 * @throws IllegalStateException if no suitable replacement strategy handler can be found
139 */
140 public static <T extends Comparable<T>> EASystem<T> from(final AbstractEAConfiguration<T> eaConfiguration,
141 final AbstractEAExecutionContext<T> eaExecutionContext, final FitnessEvaluator<T> fitnessEvaluator) {
142 Objects.requireNonNull(eaConfiguration);
143 Objects.requireNonNull(eaExecutionContext);
144 Objects.requireNonNull(fitnessEvaluator);
145
146 final var selectionPolicyHandlerResolver = new SelectionPolicyHandlerResolver<T>(eaExecutionContext);
147
148 final var parentSelectionPolicyHandler = selectionPolicyHandlerResolver
149 .resolve(eaConfiguration.parentSelectionPolicy());
150
151 final var parentSelector = parentSelectionPolicyHandler.resolve(
152 eaExecutionContext,
153 eaConfiguration,
154 selectionPolicyHandlerResolver,
155 eaConfiguration.parentSelectionPolicy());
156
157 final var mutationPolicyHandlerResolver = new MutationPolicyHandlerResolver<T>(eaExecutionContext);
158
159 final var chromosomeCombinatorResolver = new ChromosomeCombinatorResolver<T>(eaExecutionContext);
160
161 final CombinationPolicy combinationPolicy = eaConfiguration.combinationPolicy();
162 final List<ChromosomeCombinator<T>> chromosomeCombinators = eaConfiguration.chromosomeSpecs()
163 .stream()
164 .map(chromosome -> chromosomeCombinatorResolver.resolve(combinationPolicy, chromosome))
165 .collect(Collectors.toList());
166
167 final List<Mutator> mutators = new ArrayList<>();
168 final List<MutationPolicy> mutationPolicies = eaConfiguration.mutationPolicies();
169 for (int i = 0; i < mutationPolicies.size(); i++) {
170 final var mutationPolicy = mutationPolicies.get(i);
171
172 final var mutationPolicyHandler = mutationPolicyHandlerResolver.resolve(mutationPolicy);
173 final var mutator = mutationPolicyHandler
174 .createMutator(eaExecutionContext, eaConfiguration, mutationPolicyHandlerResolver, mutationPolicy);
175
176 mutators.add(mutator);
177
178 }
179
180 final var replacementStrategyHandlers = eaExecutionContext.replacementStrategyHandlers();
181 final var replacementStrategy = eaConfiguration.replacementStrategy();
182
183 final Optional<ReplacementStrategyHandler<T>> replacementStrategyHandlerOpt = replacementStrategyHandlers.stream()
184 .filter(replacementStrategyHandler -> replacementStrategyHandler.canHandle(replacementStrategy))
185 .findFirst();
186
187 final ReplacementStrategyHandler<T> replacementStrategyHandler = replacementStrategyHandlerOpt.orElseThrow(
188 () -> new IllegalStateException(
189 "Could not find an implementation to handle the replacement strategy " + replacementStrategy));
190 final var replacementStrategyImplementor = replacementStrategyHandler
191 .resolve(eaExecutionContext, eaConfiguration, selectionPolicyHandlerResolver, replacementStrategy);
192
193 final long populationSize = eaExecutionContext.populationSize();
194
195 return new EASystem<>(eaConfiguration,
196 populationSize,
197 chromosomeCombinators,
198 eaConfiguration.offspringGeneratedRatio(),
199 parentSelector,
200 mutators,
201 replacementStrategyImplementor,
202 eaExecutionContext,
203 fitnessEvaluator);
204 }
205
206 /**
207 * Creates an {@link EASystem} with synchronous fitness evaluation and explicit thread pool.
208 *
209 * <p>This method is ideal for scenarios where fitness computation is relatively fast and can benefit from parallel
210 * evaluation across multiple threads. The fitness function defined in the configuration will be called synchronously
211 * for each individual or in parallel depending on the evaluator implementation.
212 *
213 * <p>Use this method when:
214 * <ul>
215 * <li>Fitness computation is CPU-bound and relatively fast (< 100ms per evaluation)</li>
216 * <li>You want control over the thread pool used for parallel evaluation</li>
217 * <li>You need deterministic thread pool behavior for testing or resource management</li>
218 * </ul>
219 *
220 * @param <T> the type of fitness values, must be comparable for selection operations
221 * @param eaConfigurationSync the synchronous EA configuration with a simple fitness function
222 * @param eaExecutionContext the execution context containing population and termination parameters
223 * @param executorService the thread pool for parallel fitness evaluation (caller responsible for shutdown)
224 * @return a configured {@link EASystem} using synchronous fitness evaluation
225 * @throws IllegalArgumentException if any parameter is null
226 */
227 public static <T extends Comparable<T>> EASystem<T> from(final EAConfiguration<T> eaConfigurationSync,
228 final EAExecutionContext<T> eaExecutionContext, final ExecutorService executorService) {
229
230 final var fitnessEvaluator = new FitnessEvaluatorSync<>(eaExecutionContext, eaConfigurationSync, executorService);
231 return from(eaConfigurationSync, eaExecutionContext, fitnessEvaluator);
232 }
233
234 /**
235 * Creates an {@link EASystem} with synchronous fitness evaluation using the common thread pool.
236 *
237 * <p>This is the most convenient method for creating evolutionary algorithms with simple fitness functions. It uses
238 * {@link ForkJoinPool#commonPool()} for automatic parallelization without requiring explicit thread pool management.
239 *
240 * <p>This method is ideal for:
241 * <ul>
242 * <li>Quick prototyping and experimentation</li>
243 * <li>Applications where thread pool management is not critical</li>
244 * <li>Fast fitness functions that can benefit from automatic parallelization</li>
245 * <li>Educational purposes and simple examples</li>
246 * </ul>
247 *
248 * @param <T> the type of fitness values, must be comparable for selection operations
249 * @param eaConfigurationSync the synchronous EA configuration with a simple fitness function
250 * @param eaExecutionContext the execution context containing population and termination parameters
251 * @return a configured {@link EASystem} using synchronous fitness evaluation with common thread pool
252 * @throws IllegalArgumentException if any parameter is null
253 */
254 public static <T extends Comparable<T>> EASystem<T> from(final EAConfiguration<T> eaConfigurationSync,
255 final EAExecutionContext<T> eaExecutionContext) {
256 final ExecutorService executorService = ForkJoinPool.commonPool();
257 return from(eaConfigurationSync, eaExecutionContext, executorService);
258 }
259
260 /**
261 * Creates an {@link EASystem} with bulk asynchronous fitness evaluation and explicit thread pool.
262 *
263 * <p>This method is designed for expensive fitness functions that can benefit from batch processing or asynchronous
264 * evaluation. The bulk async evaluator can process entire populations at once, enabling optimization strategies like
265 * GPU acceleration, external service calls, or database batch operations.
266 *
267 * <p>Use this method when:
268 * <ul>
269 * <li>Fitness computation involves external resources (databases, web services, files)</li>
270 * <li>Evaluation can be accelerated through batch processing (GPU, vectorized operations)</li>
271 * <li>Fitness computation is expensive (> 100ms per evaluation)</li>
272 * <li>You need to optimize I/O operations through batching</li>
273 * </ul>
274 *
275 * @param <T> the type of fitness values, must be comparable for selection operations
276 * @param eaConfigurationBulkAsync the bulk async EA configuration with batch fitness evaluation
277 * @param eaExecutionContext the execution context containing population and termination parameters
278 * @param executorService the thread pool for managing asynchronous operations (caller responsible for
279 * shutdown)
280 * @return a configured {@link EASystem} using bulk asynchronous fitness evaluation
281 * @throws IllegalArgumentException if any parameter is null
282 */
283 public static <T extends Comparable<T>> EASystem<T> from(final EAConfigurationBulkAsync<T> eaConfigurationBulkAsync,
284 final EAExecutionContext<T> eaExecutionContext, final ExecutorService executorService) {
285
286 final var fitnessEvaluator = new FitnessEvaluatorBulkAsync<>(eaConfigurationBulkAsync, executorService);
287 return from(eaConfigurationBulkAsync, eaExecutionContext, fitnessEvaluator);
288 }
289
290 /**
291 * Creates an {@link EASystem} with bulk asynchronous fitness evaluation using the common thread pool.
292 *
293 * <p>This convenience method provides the benefits of bulk asynchronous evaluation without requiring explicit thread
294 * pool management. It automatically uses {@link ForkJoinPool#commonPool()} for managing asynchronous operations.
295 *
296 * <p>This method combines the convenience of automatic thread pool management with the power of bulk asynchronous
297 * evaluation, making it ideal for:
298 * <ul>
299 * <li>Expensive fitness functions in research and experimentation</li>
300 * <li>Applications requiring external resource access without complex thread management</li>
301 * <li>GPU-accelerated fitness evaluation with simple setup</li>
302 * <li>Prototype development with advanced evaluation strategies</li>
303 * </ul>
304 *
305 * @param <T> the type of fitness values, must be comparable for selection operations
306 * @param eaConfigurationBulkAsync the bulk async EA configuration with batch fitness evaluation
307 * @param eaExecutionContext the execution context containing population and termination parameters
308 * @return a configured {@link EASystem} using bulk asynchronous fitness evaluation with common thread pool
309 * @throws IllegalArgumentException if any parameter is null
310 */
311 public static <T extends Comparable<T>> EASystem<T> from(final EAConfigurationBulkAsync<T> eaConfigurationBulkAsync,
312 final EAExecutionContext<T> eaExecutionContext) {
313 final ExecutorService executorService = ForkJoinPool.commonPool();
314 return from(eaConfigurationBulkAsync, eaExecutionContext, executorService);
315 }
316
317 /**
318 * Creates an {@link EASystem} with virtual thread-based fitness evaluation.
319 *
320 * <p>This method leverages virtual threads to provide massive parallelism for fitness evaluation without the
321 * overhead of traditional platform threads. Each genotype gets its own virtual thread, making this approach ideal
322 * for I/O-bound fitness functions and large populations.
323 *
324 * <p>Virtual threads excel in scenarios involving:
325 * <ul>
326 * <li><strong>I/O-bound fitness functions</strong>: Database queries, web service calls, file operations</li>
327 * <li><strong>Large populations</strong>: Thousands of individuals without thread pool limitations</li>
328 * <li><strong>Complex simulations</strong>: Long-running evaluations that benefit from massive parallelism</li>
329 * <li><strong>External service integration</strong>: Fitness evaluations requiring network calls</li>
330 * </ul>
331 *
332 * <p>Performance characteristics:
333 * <ul>
334 * <li><strong>Memory overhead</strong>: ~200 bytes per virtual thread vs ~2MB per platform thread</li>
335 * <li><strong>Creation cost</strong>: Nearly zero compared to platform threads</li>
336 * <li><strong>Blocking behavior</strong>: Virtual threads don't block carrier threads during I/O</li>
337 * <li><strong>Scalability</strong>: Can handle millions of concurrent virtual threads</li>
338 * </ul>
339 *
340 * <p><strong>Requirements:</strong> Java 25 or later
341 *
342 * @param <T> the type of fitness values, must be comparable for selection operations
343 * @param eaConfigurationSync the synchronous EA configuration with a simple fitness function
344 * @param eaExecutionContext the execution context containing population and termination parameters
345 * @return a configured {@link EASystem} using virtual thread-based fitness evaluation
346 * @throws IllegalArgumentException if any parameter is null
347 * @throws UnsupportedOperationException if virtual threads are not available in current JVM
348 */
349 public static <T extends Comparable<T>> EASystem<T> fromWithVirtualThreads(
350 final EAConfiguration<T> eaConfigurationSync, final EAExecutionContext<T> eaExecutionContext) {
351
352 final var fitnessEvaluator = new FitnessEvaluatorVirtualThread<>(eaExecutionContext, eaConfigurationSync);
353 return from(eaConfigurationSync, eaExecutionContext, fitnessEvaluator);
354 }
355 }