evolve 0.4.0

A generic, composable genetic algorithm framework for Rust
Documentation
//! # 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`](fitness::Maximize) and [`Minimize`](fitness::Minimize) fitness comparators out of the box
//! - Closures work as fitness evaluators and comparators via blanket trait impls
//! - [`Collector`](collector::Collector) trait for customizable run results (timing, fitness history, or your own)
//! - [`Experiment`](experiment::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::Random) initialization,
//! a [`MaxGenerations`](termination::MaxGenerations) termination condition, and
//! [`Fill`](operators::sequential::combinator::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`](operators::sequential::combinator::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`](fitness::FitnessEvaluator),
//! and any closure `Fn(&F, &F) -> bool` works as a [`FitnessComparator`](fitness::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`](operators::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`](https://crates.io/crates/pooled).
//!
//! ```toml
//! [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>`](grammar::Grammar) provides runtime grammar construction, or use the
//!   [`grammar!`] proc-macro from `evolve-derive` for zero-cost compile-time grammars.
//! - [`GeFitness`](fitness::GeFitness) wraps the codon→phenotype→fitness pipeline automatically,
//!   handling grammar mapping and builder invocation.
//! - [`Bytecode<T>`](phenotype::bytecode::Bytecode) and the [`Instruction`](phenotype::bytecode::Instruction) trait
//!   provide a built-in stack-machine execution engine for evolved programs.
//! - Variable-length genome operators are included:
//!   [`RangedRandom`](initialization::RangedRandom) for initialization,
//!   `SegmentDuplication` and `SegmentDeletion` for structural mutation.

pub use evolve_derive::grammar;

pub mod algorithm;
pub mod collector;
pub mod core;
pub mod experiment;
pub mod fitness;
pub mod grammar;
pub mod initialization;
pub mod operators;
pub mod phenotype;
pub mod random;
pub mod termination;