Skip to main content

radiate_engines/
generation.rs

1use crate::Chromosome;
2use crate::context::Context;
3use radiate_core::objectives::Scored;
4use radiate_core::{Ecosystem, Front, MetricSet, Objective, Phenotype, Population, Score, Species};
5#[cfg(feature = "serde")]
6use serde::{Deserialize, Serialize};
7use std::fmt::Debug;
8use std::time::Duration;
9
10/// A [Generation] represents a single generation in the evolutionary process.
11/// It contains the ecosystem, the best solution, index, metrics, score, objective,
12/// and optionally the Pareto front for multi-objective problems.
13///
14/// This is the main structure returned by the engine after each epoch, and it provides
15/// access to all relevant information about that generation.
16///
17/// # Example
18/// ```rust
19/// use radiate_core::*;
20/// use radiate_engines::*;
21/// use std::time::Duration;
22///
23/// let engine = GeneticEngine::builder()
24///     .codec(FloatChromosome::from((10, 0.0_f32..1.0_f32)))
25///     .fitness_fn(|vec: Vec<f32>| -vec.iter().map(|x| x * x).sum::<f32>())
26///     .build();
27///
28/// let generation = engine.iter().take(10).last().unwrap();
29///
30/// let ecosystem: &Ecosystem<FloatChromosome<f32>> = generation.ecosystem();
31///
32/// let population: &Population<FloatChromosome<f32>> = generation.population();
33/// assert!(population.len() == 100);
34///
35/// let solution: &Vec<f32> = generation.value();
36/// let index: usize = generation.index();
37/// let score: &Score = generation.score();
38/// let time: Duration = generation.time();
39///
40/// assert!(solution.len() == 10);
41/// assert!(index == 10);
42/// ```
43#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
44pub struct Generation<C, T>
45where
46    C: Chromosome,
47{
48    ecosystem: Ecosystem<C>,
49    value: T,
50    index: usize,
51    metrics: MetricSet,
52    score: Score,
53    objective: Objective,
54    front: Option<Front<Phenotype<C>>>,
55}
56
57impl<C, T> Generation<C, T>
58where
59    C: Chromosome,
60{
61    pub fn score(&self) -> &Score {
62        &self.score
63    }
64
65    pub fn front(&self) -> Option<&Front<Phenotype<C>>> {
66        self.front.as_ref()
67    }
68
69    pub fn value(&self) -> &T {
70        &self.value
71    }
72
73    pub fn index(&self) -> usize {
74        self.index
75    }
76
77    pub fn metrics(&self) -> &MetricSet {
78        &self.metrics
79    }
80
81    pub fn objective(&self) -> &Objective {
82        &self.objective
83    }
84
85    pub fn ecosystem(&self) -> &Ecosystem<C> {
86        &self.ecosystem
87    }
88
89    pub fn population(&self) -> &Population<C> {
90        &self.ecosystem().population()
91    }
92
93    pub fn species(&self) -> Option<&[Species<C>]> {
94        self.ecosystem().species().map(|s| s.as_slice())
95    }
96
97    pub fn time(&self) -> Duration {
98        self.metrics()
99            .time()
100            .map(|m| m.time_statistic().map(|t| t.sum()))
101            .flatten()
102            .unwrap_or_default()
103    }
104
105    pub fn seconds(&self) -> f64 {
106        self.time().as_secs_f64()
107    }
108}
109
110impl<C: Chromosome, T> Scored for Generation<C, T> {
111    fn score(&self) -> Option<&Score> {
112        Some(&self.score)
113    }
114}
115
116impl<C, T> From<&Context<C, T>> for Generation<C, T>
117where
118    C: Chromosome + Clone,
119    T: Clone,
120{
121    fn from(context: &Context<C, T>) -> Self {
122        Generation {
123            ecosystem: context.ecosystem.clone(),
124            value: context.best.clone(),
125            index: context.index,
126            metrics: context.metrics.clone(),
127            score: context.score.clone().unwrap(),
128            objective: context.objective.clone(),
129            front: match context.objective {
130                Objective::Multi(_) => Some(context.front.read().unwrap().clone()),
131                _ => None,
132            },
133        }
134    }
135}
136
137impl<C, T> Clone for Generation<C, T>
138where
139    C: Chromosome + Clone,
140    T: Clone,
141{
142    fn clone(&self) -> Self {
143        Generation {
144            ecosystem: self.ecosystem.clone(),
145            value: self.value.clone(),
146            index: self.index,
147            metrics: self.metrics.clone(),
148            score: self.score.clone(),
149            objective: self.objective.clone(),
150            front: self.front.as_ref().map(|f| f.clone()),
151        }
152    }
153}
154
155impl<C, T> Debug for Generation<C, T>
156where
157    C: Chromosome,
158    T: Debug,
159{
160    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161        let ecosystem = &self.ecosystem;
162
163        write!(f, "Generation {{\n")?;
164        write!(f, "  metrics: {:?},\n", self.metrics)?;
165        write!(f, "  value: {:?},\n", self.value)?;
166        write!(f, "  score: {:?},\n", self.score)?;
167        write!(f, "  index: {:?},\n", self.index)?;
168        write!(f, "  size: {:?},\n", ecosystem.population().len())?;
169        write!(f, "  duration: {:?},\n", self.time())?;
170        write!(f, "  objective: {:?},\n", self.objective)?;
171
172        if let Some(species) = &ecosystem.species {
173            for s in species {
174                write!(f, "  species: {:?},\n", s)?;
175            }
176        }
177
178        write!(f, "}}")
179    }
180}
181
182impl<C, T> FromIterator<Generation<C, T>> for Front<Phenotype<C>>
183where
184    C: Chromosome + Clone,
185{
186    fn from_iter<I: IntoIterator<Item = Generation<C, T>>>(iter: I) -> Self {
187        iter.into_iter()
188            .last()
189            .map(|generation| generation.front().map(|front| front.clone()))
190            .flatten()
191            .unwrap_or_default()
192    }
193}