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 distance(&self, one: &T, two: &T) -> f32;
11}
12
13pub struct DistanceDiversityAdapter<C: Chromosome> {
14 diversity: Arc<dyn Diversity<C>>,
15}
16
17impl<C: Chromosome> DistanceDiversityAdapter<C> {
18 pub fn new(diversity: Arc<dyn Diversity<C>>) -> Self {
19 Self { diversity }
20 }
21}
22
23impl<C: Chromosome> Distance<Phenotype<C>> for DistanceDiversityAdapter<C> {
24 fn distance(&self, one: &Phenotype<C>, two: &Phenotype<C>) -> f32 {
25 self.diversity.measure(one, two)
26 }
27}
28
29pub trait Diversity<C: Chromosome>: Send + Sync {
34 fn measure(&self, geno_one: &Phenotype<C>, geno_two: &Phenotype<C>) -> f32;
35}
36
37impl<C: Chromosome, F> Diversity<C> for F
38where
39 F: Fn(&Phenotype<C>, &Phenotype<C>) -> f32 + Send + Sync,
40{
41 fn measure(&self, geno_one: &Phenotype<C>, geno_two: &Phenotype<C>) -> f32 {
42 self(geno_one, geno_two)
43 }
44}
45
46#[derive(Clone)]
50pub struct HammingDistance;
51
52impl<G, C> Diversity<C> for HammingDistance
53where
54 C: Chromosome<Gene = G>,
55 G: Gene,
56 G::Allele: PartialEq,
57{
58 fn measure(&self, geno_one: &Phenotype<C>, geno_two: &Phenotype<C>) -> f32 {
59 let geno_one = geno_one.genotype();
60 let geno_two = geno_two.genotype();
61
62 let mut distance = 0.0;
63 let mut total_genes = 0.0;
64 for (chrom_one, chrom_two) in geno_one.iter().zip(geno_two.iter()) {
65 for (gene_one, gene_two) in chrom_one.iter().zip(chrom_two.iter()) {
66 total_genes += 1.0;
67 if gene_one.allele() != gene_two.allele() {
68 distance += 1.0;
69 }
70 }
71 }
72
73 distance / total_genes
74 }
75}
76
77impl<P: AsRef<[f32]>> Distance<P> for HammingDistance {
78 fn distance(&self, one: &P, two: &P) -> f32 {
79 let vec_one = one.as_ref();
80 let vec_two = two.as_ref();
81
82 distance::hamming(vec_one, vec_two)
83 }
84}
85
86impl Novelty<Vec<f32>> for HammingDistance {
87 fn description(&self, phenotype: &Vec<f32>) -> Vec<f32> {
88 phenotype.clone()
89 }
90}
91
92#[derive(Clone)]
96pub struct EuclideanDistance;
97
98impl<G, C> Diversity<C> for EuclideanDistance
99where
100 C: Chromosome<Gene = G>,
101 G: NumericGene,
102 G::Allele: NumericAllele,
103{
104 fn measure(&self, geno_one: &Phenotype<C>, geno_two: &Phenotype<C>) -> f32 {
105 let geno_one = geno_one.genotype();
106 let geno_two = geno_two.genotype();
107
108 let mut distance = 0.0;
109 let mut total_genes = 0.0;
110 for (chrom_one, chrom_two) in geno_one.iter().zip(geno_two.iter()) {
111 for (gene_one, gene_two) in chrom_one.iter().zip(chrom_two.iter()) {
112 let one_as_f64 = gene_one.allele().extract::<f64>();
113 let two_as_f64 = gene_two.allele().extract::<f64>();
114
115 if let Some((one, two)) = one_as_f64.zip(two_as_f64) {
116 if one.is_nan() || two.is_nan() {
117 continue;
118 }
119
120 let diff = one - two;
121 distance += diff * diff;
122 total_genes += 1.0;
123 }
124 }
125 }
126
127 if total_genes == 0.0 {
128 return 0.0;
129 }
130
131 (distance / total_genes).sqrt() as f32
132 }
133}
134
135impl<P: AsRef<[f32]>> Distance<P> for EuclideanDistance {
136 fn distance(&self, one: &P, two: &P) -> f32 {
137 let vec_one = one.as_ref();
138 let vec_two = two.as_ref();
139
140 distance::euclidean(vec_one, vec_two)
141 }
142}
143
144impl Novelty<Vec<f32>> for EuclideanDistance {
145 fn description(&self, phenotype: &Vec<f32>) -> Vec<f32> {
146 phenotype.clone()
147 }
148}
149
150#[derive(Clone)]
151pub struct CosineDistance;
152
153impl<G, C> Diversity<C> for CosineDistance
154where
155 C: Chromosome<Gene = G>,
156 G: NumericGene,
157 G::Allele: NumericAllele,
158{
159 fn measure(&self, geno_one: &Phenotype<C>, geno_two: &Phenotype<C>) -> f32 {
160 let geno_one = geno_one.genotype();
161 let geno_two = geno_two.genotype();
162
163 let mut dot_product = 0.0;
164 let mut norm_one = 0.0;
165 let mut norm_two = 0.0;
166
167 for (chrom_one, chrom_two) in geno_one.iter().zip(geno_two.iter()) {
168 for (gene_one, gene_two) in chrom_one.iter().zip(chrom_two.iter()) {
169 let one_as_f64 = gene_one.allele().extract::<f64>();
170 let two_as_f64 = gene_two.allele().extract::<f64>();
171
172 if let Some((one, two)) = one_as_f64.zip(two_as_f64) {
173 if one.is_nan() || two.is_nan() {
174 continue;
175 }
176
177 dot_product += one * two;
178 norm_one += one * one;
179 norm_two += two * two;
180 }
181 }
182 }
183
184 if norm_one == 0.0 || norm_two == 0.0 {
185 return 1.0;
186 }
187
188 1.0 - (dot_product / (norm_one.sqrt() * norm_two.sqrt())) as f32
189 }
190}
191
192impl<P: AsRef<[f32]>> Distance<P> for CosineDistance {
193 fn distance(&self, one: &P, two: &P) -> f32 {
194 let vec_one = one.as_ref();
195 let vec_two = two.as_ref();
196
197 distance::cosine(vec_one, vec_two)
198 }
199}
200
201impl Novelty<Vec<f32>> for CosineDistance {
202 fn description(&self, phenotype: &Vec<f32>) -> Vec<f32> {
203 phenotype.clone()
204 }
205}