1mod alters;
2mod evaluators;
3mod objectives;
4mod population;
5mod problem;
6mod selectors;
7mod species;
8
9use crate::builder::evaluators::EvaluationParams;
10use crate::builder::objectives::OptimizeParams;
11use crate::builder::population::PopulationParams;
12use crate::builder::problem::ProblemParams;
13use crate::builder::selectors::SelectionParams;
14use crate::builder::species::SpeciesParams;
15use crate::genome::phenotype::Phenotype;
16#[cfg(feature = "serde")]
17use crate::io::CheckpointReader;
18use crate::objectives::{Objective, Optimize};
19use crate::pipeline::Pipeline;
20use crate::steps::{AuditStep, EngineStep, FilterStep, FrontStep, RecombineStep, SpeciateStep};
21use crate::{Chromosome, EvaluateStep, GeneticEngine};
22use crate::{
23 Crossover, EncodeReplace, EngineProblem, EventBus, EventHandler, Front, Mutate, Problem,
24 ReplacementStrategy, RouletteSelector, Select, TournamentSelector, context::Context,
25};
26use crate::{Generation, Result};
27use radiate_alters::{UniformCrossover, UniformMutator};
28use radiate_core::evaluator::BatchFitnessEvaluator;
29use radiate_core::problem::BatchEngineProblem;
30use radiate_core::{
31 Alterer, Diversity, Ecosystem, Evaluator, Executor, FitnessEvaluator, Genotype, Lineage, Rate,
32 Valid,
33};
34use radiate_core::{RadiateError, ensure, radiate_err};
35use radiate_expr::NamedExpr;
36#[cfg(feature = "serde")]
37use serde::Deserialize;
38use std::sync::{Arc, Mutex, RwLock};
39
40#[derive(Clone)]
41pub struct EngineParams<C, T>
42where
43 C: Chromosome + 'static,
44 T: Clone + 'static,
45{
46 pub population_params: PopulationParams<C>,
47 pub evaluation_params: EvaluationParams<C, T>,
48 pub species_params: SpeciesParams<C>,
49 pub selection_params: SelectionParams<C>,
50 pub optimization_params: OptimizeParams<C>,
51 pub problem_params: ProblemParams<C, T>,
52
53 pub alterers: Vec<Alterer<C>>,
54 pub replacement_strategy: Arc<dyn ReplacementStrategy<C>>,
55 pub handlers: Vec<Arc<Mutex<dyn EventHandler<T>>>>,
56 pub generation: Option<Generation<C, T>>,
57 pub exprs: Option<Arc<Mutex<Vec<NamedExpr>>>>,
58}
59
60pub struct GeneticEngineBuilder<C, T>
73where
74 C: Chromosome + Clone + 'static,
75 T: Clone + 'static,
76{
77 params: EngineParams<C, T>,
78 errors: Vec<RadiateError>,
79}
80
81impl<C, T> GeneticEngineBuilder<C, T>
82where
83 C: Chromosome + PartialEq + Clone,
84 T: Clone + Send,
85{
86 pub(self) fn add_error_if<F>(&mut self, condition: F, message: &str)
87 where
88 F: Fn() -> bool,
89 {
90 if condition() {
91 self.errors.push(radiate_err!(Builder: "{}", message));
92 }
93 }
94
95 pub fn replace_strategy<R: ReplacementStrategy<C> + 'static>(mut self, replace: R) -> Self {
101 self.params.replacement_strategy = Arc::new(replace);
102 self
103 }
104
105 pub fn subscribe<H>(mut self, handler: H) -> Self
110 where
111 H: EventHandler<T> + 'static,
112 {
113 self.params.handlers.push(Arc::new(Mutex::new(handler)));
114 self
115 }
116
117 pub fn generation(mut self, generation: Generation<C, T>) -> Self {
120 self.params.generation = Some(generation);
121 self
122 }
123
124 pub fn register_metrics(mut self, exprs: Vec<impl Into<NamedExpr>>) -> Self {
125 self.params.exprs = Some(Arc::new(Mutex::new(
126 exprs.into_iter().map(|e| e.into()).collect(),
127 )));
128 self
129 }
130
131 #[cfg(feature = "serde")]
135 pub fn load_checkpoint<P: AsRef<std::path::Path>>(
136 mut self,
137 path: P,
138 reader: impl CheckpointReader<C, T>,
139 ) -> Self
140 where
141 C: for<'de> Deserialize<'de>,
142 T: for<'de> Deserialize<'de>,
143 {
144 let read_generation = reader.read_checkpoint(path.as_ref().to_path_buf());
145 if let Err(e) = &read_generation {
146 self.add_error_if(|| true, &format!("Failed to read checkpoint: {}", e));
147 }
148 let generation = read_generation.expect("Failed to read checkpoint file");
149 self.generation(generation)
150 }
151}
152
153impl<C, T> GeneticEngineBuilder<C, T>
155where
156 C: Chromosome + Clone + PartialEq + 'static,
157 T: Clone + Send + Sync + 'static,
158{
159 pub fn build(self) -> GeneticEngine<C, T> {
162 match self.try_build() {
163 Ok(engine) => engine,
164 Err(e) => panic!("{e}"),
165 }
166 }
167
168 pub fn try_build(mut self) -> Result<GeneticEngine<C, T>> {
169 if !self.errors.is_empty() {
170 return Err(radiate_err!(
171 Builder: "Failed to build GeneticEngine: {:?}",
172 self.errors
173 ));
174 }
175
176 self.build_problem()?;
177 self.build_population()?;
178 self.build_alterer()?;
179 self.build_front()?;
180
181 let config = EngineConfig::<C, T>::from(&self.params);
182
183 let mut pipeline = Pipeline::<C>::default();
184
185 pipeline.add_step(Self::build_eval_step(&config));
186 pipeline.add_step(Self::build_recombine_step(&config));
187 pipeline.add_step(Self::build_filter_step(&config));
188 pipeline.add_step(Self::build_eval_step(&config));
189 pipeline.add_step(Self::build_front_step(&config));
190 pipeline.add_step(Self::build_species_step(&config));
191 pipeline.add_step(Self::build_audit_step(&config));
192
193 let event_bus = EventBus::new(config.bus_executor(), config.handlers());
194 let context = Context::from(config);
195
196 Ok(GeneticEngine::<C, T>::new(context, pipeline, event_bus))
197 }
198
199 fn build_problem(&mut self) -> Result<()> {
205 if self.params.problem_params.problem.is_some() {
206 return Ok(());
207 }
208
209 ensure!(
210 self.params.problem_params.codec.is_some(),
211 Builder: "Codec not set"
212 );
213
214 let raw_fitness_fn = self.params.problem_params.raw_fitness_fn.clone();
215 let fitness_fn = self.params.problem_params.fitness_fn.clone();
216 let batch_fitness_fn = self.params.problem_params.batch_fitness_fn.clone();
217 let raw_batch_fitness_fn = self.params.problem_params.raw_batch_fitness_fn.clone();
218
219 if batch_fitness_fn.is_some() || raw_batch_fitness_fn.is_some() {
220 self.params.problem_params.problem = Some(Arc::new(BatchEngineProblem {
221 objective: self.params.optimization_params.objectives.clone(),
222 codec: self.params.problem_params.codec.clone().unwrap(),
223 batch_fitness_fn,
224 raw_batch_fitness_fn,
225 }));
226
227 self.params.evaluation_params.evaluator = Arc::new(BatchFitnessEvaluator::new(
229 self.params.evaluation_params.fitness_executor.clone(),
230 ));
231
232 Ok(())
233 } else if fitness_fn.is_some() || raw_fitness_fn.is_some() {
234 self.params.problem_params.problem = Some(Arc::new(EngineProblem {
235 objective: self.params.optimization_params.objectives.clone(),
236 codec: self.params.problem_params.codec.clone().unwrap(),
237 fitness_fn,
238 raw_fitness_fn,
239 }));
240
241 Ok(())
242 } else {
243 Err(radiate_err!(Builder: "Fitness function not set"))
244 }
245 }
246
247 fn build_population(&mut self) -> Result<()> {
250 if self.params.population_params.ecosystem.is_some() {
251 return Ok(());
252 }
253
254 let ecosystem = match &self.params.population_params.ecosystem {
255 None => Some(match self.params.problem_params.problem.as_ref() {
256 Some(problem) => {
257 let size = self.params.population_params.population_size;
258 let mut phenotypes = Vec::with_capacity(size);
259
260 for _ in 0..size {
261 let genotype = problem.encode();
262
263 if !genotype.is_valid() {
264 return Err(radiate_err!(
265 Builder: "Encoded genotype is not valid",
266 ));
267 }
268
269 phenotypes.push(Phenotype::from((genotype, 0)));
270 }
271
272 Ecosystem::from(phenotypes)
273 }
274 None => return Err(radiate_err!(Builder: "Codec not set")),
275 }),
276 Some(ecosystem) => Some(ecosystem.clone()),
277 };
278
279 if let Some(ecosystem) = ecosystem {
280 self.params.population_params.ecosystem = Some(ecosystem);
281 }
282
283 Ok(())
284 }
285
286 fn build_alterer(&mut self) -> Result<()> {
290 if !self.params.alterers.is_empty() {
291 for alter in self.params.alterers.iter_mut() {
292 if !alter.rate().is_valid() {
293 return Err(radiate_err!(
294 Builder: "Alterer {} is not valid. Ensure rate {:?} is valid.", alter.name(), alter.rate()
295 ));
296 }
297 }
298
299 return Ok(());
300 }
301
302 let crossover = UniformCrossover::new(0.5).alterer();
303 let mutator = UniformMutator::new(0.1).alterer();
304
305 self.params.alterers.push(crossover);
306 self.params.alterers.push(mutator);
307
308 Ok(())
309 }
310
311 fn build_front(&mut self) -> Result<()> {
315 if self.params.optimization_params.front.is_some() {
316 return Ok(());
317 } else if let Some(generation) = &self.params.generation {
318 if let Some(front) = generation.front() {
319 self.params.optimization_params.front = Some(front.clone());
320 return Ok(());
321 }
322 }
323
324 let front_obj = self.params.optimization_params.objectives.clone();
325 self.params.optimization_params.front = Some(Front::new(
326 self.params.optimization_params.front_range.clone(),
327 front_obj,
328 ));
329
330 Ok(())
331 }
332
333 fn build_eval_step(config: &EngineConfig<C, T>) -> Option<Box<dyn EngineStep<C>>> {
334 let eval_step = EvaluateStep {
335 objective: config.objective(),
336 problem: config.problem(),
337 evaluator: config.evaluator(),
338 };
339
340 Some(Box::new(eval_step))
341 }
342
343 fn build_recombine_step(config: &EngineConfig<C, T>) -> Option<Box<dyn EngineStep<C>>> {
344 let offspring_selector = config.offspring_selector();
345 let survivor_selector = config.survivor_selector();
346
347 let off_name = offspring_selector.name();
348 let offspring_base_name = radiate_utils::intern!(off_name);
349 let offspring_time_name = radiate_utils::intern!(format!("{}.time", offspring_base_name));
350
351 let surv_name = survivor_selector.name();
352 let survivor_base_name = radiate_utils::intern!(surv_name);
353 let survivor_time_name = radiate_utils::intern!(format!("{}.time", survivor_base_name));
354
355 let recombine_step = RecombineStep {
356 survivor_handle: crate::steps::SurvivorRecombineHandle {
357 count: config.survivor_count(),
358 objective: config.objective(),
359 selector: survivor_selector,
360 names: (survivor_base_name, survivor_time_name),
361 },
362 offspring_handle: crate::steps::OffspringRecombineHandle {
363 count: config.offspring_count(),
364 objective: config.objective(),
365 selector: offspring_selector,
366 alters: config.alters().to_vec(),
367 lineage: config.lineage(),
368 names: (offspring_base_name, offspring_time_name),
369 },
370 };
371
372 Some(Box::new(recombine_step))
373 }
374
375 fn build_filter_step(config: &EngineConfig<C, T>) -> Option<Box<dyn EngineStep<C>>> {
376 let filter_step = FilterStep {
377 replacer: config.replacement_strategy(),
378 encoder: config.encoder(),
379 max_age: config.max_age(),
380 max_species_age: config.max_species_age(),
381 };
382
383 Some(Box::new(filter_step))
384 }
385
386 fn build_audit_step(config: &EngineConfig<C, T>) -> Option<Box<dyn EngineStep<C>>> {
387 Some(Box::new(AuditStep::new(
388 config.objective().clone(),
389 config.lineage(),
390 )))
391 }
392
393 fn build_front_step(config: &EngineConfig<C, T>) -> Option<Box<dyn EngineStep<C>>> {
394 if config.objective().is_single() {
395 return None;
396 }
397
398 let front_step = FrontStep {
399 front: config.front(),
400 };
401
402 Some(Box::new(front_step))
403 }
404
405 fn build_species_step(config: &EngineConfig<C, T>) -> Option<Box<dyn EngineStep<C>>> {
406 if config.diversity().is_none() {
407 return None;
408 }
409
410 let species_step = SpeciateStep {
411 threshold: config.species_threshold(),
412 distance: config.diversity().unwrap(),
413 executor: config.species_executor(),
414 objective: config.objective(),
415 distances: Arc::new(Mutex::new(Vec::new())),
416 assignments: Arc::new(Mutex::new(Vec::new())),
417 };
418
419 Some(Box::new(species_step))
420 }
421}
422
423impl<C, T> Default for GeneticEngineBuilder<C, T>
424where
425 C: Chromosome + Clone + 'static,
426 T: Clone + Send + 'static,
427{
428 fn default() -> Self {
429 GeneticEngineBuilder {
430 params: EngineParams {
431 population_params: PopulationParams {
432 population_size: 100,
433 max_age: 20,
434 ecosystem: None,
435 },
436 species_params: SpeciesParams {
437 diversity: None,
438 species_threshold: Rate::Fixed(0.5),
439 max_species_age: 25,
440 },
441 evaluation_params: EvaluationParams {
442 evaluator: Arc::new(FitnessEvaluator::default()),
443 fitness_executor: Arc::new(Executor::default()),
444 species_executor: Arc::new(Executor::default()),
445 bus_executor: Arc::new(Executor::default()),
446 },
447 selection_params: SelectionParams {
448 offspring_fraction: 0.8,
449 survivor_selector: Arc::new(TournamentSelector::new(3)),
450 offspring_selector: Arc::new(RouletteSelector::new()),
451 },
452 optimization_params: OptimizeParams {
453 objectives: Objective::Single(Optimize::Maximize),
454 front_range: 800..900,
455 front: None,
456 },
457 problem_params: ProblemParams {
458 codec: None,
459 problem: None,
460 fitness_fn: None,
461 batch_fitness_fn: None,
462 raw_fitness_fn: None,
463 raw_batch_fitness_fn: None,
464 },
465
466 replacement_strategy: Arc::new(EncodeReplace),
467 alterers: Vec::new(),
468 handlers: Vec::new(),
469 exprs: None,
470 generation: None,
471 },
472 errors: Vec::new(),
473 }
474 }
475}
476
477#[derive(Clone)]
478pub(crate) struct EngineConfig<C: Chromosome, T: Clone> {
479 ecosystem: Ecosystem<C>,
480 problem: Arc<dyn Problem<C, T>>,
481 survivor_selector: Arc<dyn Select<C>>,
482 offspring_selector: Arc<dyn Select<C>>,
483 replacement_strategy: Arc<dyn ReplacementStrategy<C>>,
484 alterers: Vec<Alterer<C>>,
485 species_threshold: Rate,
486 diversity: Option<Arc<dyn Diversity<C>>>,
487 evaluator: Arc<dyn Evaluator<C, T>>,
488 objective: Objective,
489 max_age: usize,
490 max_species_age: usize,
491 front: Arc<RwLock<Front<Phenotype<C>>>>,
492 lineage: Arc<RwLock<Lineage>>,
493 offspring_fraction: f32,
494 executor: EvaluationParams<C, T>,
495 handlers: Vec<Arc<Mutex<dyn EventHandler<T>>>>,
496 exprs: Option<Arc<Mutex<Vec<NamedExpr>>>>,
497 generation: Option<Generation<C, T>>,
498}
499
500impl<C: Chromosome, T: Clone> EngineConfig<C, T> {
501 pub fn ecosystem(&self) -> &Ecosystem<C> {
502 &self.ecosystem
503 }
504
505 pub fn survivor_selector(&self) -> Arc<dyn Select<C>> {
506 Arc::clone(&self.survivor_selector)
507 }
508
509 pub fn offspring_selector(&self) -> Arc<dyn Select<C>> {
510 Arc::clone(&self.offspring_selector)
511 }
512
513 pub fn replacement_strategy(&self) -> Arc<dyn ReplacementStrategy<C>> {
514 Arc::clone(&self.replacement_strategy)
515 }
516
517 pub fn alters(&self) -> &[Alterer<C>] {
518 &self.alterers
519 }
520
521 pub fn objective(&self) -> Objective {
522 self.objective.clone()
523 }
524
525 pub fn max_age(&self) -> usize {
526 self.max_age
527 }
528
529 pub fn max_species_age(&self) -> usize {
530 self.max_species_age
531 }
532
533 pub fn species_threshold(&self) -> Rate {
534 self.species_threshold.clone()
535 }
536
537 pub fn diversity(&self) -> Option<Arc<dyn Diversity<C>>> {
538 self.diversity.clone()
539 }
540
541 pub fn front(&self) -> Arc<RwLock<Front<Phenotype<C>>>> {
542 Arc::clone(&self.front)
543 }
544
545 pub fn lineage(&self) -> Arc<RwLock<Lineage>> {
546 Arc::clone(&self.lineage)
547 }
548
549 pub fn evaluator(&self) -> Arc<dyn Evaluator<C, T>> {
550 Arc::clone(&self.evaluator)
551 }
552
553 pub fn survivor_count(&self) -> usize {
554 self.ecosystem.population().len() - self.offspring_count()
555 }
556
557 pub fn offspring_count(&self) -> usize {
558 (self.ecosystem.population().len() as f32 * self.offspring_fraction) as usize
559 }
560
561 pub fn bus_executor(&self) -> Arc<Executor> {
562 Arc::clone(&self.executor.bus_executor)
563 }
564
565 pub fn species_executor(&self) -> Arc<Executor> {
566 Arc::clone(&self.executor.species_executor)
567 }
568
569 pub fn handlers(&self) -> Vec<Arc<Mutex<dyn EventHandler<T>>>> {
570 self.handlers.clone()
571 }
572
573 pub fn problem(&self) -> Arc<dyn Problem<C, T>> {
574 Arc::clone(&self.problem)
575 }
576
577 pub fn generation(&self) -> Option<Generation<C, T>>
578 where
579 C: Clone,
580 T: Clone,
581 {
582 self.generation.clone()
583 }
584
585 pub fn encoder(&self) -> Arc<dyn Fn() -> Genotype<C> + Send + Sync>
586 where
587 C: 'static,
588 T: 'static,
589 {
590 let problem = Arc::clone(&self.problem);
591 Arc::new(move || problem.encode())
592 }
593
594 pub fn exprs(&self) -> Option<Arc<Mutex<Vec<NamedExpr>>>> {
595 self.exprs.clone()
596 }
597}
598
599impl<C, T> From<&EngineParams<C, T>> for EngineConfig<C, T>
600where
601 C: Chromosome + Clone + 'static,
602 T: Clone + Send + Sync + 'static,
603{
604 fn from(params: &EngineParams<C, T>) -> Self {
605 Self {
606 ecosystem: params.population_params.ecosystem.clone().unwrap(),
607 problem: params.problem_params.problem.clone().unwrap(),
608 survivor_selector: params.selection_params.survivor_selector.clone(),
609 offspring_selector: params.selection_params.offspring_selector.clone(),
610 replacement_strategy: params.replacement_strategy.clone(),
611 alterers: params.alterers.clone(),
612 objective: params.optimization_params.objectives.clone(),
613 max_age: params.population_params.max_age,
614 max_species_age: params.species_params.max_species_age,
615 species_threshold: params.species_params.species_threshold.clone(),
616 diversity: params.species_params.diversity.clone(),
617 front: Arc::new(RwLock::new(
618 params.optimization_params.front.clone().unwrap(),
619 )),
620 lineage: Arc::new(RwLock::new(Lineage::default())),
621 offspring_fraction: params.selection_params.offspring_fraction,
622 evaluator: params.evaluation_params.evaluator.clone(),
623 executor: params.evaluation_params.clone(),
624 handlers: params.handlers.clone(),
625 generation: params.generation.clone(),
626 exprs: params.exprs.clone(),
627 }
628 }
629}
630
631impl<C, T> Into<GeneticEngineBuilder<C, T>> for EngineConfig<C, T>
632where
633 C: Chromosome + Clone + 'static,
634 T: Clone + Send + Sync + 'static,
635{
636 fn into(self) -> GeneticEngineBuilder<C, T> {
637 GeneticEngineBuilder {
638 params: EngineParams {
639 population_params: PopulationParams {
640 population_size: self.ecosystem.population().len(),
641 max_age: self.max_age,
642 ecosystem: Some(self.ecosystem),
643 },
644 species_params: SpeciesParams {
645 diversity: self.diversity,
646 species_threshold: self.species_threshold,
647 max_species_age: self.max_species_age,
648 },
649 evaluation_params: self.executor,
650 selection_params: SelectionParams {
651 offspring_fraction: self.offspring_fraction,
652 survivor_selector: self.survivor_selector,
653 offspring_selector: self.offspring_selector,
654 },
655 optimization_params: OptimizeParams {
656 objectives: self.objective,
657 front_range: self.front.read().unwrap().range().clone(),
658 front: Some(self.front.read().unwrap().clone()),
659 },
660 problem_params: ProblemParams {
661 codec: None,
662 problem: Some(self.problem),
663 fitness_fn: None,
664 batch_fitness_fn: None,
665 raw_fitness_fn: None,
666 raw_batch_fitness_fn: None,
667 },
668
669 replacement_strategy: self.replacement_strategy,
670 alterers: self.alterers,
671 handlers: self.handlers,
672 exprs: self.exprs,
673 generation: self.generation,
674 },
675 errors: Vec::new(),
676 }
677 }
678}