Skip to main content

red_queen_core/
genome.rs

1//! Genome trait and utilities.
2//!
3//! A genome is the evolvable representation of a solution.
4
5use rand::Rng;
6use serde::{Deserialize, Serialize};
7
8/// Core trait for evolvable representations.
9///
10/// Implement this trait to define how your solutions are represented,
11/// mutated, and combined.
12pub trait Genome: Clone + Send + Sync + Sized {
13    /// The phenotype type - what the genome produces when expressed.
14    type Phenotype;
15
16    /// Create a random genome.
17    fn random<R: Rng>(rng: &mut R) -> Self;
18
19    /// Mutate the genome in place.
20    ///
21    /// # Arguments
22    /// * `rng` - Random number generator
23    /// * `rate` - Mutation rate (0.0 to 1.0)
24    fn mutate<R: Rng>(&mut self, rng: &mut R, rate: f64);
25
26    /// Combine two genomes to produce offspring.
27    ///
28    /// # Arguments
29    /// * `other` - The other parent genome
30    /// * `rng` - Random number generator
31    fn crossover<R: Rng>(&self, other: &Self, rng: &mut R) -> Self;
32
33    /// Convert genome to its phenotype (the actual solution/input).
34    fn to_phenotype(&self) -> Self::Phenotype;
35
36    /// Compute distance to another genome (for novelty search).
37    ///
38    /// Default implementation returns 0.0 (no distance defined).
39    fn distance(&self, _other: &Self) -> f64 {
40        0.0
41    }
42}
43
44/// Behavior descriptor for quality-diversity algorithms.
45#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
46pub struct BehaviorDescriptor {
47    /// Values for each behavior dimension.
48    pub values: Vec<f64>,
49}
50
51impl BehaviorDescriptor {
52    /// Create a new behavior descriptor.
53    pub fn new(values: Vec<f64>) -> Self {
54        Self { values }
55    }
56
57    /// Compute Euclidean distance to another descriptor.
58    pub fn distance(&self, other: &Self) -> f64 {
59        self.values
60            .iter()
61            .zip(other.values.iter())
62            .map(|(a, b)| (a - b).powi(2))
63            .sum::<f64>()
64            .sqrt()
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71
72    #[test]
73    fn test_behavior_distance() {
74        let a = BehaviorDescriptor::new(vec![0.0, 0.0]);
75        let b = BehaviorDescriptor::new(vec![3.0, 4.0]);
76        assert!((a.distance(&b) - 5.0).abs() < 1e-10);
77    }
78
79    #[test]
80    fn test_behavior_distance_same_point() {
81        let a = BehaviorDescriptor::new(vec![1.0, 2.0, 3.0]);
82        let b = BehaviorDescriptor::new(vec![1.0, 2.0, 3.0]);
83        assert!((a.distance(&b)).abs() < 1e-10);
84    }
85
86    #[test]
87    fn test_behavior_distance_single_dimension() {
88        let a = BehaviorDescriptor::new(vec![0.0]);
89        let b = BehaviorDescriptor::new(vec![5.0]);
90        assert!((a.distance(&b) - 5.0).abs() < 1e-10);
91    }
92
93    #[test]
94    fn test_behavior_descriptor_new() {
95        let desc = BehaviorDescriptor::new(vec![1.0, 2.0, 3.0]);
96        assert_eq!(desc.values, vec![1.0, 2.0, 3.0]);
97    }
98}