1mod alters;
2mod evaluators;
3mod objectives;
4mod population;
5mod problem;
6mod selectors;
7mod species;
8
9use crate::Result;
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;
17use crate::genome::population::Population;
18use crate::objectives::{Objective, Optimize};
19use crate::pipeline::Pipeline;
20use crate::steps::{AuditStep, EngineStep, FilterStep, FrontStep, RecombineStep, SpeciateStep};
21use crate::{
22 Alter, Crossover, EncodeReplace, EngineProblem, EventBus, EventHandler, Front, Mutate, Problem,
23 ReplacementStrategy, RouletteSelector, Select, TournamentSelector, context::Context, pareto,
24};
25use crate::{Chromosome, EvaluateStep, GeneticEngine};
26use radiate_alters::{UniformCrossover, UniformMutator};
27use radiate_core::evaluator::BatchFitnessEvaluator;
28use radiate_core::problem::BatchEngineProblem;
29use radiate_core::{Diversity, Evaluator, Executor, FitnessEvaluator, Genotype, Valid};
30use radiate_core::{RadiateError, ensure, radiate_err};
31use std::cmp::Ordering;
32use std::sync::{Arc, Mutex, RwLock};
33
34#[derive(Clone)]
35pub struct EngineParams<C, T>
36where
37 C: Chromosome + 'static,
38 T: Clone + 'static,
39{
40 pub population_params: PopulationParams<C>,
41 pub evaluation_params: EvaluationParams<C, T>,
42 pub species_params: SpeciesParams<C>,
43 pub selection_params: SelectionParams<C>,
44 pub optimization_params: OptimizeParams<C>,
45 pub problem_params: ProblemParams<C, T>,
46
47 pub alterers: Vec<Arc<dyn Alter<C>>>,
48 pub replacement_strategy: Arc<dyn ReplacementStrategy<C>>,
49 pub handlers: Vec<Arc<Mutex<dyn EventHandler<T>>>>,
50}
51
52pub struct GeneticEngineBuilder<C, T>
65where
66 C: Chromosome + Clone + 'static,
67 T: Clone + 'static,
68{
69 params: EngineParams<C, T>,
70 errors: Vec<RadiateError>,
71}
72
73impl<C, T> GeneticEngineBuilder<C, T>
74where
75 C: Chromosome + PartialEq + Clone,
76 T: Clone + Send,
77{
78 pub(self) fn add_error_if<F>(&mut self, condition: F, message: &str)
79 where
80 F: Fn() -> bool,
81 {
82 if condition() {
83 self.errors.push(radiate_err!(Builder: "{}", message));
84 }
85 }
86 pub fn replace_strategy<R: ReplacementStrategy<C> + 'static>(mut self, replace: R) -> Self {
92 self.params.replacement_strategy = Arc::new(replace);
93 self
94 }
95
96 pub fn subscribe<H>(mut self, handler: H) -> Self
101 where
102 H: EventHandler<T> + 'static,
103 {
104 self.params.handlers.push(Arc::new(Mutex::new(handler)));
105 self
106 }
107}
108
109impl<C, T> GeneticEngineBuilder<C, T>
111where
112 C: Chromosome + Clone + PartialEq + 'static,
113 T: Clone + Send + Sync + 'static,
114{
115 pub fn build(self) -> GeneticEngine<C, T> {
118 match self.try_build() {
119 Ok(engine) => engine,
120 Err(e) => panic!("{e}"),
121 }
122 }
123
124 pub fn try_build(mut self) -> Result<GeneticEngine<C, T>> {
125 if !self.errors.is_empty() {
126 return Err(radiate_err!(
127 Builder: "Failed to build GeneticEngine: {:?}",
128 self.errors
129 ));
130 }
131
132 self.build_problem()?;
133 self.build_population()?;
134 self.build_alterer()?;
135 self.build_front()?;
136
137 let config = EngineConfig::<C, T>::from(&self.params);
138
139 let mut pipeline = Pipeline::<C>::default();
140
141 pipeline.add_step(Self::build_eval_step(&config));
142 pipeline.add_step(Self::build_recombine_step(&config));
143 pipeline.add_step(Self::build_filter_step(&config));
144 pipeline.add_step(Self::build_eval_step(&config));
145 pipeline.add_step(Self::build_front_step(&config));
146 pipeline.add_step(Self::build_species_step(&config));
147 pipeline.add_step(Self::build_audit_step(&config));
148
149 let event_bus = EventBus::new(config.bus_executor(), config.handlers());
150 let context = Context::from(config);
151
152 Ok(GeneticEngine::<C, T>::new(context, pipeline, event_bus))
153 }
154
155 fn build_eval_step(config: &EngineConfig<C, T>) -> Option<Box<dyn EngineStep<C>>> {
156 let evaluator = config.evaluator.clone();
157
158 let eval_step = EvaluateStep {
159 objective: config.objective.clone(),
160 problem: config.problem.clone(),
161 evaluator: evaluator.clone(),
162 };
163
164 Some(Box::new(eval_step))
165 }
166
167 fn build_recombine_step(config: &EngineConfig<C, T>) -> Option<Box<dyn EngineStep<C>>> {
168 let recombine_step = RecombineStep {
169 survivor_selector: config.survivor_selector(),
170 offspring_selector: config.offspring_selector(),
171 alters: config.alters().to_vec(),
172 survivor_count: config.survivor_count(),
173 offspring_count: config.offspring_count(),
174 objective: config.objective(),
175 };
176
177 Some(Box::new(recombine_step))
178 }
179
180 fn build_filter_step(config: &EngineConfig<C, T>) -> Option<Box<dyn EngineStep<C>>> {
181 let filter_step = FilterStep {
182 replacer: config.replacement_strategy(),
183 encoder: config.encoder(),
184 max_age: config.max_age(),
185 max_species_age: config.max_species_age(),
186 };
187
188 Some(Box::new(filter_step))
189 }
190
191 fn build_audit_step(_: &EngineConfig<C, T>) -> Option<Box<dyn EngineStep<C>>> {
192 Some(Box::new(AuditStep::default()))
193 }
194
195 fn build_front_step(config: &EngineConfig<C, T>) -> Option<Box<dyn EngineStep<C>>> {
196 if config.objective().is_single() {
197 return None;
198 }
199
200 let front_step = FrontStep {
201 front: config.front().clone(),
202 };
203
204 Some(Box::new(front_step))
205 }
206
207 fn build_species_step(config: &EngineConfig<C, T>) -> Option<Box<dyn EngineStep<C>>> {
208 if config.diversity().is_none() {
209 return None;
210 }
211
212 let species_step = SpeciateStep {
213 threashold: config.species_threshold(),
214 diversity: config.diversity().clone().unwrap(),
215 executor: config.executor().species_executor.clone(),
216 objective: config.objective(),
217 };
218
219 Some(Box::new(species_step))
220 }
221
222 fn build_problem(&mut self) -> Result<()> {
228 if self.params.problem_params.problem.is_some() {
229 return Ok(());
230 }
231
232 ensure!(
233 self.params.problem_params.codec.is_some(),
234 Builder: "Codec not set"
235 );
236
237 if let Some(batch_fn) = &self.params.problem_params.batch_fitness_fn {
238 self.params.problem_params.problem = Some(Arc::new(BatchEngineProblem {
239 objective: self.params.optimization_params.objectives.clone(),
240 codec: self.params.problem_params.codec.clone().unwrap(),
241 batch_fitness_fn: batch_fn.clone(),
242 }));
243
244 self.params.evaluation_params.evaluator = Arc::new(BatchFitnessEvaluator::new(
246 self.params.evaluation_params.fitness_executor.clone(),
247 ));
248
249 Ok(())
250 } else if let Some(fitness_fn) = &self.params.problem_params.fitness_fn {
251 self.params.problem_params.problem = Some(Arc::new(EngineProblem {
252 objective: self.params.optimization_params.objectives.clone(),
253 codec: self.params.problem_params.codec.clone().unwrap(),
254 fitness_fn: fitness_fn.clone(),
255 }));
256
257 Ok(())
258 } else {
259 Err(radiate_err!(Builder: "Fitness function not set"))
260 }
261 }
262
263 fn build_population(&mut self) -> Result<()> {
266 self.params.population_params.population = match &self.params.population_params.population {
267 None => Some(match self.params.problem_params.problem.as_ref() {
268 Some(problem) => {
269 let size = self.params.population_params.population_size;
270 let mut phenotypes = Vec::with_capacity(size);
271
272 for _ in 0..size {
273 let genotype = problem.encode();
274
275 if !genotype.is_valid() {
276 return Err(radiate_err!(
277 Builder: "Encoded genotype is not valid",
278 ));
279 }
280
281 phenotypes.push(Phenotype::from((genotype, 0)));
282 }
283
284 Population::from(phenotypes)
285 }
286 None => return Err(radiate_err!(Builder: "Codec not set")),
287 }),
288 Some(pop) => Some(pop.clone()),
289 };
290
291 Ok(())
292 }
293
294 fn build_alterer(&mut self) -> Result<()> {
298 if !self.params.alterers.is_empty() {
299 for alter in self.params.alterers.iter() {
300 if !(0.0..=1.0).contains(&alter.rate()) {
301 return Err(radiate_err!(
302 Builder: "Alterer rate must be between 0 and 1 - found {}", alter.rate()
303 ));
304 }
305 }
306
307 return Ok(());
308 }
309
310 let crossover = Arc::new(UniformCrossover::new(0.5).alterer()) as Arc<dyn Alter<C>>;
311 let mutator = Arc::new(UniformMutator::new(0.1).alterer()) as Arc<dyn Alter<C>>;
312
313 self.params.alterers.push(crossover);
314 self.params.alterers.push(mutator);
315
316 Ok(())
317 }
318
319 fn build_front(&mut self) -> Result<()> {
323 if self.params.optimization_params.front.is_some() {
324 return Ok(());
325 }
326
327 let front_obj = self.params.optimization_params.objectives.clone();
328 self.params.optimization_params.front = Some(Front::new(
329 self.params.optimization_params.front_range.clone(),
330 front_obj.clone(),
331 move |one: &Phenotype<C>, two: &Phenotype<C>| {
332 if one.score().is_none() || two.score().is_none() {
333 return Ordering::Equal;
334 }
335
336 if let (Some(one), Some(two)) = (one.score(), two.score()) {
337 if pareto::dominance(one, two, &front_obj) {
338 return Ordering::Greater;
339 } else if pareto::dominance(two, one, &front_obj) {
340 return Ordering::Less;
341 }
342 }
343
344 Ordering::Equal
345 },
346 ));
347
348 Ok(())
349 }
350}
351
352impl<C, T> Default for GeneticEngineBuilder<C, T>
353where
354 C: Chromosome + Clone + 'static,
355 T: Clone + Send + 'static,
356{
357 fn default() -> Self {
358 GeneticEngineBuilder {
359 params: EngineParams {
360 population_params: PopulationParams {
361 population_size: 100,
362 max_age: 20,
363 population: None,
364 },
365 species_params: SpeciesParams {
366 diversity: None,
367 species_threshold: 0.5,
368 max_species_age: 25,
369 },
370 evaluation_params: EvaluationParams {
371 evaluator: Arc::new(FitnessEvaluator::default()),
372 fitness_executor: Arc::new(Executor::default()),
373 species_executor: Arc::new(Executor::default()),
374 bus_executor: Arc::new(Executor::default()),
375 },
376 selection_params: SelectionParams {
377 offspring_fraction: 0.8,
378 survivor_selector: Arc::new(TournamentSelector::new(3)),
379 offspring_selector: Arc::new(RouletteSelector::new()),
380 },
381 optimization_params: OptimizeParams {
382 objectives: Objective::Single(Optimize::Maximize),
383 front_range: 800..900,
384 front: None,
385 },
386 problem_params: ProblemParams {
387 codec: None,
388 problem: None,
389 fitness_fn: None,
390 batch_fitness_fn: None,
391 },
392
393 replacement_strategy: Arc::new(EncodeReplace),
394 alterers: Vec::new(),
395 handlers: Vec::new(),
396 },
397 errors: Vec::new(),
398 }
399 }
400}
401
402#[derive(Clone)]
403pub(crate) struct EngineConfig<C: Chromosome, T: Clone> {
404 population: Population<C>,
405 problem: Arc<dyn Problem<C, T>>,
406 survivor_selector: Arc<dyn Select<C>>,
407 offspring_selector: Arc<dyn Select<C>>,
408 replacement_strategy: Arc<dyn ReplacementStrategy<C>>,
409 alterers: Vec<Arc<dyn Alter<C>>>,
410 species_threshold: f32,
411 diversity: Option<Arc<dyn Diversity<C>>>,
412 evaluator: Arc<dyn Evaluator<C, T>>,
413 objective: Objective,
414 max_age: usize,
415 max_species_age: usize,
416 front: Arc<RwLock<Front<Phenotype<C>>>>,
417 offspring_fraction: f32,
418 executor: EvaluationParams<C, T>,
419 handlers: Vec<Arc<Mutex<dyn EventHandler<T>>>>,
420}
421
422impl<C: Chromosome, T: Clone> EngineConfig<C, T> {
423 pub fn population(&self) -> &Population<C> {
424 &self.population
425 }
426
427 pub fn survivor_selector(&self) -> Arc<dyn Select<C>> {
428 Arc::clone(&self.survivor_selector)
429 }
430
431 pub fn offspring_selector(&self) -> Arc<dyn Select<C>> {
432 Arc::clone(&self.offspring_selector)
433 }
434
435 pub fn replacement_strategy(&self) -> Arc<dyn ReplacementStrategy<C>> {
436 Arc::clone(&self.replacement_strategy)
437 }
438
439 pub fn alters(&self) -> &[Arc<dyn Alter<C>>] {
440 &self.alterers
441 }
442
443 pub fn objective(&self) -> Objective {
444 self.objective.clone()
445 }
446
447 pub fn max_age(&self) -> usize {
448 self.max_age
449 }
450
451 pub fn max_species_age(&self) -> usize {
452 self.max_species_age
453 }
454
455 pub fn species_threshold(&self) -> f32 {
456 self.species_threshold
457 }
458
459 pub fn diversity(&self) -> Option<Arc<dyn Diversity<C>>> {
460 self.diversity.clone()
461 }
462
463 pub fn front(&self) -> Arc<RwLock<Front<Phenotype<C>>>> {
464 Arc::clone(&self.front)
465 }
466
467 pub fn survivor_count(&self) -> usize {
468 self.population.len() - self.offspring_count()
469 }
470
471 pub fn offspring_count(&self) -> usize {
472 (self.population.len() as f32 * self.offspring_fraction) as usize
473 }
474
475 pub fn executor(&self) -> &EvaluationParams<C, T> {
476 &self.executor
477 }
478
479 pub fn bus_executor(&self) -> Arc<Executor> {
480 Arc::clone(&self.executor.bus_executor)
481 }
482
483 pub fn handlers(&self) -> Vec<Arc<Mutex<dyn EventHandler<T>>>> {
484 self.handlers.clone()
485 }
486
487 pub fn problem(&self) -> Arc<dyn Problem<C, T>> {
488 Arc::clone(&self.problem)
489 }
490
491 pub fn encoder(&self) -> Arc<dyn Fn() -> Genotype<C> + Send + Sync>
492 where
493 C: 'static,
494 T: 'static,
495 {
496 let problem = Arc::clone(&self.problem);
497 Arc::new(move || problem.encode())
498 }
499}
500
501impl<C, T> From<&EngineParams<C, T>> for EngineConfig<C, T>
502where
503 C: Chromosome + Clone + 'static,
504 T: Clone + Send + Sync + 'static,
505{
506 fn from(params: &EngineParams<C, T>) -> Self {
507 Self {
508 population: params.population_params.population.clone().unwrap(),
509 problem: params.problem_params.problem.clone().unwrap(),
510 survivor_selector: params.selection_params.survivor_selector.clone(),
511 offspring_selector: params.selection_params.offspring_selector.clone(),
512 replacement_strategy: params.replacement_strategy.clone(),
513 alterers: params.alterers.clone(),
514 objective: params.optimization_params.objectives.clone(),
515 max_age: params.population_params.max_age,
516 max_species_age: params.species_params.max_species_age,
517 species_threshold: params.species_params.species_threshold,
518 diversity: params.species_params.diversity.clone(),
519 front: Arc::new(RwLock::new(
520 params.optimization_params.front.clone().unwrap(),
521 )),
522 offspring_fraction: params.selection_params.offspring_fraction,
523 evaluator: params.evaluation_params.evaluator.clone(),
524 executor: params.evaluation_params.clone(),
525 handlers: params.handlers.clone(),
526 }
527 }
528}