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