radiate_engines/engine.rs
1use crate::builder::GeneticEngineBuilder;
2use crate::context::Context;
3use crate::events::EngineMessage;
4use crate::iter::EngineIterator;
5use crate::pipeline::Pipeline;
6use crate::{Chromosome, EngineControl};
7use crate::{EventBus, Generation};
8use radiate_core::Engine;
9use radiate_core::error::Result;
10
11/// The [GeneticEngine] is the core component of the Radiate library's genetic algorithm implementation.
12/// The engine is designed to be fast, flexible and extensible, allowing users to
13/// customize various aspects of the genetic algorithm to suit their specific needs.
14///
15/// Essentially, it is a high-level abstraction that orchestrates all aspects of the genetic algorithm. It is
16/// responsible for managing the population of individuals, evaluating the fitness of each individual,
17/// selecting the individuals that will survive to the next generation, and creating the next generation through
18/// crossover and mutation.
19///
20/// # Examples
21/// ``` no_run
22/// use radiate_engines::*;
23///
24/// // Define a codec that encodes and decodes individuals in the population, in this case using floats.
25/// let codec = FloatCodec::matrix(1, 5, 0.0..100.0);
26/// // This codec will encode Genotype instances with 1 Chromosome and 5 FloatGenes,
27/// // with random alleles between 0.0 and 100.0. It will decode into a Vec<Vec<f32>>.
28/// // eg: [[1.0, 2.0, 3.0, 4.0, 5.0]]
29///
30/// // Create a new instance of the genetic engine with the given codec.
31/// let mut engine = GeneticEngine::builder()
32/// .codec(codec)
33/// .minimizing()
34/// .population_size(150)
35/// .max_age(15)
36/// .offspring_fraction(0.5)
37/// .offspring_selector(BoltzmannSelector::new(4_f32))
38/// .survivor_selector(TournamentSelector::new(3))
39/// .alter(alters![
40/// ArithmeticMutator::new(0.01),
41/// MeanCrossover::new(0.5)
42/// ])
43/// .fitness_fn(|genotype: Vec<Vec<f32>>| {
44/// genotype.iter().fold(0.0, |acc, chromosome| {
45/// acc + chromosome.iter().sum::<f32>()
46/// })
47/// })
48/// .build();
49///
50/// // Run the genetic algorithm until the score of the best individual is 0, then return the result.
51/// let result = engine.run(|output| output.score().as_i32() == 0);
52/// ```
53///
54/// # Type Parameters
55/// - `C`: The type of the chromosome used in the genotype, which must implement the [Chromosome] trait.
56/// - `T`: The type of the phenotype produced by the genetic algorithm, which must be `Clone`, `Send`, and `static`.
57pub struct GeneticEngine<C, T>
58where
59 C: Chromosome,
60 T: Clone + Send + Sync + 'static,
61{
62 context: Context<C, T>,
63 pipeline: Pipeline<C>,
64 bus: EventBus<T>,
65}
66
67impl<C, T> GeneticEngine<C, T>
68where
69 C: Chromosome + Clone,
70 T: Clone + Send + Sync + 'static,
71{
72 /// Creates a new genetic engine with the specified components.
73 ///
74 /// This constructor is primarily used internally by the builder pattern.
75 /// Users should create engines using `GeneticEngine::builder()`.
76 pub(crate) fn new(context: Context<C, T>, pipeline: Pipeline<C>, bus: EventBus<T>) -> Self {
77 GeneticEngine {
78 context,
79 pipeline,
80 bus,
81 }
82 }
83
84 /// Creates a new builder for configuring and constructing a genetic engine.
85 ///
86 /// The builder pattern provides a fluent interface for configuring all aspects
87 /// of the genetic algorithm, including population settings, selection strategies,
88 /// evolutionary operators, and fitness functions.
89 pub fn builder() -> GeneticEngineBuilder<C, T> {
90 GeneticEngineBuilder::default()
91 }
92
93 /// Returns a clone of the engine's control interface.
94 ///
95 /// The control interface allows for pausing, resuming, and stopping the engine's execution
96 /// from external contexts. If the control interface has not been initialized yet, this method
97 /// will create a new instance.
98 pub fn control(&mut self) -> EngineControl {
99 self.context.get_or_create_control()
100 }
101
102 /// Converts the engine into an iterator that yields generations.
103 ///
104 /// This method allows you to iterate over the evolutionary process manually,
105 /// giving you fine-grained control over when and how generations are processed.
106 /// The iterator yields `Generation` objects containing the current state and
107 /// statistics for each generation.
108 ///
109 /// # Use Cases
110 ///
111 /// Manual iteration is useful when you need to:
112 /// - Implement custom termination logic
113 /// - Monitor progress between generations
114 /// - Apply external control or adaptation
115 /// - Integrate with custom monitoring systems
116 /// - Implement interactive evolutionary algorithms
117 ///
118 /// # Note
119 ///
120 /// The iterator consumes the engine, so you can only iterate once. If you need
121 /// to run the engine multiple times, create a new instance using the builder.
122 pub fn iter(self) -> impl Iterator<Item = Generation<C, T>> {
123 let control = self.context.control.clone();
124 EngineIterator::new(self, control)
125 }
126}
127
128/// Implementation of the [Engine] trait for [GeneticEngine].
129///
130/// This implementation provides the core evolutionary logic, advancing the
131/// population through one complete generation cycle. Each call to `next()`
132/// represents one generation of evolution, including fitness evaluation,
133/// selection, reproduction, and population replacement.
134///
135/// # Evolutionary Cycle
136///
137/// Each generation follows this sequence:
138/// 1. **Event Emission**: Start of epoch events
139/// 2. **Pipeline Execution**: Run evolutionary operators
140/// 3. **Metrics Collection**: Record timing and performance data
141/// 4. **Best Individual Update**: Track improvements and best solutions
142/// 5. **Event Completion**: End of epoch events
143/// 6. **Generation Advancement**: Increment generation counter
144///
145/// # Performance Optimizations
146///
147/// - **Efficient Metrics**: Metrics are updated incrementally to minimize overhead
148/// - **Event Batching**: Events are emitted efficiently without blocking execution
149/// - **Pipeline Optimization**: Evolutionary operators are executed in optimized sequences
150impl<C, T> Engine for GeneticEngine<C, T>
151where
152 C: Chromosome + Clone,
153 T: Clone + Send + Sync + 'static,
154{
155 type Epoch = Generation<C, T>;
156
157 #[inline]
158 fn next(&mut self) -> Result<Generation<C, T>> {
159 if let Some(control) = &self.context.control {
160 if control.is_paused() {
161 control.wait_before_step();
162 }
163 }
164
165 if matches!(self.context.index, 0) {
166 self.bus.publish(EngineMessage::<C, T>::Start);
167 }
168
169 self.bus.publish(EngineMessage::EpochStart(&self.context));
170 self.pipeline.run(&mut self.context)?;
171 if self.context.try_advance_one()? {
172 self.bus.publish(EngineMessage::Improvement(&self.context));
173 }
174
175 self.bus.publish(EngineMessage::EpochEnd(&self.context));
176
177 Ok(Generation::from(&self.context))
178 }
179}
180
181/// Custom drop implementation for proper cleanup and event emission.
182///
183/// When the engine is dropped, it emits a stop event to notify any listeners
184/// that the evolutionary process has ended. This allows external systems to
185/// perform cleanup operations or finalize results.
186///
187/// # Event Emission
188///
189/// The stop event includes the final context state, allowing listeners to:
190/// - Record final metrics and statistics
191/// - Save final population state
192/// - Perform cleanup operations
193/// - Generate final reports
194/// - Integrate with external systems
195impl<C, T> Drop for GeneticEngine<C, T>
196where
197 C: Chromosome,
198 T: Clone + Send + Sync + 'static,
199{
200 fn drop(&mut self) {
201 self.bus.publish(EngineMessage::Stop(&self.context));
202 }
203}