Created
August 31, 2023 09:16
-
-
Save guiyomh/059582f441c9574335916964a18bc9a9 to your computer and use it in GitHub Desktop.
rust genetic algo
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use rand::prelude::*; | |
use crate::{ELITISM, GENERATIONS_COUNT, GENOME_SIZE, LOG, POPULATION_SIZE, SELECTION_RATIO}; | |
static mut UNIFORM_RATE: f64 = 0.5; | |
static mut MUTATION_RATE: f64 = 0.06; | |
#[derive(Debug, Clone)] | |
pub struct Gene { | |
random: f64, | |
} | |
impl Gene { | |
pub fn new() -> Self { | |
Self { | |
random: rand::thread_rng().gen::<f64>(), | |
} | |
} | |
pub fn as_int(&self, max: i32) -> i32 { | |
(self.random * (max + 1) as f64) as i32 | |
} | |
} | |
#[derive(Debug, Clone)] | |
pub struct GenomeAndResult<T> | |
where | |
T: Clone, | |
{ | |
genome: Vec<Gene>, | |
pub result: T, | |
} | |
impl<T> GenomeAndResult<T> | |
where | |
T: Clone, | |
{ | |
fn new<C>(genome: Vec<Gene>, create: C) -> Self | |
where | |
C: Fn(Vec<Gene>) -> T, | |
{ | |
Self { | |
genome: genome.clone(), | |
result: create(genome), | |
} | |
} | |
} | |
pub fn find_best_chimp<T, F, C>(create: C, fitness: F) -> GenomeAndResult<T> | |
where | |
F: Fn(&T) -> f64, | |
C: Fn(Vec<Gene>) -> T, | |
T: Clone, | |
{ | |
fn sort_by_fitness<T, F>(population: &mut Vec<GenomeAndResult<T>>, fitness: F) | |
where | |
F: Fn(&T) -> f64, | |
T: Clone, | |
{ | |
population.sort_by(|a, b| fitness(&b.result).partial_cmp(&fitness(&a.result)).unwrap()); | |
if LOG { | |
let fitness_values: Vec<i32> = population | |
.iter() | |
.map(|g| fitness(&g.result) as i32) | |
.collect(); | |
eprintln!( | |
"{}", | |
fitness_values | |
.iter() | |
.map(|f| format!("{:5}", f)) | |
.collect::<Vec<String>>() | |
.join(" ") | |
); | |
} | |
} | |
let population: Vec<GenomeAndResult<T>> = (0..POPULATION_SIZE) | |
.map(|_| GenomeAndResult::new(build_genome(GENOME_SIZE), &create)) | |
.collect(); | |
let mut chimps_few_generations_later = population.clone(); | |
sort_by_fitness(&mut chimps_few_generations_later, &fitness); | |
for _ in 0..GENERATIONS_COUNT { | |
chimps_few_generations_later = | |
build_next_generation(&chimps_few_generations_later, &create, &fitness); | |
sort_by_fitness(&mut chimps_few_generations_later, &fitness); | |
} | |
chimps_few_generations_later.first().unwrap().clone() | |
} | |
fn build_genome(size: usize) -> Vec<Gene> { | |
(0..size).map(|_| Gene::new()).collect() | |
} | |
fn build_next_generation<T, F>( | |
population: &[GenomeAndResult<T>], | |
create: F, | |
_fitness: &impl Fn(&T) -> f64, | |
) -> Vec<GenomeAndResult<T>> | |
where | |
F: Fn(Vec<Gene>) -> T, | |
T: Clone, | |
{ | |
let mut new_pop: Vec<GenomeAndResult<T>> = population.to_vec(); | |
let elitism_offset = if ELITISM { 1 } else { 0 }; | |
for i in elitism_offset..population.len() - 1 { | |
let genome1 = select(&population).genome; | |
let genome2 = select(&population).genome; | |
let mut genome = crossover(&genome1, &genome2); | |
mutate(&mut genome); | |
new_pop[i] = GenomeAndResult::new(genome, &create); | |
} | |
new_pop | |
} | |
fn select<T>(population: &[GenomeAndResult<T>]) -> GenomeAndResult<T> | |
where | |
T: Clone, | |
{ | |
let mut rng = rand::thread_rng(); | |
for (i, chimp) in population.iter().enumerate() { | |
if rng.gen::<f64>() | |
<= SELECTION_RATIO * (population.len() - i) as f64 / population.len() as f64 | |
{ | |
return chimp.clone(); | |
} | |
} | |
population[0].clone() | |
} | |
fn crossover(genome1: &[Gene], genome2: &[Gene]) -> Vec<Gene> { | |
genome1 | |
.iter() | |
.enumerate() | |
.map(|(i, gene1)| { | |
if random::<f64>() <= unsafe { UNIFORM_RATE } { | |
gene1.clone() | |
} else { | |
genome2[i].clone() | |
} | |
}) | |
.collect() | |
} | |
fn mutate(genome: &mut [Gene]) { | |
for gene in genome.iter_mut() { | |
if random::<f64>() <= unsafe { MUTATION_RATE } { | |
*gene = Gene::new() | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// for exemple | |
const ELITISM: bool = true; | |
const GENERATIONS_COUNT: usize = 100; | |
const POPULATION_SIZE: usize = 20; | |
const GENOME_SIZE: usize = 1200; | |
const SELECTION_RATIO: f64 = 0.4; | |
const LOG: bool = false; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment