Skip to main content

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}