diff --git a/CMakeLists.txt b/CMakeLists.txt index 5488db5..de611dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(blt-gp VERSION 0.0.53) +project(blt-gp VERSION 0.0.54) include(CTest) diff --git a/examples/gp_test_6.cpp b/examples/gp_test_6.cpp index e36a546..ea12c9d 100644 --- a/examples/gp_test_6.cpp +++ b/examples/gp_test_6.cpp @@ -114,7 +114,7 @@ int main() BLT_INFO("Mutation:"); for (auto& tree : pop.for_each_tree()) { - new_pop.get_individuals().emplace_back(mutator.apply(program, generator, tree)); + new_pop.get_individuals().emplace_back(mutator.apply(program, tree)); } BLT_INFO("Post-Mutation"); for (auto& tree : new_pop.for_each_tree()) diff --git a/examples/gp_test_7.cpp b/examples/gp_test_7.cpp index ed4a0aa..8ff3190 100644 --- a/examples/gp_test_7.cpp +++ b/examples/gp_test_7.cpp @@ -96,7 +96,7 @@ int main() BLT_INFO("Mutation:"); for (auto& tree : pop.for_each_tree()) { - new_pop.get_individuals().emplace_back(mutator.apply(program, generator, tree)); + new_pop.get_individuals().emplace_back(mutator.apply(program, tree)); } BLT_INFO("Post-Mutation"); for (auto& tree : new_pop.for_each_tree()) @@ -121,5 +121,12 @@ int main() } BLT_INFO("Equal values: %ld", eq); + blt::u32 seed = 691; + for (blt::size_t i = 0; i < 500; i++) + { + auto random = blt::random::pcg_int(seed); + BLT_INFO(random); + } + return 0; } \ No newline at end of file diff --git a/include/blt/gp/config.h b/include/blt/gp/config.h new file mode 100644 index 0000000..dda0683 --- /dev/null +++ b/include/blt/gp/config.h @@ -0,0 +1,128 @@ +#pragma once +/* + * Copyright (C) 2024 Brett Terpstra + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef BLT_GP_CONFIG_H +#define BLT_GP_CONFIG_H + +#include +#include +#include +#include + +namespace blt::gp +{ + struct prog_config_t + { + blt::size_t population_size = 500; + blt::size_t max_generations = 50; + blt::size_t initial_min_tree_size = 3; + blt::size_t initial_max_tree_size = 10; + + // percent chance that we will do crossover + double crossover_chance = 0.8; + // percent chance that we will do mutation + double mutation_chance = 0.1; + // everything else will just be selected + + blt::size_t elites = 0; + + bool try_mutation_on_crossover_failure = true; + + std::reference_wrapper mutator; + std::reference_wrapper crossover; + std::reference_wrapper pop_initializer; + + // default config (ramped half-and-half init) or for buildering + prog_config_t(); + + // default config with a user specified initializer + prog_config_t(const std::reference_wrapper& popInitializer); // NOLINT + + prog_config_t(size_t populationSize, const std::reference_wrapper& popInitializer); + + prog_config_t(size_t populationSize); // NOLINT + + prog_config_t& set_pop_size(blt::size_t pop) + { + population_size = pop; + return *this; + } + + prog_config_t& set_initial_min_tree_size(blt::size_t size) + { + initial_min_tree_size = size; + return *this; + } + + prog_config_t& set_initial_max_tree_size(blt::size_t size) + { + initial_max_tree_size = size; + return *this; + } + + prog_config_t& set_crossover(crossover_t& ref) + { + crossover = {ref}; + return *this; + } + + prog_config_t& set_mutation(mutation_t& ref) + { + mutator = {ref}; + return *this; + } + + prog_config_t& set_initializer(population_initializer_t& ref) + { + pop_initializer = ref; + return *this; + } + + prog_config_t& set_elite_count(blt::size_t new_elites) + { + elites = new_elites; + return *this; + } + + prog_config_t& set_crossover_chance(double new_crossover_chance) + { + crossover_chance = new_crossover_chance; + return *this; + } + + prog_config_t& set_mutation_chance(double new_mutation_chance) + { + mutation_chance = new_mutation_chance; + return *this; + } + + prog_config_t& set_max_generations(blt::size_t new_max_generations) + { + max_generations = new_max_generations; + return *this; + } + + prog_config_t& set_try_mutation_on_crossover_failure(bool new_try_mutation_on_crossover_failure) + { + try_mutation_on_crossover_failure = new_try_mutation_on_crossover_failure; + return *this; + } + }; +} + +#endif //BLT_GP_CONFIG_H diff --git a/include/blt/gp/program.h b/include/blt/gp/program.h index 4bce404..b3911f1 100644 --- a/include/blt/gp/program.h +++ b/include/blt/gp/program.h @@ -43,6 +43,8 @@ #include #include #include +#include +#include namespace blt::gp { @@ -228,104 +230,6 @@ namespace blt::gp class gp_program { public: - struct config_t - { - blt::size_t population_size = 500; - blt::size_t max_generations = 50; - blt::size_t initial_min_tree_size = 3; - blt::size_t initial_max_tree_size = 10; - - // percent chance that we will do crossover - double crossover_chance = 0.8; - // percent chance that we will do mutation - double mutation_chance = 0.1; - // everything else will just be selected - - blt::size_t elites = 0; - - bool try_mutation_on_crossover_failure = true; - - std::reference_wrapper mutator; - std::reference_wrapper crossover; - std::reference_wrapper pop_initializer; - - // default config (ramped half-and-half init) or for buildering - config_t(); - - // default config with a user specified initializer - config_t(const std::reference_wrapper& popInitializer); // NOLINT - - config_t(size_t populationSize, const std::reference_wrapper& popInitializer); - - config_t(size_t populationSize); // NOLINT - - config_t& set_pop_size(blt::size_t pop) - { - population_size = pop; - return *this; - } - - config_t& set_initial_min_tree_size(blt::size_t size) - { - initial_min_tree_size = size; - return *this; - } - - config_t& set_initial_max_tree_size(blt::size_t size) - { - initial_max_tree_size = size; - return *this; - } - - config_t& set_crossover(crossover_t& ref) - { - crossover = ref; - return *this; - } - - config_t& set_mutation(mutation_t& ref) - { - mutator = ref; - return *this; - } - - config_t& set_initializer(population_initializer_t& ref) - { - pop_initializer = ref; - return *this; - } - - config_t& set_elite_count(blt::size_t new_elites) - { - elites = new_elites; - return *this; - } - - config_t& set_crossover_chance(double new_crossover_chance) - { - crossover_chance = new_crossover_chance; - return *this; - } - - config_t& set_mutation_chance(double new_mutation_chance) - { - mutation_chance = new_mutation_chance; - return *this; - } - - config_t& set_max_generations(blt::size_t new_max_generations) - { - max_generations = new_max_generations; - return *this; - } - - config_t& set_try_mutation_on_crossover_failure(bool new_try_mutation_on_crossover_failure) - { - try_mutation_on_crossover_failure = new_try_mutation_on_crossover_failure; - return *this; - } - }; - /** * Note about context size: This is required as context is passed to every operator in the GP tree, this context will be provided by your * call to one of the evaluator functions. This was the nicest way to provide this as C++ lacks reflection @@ -338,7 +242,7 @@ namespace blt::gp system(system), engine(engine) {} - explicit gp_program(type_provider& system, std::mt19937_64 engine, config_t config): + explicit gp_program(type_provider& system, std::mt19937_64 engine, prog_config_t config): system(system), engine(engine), config(config) {} @@ -358,7 +262,7 @@ namespace blt::gp mutation_selection.pre_process(*this, current_pop, current_stats); reproduction_selection.pre_process(*this, current_pop, current_stats); - for (blt::size_t i = 0; i < config.population_size; i++) + while(next_pop.get_individuals().size() < config.population_size) { auto type = dist(get_random()); if (type > crossover_chance && type < mutation_chance) @@ -373,17 +277,23 @@ namespace blt::gp if (results) { next_pop.get_individuals().emplace_back(std::move(results->child1)); - next_pop.get_individuals().emplace_back(std::move(results->child2)); + // annoying check + if (next_pop.get_individuals().size() < config.population_size) + next_pop.get_individuals().emplace_back(std::move(results->child2)); } else { if (config.try_mutation_on_crossover_failure && choice(config.mutation_chance)) next_pop.get_individuals().emplace_back(std::move(config.mutator.get().apply(*this, p1))); else next_pop.get_individuals().push_back(p1); - if (config.try_mutation_on_crossover_failure && choice(config.mutation_chance)) - next_pop.get_individuals().emplace_back(std::move(config.mutator.get().apply(*this, p2))); - else - next_pop.get_individuals().push_back(p2); + // annoying check. + if (next_pop.get_individuals().size() < config.population_size) + { + if (config.try_mutation_on_crossover_failure && choice(config.mutation_chance)) + next_pop.get_individuals().emplace_back(std::move(config.mutator.get().apply(*this, p2))); + else + next_pop.get_individuals().push_back(p2); + } } } else if (type > mutation_chance) { @@ -550,7 +460,7 @@ namespace blt::gp blt::size_t current_generation = 0; std::mt19937_64 engine; - config_t config; + prog_config_t config; }; } diff --git a/include/blt/gp/random.h b/include/blt/gp/random.h new file mode 100644 index 0000000..436d50f --- /dev/null +++ b/include/blt/gp/random.h @@ -0,0 +1,46 @@ +#pragma once +/* + * Copyright (C) 2024 Brett Terpstra + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef BLT_GP_RANDOM_H +#define BLT_GP_RANDOM_H + +#include +#include + +namespace blt::gp +{ + + class random_t + { + public: + explicit random_t(blt::size_t seed): seed(seed) + {} + + void set_seed(blt::size_t s) + { + seed = s; + } + + + private: + blt::size_t seed; + }; + +} + +#endif //BLT_GP_RANDOM_H diff --git a/include/blt/gp/selection.h b/include/blt/gp/selection.h index 9c513bf..09490b8 100644 --- a/include/blt/gp/selection.h +++ b/include/blt/gp/selection.h @@ -21,11 +21,79 @@ #include #include +#include +#include #include namespace blt::gp { + struct selector_args + { + gp_program& program; + population_t& next_pop; + population_t& current_pop; + population_stats& current_stats; + prog_config_t& config; + random_t& random; + }; + +// template +// constexpr inline auto default_next_pop_selector = []( +// 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; +// +// while (next_pop.get_individuals().size() < config.population_size) +// { +// auto type = random.; +// if (type > crossover_chance && type < mutation_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)); +// } else +// { +// if (config.try_mutation_on_crossover_failure && program.choice(config.mutation_chance)) +// next_pop.get_individuals().emplace_back(std::move(config.mutator.get().apply(program, p1))); +// else +// next_pop.get_individuals().push_back(p1); +// // annoying check. +// if (next_pop.get_individuals().size() < config.population_size) +// { +// if (config.try_mutation_on_crossover_failure && choice(config.mutation_chance)) +// next_pop.get_individuals().emplace_back(std::move(config.mutator.get().apply(program, p2))); +// else +// next_pop.get_individuals().push_back(p2); +// } +// } +// } else if (type > 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))); +// } else +// { +// // reproduction +// auto& p = reproduction_selection.select(program, current_pop, current_stats); +// next_pop.get_individuals().push_back(p); +// } +// } +// }; + class selection_t { public: diff --git a/lib/blt b/lib/blt index 5e65416..c88f1c3 160000 --- a/lib/blt +++ b/lib/blt @@ -1 +1 @@ -Subproject commit 5e654166848d9a4bb06f93332e5997fc16d9388e +Subproject commit c88f1c3e382d9da9068cdd9ff87af6fef0ed9bd0 diff --git a/src/program.cpp b/src/program.cpp index ffe718e..3321c34 100644 --- a/src/program.cpp +++ b/src/program.cpp @@ -27,20 +27,20 @@ namespace blt::gp static crossover_t s_crossover; static ramped_half_initializer_t s_init; - gp_program::config_t::config_t(): mutator(s_mutator), crossover(s_crossover), pop_initializer(s_init) + prog_config_t::prog_config_t(): mutator(s_mutator), crossover(s_crossover), pop_initializer(s_init) { } - gp_program::config_t::config_t(const std::reference_wrapper& popInitializer): + prog_config_t::prog_config_t(const std::reference_wrapper& popInitializer): mutator(s_mutator), crossover(s_crossover), pop_initializer(popInitializer) {} - gp_program::config_t::config_t(size_t populationSize, const std::reference_wrapper& popInitializer): - population_size(populationSize), mutator(s_mutator), crossover(s_crossover), pop_initializer(s_init) + prog_config_t::prog_config_t(size_t populationSize, const std::reference_wrapper& popInitializer): + population_size(populationSize), mutator(s_mutator), crossover(s_crossover), pop_initializer(popInitializer) {} - gp_program::config_t::config_t(size_t populationSize): + prog_config_t::prog_config_t(size_t populationSize): population_size(populationSize), mutator(s_mutator), crossover(s_crossover), pop_initializer(s_init) {} } \ No newline at end of file