diff --git a/CMakeLists.txt b/CMakeLists.txt index 85b171b..caf7db2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(blt-gp VERSION 0.0.45) +project(blt-gp VERSION 0.0.46) include(CTest) diff --git a/include/blt/gp/program.h b/include/blt/gp/program.h index 5bba73f..14ac2e7 100644 --- a/include/blt/gp/program.h +++ b/include/blt/gp/program.h @@ -50,6 +50,14 @@ namespace blt::gp blt::u32 argc_context = 0; }; + 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 + blt::u16 max_crossover_tries = 5; + // if we fail to find a point in the tree, should we search forward from the last point to the end of the operators? + bool should_crossover_try_forward = false; + }; + struct operator_info { std::vector argument_types; @@ -286,10 +294,16 @@ namespace blt::gp { storage = std::move(op); } + + [[nodiscard]] inline const config_t& get_config() const + { + return config; + } private: type_provider& system; blt::gp::stack_allocator alloc; + config_t config; operator_storage storage; diff --git a/include/blt/gp/tree.h b/include/blt/gp/tree.h index 00cdc46..003616e 100644 --- a/include/blt/gp/tree.h +++ b/include/blt/gp/tree.h @@ -32,12 +32,13 @@ namespace blt::gp struct op_container_t { - op_container_t(detail::callable_t& func, detail::transfer_t& transfer, bool is_value): - func(func), transfer(transfer), is_value(is_value) + op_container_t(detail::callable_t& func, detail::transfer_t& transfer, operator_id id, bool is_value): + func(func), transfer(transfer), id(id), is_value(is_value) {} detail::callable_t& func; detail::transfer_t& transfer; + operator_id id; bool is_value; }; diff --git a/src/generators.cpp b/src/generators.cpp index d66f59d..31cfda2 100644 --- a/src/generators.cpp +++ b/src/generators.cpp @@ -65,6 +65,7 @@ namespace blt::gp tree.get_operations().emplace_back( info.function, info.transfer, + top.id, args.program.is_static(top.id)); max_depth = std::max(max_depth, top.depth); diff --git a/src/transformers.cpp b/src/transformers.cpp index 0c53011..90a8c7c 100644 --- a/src/transformers.cpp +++ b/src/transformers.cpp @@ -16,14 +16,60 @@ * along with this program. If not, see . */ #include +#include +#include namespace blt::gp { blt::expected crossover_t::apply(gp_program& program, const tree_t& p1, const tree_t& p2) // NOLINT { + const auto& config = program.get_config(); result_t result{p1, p2}; + auto& c1 = result.child1; + auto& c2 = result.child2; + auto& c1_ops = c1.get_operations(); + auto& c2_ops = c2.get_operations(); + + std::uniform_int_distribution op_sel1(1ul, c1_ops.size() - 1); + std::uniform_int_distribution op_sel2(1ul, c2_ops.size() - 1); + + blt::size_t crossover_point = op_sel1(program.get_random()); + blt::size_t attempted_point = 0; + + const auto& crossover_point_type = program.get_operator_info(c1_ops[crossover_point].id); + operator_info* attempted_point_type = nullptr; + + blt::size_t counter = 0; + do + { + if (counter >= config.max_crossover_tries) + { + if (config.should_crossover_try_forward) + { + for (auto i = attempted_point + 1; i < c2_ops.size(); i++) + { + auto* info = &program.get_operator_info(c2_ops[i].id); + if (info->return_type == crossover_point_type.return_type) + { + attempted_point = i; + attempted_point_type = info; + break; + } + } + } + // should we try again over the whole tree? probably not. + return blt::unexpected(error_t::NO_VALID_TYPE); + } else + { + attempted_point = op_sel2(program.get_random()); + attempted_point_type = &program.get_operator_info(c2_ops[attempted_point].id); + counter++; + } + } while (crossover_point_type.return_type != attempted_point_type->return_type); + + const auto& found_point_type = *attempted_point_type; return result; } diff --git a/src/tree.cpp b/src/tree.cpp index 6982c9e..3a0c690 100644 --- a/src/tree.cpp +++ b/src/tree.cpp @@ -44,7 +44,7 @@ namespace blt::gp continue; } operation.func(context, values_process, value_stack); - operations_stack.emplace_back(empty_callable, operation.transfer, true); + operations_stack.emplace_back(empty_callable, operation.transfer, operation.id, true); } return results;