cleanup crossover function
parent
5d72923998
commit
fc7cd292af
|
@ -1,5 +1,5 @@
|
||||||
cmake_minimum_required(VERSION 3.25)
|
cmake_minimum_required(VERSION 3.25)
|
||||||
project(blt-gp VERSION 0.0.90)
|
project(blt-gp VERSION 0.0.91)
|
||||||
|
|
||||||
include(CTest)
|
include(CTest)
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ namespace blt::gp
|
||||||
class crossover_t
|
class crossover_t
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
using op_iter = std::vector<blt::gp::op_container_t>::iterator;
|
||||||
enum class error_t
|
enum class error_t
|
||||||
{
|
{
|
||||||
NO_VALID_TYPE,
|
NO_VALID_TYPE,
|
||||||
|
@ -41,6 +42,11 @@ namespace blt::gp
|
||||||
tree_t child1;
|
tree_t child1;
|
||||||
tree_t child2;
|
tree_t child2;
|
||||||
};
|
};
|
||||||
|
struct crossover_point_t
|
||||||
|
{
|
||||||
|
blt::ptrdiff_t p1_crossover_point;
|
||||||
|
blt::ptrdiff_t p2_crossover_point;
|
||||||
|
};
|
||||||
struct config_t
|
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
|
// 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
|
||||||
|
@ -56,6 +62,14 @@ namespace blt::gp
|
||||||
explicit crossover_t(const config_t& config): config(config)
|
explicit crossover_t(const config_t& config): config(config)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
blt::expected<crossover_t::crossover_point_t, error_t> get_crossover_point(gp_program& program, const tree_t& c1, const tree_t& c2) const;
|
||||||
|
|
||||||
|
static blt::ptrdiff_t find_endpoint(blt::gp::gp_program& program, const std::vector<blt::gp::op_container_t>& container,
|
||||||
|
blt::ptrdiff_t start);
|
||||||
|
|
||||||
|
static void transfer_forward(blt::gp::stack_allocator& from, blt::gp::stack_allocator& to, op_iter begin, op_iter end);
|
||||||
|
static void transfer_backward(blt::gp::stack_allocator& from, blt::gp::stack_allocator& to, op_iter begin, op_iter end);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* child1 and child2 are copies of the parents, the result of selecting a crossover point and performing standard subtree crossover.
|
* child1 and child2 are copies of the parents, the result of selecting a crossover point and performing standard subtree crossover.
|
||||||
* the parents are not modified during this process
|
* the parents are not modified during this process
|
||||||
|
@ -68,7 +82,7 @@ namespace blt::gp
|
||||||
|
|
||||||
virtual ~crossover_t() = default;
|
virtual ~crossover_t() = default;
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
config_t config;
|
config_t config;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -97,7 +111,7 @@ namespace blt::gp
|
||||||
|
|
||||||
virtual ~mutation_t() = default;
|
virtual ~mutation_t() = default;
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
config_t config;
|
config_t config;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -28,11 +28,6 @@ namespace blt::gp
|
||||||
{
|
{
|
||||||
result_t result{p1, p2};
|
result_t result{p1, p2};
|
||||||
|
|
||||||
#if BLT_DEBUG_LEVEL > 0
|
|
||||||
BLT_INFO("Child 1 Stack empty? %s", result.child1.get_values().empty() ? "true" : "false");
|
|
||||||
BLT_INFO("Child 2 Stack empty? %s", result.child2.get_values().empty() ? "true" : "false");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
auto& c1 = result.child1;
|
auto& c1 = result.child1;
|
||||||
auto& c2 = result.child2;
|
auto& c2 = result.child2;
|
||||||
|
|
||||||
|
@ -42,6 +37,67 @@ namespace blt::gp
|
||||||
if (c1_ops.size() < 5 || c2_ops.size() < 5)
|
if (c1_ops.size() < 5 || c2_ops.size() < 5)
|
||||||
return blt::unexpected(error_t::TREE_TOO_SMALL);
|
return blt::unexpected(error_t::TREE_TOO_SMALL);
|
||||||
|
|
||||||
|
auto point = get_crossover_point(program, c1, c2);
|
||||||
|
|
||||||
|
if (!point)
|
||||||
|
return blt::unexpected(point.error());
|
||||||
|
|
||||||
|
auto crossover_point_begin_itr = c1_ops.begin() + point->p1_crossover_point;
|
||||||
|
auto crossover_point_end_itr = c1_ops.begin() + find_endpoint(program, c1_ops, point->p1_crossover_point);
|
||||||
|
|
||||||
|
auto found_point_begin_itr = c2_ops.begin() + point->p2_crossover_point;
|
||||||
|
auto found_point_end_itr = c2_ops.begin() + find_endpoint(program, c2_ops, point->p2_crossover_point);
|
||||||
|
|
||||||
|
stack_allocator& c1_stack_init = c1.get_values();
|
||||||
|
stack_allocator& c2_stack_init = c2.get_values();
|
||||||
|
|
||||||
|
std::vector<op_container_t> c1_operators;
|
||||||
|
std::vector<op_container_t> c2_operators;
|
||||||
|
|
||||||
|
for (const auto& op : blt::enumerate(crossover_point_begin_itr, crossover_point_end_itr))
|
||||||
|
c1_operators.push_back(op);
|
||||||
|
for (const auto& op : blt::enumerate(found_point_begin_itr, found_point_end_itr))
|
||||||
|
c2_operators.push_back(op);
|
||||||
|
|
||||||
|
stack_allocator c1_stack_after_copy;
|
||||||
|
stack_allocator c1_stack_for_copy;
|
||||||
|
stack_allocator c2_stack_after_copy;
|
||||||
|
stack_allocator c2_stack_for_copy;
|
||||||
|
|
||||||
|
// transfer all values after the crossover point. these will need to be transferred back to child2
|
||||||
|
transfer_backward(c1_stack_init, c1_stack_after_copy, c1_ops.end()-1, crossover_point_end_itr - 1);
|
||||||
|
// transfer all values for the crossover point.
|
||||||
|
transfer_backward(c1_stack_init, c1_stack_for_copy, crossover_point_end_itr - 1, crossover_point_begin_itr - 1);
|
||||||
|
// transfer child2 values for copying back into c1
|
||||||
|
transfer_backward(c2_stack_init, c2_stack_after_copy, c2_ops.end() - 1, found_point_end_itr - 1);
|
||||||
|
transfer_backward(c2_stack_init, c2_stack_for_copy, found_point_end_itr - 1, found_point_begin_itr - 1);
|
||||||
|
// now copy back into the respective children
|
||||||
|
transfer_forward(c2_stack_for_copy, c1.get_values(), found_point_begin_itr, found_point_end_itr);
|
||||||
|
transfer_forward(c1_stack_for_copy, c2.get_values(), crossover_point_begin_itr, crossover_point_end_itr);
|
||||||
|
// now copy after the crossover point back to the correct children
|
||||||
|
transfer_forward(c1_stack_after_copy, c1.get_values(), crossover_point_end_itr, c1_ops.end());
|
||||||
|
transfer_forward(c2_stack_after_copy, c2.get_values(), found_point_end_itr, c2_ops.end());
|
||||||
|
|
||||||
|
// 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());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
blt::expected<crossover_t::crossover_point_t, crossover_t::error_t> crossover_t::get_crossover_point(gp_program& program, const tree_t& c1,
|
||||||
|
const tree_t& c2) const
|
||||||
|
{
|
||||||
|
auto& c1_ops = c1.get_operations();
|
||||||
|
auto& c2_ops = c2.get_operations();
|
||||||
|
|
||||||
blt::size_t crossover_point = program.get_random().get_size_t(1ul, c1_ops.size());
|
blt::size_t crossover_point = program.get_random().get_size_t(1ul, c1_ops.size());
|
||||||
|
|
||||||
while (config.avoid_terminals && program.get_operator_info(c1_ops[crossover_point].id).argc.is_terminal())
|
while (config.avoid_terminals && program.get_operator_info(c1_ops[crossover_point].id).argc.is_terminal())
|
||||||
|
@ -90,17 +146,16 @@ namespace blt::gp
|
||||||
}
|
}
|
||||||
} while (true);
|
} while (true);
|
||||||
|
|
||||||
|
return crossover_point_t{static_cast<blt::ptrdiff_t>(crossover_point), static_cast<blt::ptrdiff_t>(attempted_point)};
|
||||||
|
}
|
||||||
|
|
||||||
|
blt::ptrdiff_t crossover_t::find_endpoint(blt::gp::gp_program& program, const std::vector<blt::gp::op_container_t>& container, blt::ptrdiff_t index)
|
||||||
|
{
|
||||||
blt::i64 children_left = 0;
|
blt::i64 children_left = 0;
|
||||||
blt::size_t index = crossover_point;
|
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
const auto& type = program.get_operator_info(c1_ops[index].id);
|
const auto& type = program.get_operator_info(container[index].id);
|
||||||
#if BLT_DEBUG_LEVEL > 1
|
|
||||||
#define MAKE_C_STR() program.get_name(c1_ops[index].id).has_value() ? std::string(program.get_name(c1_ops[index].id).value()).c_str() : std::to_string(c1_ops[index].id).c_str()
|
|
||||||
BLT_TRACE("Crossover type: %s, op: %s", std::string(program.get_typesystem().get_type(type.return_type).name()).c_str(), MAKE_C_STR());
|
|
||||||
#undef MAKE_C_STR
|
|
||||||
#endif
|
|
||||||
// this is a child to someone
|
// this is a child to someone
|
||||||
if (children_left != 0)
|
if (children_left != 0)
|
||||||
children_left--;
|
children_left--;
|
||||||
|
@ -109,148 +164,26 @@ namespace blt::gp
|
||||||
index++;
|
index++;
|
||||||
} while (children_left > 0);
|
} while (children_left > 0);
|
||||||
|
|
||||||
auto crossover_point_begin_itr = c1_ops.begin() + static_cast<blt::ptrdiff_t>(crossover_point);
|
return index;
|
||||||
auto crossover_point_end_itr = c1_ops.begin() + static_cast<blt::ptrdiff_t>(index);
|
}
|
||||||
#if BLT_DEBUG_LEVEL > 0
|
|
||||||
BLT_TRACE("[%ld %ld) %ld", crossover_point, index, index - crossover_point);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
children_left = 0;
|
void crossover_t::transfer_backward(stack_allocator& from, stack_allocator& to, crossover_t::op_iter begin, crossover_t::op_iter end)
|
||||||
index = attempted_point;
|
{
|
||||||
|
for (auto it = begin; it != end; it--)
|
||||||
do
|
|
||||||
{
|
|
||||||
const auto& type = program.get_operator_info(c2_ops[index].id);
|
|
||||||
#if BLT_DEBUG_LEVEL > 1
|
|
||||||
#define MAKE_C_STR() program.get_name(c2_ops[index].id).has_value() ? std::string(program.get_name(c2_ops[index].id).value()).c_str() : std::to_string(c2_ops[index].id).c_str()
|
|
||||||
BLT_TRACE("Found type: %s, op: %s", std::string(program.get_typesystem().get_type(type.return_type).name()).c_str(), MAKE_C_STR());
|
|
||||||
#undef MAKE_C_STR
|
|
||||||
#endif
|
|
||||||
// this is a child to someone
|
|
||||||
if (children_left != 0)
|
|
||||||
children_left--;
|
|
||||||
if (type.argc.argc > 0)
|
|
||||||
children_left += type.argc.argc;
|
|
||||||
|
|
||||||
index++;
|
|
||||||
} while (children_left > 0);
|
|
||||||
|
|
||||||
auto found_point_begin_itr = c2_ops.begin() + static_cast<blt::ptrdiff_t>(attempted_point);
|
|
||||||
auto found_point_end_itr = c2_ops.begin() + static_cast<blt::ptrdiff_t>(index);
|
|
||||||
|
|
||||||
#if BLT_DEBUG_LEVEL > 0
|
|
||||||
BLT_TRACE("[%ld %ld) %ld", attempted_point, index, index - attempted_point);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
stack_allocator& c1_stack_init = c1.get_values();
|
|
||||||
stack_allocator& c2_stack_init = c2.get_values();
|
|
||||||
|
|
||||||
std::vector<op_container_t> c1_operators;
|
|
||||||
std::vector<op_container_t> c2_operators;
|
|
||||||
|
|
||||||
for (const auto& op : blt::enumerate(crossover_point_begin_itr, crossover_point_end_itr))
|
|
||||||
c1_operators.push_back(op);
|
|
||||||
for (const auto& op : blt::enumerate(found_point_begin_itr, found_point_end_itr))
|
|
||||||
c2_operators.push_back(op);
|
|
||||||
|
|
||||||
#if BLT_DEBUG_LEVEL > 0
|
|
||||||
BLT_TRACE("Sizes: %ld %ld || Ops size: %ld %ld", c1_operators.size(), c2_operators.size(), c1_ops.size(), c2_ops.size());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
stack_allocator c1_stack_after_copy;
|
|
||||||
stack_allocator c1_stack_for_copy;
|
|
||||||
stack_allocator c2_stack_after_copy;
|
|
||||||
stack_allocator c2_stack_for_copy;
|
|
||||||
|
|
||||||
#if BLT_DEBUG_LEVEL > 1
|
|
||||||
BLT_DEBUG("Transferring past crossover 1:");
|
|
||||||
#endif
|
|
||||||
// transfer all values after the crossover point. these will need to be transferred back to child2
|
|
||||||
for (auto it = c1_ops.end() - 1; it != crossover_point_end_itr - 1; it--)
|
|
||||||
{
|
{
|
||||||
if (it->is_value)
|
if (it->is_value)
|
||||||
c1_stack_init.transfer_bytes(c1_stack_after_copy, it->type_size);
|
from.transfer_bytes(to, it->type_size);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#if BLT_DEBUG_LEVEL > 1
|
void crossover_t::transfer_forward(stack_allocator& from, stack_allocator& to, crossover_t::op_iter begin, crossover_t::op_iter end)
|
||||||
BLT_DEBUG("Transferring for crossover 1:");
|
{
|
||||||
#endif
|
|
||||||
// transfer all values for the crossover point.
|
|
||||||
for (auto it = crossover_point_end_itr - 1; it != crossover_point_begin_itr - 1; it--)
|
|
||||||
{
|
|
||||||
if (it->is_value)
|
|
||||||
c1_stack_init.transfer_bytes(c1_stack_for_copy, it->type_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if BLT_DEBUG_LEVEL > 1
|
|
||||||
BLT_DEBUG("Transferring past crossover 2:");
|
|
||||||
#endif
|
|
||||||
// transfer child2 values for copying back into c1
|
|
||||||
for (auto it = c2_ops.end() - 1; it != found_point_end_itr - 1; it--)
|
|
||||||
{
|
|
||||||
if (it->is_value)
|
|
||||||
c2_stack_init.transfer_bytes(c2_stack_after_copy, it->type_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if BLT_DEBUG_LEVEL > 1
|
|
||||||
BLT_DEBUG("Transferring for crossover 2:");
|
|
||||||
#endif
|
|
||||||
for (auto it = found_point_end_itr - 1; it != found_point_begin_itr - 1; it--)
|
|
||||||
{
|
|
||||||
if (it->is_value)
|
|
||||||
c2_stack_init.transfer_bytes(c2_stack_for_copy, it->type_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if BLT_DEBUG_LEVEL > 1
|
|
||||||
BLT_DEBUG("Transferring back for crossover 1:");
|
|
||||||
#endif
|
|
||||||
// now copy back into the respective children
|
// now copy back into the respective children
|
||||||
for (auto it = found_point_begin_itr; it != found_point_end_itr; it++)
|
for (auto it = begin; it != end; it++)
|
||||||
{
|
{
|
||||||
if (it->is_value)
|
if (it->is_value)
|
||||||
c2_stack_for_copy.transfer_bytes(c1.get_values(), it->type_size);
|
from.transfer_bytes(to, it->type_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if BLT_DEBUG_LEVEL > 1
|
|
||||||
BLT_DEBUG("Transferring back for crossover 2:");
|
|
||||||
#endif
|
|
||||||
for (auto it = crossover_point_begin_itr; it != crossover_point_end_itr; it++)
|
|
||||||
{
|
|
||||||
if (it->is_value)
|
|
||||||
c1_stack_for_copy.transfer_bytes(c2.get_values(), it->type_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if BLT_DEBUG_LEVEL > 1
|
|
||||||
BLT_DEBUG("Transferring back after crossover 1:");
|
|
||||||
#endif
|
|
||||||
// now copy after the crossover point back to the correct children
|
|
||||||
for (auto it = crossover_point_end_itr; it != c1_ops.end(); it++)
|
|
||||||
{
|
|
||||||
if (it->is_value)
|
|
||||||
c1_stack_after_copy.transfer_bytes(c1.get_values(), it->type_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if BLT_DEBUG_LEVEL > 1
|
|
||||||
BLT_DEBUG("Transferring back after crossover 2:");
|
|
||||||
#endif
|
|
||||||
for (auto it = found_point_end_itr; it != c2_ops.end(); it++)
|
|
||||||
{
|
|
||||||
if (it->is_value)
|
|
||||||
c2_stack_after_copy.transfer_bytes(c2.get_values(), it->type_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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());
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tree_t mutation_t::apply(gp_program& program, const tree_t& p)
|
tree_t mutation_t::apply(gp_program& program, const tree_t& p)
|
||||||
|
|
Loading…
Reference in New Issue