INFO
This Genetic Algorithm is implemented with
rust
Source Code: https://github.com/youmin1017/genetic-algorithm-rs/tree/main
Result
Code
There are 5 part of my code
Main function
One trait (used to define prototype of struct methods)
- Chromosome
Three struct
- Genetic Algorithm
- Population
- Sqrt Chromosome
Main Function
fn main() {
// Configurations
let uniform_rate = 0.5;
let mutation_rate = 0.015;
let tournament_size = 5;
let elitism = true;
// Create Population used to manage Chromosomes
let mut population = Population::<SqrtChromosome>::new(uniform_rate, mutation_rate);
// Create GA instance ueed to run ga algorithm
let mut ga = GeneticAlgorithm::new(uniform_rate, mutation_rate, tournament_size, elitism);
let mut generation_count = 0;
// Initialize Population, which will randomly create 500 Chromosomes
population.initialize(500);
// loop while fitness <= 0.9999999999
while population.get_fittest().get_fitness() <= 1_f64 - 10_f64.powi(-11) {
// 2.2360679775
generation_count += 1;
println!(
"Generation: {} Fittest: {:.10} Reutls: {:.10}",
generation_count,
population.get_fittest().get_fitness(),
population.get_fittest().get_result()
);
population = ga.evolve_population(&mut population);
}
}
Chromosome Trait
Trait
如果 struct 有 Chromosome 這個 Trait 就必須實作 Trait 中的函式
pub trait Chromosome
where
Self: Ord + Clone,
{
fn new(uniform_rate: f64, mutation_rage: f64, genetic_len: Option<usize>) -> Self;
fn get_fitness(&self) -> f64; // get or caculate chromosome's fitness
fn mutate(&mut self);
fn crossover(&self, other: &Self) -> Self;
}
Genetic Algorithm
pub struct GeneticAlgorithm {
rng: ThreadRng,
uniform_rate: f64,
mutation_rate: f64,
torunament_size: i32,
elitism: bool,
}
Methods
族群演化
// 族群演化
pub fn evolve_population<T>(&mut self, pop: &mut Population<T>) -> Population<T>
where
T: Chromosome,
{
let mut new_population = Population::new(self.uniform_rate, self.mutation_rate);
let elitism_offset = if self.elitism { 1 } else { 0 };
if self.elitism {
let fittest = pop.get_fittest();
new_population.chromosomes.push(fittest);
}
// 競爭與交配
for _ in elitism_offset..pop.chromosomes.len() {
let chromosome1 = self.tournament_selection(pop);
let chromosome2 = self.tournament_selection(pop);
// 勝者與勝者交配以此獲得更好的下一代
let new_chromosome = chromosome1.crossover(&chromosome2);
new_population.chromosomes.push(new_chromosome);
}
// 突變
new_population
.chromosomes
.iter_mut()
.for_each(|c| c.mutate());
new_population
}
族群競爭
fn tournament_selection<T: Chromosome>(&mut self, pop: &mut Population<T>) -> T {
let mut tournament = Population::<T>::new(self.uniform_rate, self.mutation_rate);
// 在原有族群中隨機選取n個
for _ in 0..self.torunament_size {
let random_id: usize = self.rng.gen_range(0..pop.size());
tournament
.chromosomes
.push(pop.chromosomes[random_id].clone());
}
// 從這n個挑選最合適的(獲勝者)
tournament.get_fittest()
}
Population
pub struct Population<T> // genetic type
where
T: Chromosome // T should have trait Chromosome
{
uniform_rate: f64,
mutation_rate: f64,
pub chromosomes: Vec<T>,
}
Methods
Initialize
impl<T: Chromosome> Population<T> {
pub fn initialize(&mut self, size: i32) {
for _ in 0..size {
// create new chromosome into population
let c = T::new(self.uniform_rate, self.mutation_rate, None);
self.chromosomes.push(c);
}
}
}
Get_fittest
impl<T: Chromosome> Population<T> {
pub fn get_fittest(&self) -> T {
self.chromosomes
.iter()
// Select fittest chromosome
.max_by(|a, b| a.get_fitness().partial_cmp(&b.get_fitness()).unwrap())
.unwrap()
.clone()
}
Sqrt Chromosome
#[derive(Debug, Clone)]
pub struct SqrtChromosome {
genetic_len: usize,
genetic: Vec<bool>,
fitness: f64,
target: u8,
uniform_rate: f64,
mutation_rate: f64,
}
Methods
impl Chromosome for SqrtChromosome {
fn new(uniform_rate: f64, mutation_rate: f64, genetic_len: Option<usize>) -> Self {}
fn get_fitness(&self) -> f64 {}
fn mutate(&mut self) {}
fn crossover(&self, other: &Self) -> Self {}
}
Get_fitness
fn get_fitness(&self) -> f64 {
if self.fitness == 0_f64 {
self.calculate_fitness()
} else {
self.fitness
}
}
// [..16] -> number part: [..., true, false, true] = 0b101 = 5
// [16..] -> fractional part: [true, false, true, ...] = 2^-1 + 2^-3
fn calculate_fitness(&self) -> f64 {
// spilit genetic into two part
let (x, y) = self.genetic.split_at(16);
// caculate number part
let num: u32 = x.iter().fold(0, |acc, &x| acc << 1 | x as u32);
// caculate fractional part
let fra: f64 = y
.iter()
.enumerate()
.fold(0_f64, |acc, (i, &cur)| match cur {
true => acc + 2_f64.powi(-(i as i32 + 1)),
false => acc,
});
// caculate: 1 - abs(5 - x^2)
let dec = num as f64 + fra;
let dis = (self.target as f64 - dec.powi(2)).abs();
1_f64 - dis
}
Mutate
fn mutate(&mut self) {
let mut rng = thread_rng();
for i in 1..self.genetic_len {
// randomly generate boolean with a probability mutation rate
// if happen, then randomly set nth genetic
if rng.gen_bool(self.mutation_rate) {
self.genetic[i] = rng.gen();
}
}
}
Crossover
fn crossover(&self, other: &Self) -> Self {
let mut new_chromosome = Self::new(self.uniform_rate, self.mutation_rate, None);
let mut rng = thread_rng();
// iterate both chromosome self and others
new_chromosome.genetic = self
.genetic
.iter()
.zip(other.genetic.iter())
.map(|(x, y)| {
// if uniform happen, then change genetic
if rng.gen_bool(self.uniform_rate) {
x.clone()
} else {
y.clone()
}
})
// collect all genetic into Vec<bool> and set it to new chromosome
.collect();
new_chromosome
}