Skip to main content

radiate_engines/
engine.rs

1use crate::{Chromosome, EngineRuntime, Generation, ThreadSync};
2use crate::{GenerationView, builder::GeneticEngineBuilder};
3use crate::{
4    Handler,
5    events::{
6        EngineStart, EngineStop, EpochComplete, EpochStart, EventStream, Improvement, Subscription,
7    },
8};
9use crate::{context::EvolutionContext, events::Event};
10use crate::{events::GenerationSnapshot, pipeline::Pipeline};
11use radiate_core::{Engine, EngineState};
12use radiate_core::{EngineStream, error::Result};
13
14/// The [GeneticEngine] is the core component of the Radiate library's genetic algorithm implementation.
15/// The engine is designed to be fast, flexible and extensible, allowing users to
16/// customize various aspects of the genetic algorithm to suit their specific needs.
17///
18/// Essentially, it is a high-level abstraction that orchestrates all aspects of the genetic algorithm. It is
19/// responsible for managing the population of individuals, evaluating the fitness of each individual,
20/// selecting the individuals that will survive to the next generation, and creating the next generation through
21/// crossover and mutation.
22///
23/// # Examples
24/// ``` no_run
25/// use radiate_engines::*;
26///
27/// // Define a codec that encodes and decodes individuals in the population, in this case using floats.
28/// let codec = FloatCodec::matrix(vec![5], 0.0..100.0);
29/// // This codec will encode Genotype instances with 1 Chromosome and 5 FloatGenes,
30/// // with random alleles between 0.0 and 100.0. It will decode into a Vec<Vec<f32>>.
31/// // eg: [[1.0, 2.0, 3.0, 4.0, 5.0]]
32///
33/// // Create a new instance of the genetic engine with the given codec.
34/// let mut engine = GeneticEngine::builder()
35///     .codec(codec)
36///     .minimizing()
37///     .population_size(150)
38///     .max_age(15)
39///     .offspring_fraction(0.5)
40///     .offspring_selector(BoltzmannSelector::new(4_f32))
41///     .survivor_selector(TournamentSelector::new(3))
42///     .alter(alters![
43///         ArithmeticMutator::new(0.01),
44///         MeanCrossover::new(0.5)
45///     ])
46///     .fitness_fn(|genotype: Vec<Vec<f32>>| {
47///         genotype.iter().fold(0.0, |acc, chromosome| {
48///             acc + chromosome.iter().sum::<f32>()
49///         })
50///    })
51///   .build();
52///
53/// // Run the genetic algorithm until the score of the best individual is 0, then return the result.
54/// let result = engine.run(|output| output.score().as_i32() == 0);
55/// ```
56///
57/// # Type Parameters
58/// - `C`: The type of the chromosome used in the genotype, which must implement the [Chromosome] trait.
59/// - `T`: The type of the phenotype produced by the genetic algorithm, which must be `Clone`, `Send`, and `static`.
60pub struct GeneticEngine<C, T>
61where
62    C: Chromosome,
63    T: Clone + Send + Sync + 'static,
64{
65    context: EvolutionContext<C, T>,
66    pipeline: Pipeline<C>,
67    stream: EventStream,
68}
69
70impl<C, T> GeneticEngine<C, T>
71where
72    C: Chromosome + Clone,
73    T: Clone + Send + Sync + 'static,
74{
75    /// Creates a new genetic engine with the specified components.
76    ///
77    /// This constructor is primarily used internally by the builder pattern.
78    /// Users should create engines using `GeneticEngine::builder()`.
79    pub(crate) fn new(
80        context: EvolutionContext<C, T>,
81        pipeline: Pipeline<C>,
82        stream: EventStream,
83    ) -> Self {
84        GeneticEngine {
85            context,
86            pipeline,
87            stream,
88        }
89    }
90
91    /// Creates a new builder for configuring and constructing a genetic engine.
92    ///
93    /// The builder pattern provides a fluent interface for configuring all aspects
94    /// of the genetic algorithm, including population settings, selection strategies,
95    /// evolutionary operators, and fitness functions.
96    pub fn builder() -> GeneticEngineBuilder<C, T> {
97        GeneticEngineBuilder::default()
98    }
99
100    /// Returns a clone of the engine's control interface.
101    ///
102    /// The control interface allows for pausing, resuming, and stopping the engine's execution
103    /// from external contexts. If the control interface has not been initialized yet, this method
104    /// will create a new instance.
105    pub fn control(&mut self) -> ThreadSync {
106        self.context.get_or_create_sync()
107    }
108
109    /// Converts the engine into an iterator that yields generations.
110    ///
111    /// This method allows you to iterate over the evolutionary process manually,
112    /// giving you fine-grained control over when and how generations are processed.
113    /// The iterator yields `Generation` objects containing the current state and
114    /// statistics for each generation.
115    ///
116    /// # Use Cases
117    ///
118    /// Manual iteration is useful when you need to:
119    /// - Implement custom termination logic
120    /// - Monitor progress between generations
121    /// - Apply external control or adaptation
122    /// - Integrate with custom monitoring systems
123    /// - Implement interactive evolutionary algorithms
124    ///
125    /// # Note
126    ///
127    /// The iterator consumes the engine, so you can only iterate once. If you need
128    /// to run the engine multiple times, create a new instance using the builder.
129    pub fn iter(self) -> EngineRuntime<Self>
130    where
131        C: 'static,
132    {
133        EngineRuntime::new(self)
134    }
135
136    /// Subscribes to events of type `E` emitted by the engine.
137    ///
138    /// This method returns a [Subscription] that allows you to define
139    /// how to handle events of type `E`. You can use this to listen for events
140    /// such as epoch completions, improvements, or custom messages emitted during the evolutionary process.
141    pub fn subscribe<E: Event>(&self, handler: impl Handler<E>) -> Subscription {
142        self.stream.subscribe(handler)
143    }
144}
145
146/// Implementation of the [Engine] trait for [GeneticEngine].
147///
148/// This implementation provides the core evolutionary logic, advancing the
149/// population through one complete generation cycle. Each call to `next()`
150/// represents one generation of evolution, including fitness evaluation,
151/// selection, reproduction, and population replacement.
152///
153/// # Evolutionary Cycle
154///
155/// Each generation follows this sequence:
156/// 1. **Event Emission**: Start of epoch events
157/// 2. **Pipeline Execution**: Run evolutionary operators
158/// 3. **Metrics Collection**: Record timing and performance data
159/// 4. **Best Individual Update**: Track improvements and best solutions
160/// 5. **Event Completion**: End of epoch events
161/// 6. **Generation Advancement**: Increment generation counter
162///
163/// # Performance Optimizations
164///
165/// - **Efficient Metrics**: Metrics are updated incrementally to minimize overhead
166/// - **Event Batching**: Events are emitted efficiently without blocking execution
167/// - **Pipeline Optimization**: Evolutionary operators are executed in optimized sequences
168impl<C, T> Engine for GeneticEngine<C, T>
169where
170    C: Chromosome + Clone + 'static,
171    T: Clone + Send + Sync + 'static,
172{
173    type Epoch = Generation<C, T>;
174    type Ctx = EvolutionContext<C, T>;
175
176    fn context(&self) -> &Self::Ctx {
177        &self.context
178    }
179
180    fn epoch(&self) -> Self::Epoch {
181        Generation::from(&self.context)
182    }
183
184    fn state(&self) -> EngineState {
185        self.context.state
186    }
187
188    fn start(&mut self) {
189        self.context.set_running();
190        self.stream.publish(EngineStart);
191    }
192
193    fn stop(&mut self) {
194        self.context.set_stopped();
195        self.stream.publish(EngineStop::from(&self.context));
196    }
197
198    #[inline]
199    fn step(&mut self) -> Result<()> {
200        match self.state() {
201            EngineState::PreStart => self.start(),
202            EngineState::Stopped => return Ok(()),
203            _ => {
204                if self.context.stop_requested() {
205                    self.stop();
206                    return Ok(());
207                }
208
209                if self.context.pause_requested() {
210                    self.context.set_paused();
211                    self.context.wait();
212
213                    if self.context.stop_requested() {
214                        self.stop();
215                        return Ok(());
216                    }
217
218                    self.context.set_running();
219                }
220            }
221        }
222
223        self.stream.publish(EpochStart::from(&self.context));
224        self.pipeline.run(&mut self.context)?;
225        if self.context.try_advance_one()? {
226            self.stream
227                .lazy_publish(|| Improvement::from(&self.context))?;
228        }
229        self.stream.publish(EpochComplete::from(&self.context));
230        self.stream
231            .lazy_publish(|| GenerationSnapshot::from(&self.context))?;
232
233        Ok(())
234    }
235}
236
237/// Implementation of the [EngineStream] trait for [GeneticEngine].
238impl<C, T> EngineStream for GeneticEngine<C, T>
239where
240    C: Chromosome + Clone + 'static,
241    T: Clone + Send + Sync,
242{
243    type View<'a>
244        = GenerationView<'a, C, T>
245    where
246        Self: 'a;
247
248    fn run<F>(self, limit: F) -> Result<Self::Epoch>
249    where
250        F: Fn(Self::View<'_>) -> bool + 'static,
251    {
252        self.iter().until(limit).last()
253    }
254}