1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
//! # 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 grammar;