genx 0.4.0

An easy to use and modular genetic algorithms library.
Documentation
use std::fmt::{Debug, Display};

use genx::{
    crossover::uniform_crossover,
    mutation::inversion_mutation,
    selection::{random_selection, stochastic_universal_selection},
};

use rand::{distributions::Uniform, prelude::Distribution};

struct Item {
    weight: u32,
    value: u32,
}

impl Debug for Item {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "Item {{ weight: {}, value: {} }}",
            self.weight, self.value
        )
    }
}

impl Display for Item {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "Item {{ weight: {}, value: {} }}",
            self.weight, self.value
        )
    }
}

fn main() {
    let knapsack_threshold = 80;
    let knapsack_items = vec![
        Item {
            weight: 9,
            value: 150,
        },
        Item {
            weight: 13,
            value: 35,
        },
        Item {
            weight: 15,
            value: 10,
        },
        Item {
            weight: 50,
            value: 60,
        },
        Item {
            weight: 15,
            value: 60,
        },
        Item {
            weight: 68,
            value: 45,
        },
        Item {
            weight: 27,
            value: 60,
        },
        Item {
            weight: 39,
            value: 40,
        },
        Item {
            weight: 23,
            value: 30,
        },
        Item {
            weight: 52,
            value: 10,
        },
        Item {
            weight: 11,
            value: 70,
        },
        Item {
            weight: 32,
            value: 30,
        },
        Item {
            weight: 24,
            value: 15,
        },
    ];
    let knapsack_size = knapsack_items.len();

    let iterations = 300;
    let population_size = 20;
    let mutation_probability = 0.4;
    let mut population = (0..population_size)
        .into_iter()
        .map(|_| (0..knapsack_size).map(|_| rand::random::<bool>()).collect())
        .collect::<Vec<Vec<bool>>>();

    let fitness_function = |individual: &Vec<bool>| -> f32 {
        let mut total_weight = 0;
        let mut total_value = 0;
        for (item, &is_included) in knapsack_items.iter().zip(individual.iter()) {
            if is_included {
                total_weight += item.weight;
                total_value += item.value;
            }
        }
        if total_weight > knapsack_threshold {
            total_value = 0;
        }
        1.0 + total_value as f32
    };

    let get_fitness_values = |population: &Vec<Vec<bool>>| {
        population
            .iter()
            .map(|x| fitness_function(x))
            .collect::<Vec<f32>>()
    };

    let best_fitness_value = |population: &Vec<Vec<bool>>| {
        population
            .iter()
            .max_by(|&a, &b| {
                fitness_function(a)
                    .partial_cmp(&fitness_function(b))
                    .unwrap_or(std::cmp::Ordering::Equal)
            })
            .unwrap()
            .clone()
    };

    let mut best_now = best_fitness_value(&population);
    let between = Uniform::from(0.0..1.0);
    let mut prng = rand::thread_rng();
    for _ in 0..iterations {
        let idxs = random_selection(population_size, 2, None);
        let (mut child1, mut child2) =
            uniform_crossover(&population[idxs[0]], &population[idxs[1]], 0.5, None);

        if between.sample(&mut prng) < mutation_probability {
            inversion_mutation(&mut child1, None);
        }
        if between.sample(&mut prng) < mutation_probability {
            inversion_mutation(&mut child2, None);
        }
        population.push(child1);
        population.push(child2);

        let fitness_values = get_fitness_values(&population);
        let selected_idx = stochastic_universal_selection(&fitness_values, population_size, None);
        population = selected_idx
            .iter()
            .map(|&a| population[a].clone())
            .collect::<Vec<Vec<bool>>>();
        let best = best_fitness_value(&population);
        if fitness_function(&best) > fitness_function(&best_now) {
            best_now = best;
        }
    }

    println!("{}", fitness_function(&best_now));
    println!("{:?}", best_now.iter().zip(knapsack_items).filter(|predicate| *predicate.0).map(|x| x.1).collect::<Vec<Item>>());
}