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..1.0);
20/// let population = (0..100)
21///    .map(|_| Phenotype::from((codec.encode(), 0)))
22///    .collect::<Population<FloatChromosome>>();
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    /// Reference clone an existing ecosystem. This creates a new ecosystem
42    /// with reference counted clones of the phenotypes and species. This just clones
43    /// pointers to the underlying [Population] and [Species], so any modifications to
44    /// the phenotypes will be reflected in both ecosystems. Radiate uses this internally
45    /// for efficiency when iterating over generations - this is not intended for general use.
46    ///
47    /// **Use with caution**
48    pub fn clone_ref(other: &Ecosystem<C>) -> Self
49    where
50        C: Clone,
51    {
52        Ecosystem {
53            population: Population::clone_ref(&other.population),
54            species: other.species.as_ref().map(|specs| {
55                specs
56                    .iter()
57                    .map(|species| Species::clone_ref(species))
58                    .collect()
59            }),
60        }
61    }
62
63    /// Get the number of shared phenotypes in the population.
64    /// A shared phenotype is one that is reference cloned and held
65    /// by another structure, such as a [Species]. The only time the shared
66    /// count should ever be > 0 is when the ecosystem has [Species] that
67    /// hold references to phenotypes in the main population.
68    pub fn shared_count(&self) -> usize {
69        self.population.shared_count()
70    }
71
72    /// Like [Ecosystem::shared_count], but returns true if there are
73    /// any shared phenotypes in the population. This should only be true
74    /// when the ecosystem has [Species] that hold references to phenotypes
75    /// in the main population.
76    pub fn is_shared(&self) -> bool {
77        self.shared_count() > 0
78    }
79
80    pub fn population(&self) -> &Population<C> {
81        &self.population
82    }
83
84    pub fn population_mut(&mut self) -> &mut Population<C> {
85        &mut self.population
86    }
87
88    pub fn species(&self) -> Option<&Vec<Species<C>>> {
89        self.species.as_ref()
90    }
91
92    pub fn species_mut(&mut self) -> Option<&mut Vec<Species<C>>> {
93        self.species.as_mut()
94    }
95
96    pub fn get_phenotype(&self, index: usize) -> Option<&Phenotype<C>> {
97        self.population.get(index)
98    }
99
100    pub fn get_phenotype_mut(&mut self, index: usize) -> Option<&mut Phenotype<C>> {
101        self.population.get_mut(index)
102    }
103
104    pub fn get_genotype(&self, index: usize) -> Option<&Genotype<C>> {
105        self.population.get(index).map(|p| p.genotype())
106    }
107
108    pub fn get_genotype_mut(&mut self, index: usize) -> Option<&mut Genotype<C>> {
109        self.population.get_mut(index).map(|p| p.genotype_mut())
110    }
111
112    pub fn get_species(&self, index: usize) -> Option<&Species<C>> {
113        self.species.as_ref().and_then(|s| s.get(index))
114    }
115
116    pub fn get_species_mut(&mut self, index: usize) -> Option<&mut Species<C>> {
117        self.species.as_mut().and_then(|s| s.get_mut(index))
118    }
119
120    pub fn species_mascots(&self) -> Vec<&Phenotype<C>> {
121        self.species
122            .as_ref()
123            .map(|s| s.iter().map(|spec| spec.mascot()).collect())
124            .unwrap_or_default()
125    }
126
127    pub fn push_species(&mut self, species: Species<C>) {
128        if let Some(species_list) = &mut self.species {
129            species_list.push(species);
130        } else {
131            self.species = Some(vec![species]);
132        }
133    }
134
135    /// Add a member to a species given the species index and member index in the population.
136    /// The member is reference cloned from the population and added to the species' population.
137    /// Just like with the [Ecosystem]'s `clone_ref` method, this creates a shared reference so
138    /// any modifications to the phenotype within the [Species] will be reflected in the main [Population].
139    ///
140    /// **Use with caution**
141    pub fn add_species_member(&mut self, species_idx: usize, member_idx: usize)
142    where
143        C: Clone,
144    {
145        if let Some(species) = &mut self.species {
146            if let Some(spec) = species.get_mut(species_idx) {
147                if let Some(member) = self.population.ref_clone_member(member_idx) {
148                    spec.population.push(member);
149                }
150            }
151        }
152    }
153
154    pub fn remvove_dead_species(&mut self) -> usize {
155        if let Some(species) = &mut self.species {
156            let initial_len = species.len();
157            species.retain(|spec| spec.len() > 0);
158            initial_len - species.len()
159        } else {
160            0
161        }
162    }
163}
164
165impl<C: Chromosome + Clone> Clone for Ecosystem<C> {
166    fn clone(&self) -> Self {
167        Ecosystem {
168            population: self.population.clone(),
169            species: self.species.clone(),
170        }
171    }
172}
173
174impl<C: Chromosome> From<Vec<Phenotype<C>>> for Ecosystem<C> {
175    fn from(phenotypes: Vec<Phenotype<C>>) -> Self {
176        Ecosystem {
177            population: Population::from(phenotypes),
178            species: None,
179        }
180    }
181}
182
183impl<C: Chromosome> From<Population<C>> for Ecosystem<C> {
184    fn from(population: Population<C>) -> Self {
185        Ecosystem {
186            population,
187            species: None,
188        }
189    }
190}
191
192#[cfg(test)]
193mod tests {
194    use super::*;
195    use crate::*;
196
197    #[test]
198    fn test_create_ecosystem() {
199        let codec = FloatCodec::vector(5, 0.0..1.0);
200        let phenotypes = (0..10)
201            .map(|_| Phenotype::from((codec.encode(), 0)))
202            .collect::<Vec<_>>();
203
204        let ecosystem = Ecosystem::from(phenotypes);
205        assert_eq!(ecosystem.population.len(), 10);
206        assert!(ecosystem.species.is_none());
207    }
208}