Skip to main content

radiate_engines/
generation.rs

1use crate::Chromosome;
2use crate::context::EvolutionContext;
3use radiate_core::ExprSet;
4use radiate_core::objectives::Scored;
5use radiate_core::{Ecosystem, Front, MetricSet, Objective, Phenotype, Population, Score, Species};
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8use std::fmt::Debug;
9use std::sync::{Arc, Mutex};
10use std::time::Duration;
11
12/// A [Generation] represents a single generation in the evolutionary process.
13/// It contains the ecosystem, the best solution, index, metrics, score, objective,
14/// and optionally the Pareto front for multi-objective problems.
15///
16/// This is the main structure returned by the engine after each epoch, and it provides
17/// access to all relevant information about that generation.
18///
19/// # Example
20/// ```rust
21/// use radiate_core::*;
22/// use radiate_engines::*;
23/// use std::time::Duration;
24///
25/// let engine = GeneticEngine::builder()
26///     .codec(FloatChromosome::from((10, 0.0_f32..1.0_f32)))
27///     .fitness_fn(|vec: Vec<f32>| -vec.iter().map(|x| x * x).sum::<f32>())
28///     .build();
29///
30/// let generation = engine.iter().take(10).last().unwrap();
31///
32/// let ecosystem: &Ecosystem<FloatChromosome<f32>> = generation.ecosystem();
33///
34/// let population: &Population<FloatChromosome<f32>> = generation.population();
35/// assert!(population.len() == 100);
36///
37/// let solution: &Vec<f32> = generation.value();
38/// let index: usize = generation.index();
39/// let score: &Score = generation.score();
40/// let time: Duration = generation.time();
41///
42/// assert!(solution.len() == 10);
43/// assert!(index == 10);
44/// ```
45#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
46pub struct Generation<C: Chromosome, T> {
47    ecosystem: Arc<Ecosystem<C>>,
48    value: T,
49    index: usize,
50    metrics: Arc<MetricSet>,
51    score: Score,
52    objective: Objective,
53    front: Option<Arc<Front<Phenotype<C>>>>,
54    exprs: Option<Arc<Mutex<ExprSet>>>,
55}
56
57impl<C: Chromosome, T> Generation<C, T> {
58    pub fn score(&self) -> &Score {
59        &self.score
60    }
61
62    pub fn front(&self) -> Option<&Front<Phenotype<C>>> {
63        self.front.as_deref()
64    }
65
66    pub fn value(&self) -> &T {
67        &self.value
68    }
69
70    pub fn index(&self) -> usize {
71        self.index
72    }
73
74    pub fn metrics(&self) -> &MetricSet {
75        &self.metrics
76    }
77
78    pub fn objective(&self) -> &Objective {
79        &self.objective
80    }
81
82    pub fn ecosystem(&self) -> &Ecosystem<C> {
83        &self.ecosystem
84    }
85
86    pub fn population(&self) -> &Population<C> {
87        self.ecosystem().population()
88    }
89
90    pub fn species(&self) -> Option<&[Species<C>]> {
91        self.ecosystem().species().map(|s| s.as_slice())
92    }
93
94    pub fn time(&self) -> Duration {
95        self.metrics()
96            .time()
97            .and_then(|m| m.times().map(|t| t.sum()))
98            .unwrap_or_default()
99    }
100
101    pub fn seconds(&self) -> f64 {
102        self.time().as_secs_f64()
103    }
104
105    pub fn exprs(&self) -> Option<Arc<Mutex<ExprSet>>> {
106        self.exprs.clone()
107    }
108
109    pub fn arc_ecosystem(&self) -> Arc<Ecosystem<C>> {
110        Arc::clone(&self.ecosystem)
111    }
112
113    pub fn arc_metrics(&self) -> Arc<MetricSet> {
114        Arc::clone(&self.metrics)
115    }
116}
117
118impl<C: Chromosome, T> Scored for Generation<C, T> {
119    fn score(&self) -> Option<&Score> {
120        Some(&self.score)
121    }
122}
123
124impl<C, T> From<&EvolutionContext<C, T>> for Generation<C, T>
125where
126    C: Chromosome + Clone,
127    T: Clone,
128{
129    fn from(context: &EvolutionContext<C, T>) -> Self {
130        Generation {
131            ecosystem: Arc::new(context.ecosystem.clone()),
132            value: context.best.clone(),
133            index: context.index,
134            metrics: Arc::new(context.metrics.clone()),
135            score: context.score.clone().unwrap(),
136            objective: context.objective.clone(),
137            front: match context.objective {
138                Objective::Multi(_) => Some(Arc::new(context.front.read().unwrap().clone())),
139                _ => None,
140            },
141            exprs: context.exprs.clone(),
142        }
143    }
144}
145
146impl<C, T> Clone for Generation<C, T>
147where
148    C: Chromosome + Clone,
149    T: Clone,
150{
151    fn clone(&self) -> Self {
152        Generation {
153            ecosystem: Arc::clone(&self.ecosystem),
154            value: self.value.clone(),
155            index: self.index,
156            metrics: Arc::clone(&self.metrics),
157            score: self.score.clone(),
158            objective: self.objective.clone(),
159            front: self.front.clone(),
160            exprs: self.exprs.clone(),
161        }
162    }
163}
164
165impl<C, T> Debug for Generation<C, T>
166where
167    C: Chromosome,
168    T: Debug,
169{
170    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
171        let ecosystem = &self.ecosystem;
172
173        writeln!(f, "Generation {{")?;
174        writeln!(f, "  metrics: {:?},", self.metrics)?;
175        writeln!(f, "  score: {:?},", self.score)?;
176        writeln!(f, "  index: {:?},", self.index)?;
177        writeln!(f, "  size: {:?},", ecosystem.population().len())?;
178        writeln!(f, "  duration: {:?},", self.time())?;
179        writeln!(f, "  objective: {:?},", self.objective)?;
180        if let Some(species) = &ecosystem.species {
181            writeln!(f, "  species [")?;
182            for s in species {
183                writeln!(
184                    f,
185                    "  \t{:?}  age={}",
186                    s,
187                    self.index.saturating_sub(s.generation()),
188                )?;
189            }
190            writeln!(f, "  ],")?;
191        }
192        writeln!(f, "  value: {:?},", self.value)?;
193
194        write!(f, "}}")
195    }
196}
197
198impl<C, T> Default for Generation<C, T>
199where
200    C: Chromosome + Default,
201    T: Default,
202{
203    fn default() -> Self {
204        Generation {
205            ecosystem: Arc::new(Ecosystem::default()),
206            value: T::default(),
207            index: 0,
208            metrics: Arc::new(MetricSet::default()),
209            score: Score::default(),
210            objective: Objective::default(),
211            front: None,
212            exprs: None,
213        }
214    }
215}
216
217impl<C, T> FromIterator<Generation<C, T>> for Front<Phenotype<C>>
218where
219    C: Chromosome + Clone,
220{
221    fn from_iter<I: IntoIterator<Item = Generation<C, T>>>(iter: I) -> Self {
222        iter.into_iter()
223            .last()
224            .and_then(|generation| generation.front().cloned())
225            .unwrap_or_default()
226    }
227}
228
229enum ViewInner<'a, C, T>
230where
231    C: Chromosome,
232{
233    Context(&'a EvolutionContext<C, T>),
234    Generation(&'a Generation<C, T>),
235}
236
237pub struct GenerationView<'a, C, T>
238where
239    C: Chromosome,
240{
241    inner: ViewInner<'a, C, T>,
242}
243
244impl<'a, C, T> GenerationView<'a, C, T>
245where
246    C: Chromosome,
247{
248    pub fn new(context: &'a EvolutionContext<C, T>) -> Self {
249        Self {
250            inner: ViewInner::Context(context),
251        }
252    }
253
254    pub fn score(&self) -> &Score {
255        match &self.inner {
256            ViewInner::Context(ctx) => ctx.score.as_ref().unwrap(),
257            ViewInner::Generation(epoch) => &epoch.score,
258        }
259    }
260
261    pub fn front(&self) -> Arc<Front<Phenotype<C>>>
262    where
263        C: Clone,
264    {
265        match &self.inner {
266            ViewInner::Context(ctx) => match ctx.objective {
267                Objective::Multi(_) => Arc::new(ctx.front.read().unwrap().clone()),
268                _ => Arc::default(),
269            },
270            ViewInner::Generation(epoch) => match &epoch.front {
271                Some(front) => Arc::clone(front),
272                None => Arc::default(),
273            },
274        }
275    }
276
277    pub fn value(&self) -> &T {
278        match &self.inner {
279            ViewInner::Context(ctx) => &ctx.best,
280            ViewInner::Generation(epoch) => &epoch.value,
281        }
282    }
283
284    pub fn phenotype(&self) -> &Phenotype<C> {
285        match &self.inner {
286            ViewInner::Context(ctx) => ctx.ecosystem().get_phenotype(0).as_ref().unwrap(),
287            ViewInner::Generation(epoch) => epoch.ecosystem().get_phenotype(0).as_ref().unwrap(),
288        }
289    }
290
291    pub fn index(&self) -> usize {
292        match &self.inner {
293            ViewInner::Context(ctx) => ctx.index,
294            ViewInner::Generation(epoch) => epoch.index,
295        }
296    }
297
298    pub fn metrics(&self) -> &MetricSet {
299        match &self.inner {
300            ViewInner::Context(ctx) => &ctx.metrics,
301            ViewInner::Generation(epoch) => &epoch.metrics,
302        }
303    }
304
305    pub fn objective(&self) -> &Objective {
306        match &self.inner {
307            ViewInner::Context(ctx) => &ctx.objective,
308            ViewInner::Generation(epoch) => &epoch.objective,
309        }
310    }
311
312    pub fn ecosystem(&self) -> &Ecosystem<C> {
313        match &self.inner {
314            ViewInner::Context(ctx) => &ctx.ecosystem,
315            ViewInner::Generation(epoch) => &epoch.ecosystem,
316        }
317    }
318
319    pub fn population(&self) -> &Population<C> {
320        self.ecosystem().population()
321    }
322
323    pub fn species(&self) -> Option<&[Species<C>]> {
324        self.ecosystem().species().map(|s| s.as_slice())
325    }
326
327    pub fn time(&self) -> Duration {
328        self.metrics()
329            .time()
330            .and_then(|m| m.times().map(|t| t.sum()))
331            .unwrap_or_default()
332    }
333
334    pub fn seconds(&self) -> f64 {
335        self.time().as_secs_f64()
336    }
337}
338
339impl<'a, C, T> From<GenerationView<'a, C, T>> for Generation<C, T>
340where
341    C: Chromosome + Clone,
342    T: Clone,
343{
344    fn from(val: GenerationView<'a, C, T>) -> Self {
345        match val.inner {
346            ViewInner::Context(ctx) => Generation::from(ctx),
347            ViewInner::Generation(epoch) => epoch.clone(),
348        }
349    }
350}
351
352impl<'a, C, T> From<&'a Generation<C, T>> for GenerationView<'a, C, T>
353where
354    C: Chromosome,
355{
356    fn from(epoch: &'a Generation<C, T>) -> Self {
357        Self {
358            inner: ViewInner::Generation(epoch),
359        }
360    }
361}
362
363impl<'a, C, T> From<&'a EvolutionContext<C, T>> for GenerationView<'a, C, T>
364where
365    C: Chromosome,
366{
367    fn from(ctx: &'a EvolutionContext<C, T>) -> Self {
368        Self {
369            inner: ViewInner::Context(ctx),
370        }
371    }
372}