diff --git a/CMakeLists.txt b/CMakeLists.txt index b00a43b..87ea059 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(blt-gp VERSION 0.0.51) +project(blt-gp VERSION 0.0.52) include(CTest) diff --git a/examples/gp_test_5.cpp b/examples/gp_test_5.cpp index 3bc1ac5..809504d 100644 --- a/examples/gp_test_5.cpp +++ b/examples/gp_test_5.cpp @@ -108,13 +108,13 @@ int main() blt::gp::crossover_t crossover; - auto& ind = pop.getIndividuals(); + auto& ind = pop.get_individuals(); std::vector pre; std::vector pos; blt::size_t errors = 0; BLT_INFO("Pre-Crossover:"); - for (auto& tree : pop.getIndividuals()) + for (auto& tree : pop.get_individuals()) { auto f = tree.tree.get_evaluation_value(nullptr); pre.push_back(f); @@ -123,10 +123,10 @@ int main() BLT_INFO("Crossover:"); blt::gp::population_t new_pop; - while (new_pop.getIndividuals().size() < pop.getIndividuals().size()) + while (new_pop.get_individuals().size() < pop.get_individuals().size()) { auto& random = program.get_random(); - std::uniform_int_distribution dist(0ul, pop.getIndividuals().size() - 1); + std::uniform_int_distribution dist(0ul, pop.get_individuals().size() - 1); blt::size_t first = dist(random); blt::size_t second; do @@ -149,8 +149,8 @@ int main() // results->child1.print(program, std::cout, print_literals, pretty_print, print_returns); // BLT_TRACE("Child 2: %f", results->child2.get_evaluation_value(nullptr)); // results->child2.print(program, std::cout, print_literals, pretty_print, print_returns); - new_pop.getIndividuals().push_back({std::move(results->child1)}); - new_pop.getIndividuals().push_back({std::move(results->child2)}); + new_pop.get_individuals().push_back({std::move(results->child1)}); + new_pop.get_individuals().push_back({std::move(results->child2)}); } else { switch (results.error()) @@ -163,8 +163,8 @@ int main() break; } errors++; - new_pop.getIndividuals().push_back(ind[first]); - new_pop.getIndividuals().push_back(ind[second]); + new_pop.get_individuals().push_back(ind[first]); + new_pop.get_individuals().push_back(ind[second]); } } diff --git a/examples/gp_test_6.cpp b/examples/gp_test_6.cpp index 903ac6f..8a4419b 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.getIndividuals().push_back({mutator.apply(program, generator, tree)}); + new_pop.get_individuals().push_back({mutator.apply(program, generator, 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 6778d4d..3251075 100644 --- a/examples/gp_test_7.cpp +++ b/examples/gp_test_7.cpp @@ -23,7 +23,6 @@ static constexpr long SEED = 41912; - blt::gp::type_provider type_system; blt::gp::gp_program program(type_system, std::mt19937_64{SEED}); // NOLINT @@ -97,7 +96,7 @@ int main() BLT_INFO("Mutation:"); for (auto& tree : pop.for_each_tree()) { - new_pop.getIndividuals().push_back({mutator.apply(program, generator, tree)}); + new_pop.get_individuals().push_back({mutator.apply(program, generator, tree)}); } BLT_INFO("Post-Mutation"); for (auto& tree : new_pop.for_each_tree()) diff --git a/include/blt/gp/program.h b/include/blt/gp/program.h index b4ecc2b..df7ac63 100644 --- a/include/blt/gp/program.h +++ b/include/blt/gp/program.h @@ -319,10 +319,10 @@ namespace blt::gp template void evaluate_fitness(Lambda&& fitness_function, Container& result_storage) { - for (const auto& ind : blt::enumerate(current_pop.getIndividuals())) + for (const auto& ind : blt::enumerate(current_pop.get_individuals())) ind.second.raw_fitness = static_cast(fitness_function(ind.second.tree, result_storage, ind.first)); double min = 0; - for (auto& ind : current_pop.getIndividuals()) + for (auto& ind : current_pop.get_individuals()) { if (ind.raw_fitness < min) min = ind.raw_fitness; @@ -335,10 +335,10 @@ namespace blt::gp individual* worst = nullptr; auto diff = -min; - for (auto& ind : current_pop.getIndividuals()) + for (auto& ind : current_pop.get_individuals()) { - ind.standardized_fitness = ind.raw_fitness + diff; - ind.adjusted_fitness = 1.0 / (1.0 + ind.standardized_fitness); + auto standardized_fitness = ind.raw_fitness + diff; + ind.adjusted_fitness = 1.0 / (1.0 + standardized_fitness); if (ind.adjusted_fitness > worst_fitness) { diff --git a/include/blt/gp/selection.h b/include/blt/gp/selection.h index 1df6232..9c513bf 100644 --- a/include/blt/gp/selection.h +++ b/include/blt/gp/selection.h @@ -37,6 +37,9 @@ namespace blt::gp */ virtual tree_t& select(gp_program& program, population_t& pop, population_stats& stats) = 0; + virtual void pre_process(gp_program&, population_t&, population_stats&) + {} + virtual ~selection_t() = default; }; @@ -76,6 +79,8 @@ namespace blt::gp class select_fitness_proportionate_t : public selection_t { public: + void pre_process(gp_program& program, population_t& pop, population_stats& stats) final; + tree_t& select(gp_program& program, population_t& pop, population_stats& stats) final; }; diff --git a/include/blt/gp/tree.h b/include/blt/gp/tree.h index df70464..f0208b0 100644 --- a/include/blt/gp/tree.h +++ b/include/blt/gp/tree.h @@ -114,8 +114,8 @@ namespace blt::gp { tree_t tree; double raw_fitness = 0; - double standardized_fitness = 0; double adjusted_fitness = 0; + double probability = 0; }; struct population_stats @@ -184,7 +184,7 @@ namespace blt::gp blt::size_t pos; }; - std::vector& getIndividuals() + std::vector& get_individuals() { return individuals; } @@ -193,6 +193,26 @@ namespace blt::gp { return population_tree_iterator{individuals, 0}; } + + auto begin() + { + return individuals.begin(); + } + + auto end() + { + return individuals.end(); + } + + [[nodiscard]] auto begin() const + { + return individuals.begin(); + } + + [[nodiscard]] auto end() const + { + return individuals.end(); + } private: std::vector individuals; diff --git a/src/generators.cpp b/src/generators.cpp index 343d3b1..fdd5e40 100644 --- a/src/generators.cpp +++ b/src/generators.cpp @@ -120,7 +120,7 @@ namespace blt::gp population_t pop; for (auto i = 0ul; i < args.size; i++) - pop.getIndividuals().push_back({grow.generate(args.to_gen_args())}); + pop.get_individuals().push_back({grow.generate(args.to_gen_args())}); return pop; } @@ -130,7 +130,7 @@ namespace blt::gp population_t pop; for (auto i = 0ul; i < args.size; i++) - pop.getIndividuals().push_back({full.generate(args.to_gen_args())}); + pop.get_individuals().push_back({full.generate(args.to_gen_args())}); return pop; } @@ -142,9 +142,9 @@ namespace blt::gp for (auto i = 0ul; i < args.size; i++) { if (args.program.choice()) - pop.getIndividuals().push_back({full.generate(args.to_gen_args())}); + pop.get_individuals().push_back({full.generate(args.to_gen_args())}); else - pop.getIndividuals().push_back({grow.generate(args.to_gen_args())}); + pop.get_individuals().push_back({grow.generate(args.to_gen_args())}); } return pop; @@ -162,21 +162,21 @@ namespace blt::gp for (auto i = 0ul; i < per_step; i++) { if (args.program.choice()) - pop.getIndividuals().push_back({full.generate({args.program, args.root_type, args.min_depth, depth})}); + pop.get_individuals().push_back({full.generate({args.program, args.root_type, args.min_depth, depth})}); else - pop.getIndividuals().push_back({grow.generate({args.program, args.root_type, args.min_depth, depth})}); + pop.get_individuals().push_back({grow.generate({args.program, args.root_type, args.min_depth, depth})}); } } for (auto i = 0ul; i < remainder; i++) { if (args.program.choice()) - pop.getIndividuals().push_back({full.generate(args.to_gen_args())}); + pop.get_individuals().push_back({full.generate(args.to_gen_args())}); else - pop.getIndividuals().push_back({grow.generate(args.to_gen_args())}); + pop.get_individuals().push_back({grow.generate(args.to_gen_args())}); } - blt_assert(pop.getIndividuals().size() == args.size); + blt_assert(pop.get_individuals().size() == args.size); return pop; } diff --git a/src/selection.cpp b/src/selection.cpp index 3e26c6b..a33e691 100644 --- a/src/selection.cpp +++ b/src/selection.cpp @@ -21,12 +21,12 @@ namespace blt::gp { - tree_t& select_best_t::select(gp_program&, population_t& pop, population_stats& stats) + tree_t& select_best_t::select(gp_program&, population_t& pop, population_stats&) { - auto& first = pop.getIndividuals()[0]; + auto& first = pop.get_individuals()[0]; double best_fitness = first.adjusted_fitness; tree_t* tree = &first.tree; - for (auto& ind : pop.getIndividuals()) + for (auto& ind : pop.get_individuals()) { if (ind.adjusted_fitness < best_fitness) { @@ -37,12 +37,12 @@ namespace blt::gp return *tree; } - tree_t& select_worst_t::select(gp_program&, population_t& pop, population_stats& stats) + tree_t& select_worst_t::select(gp_program&, population_t& pop, population_stats&) { - auto& first = pop.getIndividuals()[0]; + auto& first = pop.get_individuals()[0]; double worst_fitness = first.adjusted_fitness; tree_t* tree = &first.tree; - for (auto& ind : pop.getIndividuals()) + for (auto& ind : pop.get_individuals()) { if (ind.adjusted_fitness > worst_fitness) { @@ -53,23 +53,23 @@ namespace blt::gp return *tree; } - tree_t& select_random_t::select(gp_program& program, population_t& pop, population_stats& stats) + tree_t& select_random_t::select(gp_program& program, population_t& pop, population_stats&) { // TODO: use a more generic randomness solution. - std::uniform_int_distribution dist(0ul, pop.getIndividuals().size()); - return pop.getIndividuals()[dist(program.get_random())].tree; + std::uniform_int_distribution dist(0ul, pop.get_individuals().size()); + return pop.get_individuals()[dist(program.get_random())].tree; } - tree_t& select_tournament_t::select(gp_program& program, population_t& pop, population_stats& stats) + tree_t& select_tournament_t::select(gp_program& program, population_t& pop, population_stats&) { - std::uniform_int_distribution dist(0ul, pop.getIndividuals().size()); + std::uniform_int_distribution dist(0ul, pop.get_individuals().size()); - auto& first = pop.getIndividuals()[dist(program.get_random())]; + auto& first = pop.get_individuals()[dist(program.get_random())]; individual* ind = &first; double best_guy = first.adjusted_fitness; for (blt::size_t i = 0; i < selection_size - 1; i++) { - auto& sel = pop.getIndividuals()[dist(program.get_random())]; + auto& sel = pop.get_individuals()[dist(program.get_random())]; if (sel.adjusted_fitness < best_guy) { best_guy = sel.adjusted_fitness; @@ -80,9 +80,29 @@ namespace blt::gp return ind->tree; } - // https://www.google.com/search?client=firefox-b-d&sca_esv=71668abf73626b35&sca_upv=1&biw=1916&bih=940&sxsrf=ADLYWIJehgPtkALJDoTgHCiO4GNeQppSeA:1720490607140&q=roulette+wheel+selection+pseudocode&uds=ADvngMgiq8uozSRb4WPAa_ESRaBJz-G_Xhk1OLU3QFjqc3o31P4ECuIkKJxHd-cR3WUe9U7VQGpI6NRaMgYiWTMd4wNofAAaNq6X4eHYpN8cR9HmTfTw0KgYC6gI4dgu-s-5mXivdsv4QxrkVAL7yMoXacJngsiMBg&udm=2&sa=X&ved=2ahUKEwig7Oj77piHAxU3D1kFHS1lAIsQxKsJegQIDBAB&ictx=0#vhid=6iCOymnPvtyy-M&vssid=mosaic tree_t& select_fitness_proportionate_t::select(gp_program& program, population_t& pop, population_stats& stats) { + static std::uniform_real_distribution dist(0.0, 1.0); + auto choice = dist(program.get_random()); + for (const auto& ind : blt::enumerate(pop)) + { + if (ind.first == pop.get_individuals().size()-1) + return ind.second.tree; + if (choice > ind.second.probability && pop.get_individuals()[ind.first+1].probability < choice) + return ind.second.tree; + } + BLT_WARN("Unable to find individual with fitness proportionate. This should not be a possible code path!"); + return pop.get_individuals()[0].tree; + //BLT_ABORT("Unable to find individual"); + } + void select_fitness_proportionate_t::pre_process(gp_program&, population_t& pop, population_stats& stats) + { + double sum_of_prob = 0; + for (auto& ind : pop) + { + ind.probability = sum_of_prob + (ind.adjusted_fitness / stats.overall_fitness); + sum_of_prob += ind.probability; + } } } \ No newline at end of file