radiate_core/
diversity.rs1use crate::{
2 Chromosome, Gene, Phenotype,
3 chromosomes::{NumericAllele, gene::NumericGene},
4 fitness::Novelty,
5 math::distance,
6};
7use std::sync::Arc;
8
9pub trait Distance<T>: Send + Sync {
10 fn calculate(&self, one: &T, two: &T) -> f32;
11}
12
13pub trait Diversity<C: Chromosome>: Send + Sync {
18 fn measure(&self, geno_one: &Phenotype<C>, geno_two: &Phenotype<C>) -> f32;
19}
20
21pub struct DistanceDiversityAdapter<C: Chromosome> {
22 diversity: Arc<dyn Diversity<C>>,
23}
24
25impl<C: Chromosome> DistanceDiversityAdapter<C> {
26 pub fn new(diversity: Arc<dyn Diversity<C>>) -> Self {
27 Self { diversity }
28 }
29}
30
31impl<C: Chromosome> Distance<Phenotype<C>> for DistanceDiversityAdapter<C> {
32 fn calculate(&self, one: &Phenotype<C>, two: &Phenotype<C>) -> f32 {
33 self.diversity.measure(one, two)
34 }
35}
36
37#[derive(Clone)]
41pub struct HammingDistance;
42
43impl<G, C> Diversity<C> for HammingDistance
44where
45 C: Chromosome<Gene = G>,
46 G: Gene,
47 G::Allele: PartialEq,
48{
49 #[inline]
50 fn measure(&self, geno_one: &Phenotype<C>, geno_two: &Phenotype<C>) -> f32 {
51 let geno_one = geno_one.genotype();
52 let geno_two = geno_two.genotype();
53
54 let mut distance = 0.0;
55 let mut total_genes = 0.0;
56 for (chrom_one, chrom_two) in geno_one.iter().zip(geno_two.iter()) {
57 for (gene_one, gene_two) in chrom_one.iter().zip(chrom_two.iter()) {
58 total_genes += 1.0;
59 if gene_one.allele() != gene_two.allele() {
60 distance += 1.0;
61 }
62 }
63 }
64
65 distance / total_genes
66 }
67}
68
69impl<P: AsRef<[f32]>> Distance<P> for HammingDistance {
70 fn calculate(&self, one: &P, two: &P) -> f32 {
71 let vec_one = one.as_ref();
72 let vec_two = two.as_ref();
73
74 distance::hamming(vec_one, vec_two)
75 }
76}
77
78impl Novelty<Vec<f32>> for HammingDistance {
79 fn description(&self, phenotype: &Vec<f32>) -> Vec<f32> {
80 phenotype.clone()
81 }
82}
83
84#[derive(Clone)]
88pub struct EuclideanDistance;
89
90impl<G, C> Diversity<C> for EuclideanDistance
91where
92 C: Chromosome<Gene = G>,
93 G: NumericGene,
94 G::Allele: NumericAllele,
95{
96 #[inline]
97 fn measure(&self, geno_one: &Phenotype<C>, geno_two: &Phenotype<C>) -> f32 {
98 let geno_one = geno_one.genotype();
99 let geno_two = geno_two.genotype();
100
101 let mut distance = 0.0;
102 let mut total_genes = 0.0;
103 for (chrom_one, chrom_two) in geno_one.iter().zip(geno_two.iter()) {
104 for (gene_one, gene_two) in chrom_one.iter().zip(chrom_two.iter()) {
105 let one_as_f64 = gene_one.allele().extract::<f64>();
106 let two_as_f64 = gene_two.allele().extract::<f64>();
107
108 if let Some((one, two)) = one_as_f64.zip(two_as_f64) {
109 if one.is_nan() || two.is_nan() {
110 continue;
111 }
112
113 let diff = one - two;
114 distance += diff * diff;
115 total_genes += 1.0;
116 }
117 }
118 }
119
120 if total_genes == 0.0 {
121 return 0.0;
122 }
123
124 (distance / total_genes).sqrt() as f32
125 }
126}
127
128impl<P: AsRef<[f32]>> Distance<P> for EuclideanDistance {
129 fn calculate(&self, one: &P, two: &P) -> f32 {
130 let vec_one = one.as_ref();
131 let vec_two = two.as_ref();
132
133 distance::euclidean(vec_one, vec_two)
134 }
135}
136
137impl Novelty<Vec<f32>> for EuclideanDistance {
138 fn description(&self, phenotype: &Vec<f32>) -> Vec<f32> {
139 phenotype.clone()
140 }
141}
142
143#[derive(Clone)]
144pub struct CosineDistance;
145
146impl<G, C> Diversity<C> for CosineDistance
147where
148 C: Chromosome<Gene = G>,
149 G: NumericGene,
150 G::Allele: NumericAllele,
151{
152 #[inline]
153 fn measure(&self, geno_one: &Phenotype<C>, geno_two: &Phenotype<C>) -> f32 {
154 let geno_one = geno_one.genotype();
155 let geno_two = geno_two.genotype();
156
157 let mut dot_product = 0.0;
158 let mut norm_one = 0.0;
159 let mut norm_two = 0.0;
160
161 for (chrom_one, chrom_two) in geno_one.iter().zip(geno_two.iter()) {
162 for (gene_one, gene_two) in chrom_one.iter().zip(chrom_two.iter()) {
163 let one_as_f64 = gene_one.allele().extract::<f64>();
164 let two_as_f64 = gene_two.allele().extract::<f64>();
165
166 if let Some((one, two)) = one_as_f64.zip(two_as_f64) {
167 if one.is_nan() || two.is_nan() {
168 continue;
169 }
170
171 dot_product += one * two;
172 norm_one += one * one;
173 norm_two += two * two;
174 }
175 }
176 }
177
178 if norm_one == 0.0 || norm_two == 0.0 {
179 return 1.0;
180 }
181
182 1.0 - (dot_product / (norm_one.sqrt() * norm_two.sqrt())) as f32
183 }
184}
185
186impl<P: AsRef<[f32]>> Distance<P> for CosineDistance {
187 fn calculate(&self, one: &P, two: &P) -> f32 {
188 let vec_one = one.as_ref();
189 let vec_two = two.as_ref();
190
191 distance::cosine(vec_one, vec_two)
192 }
193}
194
195impl Novelty<Vec<f32>> for CosineDistance {
196 fn description(&self, phenotype: &Vec<f32>) -> Vec<f32> {
197 phenotype.clone()
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204
205 #[test]
206 fn test_hamming_distance() {
207 let distance = HammingDistance;
208 let vec_one = vec![1.0, 2.0, 3.0];
209 let vec_two = vec![1.0, 2.0, 4.0];
210
211 assert_eq!(distance.calculate(&vec_one, &vec_two), 1.0 / 3.0);
212 }
213
214 #[test]
215 fn test_euclidean_distance() {
216 let distance = EuclideanDistance;
217 let vec_one = vec![1.0, 2.0, 3.0];
218 let vec_two = vec![1.0, 2.0, 4.0];
219
220 assert_eq!(distance.calculate(&vec_one, &vec_two), 1.0);
221 }
222
223 #[test]
224 fn test_cosine_distance() {
225 let distance = CosineDistance;
226 let vec_one = vec![1.0, 2.0, 3.0];
227 let vec_two = vec![1.0, 2.0, 4.0];
228
229 assert_eq!(distance.calculate(&vec_one, &vec_two), 0.008539915);
230 }
231}