uwu silly little crossover
parent
15ccd0b615
commit
b4b7e8f454
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
@ -61,83 +61,103 @@ 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 crossover_point_begin_itr = c1_ops.begin() + point->p1_crossover_point;
|
auto selection = program.get_random().get_u32(0, 2);
|
||||||
auto crossover_point_end_itr = c1_ops.begin() + c1.find_endpoint(program, point->p1_crossover_point);
|
|
||||||
|
|
||||||
auto found_point_begin_itr = c2_ops.begin() + point->p2_crossover_point;
|
// Used to make copies of operators. Statically stored for memory caching purposes.
|
||||||
auto found_point_end_itr = c2_ops.begin() + c2.find_endpoint(program, point->p2_crossover_point);
|
// Thread local as this function cannot have external modifications / will be called from multiple threads.
|
||||||
|
|
||||||
stack_allocator& c1_stack = c1.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> c1_operators;
|
||||||
static thread_local tracked_vector<op_container_t> c2_operators;
|
static thread_local tracked_vector<op_container_t> c2_operators;
|
||||||
|
|
||||||
c1_operators.clear();
|
c1_operators.clear();
|
||||||
c2_operators.clear();
|
c2_operators.clear();
|
||||||
|
|
||||||
for (const auto& op : blt::iterate(crossover_point_begin_itr, crossover_point_end_itr))
|
switch (selection)
|
||||||
c1_operators.push_back(op);
|
{
|
||||||
for (const auto& op : blt::iterate(found_point_begin_itr, found_point_end_itr))
|
case 0:
|
||||||
c2_operators.push_back(op);
|
{
|
||||||
|
// basic crossover
|
||||||
|
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);
|
||||||
|
|
||||||
blt::size_t c1_stack_after_bytes = accumulate_type_sizes(crossover_point_end_itr, c1_ops.end());
|
auto found_point_begin_itr = c2_ops.begin() + point->p2_crossover_point;
|
||||||
blt::size_t c1_stack_for_bytes = accumulate_type_sizes(crossover_point_begin_itr, crossover_point_end_itr);
|
auto found_point_end_itr = c2_ops.begin() + c2.find_endpoint(program, point->p2_crossover_point);
|
||||||
blt::size_t c2_stack_after_bytes = accumulate_type_sizes(found_point_end_itr, c2_ops.end());
|
|
||||||
blt::size_t c2_stack_for_bytes = accumulate_type_sizes(found_point_begin_itr, found_point_end_itr);
|
|
||||||
auto c1_total = static_cast<blt::ptrdiff_t>(c1_stack_after_bytes + c1_stack_for_bytes);
|
|
||||||
auto c2_total = static_cast<blt::ptrdiff_t>(c2_stack_after_bytes + c2_stack_for_bytes);
|
|
||||||
auto copy_ptr_c1 = get_thread_pointer_for_size<struct c1_t>(c1_total);
|
|
||||||
auto copy_ptr_c2 = get_thread_pointer_for_size<struct c2_t>(c2_total);
|
|
||||||
|
|
||||||
c1_stack.reserve(c1_stack.bytes_in_head() - c1_stack_for_bytes + c2_stack_for_bytes);
|
stack_allocator& c1_stack = c1.get_values();
|
||||||
c2_stack.reserve(c2_stack.bytes_in_head() - c2_stack_for_bytes + c1_stack_for_bytes);
|
stack_allocator& c2_stack = c2.get_values();
|
||||||
|
|
||||||
c1_stack.copy_to(copy_ptr_c1, c1_total);
|
for (const auto& op : blt::iterate(crossover_point_begin_itr, crossover_point_end_itr))
|
||||||
c1_stack.pop_bytes(c1_total);
|
c1_operators.push_back(op);
|
||||||
|
for (const auto& op : blt::iterate(found_point_begin_itr, found_point_end_itr))
|
||||||
|
c2_operators.push_back(op);
|
||||||
|
|
||||||
c2_stack.copy_to(copy_ptr_c2, c2_total);
|
blt::size_t c1_stack_after_bytes = accumulate_type_sizes(crossover_point_end_itr, c1_ops.end());
|
||||||
c2_stack.pop_bytes(c2_total);
|
blt::size_t c1_stack_for_bytes = accumulate_type_sizes(crossover_point_begin_itr, crossover_point_end_itr);
|
||||||
|
blt::size_t c2_stack_after_bytes = accumulate_type_sizes(found_point_end_itr, c2_ops.end());
|
||||||
|
blt::size_t c2_stack_for_bytes = accumulate_type_sizes(found_point_begin_itr, found_point_end_itr);
|
||||||
|
auto c1_total = static_cast<blt::ptrdiff_t>(c1_stack_after_bytes + c1_stack_for_bytes);
|
||||||
|
auto c2_total = static_cast<blt::ptrdiff_t>(c2_stack_after_bytes + c2_stack_for_bytes);
|
||||||
|
auto copy_ptr_c1 = get_thread_pointer_for_size<struct c1_t>(c1_total);
|
||||||
|
auto copy_ptr_c2 = get_thread_pointer_for_size<struct c2_t>(c2_total);
|
||||||
|
|
||||||
c2_stack.copy_from(copy_ptr_c1, c1_stack_for_bytes);
|
c1_stack.reserve(c1_stack.bytes_in_head() - c1_stack_for_bytes + c2_stack_for_bytes);
|
||||||
c2_stack.copy_from(copy_ptr_c2 + c2_stack_for_bytes, c2_stack_after_bytes);
|
c2_stack.reserve(c2_stack.bytes_in_head() - c2_stack_for_bytes + c1_stack_for_bytes);
|
||||||
|
|
||||||
c1_stack.copy_from(copy_ptr_c2, c2_stack_for_bytes);
|
c1_stack.copy_to(copy_ptr_c1, c1_total);
|
||||||
c1_stack.copy_from(copy_ptr_c1 + c1_stack_for_bytes, c1_stack_after_bytes);
|
c1_stack.pop_bytes(c1_total);
|
||||||
|
|
||||||
// now swap the operators
|
c2_stack.copy_to(copy_ptr_c2, c2_total);
|
||||||
auto insert_point_c1 = crossover_point_begin_itr - 1;
|
c2_stack.pop_bytes(c2_total);
|
||||||
auto insert_point_c2 = found_point_begin_itr - 1;
|
|
||||||
|
|
||||||
// invalidates [begin, end()) so the insert points should be fine
|
c2_stack.copy_from(copy_ptr_c1, c1_stack_for_bytes);
|
||||||
c1_ops.erase(crossover_point_begin_itr, crossover_point_end_itr);
|
c2_stack.copy_from(copy_ptr_c2 + c2_stack_for_bytes, c2_stack_after_bytes);
|
||||||
c2_ops.erase(found_point_begin_itr, found_point_end_itr);
|
|
||||||
|
|
||||||
c1_ops.insert(++insert_point_c1, c2_operators.begin(), c2_operators.end());
|
c1_stack.copy_from(copy_ptr_c2, c2_stack_for_bytes);
|
||||||
c2_ops.insert(++insert_point_c2, c1_operators.begin(), c1_operators.end());
|
c1_stack.copy_from(copy_ptr_c1 + c1_stack_for_bytes, c1_stack_after_bytes);
|
||||||
|
|
||||||
|
// now swap the operators
|
||||||
|
auto insert_point_c1 = crossover_point_begin_itr - 1;
|
||||||
|
auto insert_point_c2 = found_point_begin_itr - 1;
|
||||||
|
|
||||||
|
// invalidates [begin, end()) so the insert points should be fine
|
||||||
|
c1_ops.erase(crossover_point_begin_itr, crossover_point_end_itr);
|
||||||
|
c2_ops.erase(found_point_begin_itr, found_point_end_itr);
|
||||||
|
|
||||||
|
c1_ops.insert(++insert_point_c1, c2_operators.begin(), c2_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, ¤t_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, ¤t_op_type}};
|
return {{point, current_op_type}};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue