EvolutionListener.java
package net.bmahe.genetics4j.core.evolutionlisteners;
import java.util.List;
import net.bmahe.genetics4j.core.Genotype;
import net.bmahe.genetics4j.core.spec.AbstractEAConfiguration;
/**
* Functional interface for monitoring and responding to evolution progress during genetic algorithm execution.
*
* <p>EvolutionListener provides a callback mechanism that allows external components to observe the evolutionary
* process as it unfolds. Listeners are notified after each generation with complete information about the current
* population state, enabling real-time monitoring, logging, and analysis.
*
* <p>Evolution listeners are commonly used for:
* <ul>
* <li><strong>Progress monitoring</strong>: Track fitness improvements and convergence trends</li>
* <li><strong>Data logging</strong>: Record population statistics and best solutions</li>
* <li><strong>Visualization</strong>: Update real-time charts and graphs of evolution progress</li>
* <li><strong>Adaptive control</strong>: Modify algorithm parameters based on evolution state</li>
* <li><strong>Early stopping</strong>: Implement custom termination conditions</li>
* <li><strong>Checkpointing</strong>: Save intermediate results for recovery and analysis</li>
* </ul>
*
* <p>Listeners receive comprehensive information about each generation:
* <ul>
* <li><strong>Generation number</strong>: Current iteration count (0-based)</li>
* <li><strong>Population snapshot</strong>: All genotypes in the current generation</li>
* <li><strong>Fitness values</strong>: Corresponding fitness scores for each individual</li>
* <li><strong>Completion status</strong>: Whether termination criteria have been met</li>
* </ul>
*
* <p>Implementation considerations:
* <ul>
* <li><strong>Performance impact</strong>: Listeners are called frequently; keep implementations efficient</li>
* <li><strong>Thread safety</strong>: May be called from different threads in parallel execution contexts</li>
* <li><strong>Exception handling</strong>: Uncaught exceptions may terminate the evolution process</li>
* <li><strong>Memory usage</strong>: Be careful with references to population data to avoid memory leaks</li>
* </ul>
*
* <p>Example implementations:
*
* <pre>{@code
* // Simple fitness tracker
* EvolutionListener<Double> fitnessTracker = (generation, population, fitness, isDone) -> {
* double bestFitness = fitness.stream()
* .max(Double::compare)
* .orElse(0.0);
* double avgFitness = fitness.stream()
* .mapToDouble(Double::doubleValue)
* .average()
* .orElse(0.0);
* System.out.printf("Generation %d: Best=%.3f, Avg=%.3f%n", generation, bestFitness, avgFitness);
* };
*
* // CSV logging listener
* EvolutionListener<Double> csvLogger = (generation, population, fitness, isDone) -> {
* if (generation % 10 == 0) { // Log every 10 generations
* logToCSV(generation, fitness);
* }
* };
*
* // Convergence monitor
* EvolutionListener<Double> convergenceMonitor = new ConvergenceListener(0.001, 50);
* }</pre>
*
* <p>The framework provides several built-in listener implementations:
* <ul>
* <li>{@link DefaultEvolutionListener}: Zero-dependency console output for any fitness type</li>
* <li>{@link EvolutionListenerLogTopN}: Logs top N individuals each generation using Log4j</li>
* <li>{@link SimpleEvolutionListener}: Basic console output for numeric fitness types using Log4j</li>
* <li>CSV loggers in the extras module for data export</li>
* </ul>
*
* @param <T> the type of fitness values being used in the evolutionary algorithm
* @see net.bmahe.genetics4j.core.EASystem
* @see EvolutionListeners
* @see DefaultEvolutionListener
* @see EvolutionListenerLogTopN
* @see SimpleEvolutionListener
*/
@FunctionalInterface
public interface EvolutionListener<T extends Comparable<T>> {
default void preEvaluation(AbstractEAConfiguration<T> eaConfiguration) {
}
default void postEvaluation(AbstractEAConfiguration<T> eaConfiguration) {
}
/**
* Called after each generation to notify about evolution progress.
*
* <p>This method is invoked by the evolutionary algorithm after each generation has been completed, providing access
* to the current population state and fitness values. The implementation can use this information for monitoring,
* logging, or adaptive control.
*
* <p>The method is called with:
* <ul>
* <li>Current generation number (starting from 0)</li>
* <li>Complete population of genotypes for this generation</li>
* <li>Corresponding fitness values for each individual</li>
* <li>Flag indicating whether evolution has completed</li>
* </ul>
*
* <p>Important notes:
* <ul>
* <li>Population and fitness lists are guaranteed to have the same size</li>
* <li>Fitness values correspond to genotypes at the same index</li>
* <li>Data may be shared with the evolution algorithm; avoid modification</li>
* <li>Method should execute quickly to avoid impacting evolution performance</li>
* </ul>
*
* @param generation the current generation number (0-based)
* @param population the list of genotypes in the current generation
* @param fitness the list of fitness values corresponding to each genotype
* @param isDone {@code true} if the evolution has completed, {@code false} otherwise
* @throws RuntimeException if the listener encounters an error that should halt evolution
*/
void onEvolution(final long generation, final List<Genotype> population, final List<T> fitness,
final boolean isDone);
}