everything broken but code broadly finished

dev
Brett 2025-05-21 21:57:07 -04:00
parent 3c467f2b52
commit b896d07109
7 changed files with 158 additions and 245 deletions

View File

@ -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)

View File

@ -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)
{

View File

@ -46,7 +46,7 @@ namespace blt::gp
void print_args(std::integer_sequence<u64, indices...>)
{
BLT_INFO("Arguments:");
(BLT_INFO("%ld: %s", indices, blt::type_string<Args>().c_str()), ...);
(BLT_INFO("{}: {}", indices, blt::type_string<Args>().c_str()), ...);
}
template <typename Func, u64... indices, typename... ExtraArgs>

View File

@ -397,7 +397,7 @@ namespace blt::gp
void modify_operator(size_t point, operator_id new_id, std::optional<type_id> 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;

View File

@ -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)

View File

@ -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<child_t> children_data_p1;
tracked_vector<child_t> children_data_p2;
hashmap_t<type_id, std::vector<size_t>> missing_p1_types;
hashmap_t<type_id, std::vector<size_t>> missing_p2_types;
hashset_t<size_t> correct_types;
hashset_t<size_t> p1_correct_types;
hashset_t<size_t> p2_correct_types;
std::vector<reorder_index_t> p1_reorder_types;
std::vector<reorder_index_t> p2_reorder_types;
std::vector<swap_index_t> swap_types;
std::vector<tree_t> 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<size_t> 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<size_t> 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))

View File

@ -21,6 +21,7 @@
#include <blt/logging/logging.h>
#include <blt/gp/program.h>
#include <stack>
#include <utility>
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<child_t>& 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<std::tuple<size_t, type_id, bool>> our_types;
std::vector<std::tuple<size_t, type_id, bool>> other_types;
@ -628,7 +644,12 @@ namespace blt::gp
std::vector<type_id> our_current_types;
std::vector<type_id> 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;
}
auto insert_index = storage.other_children.back().end;
for (auto& [index, type, consumed] : storage.our_types)
{
if (consumed)
continue;
if (index >= other_point_info.argument_types.size())
{
insert_index = other_tree.manipulate().easy_manipulator().insert_subtree(subtree_point_t{insert_index}, other_tree);
consumed = true;
}
}
BLT_DEBUG("Other (Swaps):");
print_child_vec(storage.other_children);
insert_index = storage.our_children.back().end;
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 (index >= point_info.argument_types.size())
if (type == other_point_info.argument_types[i])
{
insert_index = tree->manipulate().easy_manipulator().insert_subtree(subtree_point_t{insert_index}, *tree);
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 (size_t i = common_size; i < point_info.argument_types.size(); i++)
{
for (auto& [index, type, consumed] : storage.our_types)
{
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 (size_t i = common_size; i < other_point_info.argument_types.size(); i++)
{
for (auto& [index, type, consumed] : storage.other_types)
{
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<ptrdiff_t>(point)), other_tree, subtree_point_t{other_point},
other_tree.find_endpoint(static_cast<ptrdiff_t>(other_point)));
swap_operators(subtree_point_t{point}, other_tree, subtree_point_t{other_point});
}