radiate_core/
diversity.rs

1use crate::{ArithmeticGene, Chromosome, Gene, Genotype};
2
3/// Trait for measuring diversity between two [Genotype]s.
4/// Within radiate this is mostly used for speciation and determining how genetically
5/// similar two individuals are. Through this, the engine can determine
6/// whether two individuals belong to the same species or not.
7pub trait Diversity<C: Chromosome>: Send + Sync {
8    fn measure(&self, geno_one: &Genotype<C>, geno_two: &Genotype<C>) -> f32;
9}
10
11/// A concrete implementation of the [Diversity] trait that calculates the Hamming distance
12/// between two [Genotype]s. The Hamming distance is the number of positions at which the
13/// corresponding genes are different normalized by the total number of genes.
14pub struct HammingDistance;
15
16impl<G: Gene, C: Chromosome<Gene = G>> Diversity<C> for HammingDistance
17where
18    G::Allele: PartialEq,
19{
20    fn measure(&self, geno_one: &Genotype<C>, geno_two: &Genotype<C>) -> f32 {
21        let mut distance = 0.0;
22        let mut total_genes = 0.0;
23        for (chrom_one, chrom_two) in geno_one.iter().zip(geno_two.iter()) {
24            for (gene_one, gene_two) in chrom_one.iter().zip(chrom_two.iter()) {
25                total_genes += 1.0;
26                if gene_one.allele() != gene_two.allele() {
27                    distance += 1.0;
28                }
29            }
30        }
31
32        distance / total_genes
33    }
34}
35
36/// Implementation of the [Diversity] trait that calculates the Euclidean distance
37/// between two [Genotype]s. The Euclidean distance is the square root of the sum of the
38/// squared differences between the corresponding genes' alleles, normalized by the number of genes.
39pub struct EuclideanDistance;
40
41impl<G: ArithmeticGene, C: Chromosome<Gene = G>> Diversity<C> for EuclideanDistance
42where
43    G::Allele: Into<f32> + Copy,
44{
45    fn measure(&self, geno_one: &Genotype<C>, geno_two: &Genotype<C>) -> f32 {
46        let mut distance = 0.0;
47        let mut total_genes = 0.0;
48        for (chrom_one, chrom_two) in geno_one.iter().zip(geno_two.iter()) {
49            for (gene_one, gene_two) in chrom_one.iter().zip(chrom_two.iter()) {
50                let one_as_f32: f32 = (*gene_one.allele()).into();
51                let two_as_f32: f32 = (*gene_two.allele()).into();
52
53                if one_as_f32.is_nan() || two_as_f32.is_nan() {
54                    continue;
55                }
56
57                let diff = one_as_f32 - two_as_f32;
58                distance += diff * diff;
59                total_genes += 1.0;
60            }
61        }
62        if total_genes == 0.0 {
63            return 0.0;
64        }
65
66        (distance / total_genes).sqrt()
67    }
68}