things are working now. missing fitness reset was partial problem

dev-0.2.1
Brett 2024-12-24 13:06:09 -05:00
parent e1083426fc
commit 3e0fe06017
8 changed files with 210 additions and 186 deletions

View File

@ -27,7 +27,7 @@ macro(compile_options target_name)
sanitizers(${target_name})
endmacro()
project(blt-gp VERSION 0.2.4)
project(blt-gp VERSION 0.2.5)
include(CTest)

View File

@ -45,6 +45,7 @@ namespace blt::gp::example
};
bool fitness_function(const tree_t& current_tree, fitness_t& fitness, size_t) const;
public:
template <typename SEED>
rice_classification_t(SEED&& seed, const prog_config_t& config): example_base_t{std::forward<SEED>(seed), config}
@ -60,7 +61,7 @@ namespace blt::gp::example
void load_rice_data(std::string_view rice_file_path);
confusion_matrix_t test_individual(const individual_t& individual) const;
[[nodiscard]] confusion_matrix_t test_individual(const individual_t& individual) const;
void execute(const std::string_view rice_file_path)
{
@ -166,6 +167,9 @@ namespace blt::gp::example
std::cout << "\n";
}
auto& get_results() { return results; }
const auto& get_results() const { return results; }
private:
std::vector<rice_record> training_cases;
std::vector<rice_record> testing_cases;

View File

@ -122,7 +122,7 @@ bool blt::gp::example::rice_classification_t::fitness_function(const tree_t& cur
{
for (auto& training_case : training_cases)
{
auto v = current_tree.get_evaluation_value<float>(training_case);
const auto v = current_tree.get_evaluation_value<float>(training_case);
switch (training_case.type)
{
case rice_type_t::Cammeo:
@ -137,7 +137,8 @@ bool blt::gp::example::rice_classification_t::fitness_function(const tree_t& cur
}
fitness.raw_fitness = static_cast<double>(fitness.hits);
fitness.standardized_fitness = fitness.raw_fitness;
fitness.adjusted_fitness = 1.0 - (1.0 / (1.0 + fitness.standardized_fitness));
// fitness.adjusted_fitness = 1.0 - (1.0 / (1.0 + fitness.standardized_fitness));
fitness.adjusted_fitness = fitness.standardized_fitness / static_cast<double>(training_cases.size());
return static_cast<size_t>(fitness.hits) == training_cases.size();
}
@ -169,20 +170,20 @@ void blt::gp::example::rice_classification_t::load_rice_data(const std::string_v
}
}
size_t total_records = c.size() + o.size();
size_t training_size = std::min(total_records / 3, 1000ul);
for (size_t i = 0; i < training_size; i++)
const size_t total_records = c.size() + o.size();
const size_t testing_size = total_records / 3;
for (size_t i = 0; i < testing_size; i++)
{
auto& random = program.get_random();
auto& vec = random.choice() ? c : o;
auto pos = random.get_i64(0, static_cast<i64>(vec.size()));
training_cases.push_back(vec[pos]);
const auto pos = random.get_i64(0, static_cast<i64>(vec.size()));
testing_cases.push_back(vec[pos]);
vec.erase(vec.begin() + pos);
}
testing_cases.insert(testing_cases.end(), c.begin(), c.end());
testing_cases.insert(testing_cases.end(), o.begin(), o.end());
std::shuffle(testing_cases.begin(), testing_cases.end(), program.get_random());
BLT_INFO("Created training set of size %ld, testing set is of size %ld", training_size, testing_cases.size());
training_cases.insert(training_cases.end(), c.begin(), c.end());
training_cases.insert(training_cases.end(), o.begin(), o.end());
std::shuffle(training_cases.begin(), training_cases.end(), program.get_random());
BLT_INFO("Created testing set of size %ld, training set is of size %ld", testing_cases.size(), training_cases.size());
}
blt::gp::confusion_matrix_t blt::gp::example::rice_classification_t::test_individual(const individual_t& individual) const

View File

@ -35,14 +35,14 @@ int main()
.set_elite_count(2)
.set_crossover_chance(0.9)
.set_mutation_chance(0.1)
.set_reproduction_chance(0.25)
.set_reproduction_chance(0.0)
.set_max_generations(50)
.set_pop_size(500)
.set_thread_count(16);
// example on how you can change the mutation config
blt::gp::mutation_t::config_t mut_config{};
mut_config.generator = full_generator;
mut_config.generator = grow_generator;
mut_config.replacement_min_depth = 2;
mut_config.replacement_max_depth = 6;

View File

@ -490,6 +490,7 @@ namespace blt::gp
double sum_of_prob = 0;
for (const auto& [index, ind] : blt::enumerate(current_pop.get_individuals()))
{
ind.fitness = {};
if constexpr (std::is_same_v<LambdaReturn, bool> || std::is_convertible_v<LambdaReturn, bool>)
{
auto result = fitness_function(ind.tree, ind.fitness, index);
@ -565,7 +566,7 @@ namespace blt::gp
{
auto& ind = current_pop.get_individuals()[i];
ind.fitness = {};
if constexpr (std::is_same_v<LambdaReturn, bool> || std::is_convertible_v<LambdaReturn, bool>)
{
auto result = fitness_function(ind.tree, ind.fitness, i);

View File

@ -71,9 +71,14 @@ namespace blt::gp
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
blt::u16 max_crossover_tries = 5;
blt::f32 traverse_chance = 0.5;
blt::u32 min_tree_size = 5;
u32 max_crossover_tries = 5;
// if tree have fewer nodes than this number, they will not be considered for crossover
u32 min_tree_size = 3;
// used by the traverse version of get_crossover_point
// at each depth level, what chance do we have to exit with this as our point? or in other words what's the chance we continue traversing
// this is what this option configures.
f32 traverse_chance = 0.5;
// legacy settings:
@ -88,9 +93,9 @@ namespace blt::gp
explicit crossover_t(const config_t& config): config(config)
{}
std::optional<crossover_t::crossover_point_t> get_crossover_point(gp_program& program, const tree_t& c1, const tree_t& c2) const;
std::optional<crossover_point_t> get_crossover_point(gp_program& program, const tree_t& c1, const tree_t& c2) const;
std::optional<crossover_t::crossover_point_t> get_crossover_point_traverse(gp_program& program, const tree_t& c1, const tree_t& c2) const;
std::optional<crossover_point_t> get_crossover_point_traverse(gp_program& program, const tree_t& c1, const tree_t& c2) const;
std::optional<point_info_t> get_point_traverse(gp_program& program, const tree_t& t, std::optional<type_id> type) const;

View File

@ -213,7 +213,7 @@ namespace blt::gp
double raw_fitness = 0;
double standardized_fitness = 0;
double adjusted_fitness = 0;
blt::i64 hits = 0;
i64 hits = 0;
};
struct individual_t

View File

@ -26,19 +26,18 @@
namespace blt::gp
{
grow_generator_t grow_generator;
inline tree_t& get_static_tree_tl(gp_program& program)
{
static thread_local tree_t new_tree{program};
thread_local tree_t new_tree{program};
new_tree.clear(program);
return new_tree;
}
inline blt::size_t accumulate_type_sizes(detail::op_iter_t begin, detail::op_iter_t end)
inline size_t accumulate_type_sizes(detail::op_iter_t begin, detail::op_iter_t end)
{
blt::size_t total = 0;
size_t total = 0;
for (auto it = begin; it != end; ++it)
{
if (it->is_value)
@ -46,62 +45,63 @@ namespace blt::gp
}
return total;
}
template<typename>
blt::u8* get_thread_pointer_for_size(blt::size_t bytes)
template <typename>
u8* get_thread_pointer_for_size(size_t bytes)
{
static thread_local blt::expanding_buffer<blt::u8> buffer;
thread_local expanding_buffer<u8> buffer;
if (bytes > buffer.size())
buffer.resize(bytes);
return buffer.data();
}
mutation_t::config_t::config_t(): generator(grow_generator)
{}
{
}
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& c2_ops = c2.get_operations();
auto point = get_crossover_point(program, p1, p2);
const auto point = get_crossover_point(program, p1, p2);
if (!point)
return false;
auto selection = program.get_random().get_u32(0, 2);
const 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;
thread_local tracked_vector<op_container_t> c1_operators;
thread_local tracked_vector<op_container_t> c2_operators;
c1_operators.clear();
c2_operators.clear();
// TODO: more crossover!
switch (selection)
{
case 0:
case 1:
case 0:
case 1:
{
// 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);
auto found_point_begin_itr = c2_ops.begin() + point->p2_crossover_point;
auto found_point_end_itr = c2_ops.begin() + c2.find_endpoint(program, point->p2_crossover_point);
stack_allocator& c1_stack = c1.get_values();
stack_allocator& c2_stack = c2.get_values();
for (const auto& op : blt::iterate(crossover_point_begin_itr, crossover_point_end_itr))
c1_operators.push_back(op);
for (const auto& op : blt::iterate(found_point_begin_itr, found_point_end_itr))
c2_operators.push_back(op);
blt::size_t c1_stack_after_bytes = accumulate_type_sizes(crossover_point_end_itr, c1_ops.end());
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());
@ -110,40 +110,40 @@ namespace blt::gp
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);
c2_stack.reserve(c2_stack.bytes_in_head() - c2_stack_for_bytes + c1_stack_for_bytes);
c1_stack.copy_to(copy_ptr_c1, c1_total);
c1_stack.pop_bytes(c1_total);
c2_stack.copy_to(copy_ptr_c2, c2_total);
c2_stack.pop_bytes(c2_total);
c2_stack.copy_from(copy_ptr_c1, c1_stack_for_bytes);
c2_stack.copy_from(copy_ptr_c2 + c2_stack_for_bytes, c2_stack_after_bytes);
c1_stack.copy_from(copy_ptr_c2, c2_stack_for_bytes);
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;
break;
default:
break;
break;
default:
#if BLT_DEBUG_LEVEL > 0
BLT_ABORT("This place should be unreachable!");
#else
BLT_UNREACHABLE;
BLT_UNREACHABLE;
#endif
}
@ -169,26 +169,26 @@ namespace blt::gp
BLT_ABORT("Amount of bytes in stack doesn't match the number of bytes expected for the operations");
}
#endif
return true;
}
std::optional<crossover_t::crossover_point_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());
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())
crossover_point = program.get_random().get_size_t(1ul, c1_ops.size());
blt::size_t attempted_point = 0;
size_t attempted_point = 0;
const auto& crossover_point_type = program.get_operator_info(c1_ops[crossover_point].id);
operator_info_t* attempted_point_type = nullptr;
blt::size_t counter = 0;
do
{
@ -215,34 +215,32 @@ namespace blt::gp
}
// should we try again over the whole tree? probably not.
return {};
} else
{
attempted_point = program.get_random().get_size_t(1ul, c2_ops.size());
attempted_point_type = &program.get_operator_info(c2_ops[attempted_point].id);
if (config.avoid_terminals && attempted_point_type->argc.is_terminal())
continue;
if (crossover_point_type.return_type == attempted_point_type->return_type)
break;
counter++;
}
} while (true);
attempted_point = program.get_random().get_size_t(1ul, c2_ops.size());
attempted_point_type = &program.get_operator_info(c2_ops[attempted_point].id);
if (config.avoid_terminals && attempted_point_type->argc.is_terminal())
continue;
if (crossover_point_type.return_type == attempted_point_type->return_type)
break;
counter++;
}
while (true);
return crossover_point_t{static_cast<blt::ptrdiff_t>(crossover_point), static_cast<blt::ptrdiff_t>(attempted_point)};
}
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
{
auto c1_point_o = get_point_traverse_retry(program, c1, {});
const auto c1_point_o = get_point_traverse_retry(program, c1, {});
if (!c1_point_o)
return {};
auto c2_point_o = get_point_traverse_retry(program, c2, c1_point_o->type_operator_info.return_type);
const auto c2_point_o = get_point_traverse_retry(program, c2, c1_point_o->type_operator_info.return_type);
if (!c2_point_o)
return {};
return {{c1_point_o->point, c2_point_o->point}};
}
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_i64(1ul, t.get_operations().size());
@ -251,12 +249,12 @@ namespace blt::gp
return {{attempted_point, attempted_point_type}};
return {};
}
std::optional<crossover_t::point_info_t> crossover_t::get_point_traverse(gp_program& program, const tree_t& t, std::optional<type_id> type) const
{
auto& random = program.get_random();
blt::ptrdiff_t point = 0;
ptrdiff_t point = 0;
while (true)
{
auto& current_op_type = program.get_operator_info(t.get_operations()[point].id);
@ -269,88 +267,88 @@ namespace blt::gp
// traverse to a child
if (random.choice(config.traverse_chance))
{
auto args = current_op_type.argc.argc;
auto argument = random.get_size_t(0, args);
const auto args = current_op_type.argc.argc;
const auto argument = random.get_size_t(0, args);
// move to the first child
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
for (blt::size_t i = 0; i < argument; i++)
for (size_t i = 0; i < argument; i++)
point = t.find_endpoint(program, point);
continue;
}
if (!type || (type && *type == current_op_type.return_type))
return {{point, current_op_type}};
}
}
std::optional<crossover_t::point_info_t> crossover_t::get_point_traverse_retry(gp_program& program, const tree_t& t,
std::optional<type_id> type) const
{
for (blt::size_t i = 0; i < config.max_crossover_tries; i++)
for (size_t i = 0; i < config.max_crossover_tries; i++)
{
if (auto found = get_point_traverse(program, t, type))
return found;
}
return {};
}
bool mutation_t::apply(gp_program& program, const tree_t&, tree_t& c)
{
mutate_point(program, c, program.get_random().get_size_t(0ul, c.get_operations().size()));
return true;
}
blt::size_t mutation_t::mutate_point(gp_program& program, tree_t& c, blt::size_t node)
{
auto& ops_r = c.get_operations();
auto& vals_r = c.get_values();
auto begin_point = static_cast<blt::ptrdiff_t>(node);
auto end_point = c.find_endpoint(program, begin_point);
auto begin_operator_id = ops_r[begin_point].id;
const auto& type_info = program.get_operator_info(begin_operator_id);
auto begin_itr = ops_r.begin() + begin_point;
auto end_itr = ops_r.begin() + end_point;
auto& new_tree = get_static_tree_tl(program);
config.generator.get().generate(new_tree, {program, type_info.return_type, config.replacement_min_depth, config.replacement_max_depth});
auto& new_ops_r = new_tree.get_operations();
auto& new_vals_r = new_tree.get_values();
blt::size_t total_bytes_after = accumulate_type_sizes(end_itr, ops_r.end());
auto* stack_after_data = get_thread_pointer_for_size<struct mutation>(total_bytes_after);
// make a copy of any stack data after the mutation point / children.
vals_r.copy_to(stack_after_data, total_bytes_after);
// remove the bytes of the data after the mutation point and the data for the children of the mutation node.
vals_r.pop_bytes(static_cast<blt::ptrdiff_t>(total_bytes_after + accumulate_type_sizes(begin_itr, end_itr)));
// insert the new tree then move back the data from after the original mutation point.
vals_r.insert(new_vals_r);
vals_r.copy_from(stack_after_data, total_bytes_after);
auto before = begin_itr - 1;
ops_r.erase(begin_itr, end_itr);
ops_r.insert(++before, new_ops_r.begin(), new_ops_r.end());
// this will check to make sure that the tree is in a correct and executable state. it requires that the evaluation is context free!
#if BLT_DEBUG_LEVEL >= 2
// BLT_ASSERT(new_vals_r.empty());
//BLT_ASSERT(stack_after.empty());
blt::size_t bytes_expected = 0;
auto bytes_size = vals_r.size().total_used_bytes;
for (const auto& op : c.get_operations())
{
if (op.is_value)
bytes_expected += stack_allocator::aligned_size(op.type_size);
}
if (bytes_expected != bytes_size)
{
BLT_WARN_STREAM << "Stack state: " << vals_r.size() << "\n";
@ -383,13 +381,13 @@ namespace blt::gp
#endif
return begin_point + new_ops_r.size();
}
bool advanced_mutation_t::apply(gp_program& program, const tree_t& p, tree_t& c)
{
(void) p;
(void)p;
auto& ops = c.get_operations();
auto& vals = c.get_values();
for (blt::size_t c_node = 0; c_node < ops.size(); c_node++)
{
double node_mutation_chance = per_node_mutation_chance / static_cast<double>(ops.size());
@ -399,7 +397,7 @@ namespace blt::gp
#if BLT_DEBUG_LEVEL >= 2
tree_t c_copy = c;
#endif
// select an operator to apply
auto selected_point = static_cast<blt::i32>(mutation_operator::COPY);
auto choice = program.get_random().get_double();
@ -412,7 +410,8 @@ namespace blt::gp
selected_point = static_cast<blt::i32>(index);
break;
}
} else
}
else
{
if (choice > mutation_operator_chances[index - 1] && choice <= value)
{
@ -421,13 +420,13 @@ namespace blt::gp
}
}
}
switch (static_cast<mutation_operator>(selected_point))
{
case mutation_operator::EXPRESSION:
c_node += mutate_point(program, c, c_node);
break;
case mutation_operator::ADJUST:
case mutation_operator::EXPRESSION:
c_node += mutate_point(program, c, c_node);
break;
case mutation_operator::ADJUST:
{
// this is going to be evil >:3
const auto& node = ops[c_node];
@ -435,15 +434,15 @@ namespace blt::gp
{
auto& current_func_info = program.get_operator_info(ops[c_node].id);
operator_id random_replacement = program.get_random().select(
program.get_type_non_terminals(current_func_info.return_type.id));
program.get_type_non_terminals(current_func_info.return_type.id));
auto& replacement_func_info = program.get_operator_info(random_replacement);
// cache memory used for offset data.
thread_local static tracked_vector<tree_t::child_t> children_data;
children_data.clear();
c.find_child_extends(program, children_data, c_node, current_func_info.argument_types.size());
for (const auto& [index, val] : blt::enumerate(replacement_func_info.argument_types))
{
// need to generate replacement.
@ -453,23 +452,23 @@ namespace blt::gp
auto& tree = get_static_tree_tl(program);
config.generator.get().generate(tree,
{program, val.id, config.replacement_min_depth, config.replacement_max_depth});
auto& child = children_data[children_data.size() - 1 - index];
blt::size_t total_bytes_for = c.total_value_bytes(child.start, child.end);
blt::size_t total_bytes_after = c.total_value_bytes(child.end);
auto after_ptr = get_thread_pointer_for_size<struct mutation_func>(total_bytes_after);
vals.copy_to(after_ptr, total_bytes_after);
vals.pop_bytes(static_cast<blt::ptrdiff_t>(total_bytes_after + total_bytes_for));
blt::size_t total_child_bytes = tree.total_value_bytes();
vals.copy_from(tree.get_values(), total_child_bytes);
vals.copy_from(after_ptr, total_bytes_after);
ops.erase(ops.begin() + child.start, ops.begin() + child.end);
ops.insert(ops.begin() + child.start, tree.get_operations().begin(), tree.get_operations().end());
// shift over everybody after.
if (index > 0)
{
@ -479,10 +478,10 @@ namespace blt::gp
{
// remove the old tree size, then add the new tree size to get the correct positions.
new_child.start =
new_child.start - (child.end - child.start) +
static_cast<blt::ptrdiff_t>(tree.get_operations().size());
new_child.start - (child.end - child.start) +
static_cast<blt::ptrdiff_t>(tree.get_operations().size());
new_child.end =
new_child.end - (child.end - child.start) + static_cast<blt::ptrdiff_t>(tree.get_operations().size());
new_child.end - (child.end - child.start) + static_cast<blt::ptrdiff_t>(tree.get_operations().size());
}
}
child.end = static_cast<blt::ptrdiff_t>(child.start + tree.get_operations().size());
@ -504,7 +503,7 @@ namespace blt::gp
#endif
}
}
if (current_func_info.argc.argc > replacement_func_info.argc.argc)
{
blt::size_t end_index = children_data[(current_func_info.argc.argc - replacement_func_info.argc.argc) - 1].end;
@ -516,11 +515,13 @@ namespace blt::gp
vals.pop_bytes(static_cast<blt::ptrdiff_t>(total_bytes_after + total_bytes_for));
vals.copy_from(data, total_bytes_after);
ops.erase(ops.begin() + static_cast<blt::ptrdiff_t>(start_index), ops.begin() + static_cast<blt::ptrdiff_t>(end_index));
} else if (current_func_info.argc.argc == replacement_func_info.argc.argc)
}
else if (current_func_info.argc.argc == replacement_func_info.argc.argc)
{
// exactly enough args
// return types should have been replaced if needed. this part should do nothing?
} else
}
else
{
// not enough args
blt::size_t start_index = c_node + 1;
@ -528,14 +529,16 @@ namespace blt::gp
auto* data = get_thread_pointer_for_size<struct mutation_func>(total_bytes_after);
vals.copy_to(data, total_bytes_after);
vals.pop_bytes(static_cast<blt::ptrdiff_t>(total_bytes_after));
for (blt::ptrdiff_t i = static_cast<blt::ptrdiff_t>(replacement_func_info.argc.argc) - 1;
i >= current_func_info.argc.argc; i--)
{
auto& tree = get_static_tree_tl(program);
config.generator.get().generate(tree,
{program, replacement_func_info.argument_types[i].id, config.replacement_min_depth,
config.replacement_max_depth});
{
program, replacement_func_info.argument_types[i].id, config.replacement_min_depth,
config.replacement_max_depth
});
vals.insert(tree.get_values());
ops.insert(ops.begin() + static_cast<blt::ptrdiff_t>(start_index), tree.get_operations().begin(),
tree.get_operations().end());
@ -544,8 +547,10 @@ namespace blt::gp
vals.copy_from(data, total_bytes_after);
}
// now finally update the type.
ops[c_node] = {program.get_typesystem().get_type(replacement_func_info.return_type).size(), random_replacement,
program.is_operator_ephemeral(random_replacement)};
ops[c_node] = {
program.get_typesystem().get_type(replacement_func_info.return_type).size(), random_replacement,
program.is_operator_ephemeral(random_replacement)
};
}
#if BLT_DEBUG_LEVEL >= 2
if (!c.check(program, nullptr))
@ -559,16 +564,16 @@ namespace blt::gp
}
#endif
}
break;
case mutation_operator::SUB_FUNC:
break;
case mutation_operator::SUB_FUNC:
{
auto& current_func_info = program.get_operator_info(ops[c_node].id);
// need to:
// mutate the current function.
// current function is moved to one of the arguments.
// other arguments are generated.
// get a replacement which returns the same type.
auto& non_terminals = program.get_type_non_terminals(current_func_info.return_type.id);
if (non_terminals.empty())
@ -587,7 +592,8 @@ namespace blt::gp
}
}
random_replacement = program.get_random().select(program.get_type_non_terminals(current_func_info.return_type.id));
} while (true);
}
while (true);
exit:
auto& replacement_func_info = program.get_operator_info(random_replacement);
auto new_argc = replacement_func_info.argc.argc;
@ -596,19 +602,21 @@ namespace blt::gp
blt::size_t for_bytes = c.total_value_bytes(c_node, current_end);
blt::size_t after_bytes = c.total_value_bytes(current_end);
auto size = current_end - c_node;
auto combined_ptr = get_thread_pointer_for_size<struct SUB_FUNC_FOR>(for_bytes + after_bytes);
vals.copy_to(combined_ptr, for_bytes + after_bytes);
vals.pop_bytes(static_cast<blt::ptrdiff_t>(for_bytes + after_bytes));
blt::size_t start_index = c_node;
for (blt::ptrdiff_t i = new_argc - 1; i > static_cast<blt::ptrdiff_t>(arg_position); i--)
{
auto& tree = get_static_tree_tl(program);
config.generator.get().generate(tree,
{program, replacement_func_info.argument_types[i].id, config.replacement_min_depth,
config.replacement_max_depth});
{
program, replacement_func_info.argument_types[i].id, config.replacement_min_depth,
config.replacement_max_depth
});
blt::size_t total_bytes_for = tree.total_value_bytes();
vals.copy_from(tree.get_values(), total_bytes_for);
ops.insert(ops.begin() + static_cast<blt::ptrdiff_t>(start_index), tree.get_operations().begin(),
@ -621,8 +629,10 @@ namespace blt::gp
{
auto& tree = get_static_tree_tl(program);
config.generator.get().generate(tree,
{program, replacement_func_info.argument_types[i].id, config.replacement_min_depth,
config.replacement_max_depth});
{
program, replacement_func_info.argument_types[i].id, config.replacement_min_depth,
config.replacement_max_depth
});
blt::size_t total_bytes_for = tree.total_value_bytes();
vals.copy_from(tree.get_values(), total_bytes_for);
ops.insert(ops.begin() + static_cast<blt::ptrdiff_t>(start_index), tree.get_operations().begin(),
@ -630,10 +640,12 @@ namespace blt::gp
start_index += tree.get_operations().size();
}
vals.copy_from(combined_ptr + for_bytes, after_bytes);
ops.insert(ops.begin() + static_cast<blt::ptrdiff_t>(c_node),
{program.get_typesystem().get_type(replacement_func_info.return_type).size(),
random_replacement, program.is_operator_ephemeral(random_replacement)});
{
program.get_typesystem().get_type(replacement_func_info.return_type).size(),
random_replacement, program.is_operator_ephemeral(random_replacement)
});
#if BLT_DEBUG_LEVEL >= 2
if (!c.check(program, nullptr))
@ -649,8 +661,8 @@ namespace blt::gp
}
#endif
}
break;
case mutation_operator::JUMP_FUNC:
break;
case mutation_operator::JUMP_FUNC:
{
auto& info = program.get_operator_info(ops[c_node].id);
blt::size_t argument_index = -1ul;
@ -664,21 +676,21 @@ namespace blt::gp
}
if (argument_index == -1ul)
continue;
static thread_local tracked_vector<tree_t::child_t> child_data;
child_data.clear();
c.find_child_extends(program, child_data, c_node, info.argument_types.size());
auto child_index = child_data.size() - 1 - argument_index;
auto child = child_data[child_index];
auto for_bytes = c.total_value_bytes(child.start, child.end);
auto after_bytes = c.total_value_bytes(child_data.back().end);
auto storage_ptr = get_thread_pointer_for_size<struct jump_func>(for_bytes + after_bytes);
vals.copy_to(storage_ptr + for_bytes, after_bytes);
vals.pop_bytes(static_cast<blt::ptrdiff_t>(after_bytes));
for (auto i = static_cast<blt::ptrdiff_t>(child_data.size() - 1); i > static_cast<blt::ptrdiff_t>(child_index); i--)
{
auto& cc = child_data[i];
@ -710,8 +722,8 @@ namespace blt::gp
}
#endif
}
break;
case mutation_operator::COPY:
break;
case mutation_operator::COPY:
{
auto& info = program.get_operator_info(ops[c_node].id);
blt::size_t pt = -1ul;
@ -735,25 +747,26 @@ namespace blt::gp
}
if (pt == -1ul || pf == -1ul)
continue;
blt::size_t from = 0;
blt::size_t to = 0;
if (program.get_random().choice())
{
from = pt;
to = pf;
} else
}
else
{
from = pf;
to = pt;
}
static thread_local tracked_vector<tree_t::child_t> child_data;
child_data.clear();
c.find_child_extends(program, child_data, c_node, info.argument_types.size());
auto from_index = child_data.size() - 1 - from;
auto to_index = child_data.size() - 1 - to;
auto& from_child = child_data[from_index];
@ -762,37 +775,37 @@ namespace blt::gp
blt::size_t after_from_bytes = c.total_value_bytes(from_child.end);
blt::size_t to_bytes = c.total_value_bytes(to_child.start, to_child.end);
blt::size_t after_to_bytes = c.total_value_bytes(to_child.end);
auto after_bytes = std::max(after_from_bytes, after_to_bytes);
auto from_ptr = get_thread_pointer_for_size<struct copy>(from_bytes);
auto after_ptr = get_thread_pointer_for_size<struct copy_after>(after_bytes);
vals.copy_to(after_ptr, after_from_bytes);
vals.pop_bytes(static_cast<blt::ptrdiff_t>(after_from_bytes));
vals.copy_to(from_ptr, from_bytes);
vals.copy_from(after_ptr, after_from_bytes);
vals.copy_to(after_ptr, after_to_bytes);
vals.pop_bytes(static_cast<blt::ptrdiff_t>(after_to_bytes + to_bytes));
vals.copy_from(from_ptr, from_bytes);
vals.copy_from(after_ptr, after_to_bytes);
static thread_local tracked_vector<op_container_t> op_copy;
op_copy.clear();
op_copy.insert(op_copy.begin(), ops.begin() + from_child.start, ops.begin() + from_child.end);
ops.erase(ops.begin() + to_child.start, ops.begin() + to_child.end);
ops.insert(ops.begin() + to_child.start, op_copy.begin(), op_copy.end());
}
break;
case mutation_operator::END:
default:
break;
case mutation_operator::END:
default:
#if BLT_DEBUG_LEVEL > 1
BLT_ABORT("You shouldn't be able to get here!");
#else
BLT_UNREACHABLE;
BLT_UNREACHABLE;
#endif
}
}
@ -808,7 +821,7 @@ namespace blt::gp
BLT_ABORT("Tree Check Failed.");
}
#endif
return true;
}
}
}