OpenGA.Net 1.0.0

dotnet add package OpenGA.Net --version 1.0.0
                    
NuGet\Install-Package OpenGA.Net -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="OpenGA.Net" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="OpenGA.Net" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="OpenGA.Net" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add OpenGA.Net --version 1.0.0
                    
#r "nuget: OpenGA.Net, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package OpenGA.Net@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=OpenGA.Net&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=OpenGA.Net&version=1.0.0
                    
Install as a Cake Tool

OpenGA.Net logo

🧬 OpenGeneticAlgorithm.NET

License: MIT .NET NuGet Downloads Build Status

The most intuitive, powerful, and extensible genetic algorithm framework for .NET

OpenGA.Net is a high-performance, type-safe genetic algorithm library that makes evolutionary computation accessible to everyone. Whether you're solving complex optimization problems, researching machine learning, or just getting started with genetic algorithms, OpenGA.Net provides the tools you need with an elegant, fluent API.


🚀 Quick Start

Installation

dotnet add package OpenGA.Net

Your First Genetic Algorithm

A Chromosome in OpenGA.Net represents a potential solution to your optimization problem. It contains genes (the solution components) and defines how to evaluate, modify, and repair solutions.

Step 1: Create your chromosome by inheriting from the Chromosome<T> abstract class, the example below demonstrates how a Chromosome representing a potential solution to the traveling salesman problem could look like:

using OpenGA.Net;

// A chromosome for the Traveling Salesman Problem. Each chromosome represents a route through cities (genes = city sequence)
public class TspChromosome : Chromosome<int>
{
    private readonly double[,] _distanceMatrix;
    
    public TspChromosome(IList<int> cities, double[,] distanceMatrix) : base(cities)
    {
        _distanceMatrix = distanceMatrix;
    }
    
    // Calculate how "good" this route is (shorter distance = higher fitness)
    public override async Task<double> CalculateFitnessAsync()
    {
        double totalDistance = CalculateTotalDistance();
        
        return 1.0 / (1.0 + totalDistance);
    }
    
    // Randomly swap two cities in the route
    public override async Task MutateAsync(Random random)
    {
        int index1 = random.Next(Genes.Count);
        int index2 = random.Next(Genes.Count);
        (Genes[index1], Genes[index2]) = (Genes[index2], Genes[index1]);
        
        InvalidateFitness(); // Important: invalidate cached fitness after mutation
    }
    
    // Create an identical copy of this chromosome
    public override async Task<Chromosome<int>> DeepCopyAsync()
    {
        return new TspChromosome(new List<int>(Genes), _distanceMatrix);
    }
    
    // Ensure each city appears exactly once (fix any duplicates)
    public override async Task GeneticRepairAsync()
    {
        // Implementation would ensure no duplicate cities exist
        // For example: remove duplicates and fill missing cities
    }
    
    private double CalculateTotalDistance()
    {
        double distance = 0;
        for (int i = 0; i < Genes.Count - 1; i++)
        {
            distance += _distanceMatrix[Genes[i], Genes[i + 1]];
        }
        distance += _distanceMatrix[Genes[^1], Genes[0]]; // Return to start
        return distance;
    }
}

Step 2: Create your initial population:

// Generate initial population of random routes. The initial population represents a set of random solutions to the optimization problem.
var initialPopulation = new TspChromosome[100];
for (int i = 0; i < 100; i++)
{
    var cities = Enumerable.Range(0, numberOfCities).OrderBy(x => _random.Next()).ToList();
    initialPopulation[i] = new TspChromosome(cities, _distanceMatrix);
}

Step 3: Configure and run your genetic algorithm:

// Configure and run your genetic algorithm
var bestSolution = await OpenGARunner<int>
    .Initialize(initialPopulation)
    .RunToCompletionAsync();

// Get your optimized result
Console.WriteLine($"Best route: {string.Join(" → ", bestSolution.Genes)}");

That's it! 🎉 You just solved an optimization problem with a few lines of code.


🏗️ Architecture Overview

OpenGA.Net is built around four core concepts that work together seamlessly:

🧬 Chromosomes

Your solution candidates. Strongly typed and extensible. Each Chromosome<T> subclass defines how a solution is represented and evaluated. Implement:

  • CalculateFitnessAsync() — return a higher-is-better score for the current Genes
  • MutateAsync() — randomly perturb Genes to explore the search space
  • DeepCopyAsync() — produce a full copy used during crossover
  • GeneticRepairAsync() (optional) — fix invalid states after crossover/mutation

Tip: See the TSP chromosome in the Quick Start above for a concrete example. The pattern is the same for any problem: choose a gene representation, define a fitness function, add a simple mutation, and optionally repair to enforce constraints.

🎯 Parent Selection Strategies

Choose the chromosomes that will participate in mating/crossover:

Strategy When to Use Problem Characteristics Population Size Fitness Landscape
Tournament (Default) Default choice for most problems Balanced exploration/exploitation needed Any size Noisy or multimodal landscapes
Elitist Need guaranteed convergence High-quality solutions must be preserved Medium to large (50+) Clear fitness hierarchy
Roulette Wheel Fitness-proportionate diversity Wide fitness range, avoid premature convergence Large (100+) Smooth, unimodal landscapes
Boltzmann Dynamic selection pressure Need cooling schedule control Medium to large Complex, deceptive landscapes
Rank Selection Prevent fitness scaling issues Similar fitness values across population Any size Flat or highly scaled fitness
Random Maximum diversity exploration Early stages or highly exploratory search Any size Unknown or chaotic landscapes

🧬 Crossover Strategies

Create offspring by combining parent chromosomes:

Strategy When to Use Gene Representation Problem Type Preservation Needs
One-Point (Default) Fast, simple problems Order doesn't matter critically Optimization with independent variables Preserve some gene clusters
K-Point Moderate complexity balance Mixed dependencies between genes Multi-dimensional optimization Control disruption level
Uniform Maximum genetic diversity Independent genes Exploratory search, avoid local optima No specific gene clustering
Custom Domain-specific requirements Complex constraints or structures Specialized problem domains Domain-specific validation

🔄 Survivor Selection Strategies

Manage population evolution over generations:

Strategy When to Use Convergence Speed Population Diversity Resource Constraints
Elitist (Default) Most optimization problems Fast convergence Moderate diversity loss Low computational overhead
Generational Exploration-heavy search Slower, thorough exploration High diversity maintained Higher memory usage
Tournament Balanced performance Moderate convergence Good diversity balance Moderate computational cost
Age-based Long-running evolutionary systems Very slow, stable Excellent long-term diversity Requires age tracking
Boltzmann Temperature-controlled evolution Adaptive convergence Dynamic diversity control Higher computational complexity
Random Elimination Maintain population diversity Slowest convergence Maximum diversity Minimal computational overhead

🏁 Termination Strategies

Control when the genetic algorithm stops evolving:

Strategy When to Use Stopping Condition Best For Predictability
Maximum Epochs (Default) Known iteration limits Fixed number of generations Time-constrained scenarios Highly predictable runtime
Maximum Duration Real-time applications Maximum execution duration Production systems Predictable time bounds
Target Standard Deviation Diversity monitoring Low population diversity Avoiding premature convergence Adaptive stopping
Target Fitness Goal-oriented optimization Specific fitness threshold reached Known optimal solution value Adaptive based on performance

💡 Strategy Selection Examples

// High-performance optimization (fast convergence needed)
.ParentSelection(c => c.RegisterSingle(s => s.Tournament()))
.Crossover(c => c.RegisterSingle(s => s.OnePointCrossover()))
.SurvivorSelection(r => r.RegisterSingle(s => s.Elitist()))
.Termination(t => t.MaximumEpochs(100))

// Exploratory search (avoiding local optima)
.ParentSelection(c => c.RegisterSingle(s => s.Tournament()))
.Crossover(c => c.RegisterSingle(s => s.UniformCrossover()))
.SurvivorSelection(r => r.RegisterSingle(s => s.Generational()))
.Termination(t => t.TargetStandardDeviation(stdDev: 0.001))

// Production system (time-constrained)
.ParentSelection(c => c.RegisterSingle(s => s.Tournament()))
.Crossover(c => c.RegisterSingle(s => s.OnePointCrossover()))
.SurvivorSelection(r => r.RegisterSingle(s => s.Elitist()))
.Termination(t => t.MaximumDuration(TimeSpan.FromMinutes(5)))

// Quality-focused research (target fitness termination)
.ParentSelection(c => c.RegisterSingle(s => s.RouletteWheel()))
.Crossover(c => c.RegisterSingle(s => s.KPointCrossover(3)))
.SurvivorSelection(r => r.RegisterSingle(s => s.Elitist(0.2f)))
.Termination(t => t.TargetFitness(0.95).TargetStandardDeviation(stdDev: 0.001, window: 10))

🎯 Use Cases

🏭 Optimization Problems

  • Scheduling: Job shop, vehicle routing, resource allocation
  • Engineering Design: Antenna design, circuit optimization, structural engineering
  • Financial: Portfolio optimization, trading strategies, risk management

🧠 Machine Learning

  • Neural Architecture Search: Automatically design neural networks
  • Hyperparameter Tuning: Optimize ML model parameters
  • Feature Selection: Find optimal feature subsets

🎮 Game Development

  • AI Behavior: Evolve intelligent game agents
  • Level Generation: Create procedural game content
  • Balancing: Optimize game mechanics and difficulty curves

🔬 Research & Academia

  • Evolutionary Computation: Research platform for GA variants
  • Multi-objective Optimization: Pareto-optimal solutions
  • Algorithm Comparison: Benchmark different evolutionary strategies

⚙️ Advanced Configuration

For sophisticated optimization scenarios, OpenGA.Net supports advanced multi-strategy configurations with Adaptive Pursuit - an intelligent operator selection policy that learns which strategies perform best over time and automatically adjusts selection probabilities.

🧠 Adaptive Pursuit Algorithm

Adaptive Pursuit continuously monitors the performance of each configured strategy and dynamically adapts their selection probabilities based on:

  • Fitness improvement from applied operators
  • Population diversity maintenance
  • Recent performance trends with temporal weighting
  • Exploration vs exploitation balance

This creates a self-optimizing genetic algorithm that automatically discovers the best operator combinations for your specific problem.

Multi-Strategy Configuration

var result = await OpenGARunner<int>
    .Initialize(initialPopulation, minPopulationPercentage: 0.4f, maxPopulationPercentage: 2.5f)
    .WithRandomSeed(42)
    .MutationRate(0.15f)
    
    // Multi-strategy parent selection with adaptive learning
    .ParentSelection(p => p.RegisterMulti(m => m
        .Tournament(stochasticTournament: true, customWeight: 0.4f)
        .RouletteWheel(customWeight: 0.3f)
        .Rank(customWeight: 0.2f)
        .Boltzmann(temperatureDecayRate: 0.05, initialTemperature: 1.0, customWeight: 0.1f)
        .WithPolicy(policy => policy.AdaptivePursuit(
            learningRate: 0.12,
            minimumProbability: 0.05,
            rewardWindowSize: 15
        ))
    ))
    
    // Single high-performance crossover for exploitation
    .Crossover(c => c.RegisterSingle(s => s.KPointCrossover(3))
        .WithCrossoverRate(0.85f))
    
    // Multi-strategy survivor selection for dynamic population management
    .SurvivorSelection(s => s.RegisterMulti(m => m
        .Elitist(elitePercentage: 0.12f, customWeight: 0.6f)
        .Tournament(tournamentSize: 4, stochasticTournament: true, customWeight: 0.3f)
        .AgeBased(customWeight: 0.1f)
        .WithPolicy(policy => policy.AdaptivePursuit(
            learningRate: 0.08,
            diversityWeight: 0.15
        ))
    ).OverrideOffspringGenerationRate(1.3f))
    
    // Multi-condition termination
    .Termination(t => t
        .MaximumEpochs(750)
        .MaximumDuration(TimeSpan.FromMinutes(10))
        .TargetFitness(0.98)
        .TargetStandardDeviation(0.001, window: 20))
    
    .RunToCompletionAsync();

🛠️ Extensibility

Create your own strategies by inheriting from base classes:

// Custom crossover strategy
public class MyCustomCrossover<T> : BaseCrossoverStrategy<T>
{
    protected internal override async Task<IEnumerable<Chromosome<T>>> CrossoverAsync(
        Couple<T> couple, Random random)
    {
        // Your custom crossover logic
    }
}

// Custom survivor selection strategy  
public class MySurvivorSelectionStrategy<T> : BaseSurvivorSelectionStrategy<T>
{
    internal override float RecommendedOffspringGenerationRate => 0.5f;
    
    protected internal override async Task<IEnumerable<Chromosome<T>>> SelectChromosomesForEliminationAsync(
        Chromosome<T>[] population, Chromosome<T>[] offspring, Random random, int currentEpoch = 0)
    {
        // Your custom survivor selection logic
    }
}

// Custom reproduction selector
public class MyParentSelector<T> : BaseParentSelectorStrategy<T>
{
    protected internal override async Task<IEnumerable<Couple<T>>> SelectMatingPairsAsync(
        Chromosome<T>[] population, Random random, int minimumNumberOfCouples)
    {
        // Your custom parent selection logic
    }
}

// Custom termination strategy
public class MyTerminationStrategy<T> : BaseTerminationStrategy<T>
{   
    public override bool Terminate(GeneticAlgorithmState state)
    {
        // Your custom logic to control when the GA should terminate
    }
}

// Using your custom strategies
var result = await OpenGARunner<MyGeneType>
    .Initialize(initialPopulation)
    .ParentSelection(c => c.RegisterSingle(s => s.Custom(new MyParentSelector<MyGeneType>())))
    .Crossover(c => c.RegisterSingle(s => s.CustomCrossover(new MyCustomCrossover<MyGeneType>())))
    .SurvivorSelection(r => r.RegisterSingle(s => s.Custom(new MySurvivorSelectionStrategy<MyGeneType>())))
    .Termination(t => t.Custom(new MyTerminationStrategy<MyGeneType>(50)))
    .RunToCompletionAsync();

📊 Performance & Benchmarks

OpenGA.Net has been rigorously tested on classic optimization problems to demonstrate its performance and solution quality. Our comprehensive benchmark suite includes:

🔬 Benchmark Problems

  • 🗺️ Traveling Salesman Problem (TSP): 30 and 50 city instances
  • 🎒 Knapsack Problem: 50 and 100 item optimization
  • 📦 Bin Packing Problem: 50 and 100 item optimization

Performance Highlights

  • Execution Speed: 601ms-2,321ms for 500 generations on complex problems
  • Scalability: Linear scaling with population size and problem complexity
  • Solution Quality: Consistently achieves near-optimal results within 1-2% of known bounds

📈 Key Results

Problem Instance Time (500 gen) Best Result
TSP 30 cities 1,853ms 71.1% better than random tour
TSP 50 cities 1,893ms 69.7% better than random tour
Knapsack 50 items 1,863ms 1,027.8 value (99.94% efficiency)
Knapsack 100 items 601ms 2,286.2 value (99.90% efficiency)
Bin Packing 50 items 2,321ms 19 bins (vs 18 optimal)
Bin Packing 100 items 993ms 36 bins (optimal!)

Result Interpretation:

  • TSP: % improvement over random tours / absolute distance (showing significant optimization capability)
  • Knapsack: Total value achieved and efficiency vs upper bound (nearly optimal solutions)
  • Bin Packing: Bins used (achieving optimal or near-optimal solutions)

🏃 Run Benchmarks Yourself

cd OpenGA.Net.Benchmarks

# Quick performance tests
dotnet run --simple

# Detailed solution quality analysis  
dotnet run --analysis

# Comprehensive BenchmarkDotNet suite
dotnet run

📋 View Complete Benchmark Results - Detailed methodology, configurations, and analysis


🤝 Contributing

We welcome contributions! OpenGA.Net is built by the community, for the community.

🐛 Found a Bug?

Open an issue with:

  • Clear reproduction steps
  • Expected vs actual behavior
  • Environment details

🔧 Want to Code?

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/amazing-feature
  3. Make your changes with tests
  4. Submit a pull request

📄 License

OpenGA.Net is released under the MIT License. See LICENSE.md for details.

Copyright (c) 2024 Ahmed Sarnaout

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net8.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 149 8/29/2025