
Day 27: Logging and Monitoring Genetic Progress Over Generations
- Chris Woodruff
- August 21, 2025
- Genetic Algorithms
- .NET, ai, C#, dotnet, genetic algorithms, programming
- 0 Comments
As your genetic algorithms become more sophisticated, it’s no longer enough to simply observe the final output. Monitoring the evolutionary process in real time provides critical insight into convergence behavior, mutation impacts, and solution quality. Logging and monitoring allow you to diagnose performance bottlenecks, identify premature convergence, and validate the impact of parameter changes.
In this post, we will walk through practical strategies to instrument a C# genetic algorithm with logging and metrics to track fitness trends, generation performance, and genetic diversity. You will also see how to export this data for visualization or analysis.
Why Log the Evolutionary Process?
Logging the internal behavior of a GA allows you to:
- Identify when and why fitness plateaus occur
- Validate the effectiveness of crossover, mutation, and selection strategies
- Tune parameters like population size or mutation rate based on real data
- Provide transparency for reproducibility and auditing in research or production
Core Metrics to Track
The following metrics provide a clear picture of each generation’s state:
- Best fitness: The highest score in the population
- Average fitness: Population-wide performance
- Diversity: How different individuals are (measured using Hamming distance or variance)
- Elapsed time: Performance monitoring per generation
Adding Logging to Your GA Loop
Assume a basic GA loop that evolves a population of Chromosome
objects. We will augment this loop with logging.
public static void RunGA(int populationSize, int generations) { var population = InitializePopulation(populationSize); var stopwatch = new Stopwatch(); using var writer = new StreamWriter("evolution_log.csv"); writer.WriteLine("Generation,BestFitness,AverageFitness,Diversity,ElapsedMs"); for (int generation = 0; generation < generations; generation++) { stopwatch.Restart(); foreach (var individual in population) individual.Evaluate(); var best = population.Max(c => c.Fitness); var avg = population.Average(c => c.Fitness); var diversity = CalculateDiversity(population); stopwatch.Stop(); writer.WriteLine($"{generation},{best},{avg},{diversity},{stopwatch.ElapsedMilliseconds}"); population = Evolve(population); } }
Example: Diversity Metric (Hamming Distance)
Tracking genetic diversity can show whether your population is stuck in a local optimum.
public static double CalculateDiversity(List<Chromosome> population) { int totalDistance = 0; int comparisons = 0; for (int i = 0; i < population.Count; i++) { for (int j = i + 1; j < population.Count; j++) { totalDistance += HammingDistance(population[i].Genes, population[j].Genes); comparisons++; } } return comparisons > 0 ? (double)totalDistance / comparisons : 0; } public static int HammingDistance(int[] a, int[] b) { return a.Zip(b, (x, y) => x == y ? 0 : 1).Sum(); }
Using Real-Time Logging
For more advanced scenarios, consider using Serilog
, Microsoft.Extensions.Logging
, or outputting metrics to a database or Azure Application Insights.
Example using Serilog
:
Log.Logger = new LoggerConfiguration() .WriteTo.Console() .WriteTo.File("ga.log") .CreateLogger(); Log.Information("Generation {Generation}: Best={Best}, Avg={Avg}, Diversity={Diversity}", generation, best, avg, diversity);
Conclusion
By integrating logging and monitoring into your GA, you gain powerful feedback loops for optimization, debugging, and reporting. These insights help drive better decisions about your algorithm’s configuration and structure. Whether for scientific analysis or production optimization, observability is an essential part of building effective genetic systems.
In the next post, we’ll explore how to archive and compare GA results over time to support repeatability and long-term tuning strategies.