Skip to main content

Crate evolve

Crate evolve 

Source
Expand description

§evolve

A generic, composable genetic algorithm framework for Rust.

evolve provides the building blocks to assemble genetic algorithms from reusable, type-safe components. Operators are composed using combinators — chain them into pipelines, weight them probabilistically, or repeat them to fill a population — all with zero-cost abstractions.

§Features

  • Fully generic over genome type, fitness type, RNG, and fitness comparator
  • Built-in operators for selection, crossover, and mutation
  • Composable combinators for structuring the flow of the algorithm
  • Maximize and Minimize fitness comparators out of the box
  • Closures work as fitness evaluators and comparators via blanket trait impls
  • Collector trait for customizable run results (timing, fitness history, or your own)
  • Experiment runner for batch trials with configurable collectors
  • No dependencies beyond rand (optional pooled for parallel execution)

§Quick Start

The simplest way to get started is to use Random initialization, a MaxGenerations termination condition, and Fill with a mutation operator:

use evolve::{
    algorithm::EvolutionaryAlgorithm,
    fitness::Maximize,
    initialization::Random,
    operators::sequential::combinator::Fill,
    operators::sequential::mutation::RandomReset,
    termination::MaxGenerations,
};
use std::num::NonZero;

let mut ga = EvolutionaryAlgorithm::new(
    Random::new(),
    MaxGenerations::new(100),
    |args: &[u32; 2]| args[0] as usize + args[1] as usize,
    Fill::from_population_size(RandomReset::new()),
    NonZero::new(500).unwrap(),
    rand::rng(),
    Maximize,
);

let result = ga.run();
let fe = |args: &[u32; 2]| args[0] as usize + args[1] as usize;
let best = result.population().best(&fe, &Maximize);
println!("Best genome: {:?}, fitness: {:?}", best.genome(), best.fitness(&fe));

§Composing Operators

The real power of evolve comes from composing operators using combinators. A typical genetic algorithm pipeline selects parents, crosses them over, and mutates the offspring:

use evolve::{
    algorithm::EvolutionaryAlgorithm,
    fitness::Maximize,
    initialization::Random,
    operators::sequential::combinator::{Combine, Fill, Pipeline},
    operators::sequential::crossover::SinglePoint,
    operators::sequential::mutation::RandomReset,
    operators::sequential::selection::Tournament,
    termination::MaxGenerations,
};
use std::num::NonZero;

// Select two parents → crossover → mutate, repeated until the population is full
let operators = Fill::from_population_size(Pipeline::new((
    Combine::new((
        Tournament::new(NonZero::new(3).unwrap()),
        Tournament::new(NonZero::new(3).unwrap()),
    )),
    SinglePoint::new(),
    RandomReset::new(),
)));

let mut ga = EvolutionaryAlgorithm::new(
    Random::new(),
    MaxGenerations::new(200),
    |g: &[u8; 8]| g.iter().map(|x| *x as u32).sum::<u32>(),
    operators,
    NonZero::new(100).unwrap(),
    rand::rng(),
    Maximize,
);

let result = ga.run();

§Weighted Operator Selection

Use Weighted to probabilistically choose between different operators each time:

use evolve::operators::sequential::combinator::{Fill, Weighted};
use evolve::operators::sequential::mutation::RandomReset;
use evolve::operators::sequential::crossover::SinglePoint;
use std::num::NonZero;

// 75% chance of mutation, 25% chance of crossover
let operators = Fill::from_population_size(Weighted::new((
    (RandomReset::<u8>::new(), NonZero::new(3u16).unwrap()),
    (SinglePoint::<u8>::new(), NonZero::new(1u16).unwrap()),
)));

§Custom Fitness

Any closure Fn(&G) -> F works as a FitnessEvaluator, and any closure Fn(&F, &F) -> bool works as a FitnessComparator:

use evolve::{
    algorithm::EvolutionaryAlgorithm,
    initialization::Random,
    operators::sequential::combinator::Fill,
    operators::sequential::mutation::RandomReset,
    termination::MaxGenerations,
};
use std::num::NonZero;

// Custom comparator: prefer fitness values closer to 100
let mut ga = EvolutionaryAlgorithm::new(
    Random::new(),
    MaxGenerations::new(100),
    |g: &[u8; 2]| (g[0] as i32 + g[1] as i32 - 100).abs(),
    Fill::from_population_size(RandomReset::new()),
    NonZero::new(200).unwrap(),
    rand::rng(),
    |a: &i32, b: &i32| a < b, // lower distance is better
);

let result = ga.run();

§Custom Operators

Implement GeneticOperator to define your own:

use evolve::{
    core::{context::Context, offspring::Offspring, state::State},
    operators::GeneticOperator,
};

struct MyOperator;

impl<G, F, Fe, R, C> GeneticOperator<G, F, Fe, R, C> for MyOperator {
    fn apply(&self, state: &State<G, F>, ctx: &mut Context<Fe, R, C>) -> Offspring<G, F> {
        todo!()
    }
}

§Parallel Execution

Enable the parallel feature to distribute operator work across multiple threads. This adds an optional dependency on pooled.

[dependencies]
evolve = { version = "0.1.0", features = ["parallel"] }

Parallel versions of mutation, crossover, and combinators are available under operators::parallel (requires the parallel feature).

§Grammatical Evolution

The crate supports Grammatical Evolution (GE) via the grammar and phenotype modules. GE evolves variable-length integer codon sequences (Vec<u8>) that are mapped through a context-free grammar to produce executable programs.

  • Grammar<T> provides runtime grammar construction, or use the grammar! proc-macro from evolve-derive for zero-cost compile-time grammars.
  • GeFitness wraps the codon→phenotype→fitness pipeline automatically, handling grammar mapping and builder invocation.
  • Bytecode<T> and the Instruction trait provide a built-in stack-machine execution engine for evolved programs.
  • Variable-length genome operators are included: RangedRandom for initialization, SegmentDuplication and SegmentDeletion for structural mutation.

Modules§

algorithm
Algorithm runners.
collector
Collectors for gathering results from algorithm runs.
core
Core types that make up the genetic algorithm.
experiment
Experiment runner for executing multiple independent trials.
fitness
Fitness evaluation and comparison.
grammar
Grammar representation and builder for grammatical evolution.
initialization
Population initialization strategies.
operators
Genetic operators.
phenotype
Phenotype building during grammar mapping.
random
Random value generation for genomes and genes.
termination
Termination conditions for the algorithm.

Macros§

grammar
Generates a zero-cost grammar with compile-time dispatch.