1use std::cmp::Ordering;
6
7use serde::{Deserialize, Serialize};
8
9use crate::fitness::traits::FitnessValue;
10use crate::genome::traits::EvolutionaryGenome;
11
12#[derive(Clone, Debug, Serialize, Deserialize)]
16#[serde(bound = "")]
17pub struct Individual<G, F = f64>
18where
19 G: EvolutionaryGenome,
20 F: FitnessValue,
21{
22 pub genome: G,
24 pub fitness: Option<F>,
26 pub birth_generation: usize,
28 pub offspring_count: usize,
30}
31
32impl<G, F> Individual<G, F>
33where
34 G: EvolutionaryGenome,
35 F: FitnessValue,
36{
37 pub fn new(genome: G) -> Self {
39 Self {
40 genome,
41 fitness: None,
42 birth_generation: 0,
43 offspring_count: 0,
44 }
45 }
46
47 pub fn with_fitness(genome: G, fitness: F) -> Self {
49 Self {
50 genome,
51 fitness: Some(fitness),
52 birth_generation: 0,
53 offspring_count: 0,
54 }
55 }
56
57 pub fn with_generation(genome: G, generation: usize) -> Self {
59 Self {
60 genome,
61 fitness: None,
62 birth_generation: generation,
63 offspring_count: 0,
64 }
65 }
66
67 pub fn is_evaluated(&self) -> bool {
69 self.fitness.is_some()
70 }
71
72 pub fn fitness_value(&self) -> &F {
74 self.fitness
75 .as_ref()
76 .expect("Individual has not been evaluated")
77 }
78
79 pub fn fitness_f64(&self) -> f64 {
81 self.fitness_value().to_f64()
82 }
83
84 pub fn set_fitness(&mut self, fitness: F) {
95 assert!(
96 !fitness.to_f64().is_nan(),
97 "Individual::set_fitness received a NaN fitness value; \
98 fitness functions must return a finite (non-NaN) value"
99 );
100 self.fitness = Some(fitness);
101 }
102
103 pub fn into_genome(self) -> G {
105 self.genome
106 }
107
108 pub fn genome(&self) -> &G {
110 &self.genome
111 }
112
113 pub fn genome_mut(&mut self) -> &mut G {
124 self.fitness = None;
125 &mut self.genome
126 }
127
128 pub fn set_genome(&mut self, genome: G) {
133 self.genome = genome;
134 self.fitness = None;
135 }
136
137 pub fn is_better_than(&self, other: &Self) -> bool {
139 match (&self.fitness, &other.fitness) {
140 (Some(f1), Some(f2)) => f1.is_better_than(f2),
141 (Some(_), None) => true,
142 (None, Some(_)) => false,
143 (None, None) => false,
144 }
145 }
146
147 pub fn age(&self, current_generation: usize) -> usize {
149 current_generation.saturating_sub(self.birth_generation)
150 }
151}
152
153impl<G, F> PartialEq for Individual<G, F>
154where
155 G: EvolutionaryGenome + PartialEq,
156 F: FitnessValue + PartialEq,
157{
158 fn eq(&self, other: &Self) -> bool {
159 self.genome == other.genome && self.fitness == other.fitness
160 }
161}
162
163impl<G, F> PartialOrd for Individual<G, F>
164where
165 G: EvolutionaryGenome + PartialEq,
166 F: FitnessValue + PartialEq,
167{
168 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
169 match (&self.fitness, &other.fitness) {
170 (Some(f1), Some(f2)) => f1.partial_cmp(f2),
171 (Some(_), None) => Some(Ordering::Greater),
172 (None, Some(_)) => Some(Ordering::Less),
173 (None, None) => Some(Ordering::Equal),
174 }
175 }
176}
177
178pub type IndividualPair<G, F = f64> = (Individual<G, F>, Individual<G, F>);
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184 use crate::genome::real_vector::RealVector;
185 use crate::genome::traits::RealValuedGenome;
186
187 #[test]
188 fn test_individual_new() {
189 let genome = RealVector::new(vec![1.0, 2.0, 3.0]);
190 let individual: Individual<RealVector> = Individual::new(genome);
191
192 assert!(!individual.is_evaluated());
193 assert_eq!(individual.birth_generation, 0);
194 assert_eq!(individual.offspring_count, 0);
195 }
196
197 #[test]
198 fn test_individual_with_fitness() {
199 let genome = RealVector::new(vec![1.0, 2.0, 3.0]);
200 let individual = Individual::with_fitness(genome, 42.0);
201
202 assert!(individual.is_evaluated());
203 assert_eq!(individual.fitness_f64(), 42.0);
204 }
205
206 #[test]
207 fn test_individual_set_fitness() {
208 let genome = RealVector::new(vec![1.0, 2.0, 3.0]);
209 let mut individual: Individual<RealVector> = Individual::new(genome);
210
211 assert!(!individual.is_evaluated());
212 individual.set_fitness(100.0);
213 assert!(individual.is_evaluated());
214 assert_eq!(individual.fitness_f64(), 100.0);
215 }
216
217 #[test]
218 fn test_individual_is_better_than() {
219 let g1 = RealVector::new(vec![1.0]);
220 let g2 = RealVector::new(vec![2.0]);
221
222 let ind1 = Individual::with_fitness(g1, 100.0);
223 let ind2 = Individual::with_fitness(g2, 50.0);
224
225 assert!(ind1.is_better_than(&ind2));
226 assert!(!ind2.is_better_than(&ind1));
227 }
228
229 #[test]
230 fn test_individual_is_better_than_unevaluated() {
231 let g1 = RealVector::new(vec![1.0]);
232 let g2 = RealVector::new(vec![2.0]);
233
234 let ind1 = Individual::with_fitness(g1, 100.0);
235 let ind2: Individual<RealVector> = Individual::new(g2);
236
237 assert!(ind1.is_better_than(&ind2));
238 assert!(!ind2.is_better_than(&ind1));
239 }
240
241 #[test]
242 fn test_individual_age() {
243 let genome = RealVector::new(vec![1.0]);
244 let individual: Individual<RealVector> = Individual::with_generation(genome, 10);
245
246 assert_eq!(individual.age(10), 0);
247 assert_eq!(individual.age(15), 5);
248 assert_eq!(individual.age(5), 0); }
250
251 #[test]
252 fn test_individual_partial_ord() {
253 let g1 = RealVector::new(vec![1.0]);
254 let g2 = RealVector::new(vec![2.0]);
255
256 let ind1 = Individual::with_fitness(g1, 100.0);
257 let ind2 = Individual::with_fitness(g2, 50.0);
258
259 assert!(ind1 > ind2);
260 assert!(ind2 < ind1);
261 }
262
263 #[test]
264 fn test_individual_into_genome() {
265 let genome = RealVector::new(vec![1.0, 2.0, 3.0]);
266 let individual = Individual::with_fitness(genome.clone(), 42.0);
267
268 let recovered = individual.into_genome();
269 assert_eq!(recovered, genome);
270 }
271
272 #[test]
273 fn test_individual_genome_mut() {
274 let genome = RealVector::new(vec![1.0, 2.0, 3.0]);
275 let mut individual: Individual<RealVector> = Individual::new(genome);
276
277 individual.genome_mut().genes_mut()[0] = 100.0;
278 assert_eq!(individual.genome()[0], 100.0);
279 }
280
281 #[test]
282 #[should_panic(expected = "NaN")]
283 fn test_set_fitness_rejects_nan() {
284 let mut individual: Individual<RealVector> = Individual::new(RealVector::new(vec![1.0]));
286 individual.set_fitness(f64::NAN);
287 }
288
289 #[test]
290 fn test_genome_mut_clears_cached_fitness() {
291 let genome = RealVector::new(vec![1.0, 2.0, 3.0]);
293 let mut individual = Individual::with_fitness(genome, 42.0);
294 assert!(individual.is_evaluated());
295
296 individual.genome_mut().genes_mut()[0] = 100.0;
297 assert!(
298 !individual.is_evaluated(),
299 "cached fitness must be cleared after genome_mut()"
300 );
301 }
302
303 #[test]
304 fn test_set_genome_clears_cached_fitness() {
305 let mut individual = Individual::with_fitness(RealVector::new(vec![1.0]), 42.0);
307 assert!(individual.is_evaluated());
308
309 individual.set_genome(RealVector::new(vec![2.0]));
310 assert!(!individual.is_evaluated());
311 assert_eq!(individual.genome()[0], 2.0);
312 }
313}