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
//! Genetic operators.
//!
//! - [`sequential`] — single-threaded operators and combinators
use crate;
pub
/// The core trait for all genetic operators (selection, crossover, mutation, combinators, etc.).
///
/// An operator receives the current [`State`] (population + generation) and a
/// [`Context`] (fitness evaluator + RNG + comparator) and produces [`Offspring`].
///
/// # Two methods
///
/// - [`apply`](Self::apply) — receives a borrowed state. Use this when the caller
/// retains ownership of the population (e.g., [`Fill`](crate::operators::sequential::combinator::Fill)
/// calls the same operator repeatedly against the same population).
///
/// - [`transform`](Self::transform) — receives an owned state. Used by
/// [`Pipeline`](crate::operators::sequential::combinator::Pipeline) to pass
/// data between chained operators. Operators that can modify individuals in place
/// (e.g., mutation) should override this to avoid unnecessary cloning.
///
/// Most operators only need to implement [`apply`](Self::apply). The default
/// [`transform`](Self::transform) delegates to it.
///
/// # Examples
///
/// A simple operator that only implements `apply`:
///
/// ```
/// use evolve::core::{context::Context, offspring::Offspring, state::State};
/// use evolve::core::individual::Individual;
/// use evolve::operators::GeneticOperator;
/// use evolve::fitness::FitnessEvaluator;
/// use rand::{Rng, RngExt};
///
/// /// Replaces every individual's genome with a random value.
/// struct Randomize;
///
/// impl<F, R: Rng, Fe: FitnessEvaluator<u32, F>, C> GeneticOperator<u32, F, Fe, R, C> for Randomize {
/// fn apply(&self, state: &State<u32, F>, ctx: &mut Context<Fe, R, C>) -> Offspring<u32, F> {
/// let ind = Individual::new(ctx.rng().random::<u32>());
/// Offspring::Single(ind)
/// }
/// }
/// ```
///
/// An operator that overrides `transform` for in-place mutation:
///
/// ```
/// use evolve::core::{context::Context, offspring::Offspring, population::Population, state::State};
/// use evolve::core::individual::Individual;
/// use evolve::operators::GeneticOperator;
/// use evolve::fitness::FitnessEvaluator;
/// use rand::Rng;
///
/// /// Doubles every genome value in place.
/// struct DoubleGenome;
///
/// impl<F, R: Rng, Fe: FitnessEvaluator<u32, F>, C> GeneticOperator<u32, F, Fe, R, C> for DoubleGenome {
/// fn apply(&self, state: &State<u32, F>, ctx: &mut Context<Fe, R, C>) -> Offspring<u32, F> {
/// // Fallback: clone and modify
/// let pop: Population<u32, F> = state.population().iter()
/// .map(|ind| Individual::new(ind.genome() * 2))
/// .collect();
/// Offspring::Multiple(pop)
/// }
///
/// fn transform(&self, state: State<u32, F>, ctx: &mut Context<Fe, R, C>) -> Offspring<u32, F> {
/// // Optimized: modify in place without cloning
/// let pop: Population<u32, F> = state.into_population().into_iter()
/// .map(|ind| ind.mutate_genome(|g| *g *= 2))
/// .collect();
/// Offspring::Multiple(pop)
/// }
/// }
/// ```