evolution_ternary/
engine.rs1use crate::population::{TernaryPopulation, SimpleRng};
4use crate::fitness::FitnessFunction;
5use crate::selection::{SelectionMethod, Selection};
6use crate::mutation::Mutation;
7use crate::species::SpeciesTracker;
8use crate::convergence::ConvergenceDetector;
9
10#[derive(Clone, Debug)]
12pub struct EvolutionConfig {
13 pub selection: SelectionMethod,
14 pub mutation: Mutation,
15 pub max_generations: usize,
16 pub elitism: usize, pub seed: u64,
18}
19
20impl EvolutionConfig {
21 pub fn default_config() -> Self {
22 Self {
23 selection: SelectionMethod::tournament(),
24 mutation: Mutation::default_rates(),
25 max_generations: 100,
26 elitism: 2,
27 seed: 42,
28 }
29 }
30}
31
32#[derive(Clone, Debug)]
34pub struct EvolutionResult {
35 pub generations_run: usize,
36 pub best_fitness: f64,
37 pub best_strategy: Vec<u8>,
38 pub fitness_history: Vec<f64>,
39 pub converged: bool,
40 pub convergence_reason: Option<String>,
41 pub final_species: (usize, usize, usize, usize),
42}
43
44pub struct EvolutionEngine {
46 pub config: EvolutionConfig,
47 pub fitness_fn: FitnessFunction,
48 pub species_tracker: SpeciesTracker,
49 pub convergence_detector: ConvergenceDetector,
50}
51
52impl EvolutionEngine {
53 pub fn new(config: EvolutionConfig, fitness_fn: FitnessFunction) -> Self {
54 Self {
55 config,
56 fitness_fn,
57 species_tracker: SpeciesTracker::new(),
58 convergence_detector: ConvergenceDetector::default_detector(),
59 }
60 }
61
62 fn evaluate(&self, pop: &mut TernaryPopulation) {
64 for agent in &mut pop.agents {
65 agent.fitness = self.fitness_fn.evaluate(&agent.strategy);
66 }
67 }
68
69 pub fn run(&mut self, initial_pop: TernaryPopulation) -> EvolutionResult {
71 let mut rng = SimpleRng::from_seed(self.config.seed);
72 let mut pop = initial_pop;
73 let pop_size = pop.size();
74
75 self.evaluate(&mut pop);
77 pop.sort_by_fitness();
78
79 let mut fitness_history = Vec::new();
80
81 for gen in 0..self.config.max_generations {
82 let avg = pop.avg_fitness();
83 fitness_history.push(avg);
84 self.convergence_detector.record(avg);
85 self.species_tracker.record(&pop.agents);
86
87 let status = self.convergence_detector.check(&self.species_tracker);
89 if status.converged && gen > 5 {
90 let best = pop.best().clone();
91 let final_species = *self.species_tracker.history.last().unwrap();
92 return EvolutionResult {
93 generations_run: gen + 1,
94 best_fitness: best.fitness,
95 best_strategy: best.strategy,
96 fitness_history,
97 converged: true,
98 convergence_reason: status.reason,
99 final_species,
100 };
101 }
102
103 let parents = self.config.selection.select(&pop, pop_size, &mut rng);
105
106 let mut next_agents: Vec<_> = pop.agents[..self.config.elitism.min(pop_size)].to_vec();
108
109 let mut i = 0;
111 while next_agents.len() < pop_size {
112 let p1 = &parents[i % parents.len()];
113 let p2 = &parents[(i + 1) % parents.len()];
114 let (mut c1, mut c2) = self.config.mutation.crossover(p1, p2, &mut rng);
115 self.config.mutation.mutate(&mut c1, &mut rng);
116 self.config.mutation.mutate(&mut c2, &mut rng);
117 next_agents.push(c1);
118 if next_agents.len() < pop_size {
119 next_agents.push(c2);
120 }
121 i += 2;
122 }
123
124 pop.agents = next_agents;
126 self.evaluate(&mut pop);
127 pop.sort_by_fitness();
128 }
129
130 fitness_history.push(pop.avg_fitness());
132 self.convergence_detector.record(pop.avg_fitness());
133 self.species_tracker.record(&pop.agents);
134
135 let best = pop.best().clone();
136 let final_species = *self.species_tracker.history.last().unwrap();
137 let status = self.convergence_detector.check(&self.species_tracker);
138
139 EvolutionResult {
140 generations_run: self.config.max_generations,
141 best_fitness: best.fitness,
142 best_strategy: best.strategy,
143 fitness_history,
144 converged: status.converged,
145 convergence_reason: status.reason,
146 final_species,
147 }
148 }
149}