diff --git a/src/lib.rs b/src/lib.rs index e554a40..b0bf093 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -72,7 +72,7 @@ //! Add this to your `Cargo.toml`: //! ```toml //! [dependencies] -//! genx = "0.1.0" +//! genx = "0.2.3" //! ``` //! If you are not using Rust 2018 edition add this to your crate root: //! ```rust diff --git a/src/mutation/flipping.rs b/src/mutation/flipping.rs index a4120dd..d866727 100644 --- a/src/mutation/flipping.rs +++ b/src/mutation/flipping.rs @@ -1,5 +1,30 @@ use rand::{rngs::StdRng, SeedableRng, Rng}; +/** +## Description +Flipping mutation is a mutation only for binary encoded individuals. +Given the `mutation_probability` and `individual` it iterates through the boolean vector of `individual` +and generates a random number between `0.0` and `1.0`. If the number is less than `mutation_probability` +then we _flip_ the value at that index else it remains the same. + +_Note: The function can also take in an optional `seed` value of type `Option` for deterministic results._ + +## Return +The return value is a `Result<(), &'static str>` which will return error only if the `mutation_probability` is greater than `1` +## Example +```rust + use genx::mutation::flipping_mutation; + let mut individual = vec![false, true, false, false, + true, true, true, false, false, true, false, + false, true, false, false, true]; + let original_individual = individual.clone(); + match flipping_mutation(&mut individual, 0.5, Some(42)) { + Ok(_) => (), + Err(error) => panic!("{:?}", error) + }; + assert_ne!(original_individual, individual); +``` +*/ pub fn flipping_mutation(individual: &mut Vec, mutation_probability: f32, seed: Option) -> Result<(), &'static str> { if mutation_probability < 0.0 || mutation_probability > 1.0 { return Err("mutation_probability should lie between 0.0 and 1.0 inclusive"); diff --git a/src/mutation/inversion.rs b/src/mutation/inversion.rs index ebb477d..df847f9 100644 --- a/src/mutation/inversion.rs +++ b/src/mutation/inversion.rs @@ -2,6 +2,25 @@ use std::{mem::swap}; use rand::{Rng, SeedableRng, rngs::StdRng}; +/** +## Description +Inversion mutation is a mutation only for binary encoded individuals. +Given the `individual` it randomly generates two indices and then inverts +the value between those indices of the individual. + +_Note: The function can also take in an optional `seed` value of type `Option` for deterministic results._ + +## Example +```rust + use genx::mutation::inversion_mutation; + let mut individual = vec![false, true, false, false, + true, true, true, false, false, true, false, + false, true, false, false, true]; + let original_individual = individual.clone(); + inversion_mutation(&mut individual, Some(42)); + assert_ne!(original_individual, individual); +``` +*/ pub fn inversion_mutation(individual: &mut Vec, seed: Option) { let mut prng = match seed { Some(val) => StdRng::seed_from_u64(val), diff --git a/src/mutation/mod.rs b/src/mutation/mod.rs index 83ba59b..cf35014 100644 --- a/src/mutation/mod.rs +++ b/src/mutation/mod.rs @@ -14,7 +14,8 @@ //! //! All the mutation functions for binary encoded schema //! take in atleast an argument of mutable reference to -//! the boolean vector of individual to mutate. +//! the boolean vector of individual to mutate. Hence they +//! change the actual boolean Vector. //! //! Only those functions where, there is a need to constrain //! range of values that can be provided as argument will return diff --git a/src/mutation/polynomial.rs b/src/mutation/polynomial.rs index 7f1a196..e16f57f 100644 --- a/src/mutation/polynomial.rs +++ b/src/mutation/polynomial.rs @@ -1,5 +1,28 @@ use rand::{Rng, rngs::StdRng, SeedableRng}; +/** +## Description +Polynomial mutation is a mutation only for real encoded individuals. +Given the `individual`, `distribution_index` and `max_perturbation` it generates a closure +which is an exponential polynomial function using the `distribution_index`. Given a randomly generated value +in range `0.0..1.0` to the closure it returns the `perturbation_factor`. + +### Note +- A large value for `distribution_index` will make stark mutation for only a few values whereas a small `distribution_index` will cause uniform mutation for all randomly generated values (undesirable). +- The function can also take in an optional `seed` value of type `Option` for deterministic results. + +## Return +Finally returned value is `individual + calculate_perturbation_factor(random_value)*max_perturbation` + +## Example +```rust + use genx::mutation::polynomial_mutation; + + let individual = 29.11; + let result = polynomial_mutation(individual, 4.2, 4.0, Some(42)); + assert_ne!(individual, result); +``` +*/ pub fn polynomial_mutation(individual: f32, distribution_index: f32, max_perturbation: f32, seed: Option) -> f32 { let random_value = (match seed { Some(val) => StdRng::seed_from_u64(val), diff --git a/src/mutation/random.rs b/src/mutation/random.rs index 5e973ae..45f6da2 100644 --- a/src/mutation/random.rs +++ b/src/mutation/random.rs @@ -1,5 +1,27 @@ use rand::{Rng, rngs::StdRng, SeedableRng}; +/** +## Description +Random mutation is a mutation only for real encoded individuals. +Given the `individual` and `perturbation_factor` it generates a random value +in range `0.0..1.0` which is then used to linearly mutate the individual. + +#### Note +- A large value for `perturbation_factor` will make stark mutation for all values. +- The function can also take in an optional `seed` value of type `Option` for deterministic results. + +## Return +Finally returned value is `individual + (2.0*random_value - 1.0)*perturbation_factor` + +## Example +```rust + use genx::mutation::random_mutation; + + let individual = 29.11; + let result = random_mutation(individual, 4.2, Some(42)); + assert_ne!(individual, result); +``` +*/ pub fn random_mutation(individual: f32, perturbation_factor: f32, seed: Option) -> f32 { let random_value = (match seed { Some(val) => StdRng::seed_from_u64(val), diff --git a/src/mutation/scramble.rs b/src/mutation/scramble.rs index 2863909..e1b247f 100644 --- a/src/mutation/scramble.rs +++ b/src/mutation/scramble.rs @@ -2,6 +2,25 @@ use std::{mem::swap}; use rand::{Rng, SeedableRng, rngs::StdRng, seq::SliceRandom}; +/** +## Description +Scramble mutation is a mutation only for binary encoded individuals. +Given the `individual` it randomly generates two indices and then +shuffles the values between those two indices. + +_Note: The function can also take in an optional `seed` value of type `Option` for deterministic results._ + +## Example +```rust + use genx::mutation::scramble_mutation; + let mut individual = vec![false, true, false, false, + true, true, true, false, false, true, false, + false, true, false, false, true]; + let original_individual = individual.clone(); + scramble_mutation(&mut individual, Some(42)); + assert_ne!(original_individual, individual); +``` +*/ pub fn scramble_mutation(individual: &mut Vec, seed: Option) { let mut prng = match seed { Some(val) => StdRng::seed_from_u64(val), diff --git a/src/mutation/swap.rs b/src/mutation/swap.rs index 4bd66cd..2cb08f8 100644 --- a/src/mutation/swap.rs +++ b/src/mutation/swap.rs @@ -1,5 +1,23 @@ use rand::{rngs::StdRng, SeedableRng, seq::SliceRandom}; +/** +## Description +Swap mutation is a mutation only for binary encoded individuals. +Given the `individual` it randomly generates two indices and then swaps the value at those two indices. + +_Note: The function can also take in an optional `seed` value of type `Option` for deterministic results._ + +## Example +```rust + use genx::mutation::swap_mutation; + let mut individual = vec![false, true, false, false, + true, true, true, false, false, true, false, + false, true, false, false, true]; + let original_individual = individual.clone(); + swap_mutation(&mut individual, Some(11)); + assert_ne!(original_individual, individual); +``` +*/ pub fn swap_mutation(individual: &mut Vec, seed: Option) -> Result<(), &'static str> { let mut prng = match seed { Some(val) => StdRng::seed_from_u64(val), diff --git a/src/selection/mod.rs b/src/selection/mod.rs index 1e6371c..d606c80 100644 --- a/src/selection/mod.rs +++ b/src/selection/mod.rs @@ -15,6 +15,8 @@ //! point values that contains fitness values of individuals, and //! number of individuals to select. Finally functions return indices of //! selected individuals. +//! +//! You can read more about selection schemas and their working from the [wikipedia page](https://en.wikipedia.org/wiki/Selection_(genetic_algorithm)) pub mod random; diff --git a/src/selection/random.rs b/src/selection/random.rs index 33449f9..fceeb96 100644 --- a/src/selection/random.rs +++ b/src/selection/random.rs @@ -1,5 +1,26 @@ use rand::{rngs::StdRng, SeedableRng, Rng}; +/** +## Description +Random Selection is the simplest form of selection which randomly +generates an index from the provided `fitness_values` vector and that +is the selection. Same procedure is done until we have `num_parents` selected individuals. + +_Note: The function can also take in an optional `seed` value of type `Option` for deterministic results._ + +## Return + +The return value is a `Vec` pointing to the selected indices. + +## Example +```rust + use genx::selection::random_selection; + let num_parents:usize = 10; + let fitness_values = vec![10.0,0.2,9.0,4.8,7.7,8.4,3.2,9.4,9.0,11.0,4.5]; + + let result = random_selection(&fitness_values, num_parents, None); +``` +*/ pub fn random_selection(fitness_values: &Vec, num_parents: usize, seed: Option) -> Vec { let population_size = fitness_values.len(); let mut prng = match seed { diff --git a/src/selection/rank.rs b/src/selection/rank.rs index 82b44f6..8881009 100644 --- a/src/selection/rank.rs +++ b/src/selection/rank.rs @@ -1,6 +1,30 @@ use std::cmp::Ordering; use rand::{rngs::StdRng, SeedableRng, Rng}; +/** +## Description +Rank Selection is a form of proportionate selection. It generates a roulette wheel where area occupied by a particular individual is proportional to its rank in fitness vector when sorted in non decreasing order. + +For each of the `num_parents` iterations a random number between `0.0..1.0` is generated which then decides the location at which roulette wheel will stop and that particular individual's index is added to vector of selected individuals. + +### Note + +- Individuals with same fitness value are given the same ranks. +- The function can also take in an optional `seed` value of type `Option` for deterministic results. + +## Return + +The return value is a `Vec` pointing to the selected indices. + +## Example +```rust + use genx::selection::rank_selection; + let num_parents:usize = 10; + let fitness_values = vec![10.0,0.2,9.0,4.8,7.7,8.4,3.2,9.4,9.0,11.0,4.5]; + + let result = rank_selection(&fitness_values, num_parents, None); +``` +*/ pub fn rank_selection(fitness_values: &Vec, num_parents: usize, seed: Option) -> Vec { let mut fitness_values_with_index : Vec<(f32, usize)> = Vec::new(); for (i, &value) in fitness_values.iter().enumerate() { diff --git a/src/selection/roulette_wheel.rs b/src/selection/roulette_wheel.rs index 2cbc3e7..a6c1ffd 100644 --- a/src/selection/roulette_wheel.rs +++ b/src/selection/roulette_wheel.rs @@ -1,5 +1,29 @@ use rand::{rngs::StdRng, SeedableRng, Rng}; +/** +## Description +Roulette Wheel Selection is a form of proportionate selection. It generates a roulette wheel where area occupied by a particular individual is proportional to its fitness values. + +For each of the `num_parents` iterations a random number between `0.0..1.0` is generated which then decides the location at which roulette wheel will stop and that particular individual's index is added to vector of selected individuals. + +### Note + +- Individuals with same fitness value occupy same area on roulette wheel. +- The function can also take in an optional `seed` value of type `Option` for deterministic results. + +## Return + +The return value is a `Vec` pointing to the selected indices. + +## Example +```rust + use genx::selection::roulette_wheel_selection; + let num_parents:usize = 10; + let fitness_values = vec![10.0,0.2,9.0,4.8,7.7,8.4,3.2,9.4,9.0,11.0,4.5]; + + let result = roulette_wheel_selection(&fitness_values, num_parents, None); +``` +*/ pub fn roulette_wheel_selection(fitness_values: &Vec, num_parents: usize, seed: Option) -> Vec { let sum_of_fitness = fitness_values.iter().sum::(); let normalized_probabilities = fitness_values.iter().map(|&a| a/sum_of_fitness).collect::>(); diff --git a/src/selection/steady_state.rs b/src/selection/steady_state.rs index 67baffe..4569504 100644 --- a/src/selection/steady_state.rs +++ b/src/selection/steady_state.rs @@ -1,5 +1,24 @@ use std::cmp::{Ordering, min}; +/** +## Description +Steady State Selection is a sorting based selection method. It will always select the top `num_parents` in terms of fitness value from the pool of individuals. + +_Note: If `num_parents` is greater than length of `fitness_values` vector (n), then only indices from `0..n-1` will be returned._ + +## Return + +The return value is a `Vec` pointing to the selected indices. + +## Example +```rust + use genx::selection::steady_state_selection; + let num_parents:usize = 10; + let fitness_values = vec![10.0,0.2,9.0,4.8,7.7,8.4,3.2,9.4,9.0,11.0,4.5]; + + let result = steady_state_selection(&fitness_values, num_parents); +``` +*/ pub fn steady_state_selection(fitness_values: &Vec, num_parents: usize) -> Vec { let mut fitness_values_with_index : Vec<(f32, usize)> = Vec::new(); let population_size = fitness_values.len(); diff --git a/src/selection/stochastic_universal.rs b/src/selection/stochastic_universal.rs index 79f0a55..21f5155 100644 --- a/src/selection/stochastic_universal.rs +++ b/src/selection/stochastic_universal.rs @@ -1,5 +1,28 @@ use rand::{rngs::StdRng, SeedableRng, Rng}; +/** +## Description +Stochastic Universal Selection/Sampling is also a proportionate selection method very much like roulette wheel selection. The major differences is that it selects the required number of individuals (`num_parents`) in a single spin of the wheel which allows for population diversity. + +To know more about stochastic universal sampling refer the [wikipedia page](https://en.wikipedia.org/wiki/Stochastic_universal_sampling) + +### Note + +- Individuals with same fitness value occupy same area on roulette wheel. +- The function can also take in an optional `seed` value of type `Option` for deterministic results. + +## Return + +The return value is a `Vec` pointing to the selected indices. + +## Example +```rust + use genx::selection::stochastic_universal_selection; + let num_parents:usize = 10; + let fitness_values = vec![10.0,0.2,9.0,4.8,7.7,8.4,3.2,9.4,9.0,11.0,4.5]; + let result = stochastic_universal_selection(&fitness_values, num_parents, None); +``` +*/ pub fn stochastic_universal_selection(fitness_values: &Vec, num_parents: usize, seed: Option) -> Vec { let sum_of_fitness = fitness_values.iter().sum::(); let mut fitness_scale:Vec = Vec::new(); diff --git a/src/selection/tournament.rs b/src/selection/tournament.rs index 56e054b..ea6c030 100644 --- a/src/selection/tournament.rs +++ b/src/selection/tournament.rs @@ -1,6 +1,28 @@ use rand::{seq::SliceRandom, rngs::StdRng, SeedableRng}; use std::cmp::{Ordering, min}; +/** +## Description +Tournament Selection is a randomized sorting based selection method. We conduct `num_parents` tournaments where we randomly select `tournament_size` individuals. Out of those selected for a particular tournament, the fittest one is added to the vector of selected indices. + +### Note: + +- If `tournament_size` is greater than length of `fitness_values` vector (n), then overall fittest individual gets selected from all the tournaments. +- The function can also take in an optional `seed` value of type `Option` for deterministic results. + +## Return + +The return value is a `Vec` pointing to the selected indices. + +## Example +```rust + use genx::selection::tournament_selection; + let num_parents:usize = 10; + let fitness_values = vec![10.0,0.2,9.0,4.8,7.7,8.4,3.2,9.4,9.0,11.0,4.5]; + + let result = tournament_selection(&fitness_values, num_parents, 4, None); +``` +*/ pub fn tournament_selection(fitness_values: &Vec, num_parents: usize, tournament_size: usize, seed: Option) -> Vec { let mut fitness_values_with_index : Vec<(f32, usize)> = Vec::new(); for (i, &value) in fitness_values.iter().enumerate() {