26 releases (11 stable)

1.6.0 Oct 30, 2024
1.3.2 Nov 22, 2023
0.9.2 Aug 22, 2023
0.8.4 Jul 26, 2023
0.5.1 Dec 26, 2022

#38 in Machine learning

Download history 346/week @ 2024-08-23 176/week @ 2024-08-30 2/week @ 2024-09-06 38/week @ 2024-09-13 79/week @ 2024-09-20 13/week @ 2024-09-27 2/week @ 2024-10-04 2/week @ 2024-10-11 100/week @ 2024-10-25 27/week @ 2024-11-01 3/week @ 2024-11-08 4/week @ 2024-11-15 2/week @ 2024-11-22

74 downloads per month

Apache-2.0

96KB
1.5K SLoC

RUST genetic algorithms library

Codacy Badge Rust Unit Tests

Description

This library provides a simple framework for implementing genetic algorithms (GA) with Rust.

This library also supports multithreading and adaptive genetic algorithms.

Table of content

Documentation

See docs.rs

Features

Traits

This release uses traits for generic implementations.

These traits are inside the traits module:

  • GeneT: This trait must be implemented on your own gene representation.
    • new(): Optional. This is the constructor function.
    • get_id(): Optional. This function must return the id of the gene.
    • set_id(): Sets the id of the gene.
  • GenotypeT: This trait must be implemented on your own genotype representation.
    • Gene: This is the GeneT associated type.
    • new(): Optional. This is the constructor function.
    • new_gene(): Optional. Must return Self::Gene.
    • get_dna(): Must return the array of genes (GeneT).
    • set_dna(dna: &[Self::Gene]): Must set the array of genes (GeneT).
    • set_gene(gene_index: usize, gene: Self::Gene): Optional. This method replaces a gene at the specified gene_index position.
    • calculate_fitness(): Optional. This function must calculate the fitness of the individual (or the genotype) in f64.
    • get_fitness(): Returns the fitness previously calculated by calculate_fitness().
    • set_fitness(fitness: f64): Sets the fitness value.
    • get_age(): Returns the age of the genotype.
    • set_age(age: i32): Sets the age of the genotype.

Operators

Within the operations module we have the following operators:

  • Crossover
    • Cycle
    • Multipoint
    • Uniform
  • Mutation
    • Swap
    • Inversion
    • Scramble
  • Selection
    • Random
    • Roulette Wheel
    • Stochastic Universal Sampling
    • Tournament
  • Survivor
    • Fitness based
    • Age based

Population

In genetic algorithms, operators are applied over a population of individuals, and over a set of rules (not yet implemented). Within the population module, the Population structure will define the population.

Runner

Since genetic algorithms run over several generations, there is a run function in this library within the ga module that facilitates the process. This function needs the GaConfiguration structure, which contains the operators to be used, the maximum number of generations, the problem solver (Maximization or Minimization), etc., and the Population structure, which is in the population module.

GA Configuration

Within this library, you can configure the way genetic algorithms are executed by using the configuration structure GaConfiguration. This structure has the following attributes:

  • adaptive_ga: Specifies if the Genetic Algorithms are adaptive or not.
  • number_of_threads: Optional. Indicates how many threads will be executed simultaneously.
  • limit_configuration: It configures the limits of the Genetic Algorithms with the LimitConfiguration structure.
  • selection_configuration: It configures the selection method with the SelectionConfiguration structure.
  • crossover_configuration: It configures the crossover method with the CrossoverConfiguration structure.
  • mutation_configuration: It configures the mutation method with the MutationConfiguration structure.
  • survivor: Specifies which survivor operator to use.
  • log_level: Optional. It configures the maximum log level we want to have. If this value is none, logs will be disabled.

SelectionConfiguration:

  • number_of_couples: Optional. This attribute applies only to stochastic universal sampling. It specifies the number of pairs to select from the population. By defaultthe value will be the half of the population size.
  • method: Specifies which selection operator to use.

CrossoverConfiguration:

  • number_of_points: Optional. This attribute is only valid for crossover multipoint and indicates how many points are made within the DNA during crossover operations.
  • probability_max: Optional. Specifies the maximum probability that two parents are crossed. This number must be between 0.0 and 1.0, both inclusive. In case of adaptive genetic algorithms, this parameter is mandatory and must be greater than probability_min.
  • probability_min: Optional. Specifies the minimum probability that two parents are crossed. This number must be between 0.0 and 1.0, both inclusive. In case of adaptive genetic algorithms, this parameter is mandatory and must be lower than probability_max.
  • method: Specifies which crossover operator to use.

MutationConfiguration:

  • probability_max: Optional. Specifies the maximum probability that a genotype is mutated. This number must be between 0.0 and 1.0, both inclusive. In case of adaptive genetic algorithms, this parameter is mandatory and must be greater than probability_min.

  • probability_min: Optional. Specifies the minimum probability that a genotype is mutated. This number must be between 0.0 and 1.0, both inclusive. In case of adaptive genetic algorithms, this parameter is mandatory and must be lower than probability_max.

  • method: Specifies which mutation operator to use.

LimitConfiguration:

  • problem_solving: You can choose between a minimization problem and a maximization problem.
  • max_generations: If the result is not optimal, this attribute indicates the maximum number of generations to run before stopping.
  • fitness_target: Optional. The fitness of the best individual.
  • get_best_individual_by_generation: Optional. Tells the runner to return the best individual by generation.
  • population_size: Size of the population to be executed.
  • genes_per_individual: Number of genes that each individual must have.
  • needs_unique_ids: Optional. Indicates whether each gene must have unique numbering.
  • alleles_can_be_repeated: Indicates whether the same allele can be repeated in an individual.

Example

A simple example of use could be minimizing a genotype whose gene has only one id.

Creation of the gene and genotype structure

Use the traits. use genetic_algorithms::{operations::{Selection, Crossover, Mutation, Survivor}, population::Population, traits::{GenotypeT, ConfigurationT}, configuration::ProblemSolving, ga};

Define the gene structure.

#[derive(Debug, Copy, Clone, Default, PartialEq)]
pub struct Gene{
    pub id: i32,
}
impl GeneT for Gene{
    fn get_id(&self) -> &i32{
        return &self.id;
    }
    fn set_id(&mut self, id: i32)->&mut Self {
        self.id = id;
    }
}

Define the genotype structure, and the fitness calculation.

#[derive(Debug, Clone, Default, PartialEq)]
pub struct Genotype{
    pub dna: Vec<Gene>,
    pub fitness: f64,
    pub age: i32,
}
impl GenotypeT for Genotype{
    type Gene = Gene;
    fn get_dna(&self) -> &[Self::Gene] {
        &self.dna
    }
    fn get_fitness(&self) -> f64 {
        return self.fitness;
    }
    fn set_fitness(&mut self, fitness: f64) ->&mut Self {
        self.fitness = fitness;
    }
    fn set_age(&mut self, age:i32) ->&mut Self {
        self.age = age;
    }
    fn get_age(&self) -> i32 {
        self.age
    }
    fn calculate_fitness(&mut self) {
        
        self.fitness = 0.0;
        let mut position = 0;

        for i in &self.dna{
            let fitness = f64::from(i.get_id()*position);
            self.fitness += fitness;
            position += 1;
        }
    }
    fn set_dna(&mut self, dna: &[Self::Gene]) ->&mut Self{
        self.dna = dna.to_vec();
    }
}

Define the Alleles

  let binding =  vec![Gene{id:1}, Gene{id:2}, Gene{id:3}, Gene{id:4},
                                   Gene{id:5}, Gene{id:6}, Gene{id:7}, Gene{id:8}];
  let alleles = binding.as_slice();

Finally, configure and run the GA.

let population = ga::Ga::new()
                    .with_threads(8)
                    .with_problem_solving(ProblemSolving::Maximization)
                    .with_selection_method(Selection::Tournament)
                    .with_number_of_couples(10)
                    .with_crossover_method(Crossover::Cycle)
                    .with_mutation_method(Mutation::Swap)
                    .with_survivor_method(Survivor::Fitness)
                    .with_alleles(alleles)
                    .with_genes_per_individual(6)
                    .with_population_size(100)
                    .run();

If you want to receive a notification every few generations and when the genetic algorithms have terminated and why, this is possible via a callback function. This function has to be of the form Fn(&i32,&Population, TerminationCause); Following the previous case, an example could be the following:

fn callback_function(generation_number: &i32, population: &Population<Genotype>, termination_cause: TerminationCause){
  print!("Callback received");
}
let population = ga::Ga::new()
                    .with_threads(8)
                    .with_problem_solving(ProblemSolving::Maximization)
                    .with_selection_method(Selection::Tournament)
                    .with_number_of_couples(10)
                    .with_crossover_method(Crossover::Cycle)
                    .with_mutation_method(Mutation::Swap)
                    .with_survivor_method(Survivor::Fitness)
                    .with_alleles(alleles)
                    .with_genes_per_individual(6)
                    .with_population_size(100)
                    .run_with_callback(Some(callback_function), 8);

Other examples

Usage

Add this to your Cargo.toml:

[dependencies]
genetic_algorithms = "1.6.0"

Dependencies

~0.8–1.2MB
~25K SLoC