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}