Skip to main content

radiate_core/genome/
ecosystem.rs

1use super::{Chromosome, Genotype, Phenotype, Population, Species};
2#[cfg(feature = "serde")]
3use serde::{Deserialize, Serialize};
4
5/// An ecosystem containing a population and optional species.
6/// This structure is the main container for solutions generated throughout the evolutionary process.
7/// The optional species field allows for organizing the population into distinct groups,
8/// each represented by a mascot phenotype.
9///
10/// When using [Species] within the ecosystem, it is important to manage the population
11/// members appropriately. Species hold shared references to phenotypes in the main population,
12/// so any modifications to the population should ensure that these references remain valid.
13///
14/// # Example
15/// ```rust
16/// use radiate_core::*;
17///
18/// // Create a simple ecosystem
19/// let codec = FloatCodec::vector(10, 0.0_f64..1.0_f64);
20/// let population = (0..100)
21///    .map(|_| Phenotype::from((codec.encode(), 0)))
22///    .collect::<Population<FloatChromosome<f64>>>();
23///
24/// let ecosystem = Ecosystem::new(population);
25/// ```
26#[derive(Debug, Default)]
27#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
28pub struct Ecosystem<C: Chromosome> {
29    pub population: Population<C>,
30    pub species: Option<Vec<Species<C>>>,
31}
32
33impl<C: Chromosome> Ecosystem<C> {
34    pub fn new(population: Population<C>) -> Self {
35        Ecosystem {
36            population,
37            species: None,
38        }
39    }
40
41    /// Get the number of shared phenotypes in the population.
42    /// A shared phenotype is one that is reference cloned and held
43    /// by another structure, such as a [Species]. The only time the shared
44    /// count should ever be > 0 is when the ecosystem has [Species] that
45    /// hold references to phenotypes in the main population.
46    pub fn shared_count(&self) -> usize {
47        self.population.shared_count()
48    }
49
50    /// Like [Ecosystem::shared_count], but returns true if there are
51    /// any shared phenotypes in the population. This should only be true
52    /// when the ecosystem has [Species] that hold references to phenotypes
53    /// in the main population.
54    pub fn is_shared(&self) -> bool {
55        self.shared_count() > 0
56    }
57
58    pub fn population(&self) -> &Population<C> {
59        &self.population
60    }
61
62    pub fn population_mut(&mut self) -> &mut Population<C> {
63        &mut self.population
64    }
65
66    pub fn species(&self) -> Option<&Vec<Species<C>>> {
67        self.species.as_ref()
68    }
69
70    pub fn species_mut(&mut self) -> Option<&mut Vec<Species<C>>> {
71        self.species.as_mut()
72    }
73
74    pub fn get_phenotype(&self, index: usize) -> Option<&Phenotype<C>> {
75        self.population.get(index)
76    }
77
78    pub fn get_phenotype_mut(&mut self, index: usize) -> Option<&mut Phenotype<C>> {
79        self.population.get_mut(index)
80    }
81
82    pub fn get_genotype(&self, index: usize) -> Option<&Genotype<C>> {
83        self.population.get(index).map(|p| p.genotype())
84    }
85
86    pub fn get_genotype_mut(&mut self, index: usize) -> Option<&mut Genotype<C>> {
87        self.population.get_mut(index).map(|p| p.genotype_mut())
88    }
89
90    pub fn get_species(&self, index: usize) -> Option<&Species<C>> {
91        self.species.as_ref().and_then(|s| s.get(index))
92    }
93
94    pub fn get_species_mut(&mut self, index: usize) -> Option<&mut Species<C>> {
95        self.species.as_mut().and_then(|s| s.get_mut(index))
96    }
97
98    pub fn species_mascots(&self) -> Vec<&Phenotype<C>> {
99        self.species
100            .as_ref()
101            .map(|s| s.iter().map(|spec| spec.mascot()).collect())
102            .unwrap_or_default()
103    }
104
105    pub fn push_species(&mut self, species: Species<C>) {
106        if let Some(species_list) = &mut self.species {
107            species_list.push(species);
108        } else {
109            self.species = Some(vec![species]);
110        }
111    }
112
113    /// Add a member to a species given the species index and member index in the population.
114    /// The member is reference cloned from the population and added to the species' population.
115    /// Just like with the [Ecosystem]'s `clone_ref` method, this creates a shared reference so
116    /// any modifications to the phenotype within the [Species] will be reflected in the main [Population].
117    pub fn add_species_member(&mut self, species_idx: usize, member_idx: usize)
118    where
119        C: Clone,
120    {
121        if let Some(species) = &mut self.species {
122            if let Some(spec) = species.get_mut(species_idx) {
123                if let Some(member) = self.population.ref_clone_member(member_idx) {
124                    spec.population.push(member);
125                }
126            }
127        }
128    }
129
130    pub fn remove_dead_species(&mut self) -> usize {
131        if let Some(species) = &mut self.species {
132            let initial_len = species.len();
133            species.retain(|spec| spec.len() > 0);
134            initial_len - species.len()
135        } else {
136            0
137        }
138    }
139}
140
141impl<C: Chromosome + Clone> Clone for Ecosystem<C> {
142    fn clone(&self) -> Self {
143        Ecosystem {
144            population: self.population.clone(),
145            species: self.species.clone(),
146        }
147    }
148}
149
150impl<C: Chromosome> From<Vec<Phenotype<C>>> for Ecosystem<C> {
151    fn from(phenotypes: Vec<Phenotype<C>>) -> Self {
152        Ecosystem {
153            population: Population::from(phenotypes),
154            species: None,
155        }
156    }
157}
158
159impl<C: Chromosome> From<Population<C>> for Ecosystem<C> {
160    fn from(population: Population<C>) -> Self {
161        Ecosystem {
162            population,
163            species: None,
164        }
165    }
166}
167
168#[cfg(test)]
169mod tests {
170    use super::*;
171    use crate::*;
172
173    #[test]
174    fn test_create_ecosystem() {
175        let codec = FloatCodec::vector(5, 0.0..1.0);
176        let phenotypes = (0..10)
177            .map(|_| Phenotype::from((codec.encode(), 0)))
178            .collect::<Vec<_>>();
179
180        let ecosystem = Ecosystem::from(phenotypes);
181        assert_eq!(ecosystem.population.len(), 10);
182        assert!(ecosystem.species.is_none());
183    }
184}