diff --git a/.idea/editor.xml b/.idea/editor.xml index a164eb6..521c365 100644 --- a/.idea/editor.xml +++ b/.idea/editor.xml @@ -1,488 +1,249 @@ - \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 8bf9b9f..38dc036 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -1,6 +1,8 @@ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index dc58c56..bc1212e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,7 @@ macro(compile_options target_name) sanitizers(${target_name}) endmacro() -project(blt-gp VERSION 0.5.13) +project(blt-gp VERSION 0.5.14) include(CTest) diff --git a/include/blt/gp/transformers.h b/include/blt/gp/transformers.h index e7970f4..36109df 100644 --- a/include/blt/gp/transformers.h +++ b/include/blt/gp/transformers.h @@ -55,9 +55,6 @@ namespace blt::gp } } - /** - * Base class for crossover which performs basic subtree crossover on two random nodes in the parent tree - */ class crossover_t { public: @@ -67,12 +64,6 @@ namespace blt::gp operator_info_t& type_operator_info; }; - struct crossover_point_t - { - tree_t::subtree_point_t p1_crossover_point; - tree_t::subtree_point_t p2_crossover_point; - }; - struct config_t { // number of times crossover will try to pick a valid point in the tree. this is purely based on the return type of the operators @@ -93,8 +84,6 @@ namespace blt::gp bool traverse = false; }; - crossover_t() = default; - explicit crossover_t(const config_t& config): config(config) { } @@ -104,9 +93,36 @@ namespace blt::gp return config; } - std::optional get_crossover_point(const tree_t& c1, const tree_t& c2) const; + virtual ~crossover_t() = default; - std::optional get_crossover_point_traverse(const tree_t& c1, const tree_t& c2) const; + protected: + config_t config; + }; + + /** + * Base class for crossover which performs basic subtree crossover on two random nodes in the parent tree + */ + class subtree_crossover_t : public crossover_t + { + public: + struct crossover_point_t + { + tree_t::subtree_point_t p1_crossover_point; + tree_t::subtree_point_t p2_crossover_point; + }; + + + subtree_crossover_t(): crossover_t(config_t{}) + { + } + + explicit subtree_crossover_t(const config_t& config): crossover_t(config) + { + } + + [[nodiscard]] std::optional get_crossover_point(const tree_t& c1, const tree_t& c2) const; + + [[nodiscard]] std::optional get_crossover_point_traverse(const tree_t& c1, const tree_t& c2) const; /** * child1 and child2 are copies of the parents, the result of selecting a crossover point and performing standard subtree crossover. @@ -114,20 +130,21 @@ namespace blt::gp * @param program reference to the global program container responsible for managing these trees * @param p1 reference to the first parent * @param p2 reference to the second parent - * @return expected pair of child otherwise returns error enum + * @param c1 reference to output child 1 + * @param c2 reference to output child 2 + * @return true if function succeeded, otherwise false */ virtual bool apply(gp_program& program, const tree_t& p1, const tree_t& p2, tree_t& c1, tree_t& c2); // NOLINT - virtual ~crossover_t() = default; + ~subtree_crossover_t() override = default; protected: [[nodiscard]] std::optional get_point_traverse_retry(const tree_t& t, std::optional type) const; - - config_t config; }; class advanced_crossover_t : public crossover_t { + public: bool apply(gp_program& program, const tree_t& p1, const tree_t& p2, tree_t& c1, tree_t& c2) override; }; diff --git a/src/transformers.cpp b/src/transformers.cpp index b6f6b3b..98c5a1e 100644 --- a/src/transformers.cpp +++ b/src/transformers.cpp @@ -65,7 +65,7 @@ namespace blt::gp { } - bool crossover_t::apply(gp_program& program, const tree_t& p1, const tree_t& p2, tree_t& c1, tree_t& c2) // NOLINT + bool subtree_crossover_t::apply(gp_program& program, const tree_t& p1, const tree_t& p2, tree_t& c1, tree_t& c2) // NOLINT { if (p1.size() < config.min_tree_size || p2.size() < config.min_tree_size) return false; @@ -90,7 +90,7 @@ namespace blt::gp return true; } - std::optional crossover_t::get_crossover_point(const tree_t& c1, + std::optional subtree_crossover_t::get_crossover_point(const tree_t& c1, const tree_t& c2) const { const auto first = c1.select_subtree(config.terminal_chance); @@ -102,7 +102,7 @@ namespace blt::gp return {{first, *second}}; } - std::optional crossover_t::get_crossover_point_traverse(const tree_t& c1, + std::optional subtree_crossover_t::get_crossover_point_traverse(const tree_t& c1, const tree_t& c2) const { auto c1_point_o = get_point_traverse_retry(c1, {}); @@ -114,7 +114,7 @@ namespace blt::gp return {{*c1_point_o, *c2_point_o}}; } - std::optional crossover_t::get_point_traverse_retry(const tree_t& t, const std::optional type) const + std::optional subtree_crossover_t::get_point_traverse_retry(const tree_t& t, const std::optional type) const { if (type) return t.select_subtree_traverse(*type, config.max_crossover_tries, config.terminal_chance, config.depth_multiplier); @@ -131,24 +131,13 @@ namespace blt::gp { // single point crossover (only if operators at this point are "compatible") case 0: - case0: { - std::optional point; - - if (config.traverse) - point = get_crossover_point_traverse(p1, p2); - else - point = get_crossover_point(p1, p2); - - if (!point) - return false; // check if can work // otherwise goto case2 } // Mating crossover analogs to same species breeding. Only works if tree is mostly similar case 1: - case1: { // if fails got to case0 if (false) @@ -156,8 +145,7 @@ namespace blt::gp } // Subtree crossover, select random points inside trees and swap their subtrees case 2: - case2: - return crossover_t::apply(program, p1, p2, c1, c2); + return subtree_crossover_t{}.apply(program, p1, p2, c1, c2); default: #if BLT_DEBUG_LEVEL > 0 BLT_ABORT("This place should be unreachable!"); diff --git a/tests/serialization_test.cpp b/tests/serialization_test.cpp index 9965f25..a531730 100644 --- a/tests/serialization_test.cpp +++ b/tests/serialization_test.cpp @@ -145,7 +145,11 @@ int main() { std::ifstream stream{"serialization_test2.data", std::ios::binary}; blt::fs::fstream_reader_t reader{stream}; - test_program.load_state(reader); + if (auto err = test_program.load_state(reader)) + { + BLT_ERROR("Error: {}", blt::gp::errors::serialization::to_string(*err)); + BLT_ABORT("Expected program to succeeded without returning an error state!"); + } for (const auto [saved, loaded] : blt::zip(program.get_stats_histories(), test_program.get_stats_histories())) { @@ -156,11 +160,12 @@ int main() } } } - std::ifstream stream{"serialization_test2.data", std::ios::binary}; - blt::fs::fstream_reader_t reader{stream}; - if (auto err = bad_program.load_state(reader)) { - BLT_ERROR(blt::gp::errors::serialization::to_string(*err)); - BLT_ASSERT(false && "Expected program to throw an exception when parsing state data into an incompatible program!"); + std::ifstream stream{"serialization_test2.data", std::ios::binary}; + blt::fs::fstream_reader_t reader{stream}; + if (!bad_program.load_state(reader).has_value()) + { + BLT_ABORT("Expected program to throw an exception when parsing state data into an incompatible program!"); + } } }