diff --git a/CMakeLists.txt b/CMakeLists.txt index 2948b17..2419d09 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.33) +project(blt-gp VERSION 0.5.34) include(CTest) diff --git a/examples/src/rice_classification.cpp b/examples/src/rice_classification.cpp index b31325d..5bae2c1 100644 --- a/examples/src/rice_classification.cpp +++ b/examples/src/rice_classification.cpp @@ -40,7 +40,7 @@ blt::gp::prog_config_t config = blt::gp::prog_config_t() .set_reproduction_chance(0) .set_max_generations(50) .set_pop_size(500) - .set_thread_count(0); + .set_thread_count(1); int main(int argc, const char** argv) { diff --git a/include/blt/gp/operations.h b/include/blt/gp/operations.h index 1fe2e69..ec6fb34 100644 --- a/include/blt/gp/operations.h +++ b/include/blt/gp/operations.h @@ -46,7 +46,7 @@ namespace blt::gp void print_args(std::integer_sequence) { BLT_INFO("Arguments:"); - (BLT_INFO("%ld: %s", indices, blt::type_string().c_str()), ...); + (BLT_INFO("{}: {}", indices, blt::type_string().c_str()), ...); } template diff --git a/include/blt/gp/tree.h b/include/blt/gp/tree.h index 45ccbfd..12e1510 100644 --- a/include/blt/gp/tree.h +++ b/include/blt/gp/tree.h @@ -397,7 +397,7 @@ namespace blt::gp void modify_operator(size_t point, operator_id new_id, std::optional return_type = {}) const; - void swap_operators(subtree_point_t point, ptrdiff_t extent, tree_t& other_tree, subtree_point_t other_point, ptrdiff_t other_extent) const; + void swap_operators(subtree_point_t point, tree_t& other_tree, subtree_point_t other_point) const; void swap_operators(size_t point, tree_t& other_tree, size_t other_point) const; diff --git a/src/program.cpp b/src/program.cpp index c3bcf88..747e792 100644 --- a/src/program.cpp +++ b/src/program.cpp @@ -31,8 +31,8 @@ namespace blt::gp // this is largely to not break the tests :3 // it's also to allow for quick setup of a gp program if you don't care how crossover or mutation is handled static advanced_mutation_t s_mutator; - static subtree_crossover_t s_crossover; - // static one_point_crossover_t s_crossover; + // static subtree_crossover_t s_crossover; + static one_point_crossover_t s_crossover; static ramped_half_initializer_t s_init; prog_config_t::prog_config_t(): mutator(s_mutator), crossover(s_crossover), pop_initializer(s_init) diff --git a/src/transformers.cpp b/src/transformers.cpp index 28ff584..25e3087 100644 --- a/src/transformers.cpp +++ b/src/transformers.cpp @@ -143,225 +143,7 @@ namespace blt::gp return false; } - const auto& p1_operator = p1.get_operator(point1.get_point()); - const auto& p2_operator = p2.get_operator(point2.get_point()); - - const auto& p1_info = program.get_operator_info(p1_operator.id()); - const auto& p2_info = program.get_operator_info(p2_operator.id()); - - struct reorder_index_t - { - size_t index1; - size_t index2; - }; - - struct swap_index_t - { - size_t p1_index; - size_t p2_index; - }; - - thread_local struct type_resolver_t - { - tracked_vector children_data_p1; - tracked_vector children_data_p2; - hashmap_t> missing_p1_types; - hashmap_t> missing_p2_types; - hashset_t correct_types; - hashset_t p1_correct_types; - hashset_t p2_correct_types; - std::vector p1_reorder_types; - std::vector p2_reorder_types; - std::vector swap_types; - std::vector temp_trees; - - void print_missing_types() - { - for (const auto& [id, v] : missing_p1_types) - { - if (!v.empty()) - { - BLT_INFO("(P1) For type {} missing indexes:", id); - for (const auto idx : v) - BLT_INFO("\t{}", idx); - BLT_INFO("----"); - } - } - for (const auto& [id, v] : missing_p2_types) - { - if (!v.empty()) - { - BLT_INFO("(P2) For type {} missing indexes:", id); - for (const auto idx : v) - BLT_INFO("\t{}", idx); - BLT_INFO("----"); - } - } - } - - std::optional get_p1_index(const type_id& id) - { - if (!missing_p1_types.contains(id)) - return {}; - if (missing_p1_types[id].empty()) - return {}; - auto idx = missing_p1_types[id].back(); - missing_p1_types[id].pop_back(); - return idx; - } - - std::optional get_p2_index(const type_id& id) - { - if (!missing_p2_types.contains(id)) - return {}; - if (missing_p2_types[id].empty()) - return {}; - auto idx = missing_p2_types[id].back(); - missing_p2_types[id].pop_back(); - return idx; - } - - [[nodiscard]] bool handled_p1(const size_t index) const - { - return correct_types.contains(index) || p1_correct_types.contains(index); - } - - [[nodiscard]] bool handled_p2(const size_t index) const - { - return correct_types.contains(index) || p2_correct_types.contains(index); - } - - void clear(gp_program& program) - { - children_data_p1.clear(); - children_data_p2.clear(); - correct_types.clear(); - p1_correct_types.clear(); - p2_correct_types.clear(); - p1_reorder_types.clear(); - p2_reorder_types.clear(); - swap_types.clear(); - for (auto& tree : temp_trees) - tree.clear(program); - for (auto& [id, v] : missing_p1_types) - v.clear(); - for (auto& [id, v] : missing_p2_types) - v.clear(); - } - } resolver; - resolver.clear(program); - - auto min_size = std::min(p1_info.argument_types.size(), p2_info.argument_types.size()); - - // resolve type information - for (size_t i = 0; i < min_size; i++) - { - if (p1_info.argument_types[i] != p2_info.argument_types[i]) - { - resolver.missing_p1_types[p1_info.argument_types[i].id].push_back(i); - resolver.missing_p2_types[p2_info.argument_types[i].id].push_back(i); - } else - resolver.correct_types.insert(i); - } - - for (size_t i = min_size; i < p1_info.argument_types.size(); i++) - resolver.missing_p1_types[p1_info.argument_types[i].id].push_back(i); - - for (size_t i = min_size; i < p2_info.argument_types.size(); i++) - resolver.missing_p2_types[p2_info.argument_types[i].id].push_back(i); - - // if swaping p1 -> p2 and p2 -> p1, we may already have the types we need just in a different order - - // first, make a list of types which can simply be reordered - for (size_t i = 0; i < p1_info.argument_types.size(); i++) - { - if (resolver.correct_types.contains(i)) - continue; - if (auto index = resolver.get_p2_index(p1_info.argument_types[i].id)) - { - resolver.p2_reorder_types.push_back({i, *index}); - resolver.p2_correct_types.insert(i); - } - } - - BLT_DEBUG("Operator C1 {} expects types: ", p1_operator.id()); - for (const auto [i, type] : enumerate(p1_info.argument_types)) - BLT_TRACE("{} -> {}", i, type); - BLT_DEBUG("Operator C2 {} expects types: ", p2_operator.id()); - for (const auto [i, type] : enumerate(p2_info.argument_types)) - BLT_TRACE("{} -> {}", i, type); - resolver.print_missing_types(); - - for (size_t i = 0; i < p2_info.argument_types.size(); i++) - { - if (resolver.correct_types.contains(i)) - continue; - if (auto index = resolver.get_p1_index(p2_info.argument_types[i].id)) - { - resolver.p1_reorder_types.push_back({i, *index}); - resolver.p1_correct_types.insert(i); - } - } - - // next we need to figure out which types need to be swapped - for (size_t i = 0; i < p1_info.argument_types.size(); i++) - { - if (resolver.handled_p2(i)) - continue; - if (auto index = resolver.get_p1_index(p1_info.argument_types[i].id)) - resolver.swap_types.push_back({*index, i}); - } - - for (size_t i = 0; i < p2_info.argument_types.size(); i++) - { - if (resolver.handled_p1(i)) - continue; - if (auto index = resolver.get_p2_index(p2_info.argument_types[i].id)) - resolver.swap_types.push_back({i, *index}); - } - - // now we do the swap - p1.find_child_extends(resolver.children_data_p1, point1.get_point(), p1_info.argument_types.size()); - p2.find_child_extends(resolver.children_data_p2, point2.get_point(), p2_info.argument_types.size()); - - for (const auto& [index1, index2] : resolver.p1_reorder_types) - { - BLT_DEBUG("Reordering in C1: {} -> {}", index1, index2); - c1.manipulate().easy_manipulator().swap_subtrees(resolver.children_data_p1[index1], c1, resolver.children_data_p1[index2]); - } - - for (const auto& [index1, index2] : resolver.p2_reorder_types) - { - BLT_DEBUG("Reordering in C2: {} -> {}", index1, index2); - c2.manipulate().easy_manipulator().swap_subtrees(resolver.children_data_p2[index1], c2, resolver.children_data_p2[index2]); - } - - auto c1_insert = resolver.children_data_p1.back().end; - auto c2_insert = resolver.children_data_p2.back().end; - - for (const auto& [p1_index, p2_index] : resolver.swap_types) - { - if (p1_index < p1_info.argument_types.size() && p2_index < p2_info.argument_types.size()) - c1.manipulate().easy_manipulator().swap_subtrees(resolver.children_data_p1[p1_index], c2, resolver.children_data_p2[p2_index]); - else if (p1_index < p1_info.argument_types.size() && p2_index >= p2_info.argument_types.size()) - { - BLT_TRACE("(P1 IS UNDER!) Trying to swap P1 {} for P2 {} (Sizes: P1: {} P2: {})", p1_index, p2_index, p1_info.argument_types.size(), p2_info.argument_types.size()); - BLT_TRACE("Inserting into P2 from P1!"); - c1.manipulate().easy_manipulator().copy_subtree(resolver.children_data_p1[p1_index], resolver.temp_trees[0]); - c1.manipulate().easy_manipulator().delete_subtree(resolver.children_data_p1[p1_index]); - c2_insert = c2.manipulate().easy_manipulator().insert_subtree(subtree_point_t{c1_insert}, resolver.temp_trees[0]); - } else if (p2_index < p2_info.argument_types.size() && p1_index >= p1_info.argument_types.size()) - { - BLT_TRACE("(P2 IS UNDER!) Trying to swap P1 {} for P2 {} (Sizes: P1: {} P2: {})", p1_index, p2_index, p1_info.argument_types.size(), p2_info.argument_types.size()); - } else - { - BLT_WARN("This should be an impossible state!"); - } - } - - - c1.manipulate().easy_manipulator().modify_operator(point1.get_point(), p2_operator.id(), p2_info.return_type); - c2.manipulate().easy_manipulator().modify_operator(point2.get_point(), p1_operator.id(), p1_info.return_type); + c1.manipulate().easy_manipulator().swap_operators(point1, c2, point2); #if BLT_DEBUG_LEVEL >= 2 if (!c1.check(detail::debug::context_ptr) || !c2.check(detail::debug::context_ptr)) diff --git a/src/tree.cpp b/src/tree.cpp index b5914ce..7ccf34c 100644 --- a/src/tree.cpp +++ b/src/tree.cpp @@ -21,6 +21,7 @@ #include #include #include +#include namespace blt::gp { @@ -36,6 +37,18 @@ namespace blt::gp } }; + void print_child(child_t child) + { + BLT_TRACE("\tChild: {} -> {} ( {} )", child.start, child.end, child.size()); + } + + void print_child_vec(const std::vector& children) + { + BLT_TRACE("Children: "); + for (const auto& c : children) + print_child(c); + } + std::ostream& create_indent(std::ostream& out, blt::size_t amount, bool pretty_print) { if (!pretty_print) @@ -346,6 +359,8 @@ namespace blt::gp tree->values.copy_to(ptr, size_total); tree->values.pop_bytes(size_total); + BLT_TRACE("Removing Bytes! {} = First {} + Between {} + Last {} + After {}", size_total, size_first, size_between, size_last, size_after); + tree->values.copy_from(ptr + size_first + size_between, size_last); tree->values.copy_from(ptr + size_first, size_between); tree->values.copy_from(ptr, size_first); @@ -597,8 +612,7 @@ namespace blt::gp } } - void slow_tree_manipulator_t::swap_operators(const subtree_point_t point, const ptrdiff_t extent, tree_t& other_tree, const subtree_point_t other_point, - const ptrdiff_t other_extent) const + void slow_tree_manipulator_t::swap_operators(const subtree_point_t point, tree_t& other_tree, const subtree_point_t other_point) const { const auto point_info = tree->m_program->get_operator_info(tree->operations[point.get_spoint()].id()); const auto other_point_info = tree->m_program->get_operator_info(other_tree.operations[other_point.get_spoint()].id()); @@ -612,10 +626,12 @@ namespace blt::gp // const auto our_bytes_after = calculate_ephemeral_size(our_end_point_iter, tree->operations.end()); // const auto other_bytes_after = calculate_ephemeral_size(other_end_point_iter, other_tree.operations.end()); - thread_local struct + thread_local struct storage_t { - buffer_wrapper_t our_after; - buffer_wrapper_t other_after; + tree_t temp; + + buffer_wrapper_t our_after{}; + buffer_wrapper_t other_after{}; // argument index -> type_id for missing types std::vector> our_types; std::vector> other_types; @@ -628,7 +644,12 @@ namespace blt::gp std::vector our_current_types; std::vector other_current_types; - } storage; + + explicit storage_t(tree_t tree): temp(std::move(tree)) + { + } + } storage{tree_t{*tree->m_program}}; + storage.temp.clear(*tree->m_program); storage.our_types.clear(); storage.other_types.clear(); storage.our_swaps.clear(); @@ -657,11 +678,19 @@ namespace blt::gp storage.our_types.emplace_back(i, point_info.argument_types[i], false); storage.other_types.emplace_back(i, other_point_info.argument_types[i], false); } + storage.our_current_types.emplace_back(point_info.argument_types[i]); + storage.other_current_types.emplace_back(other_point_info.argument_types[i]); } for (size_t i = common_size; i < point_info.argument_types.size(); ++i) + { storage.our_types.emplace_back(i, point_info.argument_types[i], false); + storage.our_current_types.emplace_back(point_info.argument_types[i]); + } for (size_t i = common_size; i < other_point_info.argument_types.size(); ++i) + { storage.other_types.emplace_back(i, other_point_info.argument_types[i], false); + storage.other_current_types.emplace_back(other_point_info.argument_types[i]); + } for (const auto& [index, type, _] : storage.our_types) { @@ -689,6 +718,8 @@ namespace blt::gp } tree->find_child_extends(storage.our_children, point.get_point(), point_info.argc.argc); + BLT_DEBUG("Ours:"); + print_child_vec(storage.our_children); // apply the swaps for (const auto& [i, j] : storage.our_swaps) @@ -698,9 +729,18 @@ namespace blt::gp const auto s2 = storage.our_children[j].size(); storage.our_children[i].end = storage.our_children[i].start + s1; storage.our_children[j].end = storage.our_children[j].start + s2; + + const auto tmp = storage.our_current_types[i]; + storage.our_current_types[i] = storage.our_current_types[j]; + storage.our_current_types[j] = tmp; } + BLT_DEBUG("Ours (Swaps):"); + print_child_vec(storage.our_children); other_tree.find_child_extends(storage.other_children, other_point.get_point(), other_point_info.argc.argc); + BLT_DEBUG("-----------"); + BLT_DEBUG("Other:"); + print_child_vec(storage.other_children); for (const auto& [i, j] : storage.other_swaps) { @@ -709,37 +749,128 @@ namespace blt::gp const auto s2 = storage.other_children[j].size(); storage.other_children[i].end = storage.other_children[i].start + s1; storage.other_children[j].end = storage.other_children[j].start + s2; + + const auto tmp = storage.other_current_types[i]; + storage.other_current_types[i] = storage.other_current_types[j]; + storage.other_current_types[j] = tmp; + } + + BLT_DEBUG("Other (Swaps):"); + print_child_vec(storage.other_children); + + BLT_INFO("-----------"); + + for (size_t i = 0; i < common_size; i++) + { + if (storage.our_current_types[i] != other_point_info.argument_types[i]) + { + for (auto& [index, type, consumed] : storage.other_types) + { + if (consumed) + continue; + if (type == other_point_info.argument_types[i]) + { + consumed = true; + const auto s1 = storage.other_children[index].size(); + const auto s2 = storage.our_children[i].size(); + swap_subtrees(storage.our_children[i], other_tree, storage.other_children[index]); + storage.our_children[i].end = storage.our_children[i].start + s1; + storage.other_children[index].end = storage.other_children[index].start + s2; + goto b1; + } + } +#if BLT_DEBUG_LEVEL >= 1 + BLT_ERROR("Unable to find type for position {}, expected type {} but found type {}. ", i, other_point_info.argument_types[i], storage.our_current_types[i]); + BLT_ABORT("Failure state in swap operators!"); +#endif + b1:{} + } + if (storage.other_current_types[i] != point_info.argument_types[i]) + { + for (auto& [index, type, consumed] : storage.our_types) + { + if (consumed) + continue; + if (type == point_info.argument_types[i]) + { + consumed = true; + const auto s1 = storage.our_children[index].size(); + const auto s2 = storage.other_children[i].size(); + other_tree.manipulate().easy_manipulator().swap_subtrees(storage.other_children[i], *tree, storage.our_children[index]); + storage.other_children[i].end = storage.other_children[i].start + s1; + storage.our_children[index].end = storage.our_children[index].start + s2; + goto b2; + } + } +#if BLT_DEBUG_LEVEL >= 1 + BLT_WARN("Unable to find type for position {}, expected type {} but found type {}. ", i, point_info.argument_types[i], storage.other_current_types[i]); + BLT_ABORT("Failure state in swap operators!"); +#endif + b2:{} + } } auto insert_index = storage.other_children.back().end; - for (auto& [index, type, consumed] : storage.our_types) + for (size_t i = common_size; i < point_info.argument_types.size(); i++) { - if (consumed) - continue; - if (index >= other_point_info.argument_types.size()) + for (auto& [index, type, consumed] : storage.our_types) { - insert_index = other_tree.manipulate().easy_manipulator().insert_subtree(subtree_point_t{insert_index}, other_tree); - consumed = true; + if (consumed) + continue; + if (index != i) + continue; + if (type == point_info.argument_types[i]) + { + storage.temp.clear(*tree->m_program); + copy_subtree(storage.our_children[i], storage.temp); + insert_index = other_tree.manipulate().easy_manipulator().insert_subtree(subtree_point_t{insert_index}, storage.temp); + consumed = true; + goto b3; + } } +#if BLT_DEBUG_LEVEL >= 1 + BLT_WARN("Unable to find type for position {}, expected type {}", i, point_info.argument_types[i]); + BLT_ABORT("Failure state in swap operators!"); +#endif + b3:{} } insert_index = storage.our_children.back().end; - for (auto& [index, type, consumed] : storage.other_types) + for (size_t i = common_size; i < other_point_info.argument_types.size(); i++) { - if (consumed) - continue; - if (index >= point_info.argument_types.size()) + for (auto& [index, type, consumed] : storage.other_types) { - insert_index = tree->manipulate().easy_manipulator().insert_subtree(subtree_point_t{insert_index}, *tree); - consumed = true; + if (consumed) + continue; + if (index != i) + continue; + if (type == other_point_info.argument_types[i]) + { + storage.temp.clear(*tree->m_program); + other_tree.manipulate().easy_manipulator().copy_subtree(storage.other_children[i], storage.temp); + insert_index = insert_subtree(subtree_point_t{insert_index}, storage.temp); + consumed = true; + goto b4; + } } + b4:{} } + +#if BLT_DEBUG_LEVEL >= 1 + for (const auto& [index, type, consumed] : storage.our_types) + BLT_ASSERT_MSG(consumed, ("Expected type to be consumed, was not at index " + std::to_string(index)).c_str()); + for (const auto& [index, type, consumed] : storage.other_types) + BLT_ASSERT_MSG(consumed, ("Expected type to be consumed, was not at index " + std::to_string(index)).c_str()); +#endif + + auto op = tree->operations[point.get_spoint()]; + tree->operations[point.get_spoint()] = other_tree.operations[other_point.get_spoint()]; + other_tree.operations[other_point.get_spoint()] = op; } void slow_tree_manipulator_t::swap_operators(const size_t point, tree_t& other_tree, const size_t other_point) const { - swap_operators(subtree_point_t{point}, tree->find_endpoint(static_cast(point)), other_tree, subtree_point_t{other_point}, - other_tree.find_endpoint(static_cast(other_point))); + swap_operators(subtree_point_t{point}, other_tree, subtree_point_t{other_point}); }