1mod alters;
2pub(crate) mod config;
3mod evaluators;
4mod objectives;
5mod population;
6mod problem;
7mod selectors;
8mod species;
9
10use crate::builder::evaluators::EvaluationParams;
11use crate::builder::objectives::OptimizeParams;
12use crate::builder::population::PopulationParams;
13use crate::builder::problem::ProblemParams;
14use crate::builder::selectors::SelectionParams;
15use crate::builder::species::SpeciesParams;
16use crate::genome::phenotype::Phenotype;
17#[cfg(feature = "serde")]
18use crate::io::FileReader;
19use crate::objectives::{Objective, Optimize};
20use crate::pipeline::Pipeline;
21use crate::steps::{
22 EngineStep, FilterStep, FrontStep, MetricStep, RecombineStep, SelectConfig, SpeciateStep,
23};
24use crate::{Chromosome, EvaluateStep, GeneticEngine};
25use crate::{
26 Crossover, EncodeReplace, EventBus, EventHandler, Front, Mutate, ReplacementStrategy,
27 RouletteSelector, TournamentSelector, context::EvolutionContext,
28};
29use crate::{Generation, Result};
30use config::EngineConfig;
31use radiate_alters::{UniformCrossover, UniformMutator};
32use radiate_core::MetricQuery;
33use radiate_core::evaluator::BatchFitnessEvaluator;
34use radiate_core::problem::{BatchEngineProblem, EngineProblem};
35use radiate_core::{Alterer, Ecosystem, Executor, FitnessEvaluator, Rate, Valid};
36use radiate_core::{RadiateError, ensure, radiate_err};
37use radiate_utils::VersionedCounts;
38#[cfg(feature = "serde")]
39use serde::Deserialize;
40use std::sync::{Arc, Mutex};
41
42#[derive(Clone)]
43pub struct EngineParams<C, T>
44where
45 C: Chromosome + 'static,
46 T: Clone + 'static,
47{
48 pub population_params: PopulationParams<C>,
49 pub evaluation_params: EvaluationParams<C, T>,
50 pub species_params: SpeciesParams<C>,
51 pub selection_params: SelectionParams<C>,
52 pub optimization_params: OptimizeParams<C>,
53 pub problem_params: ProblemParams<C, T>,
54
55 pub alterers: Vec<Alterer<C>>,
56 pub replacement_strategy: Arc<dyn ReplacementStrategy<C>>,
57 pub handlers: Vec<Arc<Mutex<dyn EventHandler<T>>>>,
58 pub generation: Option<Generation<C, T>>,
59 pub exprs: Option<Arc<Mutex<Vec<MetricQuery>>>>,
60}
61
62pub struct GeneticEngineBuilder<C, T>
75where
76 C: Chromosome + 'static,
77 T: Clone + 'static,
78{
79 params: EngineParams<C, T>,
80 errors: Vec<RadiateError>,
81}
82
83impl<C, T> GeneticEngineBuilder<C, T>
84where
85 C: Chromosome + PartialEq + Clone,
86 T: Clone + Send,
87{
88 pub(self) fn add_error_if<F>(&mut self, condition: F, message: &str)
89 where
90 F: Fn() -> bool,
91 {
92 if condition() {
93 self.errors.push(radiate_err!(Builder: "{}", message));
94 }
95 }
96
97 pub fn replace_strategy<R: ReplacementStrategy<C> + 'static>(mut self, replace: R) -> Self {
103 self.params.replacement_strategy = Arc::new(replace);
104 self
105 }
106
107 pub fn subscribe<H>(mut self, handler: H) -> Self
112 where
113 H: EventHandler<T> + 'static,
114 {
115 self.params.handlers.push(Arc::new(Mutex::new(handler)));
116 self
117 }
118
119 pub fn generation(mut self, generation: Generation<C, T>) -> Self {
122 self.params.generation = Some(generation);
123 self
124 }
125
126 pub fn register_metrics(mut self, exprs: Vec<impl Into<MetricQuery>>) -> Self {
127 self.params.exprs = Some(Arc::new(Mutex::new(
128 exprs.into_iter().map(|e| e.into()).collect(),
129 )));
130 self
131 }
132
133 #[cfg(feature = "serde")]
137 pub fn load_checkpoint<P: AsRef<std::path::Path>>(
138 mut self,
139 path: P,
140 reader: impl FileReader<Generation<C, T>>,
141 ) -> Self
142 where
143 C: for<'de> Deserialize<'de>,
144 T: for<'de> Deserialize<'de>,
145 {
146 let read_generation = reader.read(path.as_ref().to_path_buf());
147 if let Err(e) = &read_generation {
148 self.add_error_if(|| true, &format!("Failed to read checkpoint: {}", e));
149 }
150 let generation = read_generation.expect("Failed to read checkpoint file");
151 self.generation(generation)
152 }
153}
154
155impl<C, T> GeneticEngineBuilder<C, T>
157where
158 C: Chromosome + Clone + PartialEq + 'static,
159 T: Clone + Send + Sync + 'static,
160{
161 pub fn build(self) -> GeneticEngine<C, T> {
164 match self.try_build() {
165 Ok(engine) => engine,
166 Err(e) => panic!("{e}"),
167 }
168 }
169
170 pub fn try_build(mut self) -> Result<GeneticEngine<C, T>> {
171 if !self.errors.is_empty() {
172 return Err(radiate_err!(
173 Builder: "Failed to build GeneticEngine: {:?}",
174 self.errors
175 ));
176 }
177
178 self.build_problem()?;
179 self.build_population()?;
180 self.build_alterer()?;
181 self.build_front()?;
182
183 let config = EngineConfig::<C, T>::from(&self.params);
184
185 let mut pipeline = Pipeline::<C>::default();
186
187 pipeline.add_step(Self::build_eval_step(&config));
188 pipeline.add_step(Self::build_recombine_step(&config));
189 pipeline.add_step(Self::build_filter_step(&config));
190 pipeline.add_step(Self::build_eval_step(&config));
191 pipeline.add_step(Self::build_front_step(&config));
192 pipeline.add_step(Self::build_species_step(&config));
193 pipeline.add_step(Self::build_audit_step(&config));
194
195 let event_bus = EventBus::new(config.bus_executor(), config.handlers());
196 let context = EvolutionContext::from(config);
197
198 Ok(GeneticEngine::<C, T>::new(context, pipeline, event_bus))
199 }
200
201 fn build_problem(&mut self) -> Result<()> {
207 if self.params.problem_params.problem.is_some() {
208 return Ok(());
209 }
210
211 ensure!(
212 self.params.problem_params.codec.is_some(),
213 Builder: "Codec not set"
214 );
215
216 let raw_fitness_fn = self.params.problem_params.raw_fitness_fn.clone();
217 let fitness_fn = self.params.problem_params.fitness_fn.clone();
218 let batch_fitness_fn = self.params.problem_params.batch_fitness_fn.clone();
219 let raw_batch_fitness_fn = self.params.problem_params.raw_batch_fitness_fn.clone();
220
221 if batch_fitness_fn.is_some() || raw_batch_fitness_fn.is_some() {
222 self.params.problem_params.problem = Some(Arc::new(BatchEngineProblem {
223 objective: self.params.optimization_params.objectives.clone(),
224 codec: self.params.problem_params.codec.clone().unwrap(),
225 batch_fitness_fn,
226 raw_batch_fitness_fn,
227 }));
228
229 self.params.evaluation_params.evaluator = Arc::new(BatchFitnessEvaluator::new(
231 self.params.evaluation_params.fitness_executor.clone(),
232 ));
233
234 Ok(())
235 } else if fitness_fn.is_some() || raw_fitness_fn.is_some() {
236 self.params.problem_params.problem = Some(Arc::new(EngineProblem {
237 objective: self.params.optimization_params.objectives.clone(),
238 codec: self.params.problem_params.codec.clone().unwrap(),
239 fitness_fn,
240 raw_fitness_fn,
241 }));
242
243 Ok(())
244 } else {
245 Err(radiate_err!(Builder: "Fitness function not set"))
246 }
247 }
248
249 fn build_population(&mut self) -> Result<()> {
252 if self.params.population_params.ecosystem.is_some() {
253 return Ok(());
254 }
255
256 let ecosystem = match &self.params.population_params.ecosystem {
257 None => Some(match self.params.problem_params.problem.as_ref() {
258 Some(problem) => {
259 let size = self.params.population_params.population_size;
260 let mut phenotypes = Vec::with_capacity(size);
261
262 for _ in 0..size {
263 let genotype = problem.encode();
264
265 if !genotype.is_valid() {
266 return Err(radiate_err!(
267 Builder: "Encoded genotype is not valid",
268 ));
269 }
270
271 phenotypes.push(Phenotype::from((genotype, 0)));
272 }
273
274 Ecosystem::from(phenotypes)
275 }
276 None => return Err(radiate_err!(Builder: "Codec not set")),
277 }),
278 Some(ecosystem) => Some(ecosystem.clone()),
279 };
280
281 if let Some(ecosystem) = ecosystem {
282 self.params.population_params.ecosystem = Some(ecosystem);
283 }
284
285 Ok(())
286 }
287
288 fn build_alterer(&mut self) -> Result<()> {
292 if !self.params.alterers.is_empty() {
293 for alter in self.params.alterers.iter_mut() {
294 if !alter.rate().is_valid() {
295 return Err(radiate_err!(
296 Builder: "Alterer {} is not valid. Ensure rate {:?} is valid.", alter.name(), alter.rate()
297 ));
298 }
299 }
300
301 return Ok(());
302 }
303
304 let crossover = UniformCrossover::new(0.5).alterer();
305 let mutator = UniformMutator::new(0.1).alterer();
306
307 self.params.alterers.push(crossover);
308 self.params.alterers.push(mutator);
309
310 Ok(())
311 }
312
313 fn build_front(&mut self) -> Result<()> {
317 if self.params.optimization_params.front.is_some() {
318 return Ok(());
319 } else if let Some(generation) = &self.params.generation
320 && let Some(front) = generation.front()
321 {
322 self.params.optimization_params.front = Some(front.clone());
323 return Ok(());
324 }
325
326 let front_obj = self.params.optimization_params.objectives.clone();
327 self.params.optimization_params.front = Some(Front::new(
328 self.params.optimization_params.front_range.clone(),
329 front_obj,
330 ));
331
332 Ok(())
333 }
334
335 fn build_eval_step(config: &EngineConfig<C, T>) -> Option<Box<dyn EngineStep<C>>> {
336 let eval_step = EvaluateStep {
337 objective: config.objective(),
338 problem: config.problem(),
339 evaluator: config.evaluator(),
340 };
341
342 Some(Box::new(eval_step))
343 }
344
345 fn build_recombine_step(config: &EngineConfig<C, T>) -> Option<Box<dyn EngineStep<C>>> {
346 let offspring_selector = config.offspring_selector();
347 let survivor_selector = config.survivor_selector();
348
349 let off_name = offspring_selector.name();
350 let offspring_base_name = radiate_utils::intern!(off_name);
351 let offspring_time_name = radiate_utils::intern!(format!("{}.time", offspring_base_name));
352
353 let surv_name = survivor_selector.name();
354 let survivor_base_name = radiate_utils::intern!(surv_name);
355 let survivor_time_name = radiate_utils::intern!(format!("{}.time", survivor_base_name));
356
357 let survivor_select = SelectConfig {
358 selector: survivor_selector,
359 count: config.survivor_count(),
360 names: (survivor_base_name, survivor_time_name),
361 };
362
363 let offspring_select = SelectConfig {
364 selector: offspring_selector,
365 count: config.offspring_count(),
366 names: (offspring_base_name, offspring_time_name),
367 };
368
369 let recombine_step = RecombineStep {
370 survivor: crate::steps::SurvivorConfig {
371 select: survivor_select,
372 },
373 offspring: crate::steps::OffspringConfig {
374 select: offspring_select,
375 alters: config.alters().to_vec(),
376 },
377 objective: config.objective(),
378 survivor_counts: VersionedCounts::new(),
379 offspring_counts: VersionedCounts::new(),
380 };
381
382 Some(Box::new(recombine_step))
383 }
384
385 fn build_filter_step(config: &EngineConfig<C, T>) -> Option<Box<dyn EngineStep<C>>> {
386 let filter_step = FilterStep {
387 replacer: config.replacement_strategy(),
388 encoder: config.encoder(),
389 max_age: config.max_age(),
390 max_species_age: config.max_species_age(),
391 };
392
393 Some(Box::new(filter_step))
394 }
395
396 fn build_audit_step(config: &EngineConfig<C, T>) -> Option<Box<dyn EngineStep<C>>> {
397 Some(Box::new(MetricStep::new(
398 config.objective().clone(),
399 config.exprs().clone(),
400 )))
401 }
402
403 fn build_front_step(config: &EngineConfig<C, T>) -> Option<Box<dyn EngineStep<C>>> {
404 if config.objective().is_single() {
405 return None;
406 }
407
408 let front_step = FrontStep {
409 front: config.front(),
410 };
411
412 Some(Box::new(front_step))
413 }
414
415 fn build_species_step(config: &EngineConfig<C, T>) -> Option<Box<dyn EngineStep<C>>> {
416 let diversity = config.diversity()?;
417
418 let species_step = SpeciateStep {
419 threshold: config.species_threshold(),
420 distance: diversity,
421 executor: config.species_executor(),
422 objective: config.objective(),
423 distances: Vec::new(),
424 assignments: Arc::new(Mutex::new(Vec::new())),
425 };
426
427 Some(Box::new(species_step))
428 }
429}
430
431impl<C, T> Default for GeneticEngineBuilder<C, T>
432where
433 C: Chromosome + 'static,
434 T: Clone + Send + 'static,
435{
436 fn default() -> Self {
437 GeneticEngineBuilder {
438 params: EngineParams {
439 population_params: PopulationParams {
440 population_size: 100,
441 max_age: 20,
442 ecosystem: None,
443 },
444 species_params: SpeciesParams {
445 diversity: None,
446 species_threshold: Rate::Fixed(0.5),
447 max_species_age: 25,
448 target_species_count: None,
449 },
450 evaluation_params: EvaluationParams {
451 evaluator: Arc::new(FitnessEvaluator::default()),
452 fitness_executor: Arc::new(Executor::default()),
453 species_executor: Arc::new(Executor::default()),
454 bus_executor: Arc::new(Executor::default()),
455 },
456 selection_params: SelectionParams {
457 offspring_fraction: 0.8,
458 survivor_selector: Arc::new(TournamentSelector::new(3)),
459 offspring_selector: Arc::new(RouletteSelector::new()),
460 },
461 optimization_params: OptimizeParams {
462 objectives: Objective::Single(Optimize::Maximize),
463 front_range: 800..900,
464 front: None,
465 },
466 problem_params: ProblemParams {
467 codec: None,
468 problem: None,
469 fitness_fn: None,
470 batch_fitness_fn: None,
471 raw_fitness_fn: None,
472 raw_batch_fitness_fn: None,
473 },
474
475 replacement_strategy: Arc::new(EncodeReplace),
476 alterers: Vec::new(),
477 handlers: Vec::new(),
478 exprs: None,
479 generation: None,
480 },
481 errors: Vec::new(),
482 }
483 }
484}