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::diversity::DistanceDiversityAdapter;
27use radiate_core::evaluator::BatchFitnessEvaluator;
28use radiate_core::problem::BatchEngineProblem;
29use radiate_core::{
30 Alterer, Diversity, Ecosystem, Evaluator, Executor, FitnessEvaluator, Genotype, Valid,
31};
32use radiate_core::{RadiateError, ensure, radiate_err};
33#[cfg(feature = "serde")]
34use serde::Deserialize;
35use std::sync::{Arc, Mutex, RwLock};
36
37#[derive(Clone)]
38pub struct EngineParams<C, T>
39where
40 C: Chromosome + 'static,
41 T: Clone + 'static,
42{
43 pub population_params: PopulationParams<C>,
44 pub evaluation_params: EvaluationParams<C, T>,
45 pub species_params: SpeciesParams<C>,
46 pub selection_params: SelectionParams<C>,
47 pub optimization_params: OptimizeParams<C>,
48 pub problem_params: ProblemParams<C, T>,
49
50 pub alterers: Vec<Alterer<C>>,
51 pub replacement_strategy: Arc<dyn ReplacementStrategy<C>>,
52 pub handlers: Vec<Arc<Mutex<dyn EventHandler<T>>>>,
53 pub generation: Option<Generation<C, T>>,
54}
55
56pub struct GeneticEngineBuilder<C, T>
69where
70 C: Chromosome + Clone + 'static,
71 T: Clone + 'static,
72{
73 params: EngineParams<C, T>,
74 errors: Vec<RadiateError>,
75}
76
77impl<C, T> GeneticEngineBuilder<C, T>
78where
79 C: Chromosome + PartialEq + Clone,
80 T: Clone + Send,
81{
82 pub(self) fn add_error_if<F>(&mut self, condition: F, message: &str)
83 where
84 F: Fn() -> bool,
85 {
86 if condition() {
87 self.errors.push(radiate_err!(Builder: "{}", message));
88 }
89 }
90
91 pub fn replace_strategy<R: ReplacementStrategy<C> + 'static>(mut self, replace: R) -> Self {
97 self.params.replacement_strategy = Arc::new(replace);
98 self
99 }
100
101 pub fn subscribe<H>(mut self, handler: H) -> Self
106 where
107 H: EventHandler<T> + 'static,
108 {
109 self.params.handlers.push(Arc::new(Mutex::new(handler)));
110 self
111 }
112
113 pub fn generation(mut self, generation: Generation<C, T>) -> Self {
116 self.params.generation = Some(generation);
117 self
118 }
119
120 #[cfg(feature = "serde")]
124 pub fn load_checkpoint<P: AsRef<std::path::Path>>(mut self, path: P) -> Self
125 where
126 C: for<'de> Deserialize<'de>,
127 T: for<'de> Deserialize<'de>,
128 {
129 let file_cont = std::fs::read_to_string(&path);
130 self.add_error_if(
131 || file_cont.is_err(),
132 &format!(
133 "Failed to read checkpoint file at path: {}",
134 path.as_ref().display()
135 ),
136 );
137
138 let generation = serde_json::from_str::<Generation<C, T>>(
139 &file_cont.expect("Failed to read checkpoint file"),
140 )
141 .map_err(|e| radiate_err!(Builder: "Failed to deserialize checkpoint file: {}", e));
142
143 self.add_error_if(
144 || generation.is_err(),
145 &format!(
146 "Failed to deserialize checkpoint file at path: {} ",
147 path.as_ref().display(),
148 ),
149 );
150
151 self.generation(generation.unwrap())
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 = Context::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() {
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 if let Some(front) = generation.front() {
321 self.params.optimization_params.front = Some(front.clone());
322 return Ok(());
323 }
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 recombine_step = RecombineStep {
347 survivor_handle: crate::steps::SurvivorRecombineHandle {
348 count: config.survivor_count(),
349 objective: config.objective(),
350 selector: config.survivor_selector(),
351 },
352 offspring_handle: crate::steps::OffspringRecombineHandle {
353 count: config.offspring_count(),
354 objective: config.objective(),
355 selector: config.offspring_selector(),
356 alters: config.alters().to_vec(),
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(_: &EngineConfig<C, T>) -> Option<Box<dyn EngineStep<C>>> {
375 Some(Box::new(AuditStep::default()))
376 }
377
378 fn build_front_step(config: &EngineConfig<C, T>) -> Option<Box<dyn EngineStep<C>>> {
379 if config.objective().is_single() {
380 return None;
381 }
382
383 let front_step = FrontStep {
384 front: config.front(),
385 };
386
387 Some(Box::new(front_step))
388 }
389
390 fn build_species_step(config: &EngineConfig<C, T>) -> Option<Box<dyn EngineStep<C>>> {
391 if config.diversity().is_none() {
392 return None;
393 }
394
395 let species_step = SpeciateStep {
396 threashold: config.species_threshold(),
397 distance: Arc::new(DistanceDiversityAdapter::new(config.diversity().unwrap())),
398 executor: config.species_executor(),
399 objective: config.objective(),
400 distances: Arc::new(Mutex::new(Vec::new())),
401 assignments: Arc::new(Mutex::new(Vec::new())),
402 };
403
404 Some(Box::new(species_step))
405 }
406}
407
408impl<C, T> Default for GeneticEngineBuilder<C, T>
409where
410 C: Chromosome + Clone + 'static,
411 T: Clone + Send + 'static,
412{
413 fn default() -> Self {
414 GeneticEngineBuilder {
415 params: EngineParams {
416 population_params: PopulationParams {
417 population_size: 100,
418 max_age: 20,
419 ecosystem: None,
420 },
421 species_params: SpeciesParams {
422 diversity: None,
423 species_threshold: 0.5,
424 max_species_age: 25,
425 },
426 evaluation_params: EvaluationParams {
427 evaluator: Arc::new(FitnessEvaluator::default()),
428 fitness_executor: Arc::new(Executor::default()),
429 species_executor: Arc::new(Executor::default()),
430 bus_executor: Arc::new(Executor::default()),
431 },
432 selection_params: SelectionParams {
433 offspring_fraction: 0.8,
434 survivor_selector: Arc::new(TournamentSelector::new(3)),
435 offspring_selector: Arc::new(RouletteSelector::new()),
436 },
437 optimization_params: OptimizeParams {
438 objectives: Objective::Single(Optimize::Maximize),
439 front_range: 800..900,
440 front: None,
441 },
442 problem_params: ProblemParams {
443 codec: None,
444 problem: None,
445 fitness_fn: None,
446 batch_fitness_fn: None,
447 raw_fitness_fn: None,
448 raw_batch_fitness_fn: None,
449 },
450
451 replacement_strategy: Arc::new(EncodeReplace),
452 alterers: Vec::new(),
453 handlers: Vec::new(),
454 generation: None,
455 },
456 errors: Vec::new(),
457 }
458 }
459}
460
461#[derive(Clone)]
462pub(crate) struct EngineConfig<C: Chromosome, T: Clone> {
463 ecosystem: Ecosystem<C>,
464 problem: Arc<dyn Problem<C, T>>,
465 survivor_selector: Arc<dyn Select<C>>,
466 offspring_selector: Arc<dyn Select<C>>,
467 replacement_strategy: Arc<dyn ReplacementStrategy<C>>,
468 alterers: Vec<Alterer<C>>,
469 species_threshold: f32,
470 diversity: Option<Arc<dyn Diversity<C>>>,
471 evaluator: Arc<dyn Evaluator<C, T>>,
472 objective: Objective,
473 max_age: usize,
474 max_species_age: usize,
475 front: Arc<RwLock<Front<Phenotype<C>>>>,
476 offspring_fraction: f32,
477 executor: EvaluationParams<C, T>,
478 handlers: Vec<Arc<Mutex<dyn EventHandler<T>>>>,
479 generation: Option<Generation<C, T>>,
480}
481
482impl<C: Chromosome, T: Clone> EngineConfig<C, T> {
483 pub fn ecosystem(&self) -> &Ecosystem<C> {
484 &self.ecosystem
485 }
486
487 pub fn survivor_selector(&self) -> Arc<dyn Select<C>> {
488 Arc::clone(&self.survivor_selector)
489 }
490
491 pub fn offspring_selector(&self) -> Arc<dyn Select<C>> {
492 Arc::clone(&self.offspring_selector)
493 }
494
495 pub fn replacement_strategy(&self) -> Arc<dyn ReplacementStrategy<C>> {
496 Arc::clone(&self.replacement_strategy)
497 }
498
499 pub fn alters(&self) -> &[Alterer<C>] {
500 &self.alterers
501 }
502
503 pub fn objective(&self) -> Objective {
504 self.objective.clone()
505 }
506
507 pub fn max_age(&self) -> usize {
508 self.max_age
509 }
510
511 pub fn max_species_age(&self) -> usize {
512 self.max_species_age
513 }
514
515 pub fn species_threshold(&self) -> f32 {
516 self.species_threshold
517 }
518
519 pub fn diversity(&self) -> Option<Arc<dyn Diversity<C>>> {
520 self.diversity.clone()
521 }
522
523 pub fn front(&self) -> Arc<RwLock<Front<Phenotype<C>>>> {
524 Arc::clone(&self.front)
525 }
526
527 pub fn evaluator(&self) -> Arc<dyn Evaluator<C, T>> {
528 Arc::clone(&self.evaluator)
529 }
530
531 pub fn survivor_count(&self) -> usize {
532 self.ecosystem.population().len() - self.offspring_count()
533 }
534
535 pub fn offspring_count(&self) -> usize {
536 (self.ecosystem.population().len() as f32 * self.offspring_fraction) as usize
537 }
538
539 pub fn bus_executor(&self) -> Arc<Executor> {
540 Arc::clone(&self.executor.bus_executor)
541 }
542
543 pub fn species_executor(&self) -> Arc<Executor> {
544 Arc::clone(&self.executor.species_executor)
545 }
546
547 pub fn handlers(&self) -> Vec<Arc<Mutex<dyn EventHandler<T>>>> {
548 self.handlers.clone()
549 }
550
551 pub fn problem(&self) -> Arc<dyn Problem<C, T>> {
552 Arc::clone(&self.problem)
553 }
554
555 pub fn generation(&self) -> Option<Generation<C, T>>
556 where
557 C: Clone,
558 T: Clone,
559 {
560 self.generation.clone()
561 }
562
563 pub fn encoder(&self) -> Arc<dyn Fn() -> Genotype<C> + Send + Sync>
564 where
565 C: 'static,
566 T: 'static,
567 {
568 let problem = Arc::clone(&self.problem);
569 Arc::new(move || problem.encode())
570 }
571}
572
573impl<C, T> From<&EngineParams<C, T>> for EngineConfig<C, T>
574where
575 C: Chromosome + Clone + 'static,
576 T: Clone + Send + Sync + 'static,
577{
578 fn from(params: &EngineParams<C, T>) -> Self {
579 Self {
580 ecosystem: params.population_params.ecosystem.clone().unwrap(),
581 problem: params.problem_params.problem.clone().unwrap(),
582 survivor_selector: params.selection_params.survivor_selector.clone(),
583 offspring_selector: params.selection_params.offspring_selector.clone(),
584 replacement_strategy: params.replacement_strategy.clone(),
585 alterers: params.alterers.clone(),
586 objective: params.optimization_params.objectives.clone(),
587 max_age: params.population_params.max_age,
588 max_species_age: params.species_params.max_species_age,
589 species_threshold: params.species_params.species_threshold,
590 diversity: params.species_params.diversity.clone(),
591 front: Arc::new(RwLock::new(
592 params.optimization_params.front.clone().unwrap(),
593 )),
594 offspring_fraction: params.selection_params.offspring_fraction,
595 evaluator: params.evaluation_params.evaluator.clone(),
596 executor: params.evaluation_params.clone(),
597 handlers: params.handlers.clone(),
598 generation: params.generation.clone(),
599 }
600 }
601}
602
603impl<C, T> Into<GeneticEngineBuilder<C, T>> for EngineConfig<C, T>
604where
605 C: Chromosome + Clone + 'static,
606 T: Clone + Send + Sync + 'static,
607{
608 fn into(self) -> GeneticEngineBuilder<C, T> {
609 GeneticEngineBuilder {
610 params: EngineParams {
611 population_params: PopulationParams {
612 population_size: self.ecosystem.population().len(),
613 max_age: self.max_age,
614 ecosystem: Some(self.ecosystem),
615 },
616 species_params: SpeciesParams {
617 diversity: self.diversity,
618 species_threshold: self.species_threshold,
619 max_species_age: self.max_species_age,
620 },
621 evaluation_params: self.executor,
622 selection_params: SelectionParams {
623 offspring_fraction: self.offspring_fraction,
624 survivor_selector: self.survivor_selector,
625 offspring_selector: self.offspring_selector,
626 },
627 optimization_params: OptimizeParams {
628 objectives: self.objective,
629 front_range: self.front.read().unwrap().range().clone(),
630 front: Some(self.front.read().unwrap().clone()),
631 },
632 problem_params: ProblemParams {
633 codec: None,
634 problem: Some(self.problem),
635 fitness_fn: None,
636 batch_fitness_fn: None,
637 raw_fitness_fn: None,
638 raw_batch_fitness_fn: None,
639 },
640
641 replacement_strategy: self.replacement_strategy,
642 alterers: self.alterers,
643 handlers: self.handlers,
644 generation: self.generation,
645 },
646 errors: Vec::new(),
647 }
648 }
649}