evolve
A generic, composable genetic algorithm framework for Rust.
Note: This library is in rapid development. While breaking changes will be avoided where possible, there is no guarantee of API stability until 1.0.
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
MaximizeandMinimizefitness comparators out of the box- Closures work as fitness evaluators and comparators via blanket trait impls
- Minimal dependencies:
randandvecpool(optionalpooledfor parallel execution)
Available Operators
Selection
- Tournament — tournament selection with replacement
- Elitism — preserve the best N individuals
- RouletteWheel — fitness-proportionate selection
- Rank — rank-based selection (avoids premature convergence)
- Sus — stochastic universal sampling (low-variance fitness-proportionate)
Crossover
- SinglePoint — single-point crossover
- TwoPoint — two-point crossover
- Uniform — per-gene random parent selection
- Arithmetic — numeric blending for continuous genomes (f32/f64)
Mutation
- RandomReset — replace a random gene with a new random value
- Gaussian — add Gaussian noise (continuous genomes)
- Creep — add a small random offset (discrete genomes)
- Swap — swap two random genes (permutation problems)
- Inversion — reverse a random segment (permutation problems)
- Scramble — shuffle a random segment (permutation problems)
- SegmentDuplication — duplicate a random segment (variable-length)
- SegmentDeletion — delete a random segment (variable-length)
Combinators
- Pipeline — chain operators sequentially
- Fill — repeat an operator until target population size
- Combine — run operators on same input, merge outputs
- Weighted — probabilistically select one operator per call
- Repeat — apply an operator N times
- Proportional — split output proportionally across operators
- Conditional — apply operator A or B based on a predicate
Utility
- Identity — no-op pass-through
- WithRate — apply an operator per-individual with a given probability
Quick Start
use ;
use NonZero;
Builder Pattern
The GA can also be constructed incrementally with a builder:
use ;
use NonZero;
Custom Operators
Implement GeneticOperator to define your own:
use ;
;
Parallel Execution
Enable the parallel feature to run operators across multiple threads:
[]
= { = "0.3", = ["parallel"] }
Parallel operators distribute work across a thread pool using the pooled crate:
use ;
use NonZero;
A Runtime is created automatically (defaulting to available_parallelism threads) or can be configured via the builder's .runtime() method:
// Use a custom thread pool size
let ga = builder
// ...
.runtime
.build;
Grammatical Evolution
Evolve programs by mapping integer codon sequences through a grammar. Each individual is a variable-length Vec<u8> (or any unsigned integer); codons select productions during derivation, producing a program that is then evaluated for fitness.
Using the grammar! macro (zero-cost, compile-time)
use grammar;
use ;
use GeFitness;
grammar!
Using the runtime Grammar<T> builder
use Grammar;
let grammar = builder
.rule
.rule
.start
.build;
Running GE
use ;
use NonZero;
let fitness = new;
let mut ea = new;
let result = ea.run;
Variable-length operators
GE benefits from variable-length genomes. Use RangedRandom for initialization (produces genomes of random length within a range) and the variable-length mutation operators SegmentDuplication and SegmentDeletion to explore different codon lengths during evolution.
Collectors
The run() method uses a Standard collector that records best fitness and timing per generation:
let result = ea.run;
println!;
println!;
println!;
println!;
Use run_with for a different collector:
use ;
let result = ea.run_with; // just population + generation count
ea.run_with; // discard everything, returns ()
ea.run_with; // prints each generation, returns ()
ea.run_with;
Implement the Collector trait for custom data collection:
use Collector;
use State;
;
Experiment Runner
Run multiple independent trials for statistical analysis:
use Experiment;
use Standard;
let results = new.run;
for in results.iter.enumerate
Contributing
Contributions are welcome! Feel free to open an issue for bug reports, feature requests, or questions. Pull requests are also appreciated.
AI Disclosure
AI was used only to assist with writing comments, writing tests, writing examples, and as a rubber duck to discuss ideas with. All final decisions and code were written by a human.