uwu silly little crossover

main
Brett 2024-09-11 13:10:44 -04:00
parent 15ccd0b615
commit b4b7e8f454
3 changed files with 90 additions and 71 deletions

View File

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.25) cmake_minimum_required(VERSION 3.25)
project(blt-gp VERSION 0.1.55) project(blt-gp VERSION 0.1.56)
include(CTest) include(CTest)

View File

@ -60,8 +60,8 @@ namespace blt::gp
public: public:
struct point_info_t struct point_info_t
{ {
type_id return_type; blt::ptrdiff_t point;
operator_info_t* type_operator_info; operator_info_t& type_operator_info;
}; };
struct crossover_point_t struct crossover_point_t
{ {
@ -72,7 +72,8 @@ namespace blt::gp
{ {
// 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 // 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; blt::u16 max_crossover_tries = 5;
blt::f32 traverse_chance = 0.75; blt::f32 traverse_chance = 0.5;
blt::u32 min_tree_size = 5;
// legacy settings: // legacy settings:

View File

@ -61,17 +61,31 @@ 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 crossover_t::apply(gp_program& program, const tree_t& p1, const tree_t& p2, tree_t& c1, tree_t& c2) // NOLINT
{ {
if (p1.get_operations().size() < config.min_tree_size || p2.get_operations().size() < config.min_tree_size)
return false;
auto& c1_ops = c1.get_operations(); auto& c1_ops = c1.get_operations();
auto& c2_ops = c2.get_operations(); auto& c2_ops = c2.get_operations();
if (c1_ops.size() < 5 || c2_ops.size() < 5)
return false;
auto point = get_crossover_point(program, p1, p2); auto point = get_crossover_point(program, p1, p2);
if (!point) if (!point)
return false; return false;
auto selection = program.get_random().get_u32(0, 2);
// Used to make copies of operators. Statically stored for memory caching purposes.
// Thread local as this function cannot have external modifications / will be called from multiple threads.
static thread_local tracked_vector<op_container_t> c1_operators;
static thread_local tracked_vector<op_container_t> c2_operators;
c1_operators.clear();
c2_operators.clear();
switch (selection)
{
case 0:
{
// basic crossover
auto crossover_point_begin_itr = c1_ops.begin() + point->p1_crossover_point; auto crossover_point_begin_itr = c1_ops.begin() + point->p1_crossover_point;
auto crossover_point_end_itr = c1_ops.begin() + c1.find_endpoint(program, point->p1_crossover_point); auto crossover_point_end_itr = c1_ops.begin() + c1.find_endpoint(program, point->p1_crossover_point);
@ -81,13 +95,6 @@ namespace blt::gp
stack_allocator& c1_stack = c1.get_values(); stack_allocator& c1_stack = c1.get_values();
stack_allocator& c2_stack = c2.get_values(); stack_allocator& c2_stack = c2.get_values();
// we have to make a copy because we will modify the underlying storage.
static thread_local tracked_vector<op_container_t> c1_operators;
static thread_local tracked_vector<op_container_t> c2_operators;
c1_operators.clear();
c2_operators.clear();
for (const auto& op : blt::iterate(crossover_point_begin_itr, crossover_point_end_itr)) for (const auto& op : blt::iterate(crossover_point_begin_itr, crossover_point_end_itr))
c1_operators.push_back(op); c1_operators.push_back(op);
for (const auto& op : blt::iterate(found_point_begin_itr, found_point_end_itr)) for (const auto& op : blt::iterate(found_point_begin_itr, found_point_end_itr))
@ -127,17 +134,30 @@ namespace blt::gp
c1_ops.insert(++insert_point_c1, c2_operators.begin(), c2_operators.end()); c1_ops.insert(++insert_point_c1, c2_operators.begin(), c2_operators.end());
c2_ops.insert(++insert_point_c2, c1_operators.begin(), c1_operators.end()); c2_ops.insert(++insert_point_c2, c1_operators.begin(), c1_operators.end());
}
break;
case 1: {
}
break;
default:
#if BLT_DEBUG_LEVEL > 0
BLT_ABORT("This place should be unreachable!");
#else
BLT_UNREACHABLE;
#endif
}
#if BLT_DEBUG_LEVEL >= 2 #if BLT_DEBUG_LEVEL >= 2
blt::size_t c1_found_bytes = c1.get_values().size().total_used_bytes; blt::size_t c1_found_bytes = c1.get_values().size().total_used_bytes;
blt::size_t c2_found_bytes = c2.get_values().size().total_used_bytes; blt::size_t c2_found_bytes = c2.get_values().size().total_used_bytes;
blt::size_t c1_expected_bytes = std::accumulate(result.child1.get_operations().begin(), result.child1.get_operations().end(), 0ul, blt::size_t c1_expected_bytes = std::accumulate(c1.get_operations().begin(), c1.get_operations().end(), 0ul,
[](const auto& v1, const auto& v2) { [](const auto& v1, const auto& v2) {
if (v2.is_value) if (v2.is_value)
return v1 + stack_allocator::aligned_size(v2.type_size); return v1 + stack_allocator::aligned_size(v2.type_size);
return v1; return v1;
}); });
blt::size_t c2_expected_bytes = std::accumulate(result.child2.get_operations().begin(), result.child2.get_operations().end(), 0ul, blt::size_t c2_expected_bytes = std::accumulate(c2.get_operations().begin(), c2.get_operations().end(), 0ul,
[](const auto& v1, const auto& v2) { [](const auto& v1, const auto& v2) {
if (v2.is_value) if (v2.is_value)
return v1 + stack_allocator::aligned_size(v2.type_size); return v1 + stack_allocator::aligned_size(v2.type_size);
@ -214,24 +234,22 @@ namespace blt::gp
std::optional<crossover_t::crossover_point_t> crossover_t::get_crossover_point_traverse(gp_program& program, const tree_t& c1, std::optional<crossover_t::crossover_point_t> crossover_t::get_crossover_point_traverse(gp_program& program, const tree_t& c1,
const tree_t& c2) const const tree_t& c2) const
{ {
crossover_t::point_info_t c1_point{}; auto c1_point_o = get_point_traverse_retry(program, c1, {});
crossover_t::point_info_t c2_point{}; if (!c1_point_o)
return {};
if (auto point = get_point_traverse_retry(program, c1, {})) auto c2_point_o = get_point_traverse_retry(program, c2, c1_point_o->type_operator_info.return_type);
c1_point = *point; if (!c2_point_o)
else
return {}; return {};
return {{c1_point_o->point, c2_point_o->point}};
return {};
} }
std::optional<crossover_t::point_info_t> crossover_t::random_place_of_type(gp_program& program, const tree_t& t, type_id type) std::optional<crossover_t::point_info_t> crossover_t::random_place_of_type(gp_program& program, const tree_t& t, type_id type)
{ {
auto attempted_point = program.get_random().get_size_t(1ul, t.get_operations().size()); auto attempted_point = program.get_random().get_i64(1ul, t.get_operations().size());
auto& attempted_point_type = program.get_operator_info(t.get_operations()[attempted_point].id); auto& attempted_point_type = program.get_operator_info(t.get_operations()[attempted_point].id);
if (type == attempted_point_type.return_type) if (type == attempted_point_type.return_type)
return {{attempted_point, &attempted_point_type}}; return {{attempted_point, attempted_point_type}};
return {}; return {};
} }
@ -247,7 +265,7 @@ namespace blt::gp
{ {
if (type && *type != current_op_type.return_type) if (type && *type != current_op_type.return_type)
return {}; return {};
return {{point, &current_op_type}}; return {{point, current_op_type}};
} }
// traverse to a child // traverse to a child
if (random.choice(config.traverse_chance)) if (random.choice(config.traverse_chance))
@ -259,12 +277,12 @@ namespace blt::gp
point += 1; point += 1;
// loop through all the children we wish to skip. The result will be the first node of the next child, becoming the new parent // loop through all the children we wish to skip. The result will be the first node of the next child, becoming the new parent
for (blt::size_t i = 0; i < argument; i++) for (blt::size_t i = 0; i < argument; i++)
point += t.find_endpoint(program, point); point = t.find_endpoint(program, point);
continue; continue;
} }
if (!type || (type && *type == current_op_type.return_type)) if (!type || (type && *type == current_op_type.return_type))
return {{point, &current_op_type}}; return {{point, current_op_type}};
} }
} }