From 2081dd3e5f19171ca171a1457930845c8b04361b Mon Sep 17 00:00:00 2001 From: Brett Laptop Date: Mon, 15 Jul 2024 17:51:16 -0400 Subject: [PATCH] add new selection, think there is a bug in elitsm --- CMakeLists.txt | 2 +- include/blt/gp/config.h | 12 +++++-- include/blt/gp/program.h | 3 +- include/blt/gp/selection.h | 66 ++++++++++++++++++++++++++++++++++---- 4 files changed, 72 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ba76e40..a09a0bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(blt-gp VERSION 0.0.79) +project(blt-gp VERSION 0.0.80) include(CTest) diff --git a/include/blt/gp/config.h b/include/blt/gp/config.h index 46e9616..a1682a3 100644 --- a/include/blt/gp/config.h +++ b/include/blt/gp/config.h @@ -38,6 +38,8 @@ namespace blt::gp double crossover_chance = 0.8; // percent chance that we will do mutation double mutation_chance = 0.1; + // percent chance we will do reproduction (copy individual) + double reproduction_chance = 0; // everything else will just be selected blt::size_t elites = 0; @@ -51,8 +53,8 @@ namespace blt::gp blt::size_t threads = std::thread::hardware_concurrency(); // number of elements each thread should pull per execution. this is for granularity performance and can be optimized for better results! blt::size_t evaluation_size = 4; - - // default config (ramped half-and-half init) or for buildering + + // default config (ramped half-and-half init) or for buildering prog_config_t(); // default config with a user specified initializer @@ -143,6 +145,12 @@ namespace blt::gp evaluation_size = s; return *this; } + + prog_config_t& set_reproduction_chance(double chance) + { + reproduction_chance = chance; + return *this; + } }; } diff --git a/include/blt/gp/program.h b/include/blt/gp/program.h index 4ba88fa..96afd3c 100644 --- a/include/blt/gp/program.h +++ b/include/blt/gp/program.h @@ -262,7 +262,8 @@ namespace blt::gp mutation_selection.pre_process(*this, current_pop, current_stats); reproduction_selection.pre_process(*this, current_pop, current_stats); - func(get_selector_args(), std::forward(crossover_selection), std::forward(mutation_selection), + auto args = get_selector_args(); + func(args, std::forward(crossover_selection), std::forward(mutation_selection), std::forward(reproduction_selection)); } diff --git a/include/blt/gp/selection.h b/include/blt/gp/selection.h index ec3ce87..7e68f31 100644 --- a/include/blt/gp/selection.h +++ b/include/blt/gp/selection.h @@ -38,15 +38,9 @@ namespace blt::gp random_t& random; }; - template - constexpr inline auto default_next_pop_creator = []( - selector_args&& args, Crossover&& crossover_selection, Mutation&& mutation_selection, Reproduction&& reproduction_selection) { + constexpr inline auto perform_elitism = [](const selector_args& args) { auto& [program, next_pop, current_pop, current_stats, config, random] = args; - double total_prob = config.mutation_chance + config.crossover_chance; - double crossover_chance = config.crossover_chance / total_prob; - double mutation_chance = crossover_chance + config.mutation_chance / total_prob; - if (config.elites > 0) { std::vector> values; @@ -77,6 +71,18 @@ namespace blt::gp for (blt::size_t i = 0; i < config.elites; i++) next_pop.get_individuals().push_back(current_pop.get_individuals()[values[i].first]); } + }; + + template + constexpr inline auto default_next_pop_creator = []( + const selector_args& args, Crossover crossover_selection, Mutation mutation_selection, Reproduction reproduction_selection) { + auto& [program, next_pop, current_pop, current_stats, config, random] = args; + + double total_prob = config.mutation_chance + config.crossover_chance; + double crossover_chance = config.crossover_chance / total_prob; + double mutation_chance = crossover_chance + config.mutation_chance / total_prob; + + perform_elitism(args); while (next_pop.get_individuals().size() < config.population_size) { @@ -125,6 +131,52 @@ namespace blt::gp } }; + template + constexpr inline auto non_deterministic_next_pop_creator = []( + const blt::gp::selector_args& args, Crossover crossover_selection, Mutation mutation_selection, Reproduction reproduction_selection) { + auto& [program, next_pop, current_pop, current_stats, config, random] = args; + + perform_elitism(args); + + while (next_pop.get_individuals().size() < config.population_size) + { + // everyone gets a chance once per loop. + if (random.choice(config.crossover_chance)) + { + // crossover + auto& p1 = crossover_selection.select(program, current_pop, current_stats); + auto& p2 = crossover_selection.select(program, current_pop, current_stats); + + auto results = config.crossover.get().apply(program, p1, p2); + + // if crossover fails, we can check for mutation on these guys. otherwise straight copy them into the next pop + if (results) + { + next_pop.get_individuals().emplace_back(std::move(results->child1)); + // annoying check + if (next_pop.get_individuals().size() < config.population_size) + next_pop.get_individuals().emplace_back(std::move(results->child2)); + } + } + if (next_pop.get_individuals().size() >= config.population_size) + break; + if (random.choice(config.mutation_chance)) + { + // mutation + auto& p = mutation_selection.select(program, current_pop, current_stats); + next_pop.get_individuals().emplace_back(std::move(config.mutator.get().apply(program, p))); + } + if (next_pop.get_individuals().size() >= config.population_size) + break; + if (config.reproduction_chance > 0 && random.choice(config.reproduction_chance)) + { + // reproduction + auto& p = reproduction_selection.select(program, current_pop, current_stats); + next_pop.get_individuals().push_back(individual{p}); + } + } + }; + class selection_t { public: