genetic_algorithms 2.2.0

Library for solving genetic algorithm problems
Documentation

genetic_algorithms (v2.1.0)

Rust Unit Tests

Modular and concurrent Genetic Algorithms (GA) library for Rust featuring:

  • Clear abstractions (traits for genes, chromosomes, and configuration).
  • Composable operators (selection, crossover, mutation, survivor, extension).
  • Multi-threaded execution via rayon (fitness evaluation, reproduction, mutation in parallel).
  • Adaptive GA mode (dynamic crossover and mutation probabilities based on population performance).
  • Elitism support (preserve top N individuals across generations).
  • Extension strategies for population diversity control (mass extinction, genesis, degeneration, deduplication).
  • Population diversity metric tracked per generation.
  • Lifecycle reporter hooks (on_start, on_generation_complete, on_new_best, on_finish).
  • Compound stopping criteria (stagnation, convergence, time limit).
  • Cow for minimizing unnecessary DNA copies.
  • Optional visualization feature for PNG/SVG fitness and diversity charts.

Table of Contents

Documentation

Latest published docs: docs.rs/genetic_algorithms

Status & Key Changes 2.0.0

Main differences vs 1.x:

  • Crate version: 2.0.0 (update your Cargo.toml).
  • Structured error handling: all operators return Result<T, GaError> instead of panicking.
  • Parallelism via rayon replacing manual thread management.
  • GeneT::get_id() now returns i32 directly.
  • ChromosomeT::set_dna now uses Cow<'a, [Gene]> to avoid redundant copies.
  • Added Range<T> genotype alongside Binary (supports numeric ranges).
  • New operators: SinglePoint, Order (OX), BitFlip, Value, Creep, Gaussian mutations; SBX, BlendAlpha crossovers; Rank selection.
  • Elitism: with_elitism(n) preserves the top N individuals across generations.
  • Compound stopping criteria: stagnation detection, convergence threshold, time limit.
  • Per-generation statistics tracking via GenerationStats.
  • Adaptive probability helpers: aga_probability for crossover & mutation.
  • Expanded configuration: FixedFitness, logging levels (Off..Trace), configurable threads.
  • Internal benchmarks use Criterion 0.7 (pprof integration removed due to version conflict).

Features

Traits

  • GeneT:
    • Requires: Default + Clone + Send + Sync.
    • Key methods: new(), get_id() -> i32, set_id(i32).
  • ChromosomeT:
    • Associated: type Gene: GeneT.
    • Supports: get_dna(), set_dna(Cow<[Gene]>), set_gene(idx, gene), set_fitness_fn(...), calculate_fitness(), get_fitness(), set_fitness(f64), get_age(), set_age(i32).
    • Derived metric: get_fitness_distance(&fitness_target).
  • ConfigurationT: builder-style API for Ga / GaConfiguration.

Included Genotypes & Chromosomes

  • genotypes::Binary (boolean gene).
  • genotypes::Range<T> (values constrained to one or more (min,max) intervals).
  • genotypes::List<T> (values drawn from a finite symbolic alphabet).
  • chromosomes::Range<T> (chromosome built from Range<T> genes).
  • chromosomes::ListChromosome<T> (chromosome built from List<T> genes).
  • (Custom chromosomes can be added by implementing ChromosomeT).

Initializers

  • initializers::binary_random_initialization.
  • initializers::range_random_initialization.
  • initializers::list_random_initialization (for List<T> chromosomes, with repetition).
  • initializers::list_random_initialization_without_repetitions (permutation problems).
  • initializers::generic_random_initialization (takes allele slice, optional unique IDs).
  • initializers::generic_random_initialization_without_repetitions (no allele repetition).

Operators

  • Selection: Random, RouletteWheel, StochasticUniversalSampling, Tournament, Rank.
  • Crossover: Cycle, MultiPoint, Uniform, SinglePoint, Order (OX), Sbx (Simulated Binary), BlendAlpha (BLX-α).
  • Mutation: Swap, Inversion, Scramble, Value (Range), BitFlip (Binary), Creep (uniform perturbation), Gaussian (normal perturbation), ListValue (List).
  • Survivor: Fitness (keep best), Age (prefer younger / age-based pruning).
  • Extension: Noop, MassExtinction, MassGenesis, MassDegeneration, MassDeduplication.

Observer (GaObserver)

Note: Reporter<U> is deprecated since 2.2.0 and will be removed in v3.0.0. Use GaObserver instead.

Attach a lifecycle observer to any GA engine via .with_observer(Arc::new(my_observer)). All hooks use &self — safe for use in rayon parallel regions. Zero overhead when no observer is attached (stored as Option<Arc<_>>).

Core trait: GaObserver<U>

Hooks fired by Ga<U> on every generation cycle:

Hook When it fires
on_selection_complete After parent selection
on_crossover_complete After crossover batch
on_mutation_complete After mutation batch
on_fitness_evaluation_complete After fitness re-evaluation
on_survivor_selection_complete After survivor selection
on_new_best When a new best chromosome is found
on_stagnation When no improvement for N generations
on_extension_triggered When diversity extension fires
on_generation_end End of each generation (with GenerationStats)
on_run_start Before the first generation
on_run_end After the last generation

Engine-specific sub-traits

  • IslandGaObserver<U> — additional hooks for island migration events; attach via IslandGa::with_observer.
  • Nsga2Observer<U> — additional hooks for NSGA-II pareto-front and crowding events; attach via Nsga2::with_observer.

Built-in observers

LogObserver — logs every hook via the log crate. No feature flags required. Implements GaObserver, IslandGaObserver, and Nsga2Observer.

use std::sync::Arc;
use genetic_algorithms::{Ga, LogObserver};

let ga = Ga::new(config, population)
    .with_observer(Arc::new(LogObserver))
    .run();

CompositeObserver<U> — fan-out observer that forwards all hooks to a list of inner observers. Inner observers must implement AllObserver<U> (a supertrait marker).

use std::sync::Arc;
use genetic_algorithms::{Ga, CompositeObserver, LogObserver};

let composite = CompositeObserver::new()
    .add(Arc::new(LogObserver));

let ga = Ga::new(config, population)
    .with_observer(Arc::new(composite))
    .run();

MetricsObserver — emits metrics-crate gauges, counters, and histograms per generation. Requires feature flag:

genetic_algorithms = { version = "2.2.0", features = ["observer-metrics"] }
#[cfg(feature = "observer-metrics")]
use genetic_algorithms::MetricsObserver;

#[cfg(feature = "observer-metrics")]
let composite = composite.add(Arc::new(MetricsObserver::new("my_run")));

Emitted metrics: ga.generation.best_fitness, ga.generation.mean_fitness, ga.generation.diversity, ga.operator.*_ms histograms, ga.event.new_best / ga.event.stagnation / ga.event.extension_triggered counters.

TracingObserver — emits tracing-crate spans and events. Requires feature flag:

genetic_algorithms = { version = "2.2.0", features = ["observer-tracing"] }

Custom observer

Implement GaObserver<U> (or IslandGaObserver<U> / Nsga2Observer<U>) on your own type. Only override the hooks you care about — all hooks have default no-op implementations:

use genetic_algorithms::{GaObserver, stats::GenerationStats};

struct MyObserver;

impl<U: genetic_algorithms::traits::ChromosomeT> GaObserver<U> for MyObserver {
    fn on_generation_end(&self, stats: &GenerationStats) {
        println!("Gen {} best={:.4}", stats.generation, stats.best_fitness);
    }
}

Visualization

Optional feature flag. Add to Cargo.toml:

genetic_algorithms = { version = "2.1.0", features = ["visualization"] }

Three functions in genetic_algorithms::visualization:

// Fitness over generations (best, average, worst lines)
visualization::plot_fitness(&stats, "fitness.png")?;
visualization::plot_fitness(&stats, "fitness.svg")?;

// Diversity over generations
visualization::plot_diversity(&stats, "diversity.png")?;

// Fitness distribution for a generation (raw fitness values)
let fitness: Vec<f64> = population.iter().map(|c| c.fitness()).collect();
visualization::plot_histogram(&fitness, "distribution.png")?;

Format is determined by path extension (.png or .svg). All functions return Result<(), VisualizationError>.

GA Configuration

GaConfiguration (or the Ga builder) exposes:

  • Limits: problem_solving (Minimization | Maximization | FixedFitness), max_generations, fitness_target, population_size, genes_per_chromosome, needs_unique_ids, alleles_can_be_repeated.
  • Selection: number_of_couples, method.
  • Crossover: number_of_points (MultiPoint), probability_min / probability_max, method, sbx_eta (SBX distribution index), blend_alpha (BLX-α alpha parameter).
  • Mutation: probability_min / probability_max, method, step (Creep step size), sigma (Gaussian standard deviation).
  • Survivor: survivor.
  • Elitism: elitism_count — preserve the top N individuals unchanged across generations.
  • Extension: extension_configuration — optional diversity control strategies. Configure with with_extension_method(), with_extension_diversity_threshold(), with_extension_survival_rate(), with_extension_mutation_rounds(), with_extension_elite_count().
  • Stopping criteria: StoppingCriteria with stagnation_generations, convergence_threshold, max_duration_secs. The GA stops when any enabled criterion is met.
  • Infra: adaptive_ga, number_of_threads, log_level.
  • Progress (present but not yet wired): save_progress_configuration (future/experimental).

Adaptive GA

When adaptive_ga = true:

  • Crossover & mutation probabilities are recomputed per parent pair using relative fitness (f_max, f_avg).
  • You must set both probability_min and probability_max for crossover and mutation. When adaptive_ga = false:
  • If probability_max is absent, defaults to 1.0 (operator always applied).

Multithreading & Performance

  • with_threads(n) configures parallelism (internally uses rayon).
  • Selection, crossover, mutation, and fitness evaluation are parallelized each generation.
  • Cow prevents needless cloning of DNA vectors.

Quick Example

Minimal GA using Range<i32> chromosomes targeting fitness 0 (minimization):

use genetic_algorithms::ga::Ga;
use genetic_algorithms::traits::ConfigurationT;
use genetic_algorithms::configuration::ProblemSolving;
use genetic_algorithms::operations::{Selection, Crossover, Mutation, Survivor};
use genetic_algorithms::genotypes::Range as RangeGene;
use genetic_algorithms::initializers::range_random_initialization;
use genetic_algorithms::chromosomes::Range as RangeChromosome; // Chromosome type

fn fitness_fn(dna: &[RangeGene<i32>]) -> f64 { // Replace with domain logic
    dna.iter().map(|g| g.get_value() as f64).sum() // Simple example
}

let alleles = vec![RangeGene::new(0, vec![(0, 7)], 0)];
let alleles_clone = alleles.clone();
let mut ga = Ga::<RangeChromosome<i32>>::new();
let _population = ga
    .with_genes_per_chromosome(8)
    .with_population_size(100)
    .with_initialization_fn(move |genes, _, _| {
        range_random_initialization(genes, Some(&alleles_clone), Some(false))
    })
    .with_fitness_fn(fitness_fn)
    .with_selection_method(Selection::Tournament)
    .with_crossover_method(Crossover::Uniform)
    .with_mutation_method(Mutation::Swap)
    .with_survivor_method(Survivor::Fitness)
    .with_problem_solving(ProblemSolving::Minimization)
    .with_max_generations(500)
    .with_fitness_target(0.0)
    .with_threads(4)
    .run();

Full Example (Range)

Includes adaptive GA and probabilities:

use genetic_algorithms::{ga::Ga, traits::ConfigurationT};
use genetic_algorithms::configuration::ProblemSolving;
use genetic_algorithms::operations::{Selection, Crossover, Mutation, Survivor};
use genetic_algorithms::genotypes::Range as RangeGene;
use genetic_algorithms::chromosomes::Range as RangeChromosome;
use genetic_algorithms::initializers::range_random_initialization;

fn fitness_fn(dna: &[RangeGene<i32>]) -> f64 {
    // Penalize greater values: minimization goal
    dna.iter().map(|g| g.get_value() as f64).sum()
}

let alleles = vec![RangeGene::new(0, vec![(0, 50)], 0)];
let alleles_clone = alleles.clone();
let mut ga = Ga::<RangeChromosome<i32>>::new();
let population = ga
    .with_adaptive_ga(true)
    .with_genes_per_chromosome(16)
    .with_population_size(200)
    .with_initialization_fn(move |genes, _, _| {
        range_random_initialization(genes, Some(&alleles_clone), Some(false))
    })
    .with_fitness_fn(fitness_fn)
    .with_selection_method(Selection::StochasticUniversalSampling)
    .with_crossover_method(Crossover::MultiPoint)
    .with_crossover_number_of_points(3)
    .with_crossover_probability_min(0.4)
    .with_crossover_probability_max(0.9)
    .with_mutation_method(Mutation::Inversion)
    .with_mutation_probability_min(0.05)
    .with_mutation_probability_max(0.3)
    .with_survivor_method(Survivor::Fitness)
    .with_problem_solving(ProblemSolving::Minimization)
    .with_max_generations(2000)
    .with_fitness_target(0.0)
    .with_threads(8)
    .with_logs(genetic_algorithms::configuration::LogLevel::Info)
    .run();

println!("Best fitness: {}", population.unwrap().best_chromosome.get_fitness());

Examples

Run any example directly with cargo run:

Example Domain Command
rastrigin Continuous optimization cargo run --example rastrigin
nsga2_zdt1 Multi-objective (NSGA-II) cargo run --example nsga2_zdt1
island_model Parallel / island model cargo run --example island_model
job_scheduling Permutation / scheduling cargo run --example job_scheduling
feature_selection Binary / adaptive GA cargo run --example feature_selection
niching Multimodal / niching cargo run --example niching
knapsack_binary Binary / combinatorial cargo run --example knapsack_binary
nqueens_range Constraint satisfaction cargo run --example nqueens_range
onemax_binary Binary / baseline cargo run --example onemax_binary
onemax_extension Binary / diversity control cargo run --example onemax_extension

Usage

Add to your Cargo.toml:

[dependencies]
genetic_algorithms = "2.1.0"

# Optional: enable PNG/SVG chart generation
# genetic_algorithms = { version = "2.1.0", features = ["visualization"] }

Development

Build

cargo build            # Debug build
cargo build --release  # Optimized release build

Run Tests

cargo test                              # Run all tests
cargo test --features serde             # Include serde tests
cargo test --features visualization     # Include visualization tests
cargo test -- --nocapture               # With visible stdout/stderr

Run Benchmarks

Benchmarks use Criterion and are located in benches/.

cargo bench                       # Run all benchmarks
cargo bench --bench crossover     # Run only crossover benchmarks
cargo bench --bench mutation      # Run only mutation benchmarks
cargo bench --bench selection     # Run only selection benchmarks
cargo bench --bench survivor      # Run only survivor benchmarks
cargo bench --no-run              # Only compile benchmarks (useful for CI)

Reports are generated in target/criterion/ and can be viewed in a browser.

Code Quality

cargo fmt --check   # Check formatting (no changes)
cargo fmt           # Auto-format code
cargo clippy        # Run linter

Roadmap / Notes

  • save_progress_configuration: fields present but not wired into the main loop yet (future: periodic population / best chromosome persistence).
  • Optional flamegraph profiling integration removed to avoid Criterion version conflicts.
  • For heavy fitness functions consider external profiling tools.

License

Apache-2.0. See LICENSE file.