fix copy op, change selection behaviour

dev-func-drop
Brett 2025-01-21 21:20:16 -05:00
parent 82c535dbd1
commit a58fe64c0e
7 changed files with 210 additions and 238 deletions

View File

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

View File

@ -35,7 +35,7 @@ blt::gp::prog_config_t config = blt::gp::prog_config_t()
.set_initial_min_tree_size(2) .set_initial_min_tree_size(2)
.set_initial_max_tree_size(6) .set_initial_max_tree_size(6)
.set_elite_count(2) .set_elite_count(2)
.set_crossover_chance(0.9) .set_crossover_chance(0.8)
.set_mutation_chance(0.1) .set_mutation_chance(0.1)
.set_reproduction_chance(0) .set_reproduction_chance(0)
.set_max_generations(50) .set_max_generations(50)

View File

@ -568,9 +568,9 @@ namespace blt::gp
while (thread_helper.next_gen_left > 0) while (thread_helper.next_gen_left > 0)
{ {
blt::size_t size = 0; size_t size = 0;
blt::size_t begin = 0; size_t begin = 0;
blt::size_t end = thread_helper.next_gen_left.load(std::memory_order_relaxed); size_t end = thread_helper.next_gen_left.load(std::memory_order_relaxed);
do do
{ {
size = std::min(end, config.evaluation_size); size = std::min(end, config.evaluation_size);
@ -766,26 +766,24 @@ namespace blt::gp
{ {
if (get_random().choice(selection_probabilities.crossover_chance)) if (get_random().choice(selection_probabilities.crossover_chance))
{ {
// if (c2 == nullptr) auto ptr = c2;
// return 0; if (ptr == nullptr)
// auto ptr = c2; ptr = &tree_t::get_thread_local(*this);
// if (ptr == nullptr)
// ptr = &tree_t::get_thread_local(*this);
#ifdef BLT_TRACK_ALLOCATIONS #ifdef BLT_TRACK_ALLOCATIONS
auto state = tracker.start_measurement_thread_local(); auto state = tracker.start_measurement_thread_local();
#endif #endif
const tree_t* p1; const tree_t* p1;
const tree_t* p2; const tree_t* p2;
size_t runs = 0; size_t runs = 0;
tree_t tree{*this};
do do
{ {
// BLT_TRACE("%lu %p %p", runs, &c1, &tree); // BLT_TRACE("%lu %p %p", runs, &c1, &tree);
p1 = &crossover.select(*this, current_pop); p1 = &crossover.select(*this, current_pop);
p2 = &crossover.select(*this, current_pop); p2 = &crossover.select(*this, current_pop);
// BLT_TRACE("%p %p || %lu", p1, p2, current_pop.get_individuals().size());
c1.copy_fast(*p1); c1.copy_fast(*p1);
tree.copy_fast(*p2); ptr->copy_fast(*p2);
// ptr->copy_fast(*p2); // ptr->copy_fast(*p2);
if (++runs >= config.crossover.get().get_config().max_crossover_iterations) if (++runs >= config.crossover.get().get_config().max_crossover_iterations)
@ -794,7 +792,7 @@ namespace blt::gp
crossover_calls.value(1); crossover_calls.value(1);
#endif #endif
} }
while (!config.crossover.get().apply(*this, *p1, *p2, c1, tree)); while (!config.crossover.get().apply(*this, *p1, *p2, c1, *ptr));
#ifdef BLT_TRACK_ALLOCATIONS #ifdef BLT_TRACK_ALLOCATIONS
tracker.stop_measurement_thread_local(state); tracker.stop_measurement_thread_local(state);
crossover_calls.call(); crossover_calls.call();
@ -804,10 +802,11 @@ namespace blt::gp
crossover_allocations.set_value(std::max(crossover_allocations.get_value(), state.getAllocatedByteDifference())); crossover_allocations.set_value(std::max(crossover_allocations.get_value(), state.getAllocatedByteDifference()));
} }
#endif #endif
// if (c2 == nullptr) if (c2 == nullptr)
// tree_t::get_thread_local(*this); {
if (c2 != nullptr) tree_t::get_thread_local(*this).clear(*this);
*c2 = tree; return 1;
}
return 2; return 2;
} }
if (get_random().choice(selection_probabilities.mutation_chance)) if (get_random().choice(selection_probabilities.mutation_chance))

View File

@ -30,16 +30,16 @@ namespace blt::gp
{ {
namespace detail namespace detail
{ {
template<typename T> template <typename T>
inline static constexpr double sum(const T& array) inline static constexpr double sum(const T& array)
{ {
double init = 0.0; double init = 0.0;
for (double i : array) for (const double i : array)
init += i; init += i;
return init; return init;
} }
template<blt::size_t size, typename... Args> template <size_t size, typename... Args>
static constexpr std::array<double, size> aggregate_array(Args... list) static constexpr std::array<double, size> aggregate_array(Args... list)
{ {
std::array<double, size> data{list...}; std::array<double, size> data{list...};
@ -54,145 +54,151 @@ namespace blt::gp
return data; return data;
} }
} }
class crossover_t class crossover_t
{ {
public: public:
struct point_info_t struct point_info_t
{ {
ptrdiff_t point; ptrdiff_t point;
operator_info_t& type_operator_info; operator_info_t& type_operator_info;
}; };
struct crossover_point_t
{
tree_t::subtree_point_t p1_crossover_point;
tree_t::subtree_point_t p2_crossover_point;
};
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
u32 max_crossover_tries = 5;
// how many times the crossover function can fail before we will skip this operation.
u32 max_crossover_iterations = 10;
// if tree have fewer nodes than this number, they will not be considered for crossover
// should be at least 5 as crossover will not select the root node.
u32 min_tree_size = 5;
// 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 depth_multiplier = 0.5;
// how often should we select terminals over functions. By default, we only allow selection of terminals 10% of the time
// this applies to both types of crossover point functions. Traversal will use the parent if it should not pick a terminal.
f32 terminal_chance = 0.1;
// use traversal to select point instead of random selection
bool traverse = false;
};
crossover_t() = default;
explicit crossover_t(const config_t& config): config(config)
{}
[[nodiscard]] const config_t& get_config() const struct crossover_point_t
{ {
return config; tree_t::subtree_point_t p1_crossover_point;
} tree_t::subtree_point_t p2_crossover_point;
};
std::optional<crossover_point_t> get_crossover_point(const tree_t& c1, const tree_t& c2) const;
struct config_t
std::optional<crossover_point_t> get_crossover_point_traverse(const tree_t& c1, const tree_t& c2) const; {
// 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
/** u32 max_crossover_tries = 5;
* child1 and child2 are copies of the parents, the result of selecting a crossover point and performing standard subtree crossover. // how many times the crossover function can fail before we will skip this operation.
* the parents are not modified during this process u32 max_crossover_iterations = 10;
* @param program reference to the global program container responsible for managing these trees // if tree have fewer nodes than this number, they will not be considered for crossover
* @param p1 reference to the first parent // should be at least 5 as crossover will not select the root node.
* @param p2 reference to the second parent u32 min_tree_size = 5;
* @return expected pair of child otherwise returns error enum // 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
virtual bool apply(gp_program& program, const tree_t& p1, const tree_t& p2, tree_t& c1, tree_t& c2); // NOLINT // this is what this option configures.
f32 depth_multiplier = 0.5;
virtual ~crossover_t() = default; // how often should we select terminals over functions. By default, we only allow selection of terminals 10% of the time
// this applies to both types of crossover point functions. Traversal will use the parent if it should not pick a terminal.
protected: f32 terminal_chance = 0.1;
[[nodiscard]] std::optional<tree_t::subtree_point_t> get_point_traverse_retry(const tree_t& t, std::optional<type_id> type) const; // use traversal to select point instead of random selection
bool traverse = false;
config_t config; };
crossover_t() = default;
explicit crossover_t(const config_t& config): config(config)
{
}
[[nodiscard]] const config_t& get_config() const
{
return config;
}
std::optional<crossover_point_t> get_crossover_point(const tree_t& c1, const tree_t& c2) const;
std::optional<crossover_point_t> get_crossover_point_traverse(const tree_t& c1, const tree_t& c2) const;
/**
* 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
* @param program reference to the global program container responsible for managing these trees
* @param p1 reference to the first parent
* @param p2 reference to the second parent
* @return expected pair of child otherwise returns error enum
*/
virtual bool apply(gp_program& program, const tree_t& p1, const tree_t& p2, tree_t& c1, tree_t& c2); // NOLINT
virtual ~crossover_t() = default;
protected:
[[nodiscard]] std::optional<tree_t::subtree_point_t> get_point_traverse_retry(const tree_t& t, std::optional<type_id> type) const;
config_t config;
}; };
class mutation_t class mutation_t
{ {
public: public:
struct config_t struct config_t
{
blt::size_t replacement_min_depth = 2;
blt::size_t replacement_max_depth = 6;
std::reference_wrapper<tree_generator_t> generator;
config_t(tree_generator_t& generator): generator(generator) // NOLINT
{ {
blt::size_t replacement_min_depth = 2; }
blt::size_t replacement_max_depth = 6;
config_t();
std::reference_wrapper<tree_generator_t> generator; };
config_t(tree_generator_t& generator): generator(generator) // NOLINT mutation_t() = default;
{}
explicit mutation_t(const config_t& config): config(config)
config_t(); {
}; }
mutation_t() = default; virtual bool apply(gp_program& program, const tree_t& p, tree_t& c);
explicit mutation_t(const config_t& config): config(config) // returns the point after the mutation
{} size_t mutate_point(gp_program& program, tree_t& c, tree_t::subtree_point_t node) const;
virtual bool apply(gp_program& program, const tree_t& p, tree_t& c); virtual ~mutation_t() = default;
// returns the point after the mutation protected:
size_t mutate_point(gp_program& program, tree_t& c, tree_t::subtree_point_t node) const; config_t config;
virtual ~mutation_t() = default;
protected:
config_t config;
}; };
class advanced_mutation_t : public mutation_t class advanced_mutation_t : public mutation_t
{ {
public: public:
enum class mutation_operator : blt::i32 enum class mutation_operator : i32
{ {
EXPRESSION, // Generate a new random expression EXPRESSION, // Generate a new random expression
ADJUST, // adjust the value of the type. (if it is a function it will mutate it to a different one) ADJUST, // adjust the value of the type. (if it is a function it will mutate it to a different one)
SUB_FUNC, // subexpression becomes argument to new random function. Other args are generated. SUB_FUNC, // subexpression becomes argument to new random function. Other args are generated.
JUMP_FUNC, // subexpression becomes this new node. Other arguments discarded. JUMP_FUNC, // subexpression becomes this new node. Other arguments discarded.
COPY, // node can become copy of another subexpression. COPY, // node can become copy of another subexpression.
END, // helper END, // helper
}; };
advanced_mutation_t() = default; advanced_mutation_t() = default;
explicit advanced_mutation_t(const config_t& config): mutation_t(config) explicit advanced_mutation_t(const config_t& config): mutation_t(config)
{} {
}
bool apply(gp_program& program, const tree_t& p, tree_t& c) final;
bool apply(gp_program& program, const tree_t& p, tree_t& c) final;
advanced_mutation_t& set_per_node_mutation_chance(double v)
{ advanced_mutation_t& set_per_node_mutation_chance(double v)
per_node_mutation_chance = v; {
return *this; per_node_mutation_chance = v;
} return *this;
}
private:
static constexpr auto operators_size = static_cast<blt::i32>(mutation_operator::END); private:
private: static constexpr auto operators_size = static_cast<blt::i32>(mutation_operator::END);
// this value is adjusted inversely to the size of the tree.
double per_node_mutation_chance = 5.0; private:
// this value is adjusted inversely to the size of the tree.
static constexpr std::array<double, operators_size> mutation_operator_chances = detail::aggregate_array<operators_size>( double per_node_mutation_chance = 5.0;
0.25, // EXPRESSION
0.15, // ADJUST static constexpr std::array<double, operators_size> mutation_operator_chances = detail::aggregate_array<operators_size>(
0.01, // SUB_FUNC 0.25, // EXPRESSION
0.01, // JUMP_FUNC 0.20, // ADJUST
0.05 // COPY 0.05, // SUB_FUNC
); 0.15, // JUMP_FUNC
0.10 // COPY
);
}; };
} }
#endif //BLT_GP_TRANSFORMERS_H #endif //BLT_GP_TRANSFORMERS_H

@ -1 +1 @@
Subproject commit 74c1010118c3ae13f27499f564ce477b23ae0b0a Subproject commit baa5952666594ce0d07a2b013e46c4bc343ba164

View File

@ -26,7 +26,7 @@
namespace blt::gp namespace blt::gp
{ {
#if BLT_DEBUG_LEVEL >= 2 #if BLT_DEBUG_LEVEL >= 2 || defined(BLT_TRACK_ALLOCATIONS)
std::atomic_uint64_t mutate_point_counter = 0; std::atomic_uint64_t mutate_point_counter = 0;
std::atomic_uint64_t mutate_expression_counter = 0; std::atomic_uint64_t mutate_expression_counter = 0;
std::atomic_uint64_t mutate_adjust_counter = 0; std::atomic_uint64_t mutate_adjust_counter = 0;
@ -36,35 +36,31 @@ namespace blt::gp
inline void print_mutate_stats() inline void print_mutate_stats()
{ {
std::cerr << "Mutation statistics:" << std::endl; std::cerr << "Mutation statistics (Total: " << (mutate_point_counter + mutate_expression_counter + mutate_adjust_counter +
mutate_sub_func_counter + mutate_jump_counter + mutate_copy_counter) << "):" << std::endl;
std::cerr << "\tSuccessful Point Mutations: " << mutate_point_counter << std::endl; std::cerr << "\tSuccessful Point Mutations: " << mutate_point_counter << std::endl;
std::cerr << "\tSuccessful Expression Mutations: " << mutate_expression_counter << std::endl; std::cerr << "\tSuccessful Expression Mutations: " << mutate_expression_counter << std::endl;
std::cerr << "\tSuccessful Adjust Mutations: " << mutate_adjust_counter << std::endl; std::cerr << "\tSuccessful Adjust Mutations: " << mutate_adjust_counter << std::endl;
std::cerr << "\tSuccessful Sub Func Mutations: " << mutate_sub_func_counter << std::endl; std::cerr << "\tSuccessful Sub Func Mutations: " << mutate_sub_func_counter << std::endl;
std::cerr << "\tSuccessful Func Jump Mutations: " << mutate_jump_counter << std::endl; std::cerr << "\tSuccessful Jump Mutations: " << mutate_jump_counter << std::endl;
std::cerr << "\tSuccessful Copy Mutations: " << mutate_copy_counter << std::endl; std::cerr << "\tSuccessful Copy Mutations: " << mutate_copy_counter << std::endl;
} }
#ifdef BLT_TRACK_ALLOCATIONS
struct run_me_baby
{
~run_me_baby()
{
print_mutate_stats();
}
};
run_me_baby this_will_run_when_program_exits;
#endif
#endif #endif
grow_generator_t grow_generator; grow_generator_t grow_generator;
inline tree_t& get_static_tree_tl(gp_program& program)
{
thread_local tree_t new_tree{program};
new_tree.clear(program);
return new_tree;
}
// TODO: consolidate the two copies of this. other is in tree.cpp
template <typename>
static u8* get_thread_pointer_for_size(const size_t bytes)
{
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) mutation_t::config_t::config_t(): generator(grow_generator)
{ {
} }
@ -147,7 +143,7 @@ namespace blt::gp
size_t mutation_t::mutate_point(gp_program& program, tree_t& c, const tree_t::subtree_point_t node) const size_t mutation_t::mutate_point(gp_program& program, tree_t& c, const tree_t::subtree_point_t node) const
{ {
auto& new_tree = get_static_tree_tl(program); auto& new_tree = tree_t::get_thread_local(program);
config.generator.get().generate(new_tree, {program, node.type, config.replacement_min_depth, config.replacement_max_depth}); config.generator.get().generate(new_tree, {program, node.type, config.replacement_min_depth, config.replacement_max_depth});
c.replace_subtree(node, new_tree); c.replace_subtree(node, new_tree);
@ -159,6 +155,8 @@ namespace blt::gp
print_mutate_stats(); print_mutate_stats();
throw std::runtime_error("Mutate Point tree check failed"); throw std::runtime_error("Mutate Point tree check failed");
} }
#endif
#if defined(BLT_TRACK_ALLOCATIONS) || BLT_DEBUG_LEVEL >= 2
++mutate_point_counter; ++mutate_point_counter;
#endif #endif
return node.pos + new_tree.size(); return node.pos + new_tree.size();
@ -177,23 +175,13 @@ namespace blt::gp
// select an operator to apply // select an operator to apply
auto selected_point = static_cast<i32>(mutation_operator::COPY); auto selected_point = static_cast<i32>(mutation_operator::COPY);
auto choice = program.get_random().get_double(); auto choice = program.get_random().get_double();
for (const auto& [index, value] : blt::enumerate(mutation_operator_chances))
for (const auto& [index, value] : enumerate(mutation_operator_chances))
{ {
if (index == 0) if (choice <= value)
{ {
if (choice <= value) selected_point = static_cast<i32>(index);
{ break;
selected_point = static_cast<blt::i32>(index);
break;
}
}
else
{
if (choice > mutation_operator_chances[index - 1] && choice <= value)
{
selected_point = static_cast<blt::i32>(index);
break;
}
} }
} }
@ -201,7 +189,7 @@ namespace blt::gp
{ {
case mutation_operator::EXPRESSION: case mutation_operator::EXPRESSION:
c_node += mutate_point(program, c, c.subtree_from_point(static_cast<ptrdiff_t>(c_node))); c_node += mutate_point(program, c, c.subtree_from_point(static_cast<ptrdiff_t>(c_node)));
#if BLT_DEBUG_LEVEL >= 2 #if BLT_TRACK_ALLOCATIONS || BLT_DEBUG_LEVEL >= 2
++mutate_expression_counter; ++mutate_expression_counter;
#endif #endif
break; break;
@ -228,7 +216,7 @@ namespace blt::gp
if (index < current_func_info.argument_types.size() && val.id != current_func_info.argument_types[index].id) if (index < current_func_info.argument_types.size() && val.id != current_func_info.argument_types[index].id)
{ {
// TODO: new config? // TODO: new config?
auto& tree = get_static_tree_tl(program); auto& tree = tree_t::get_thread_local(program);
config.generator.get().generate(tree, config.generator.get().generate(tree,
{program, val.id, config.replacement_min_depth, config.replacement_max_depth}); {program, val.id, config.replacement_min_depth, config.replacement_max_depth});
@ -251,15 +239,6 @@ namespace blt::gp
} }
} }
child_end = static_cast<ptrdiff_t>(child_start + tree.size()); child_end = static_cast<ptrdiff_t>(child_start + tree.size());
#if BLT_DEBUG_LEVEL >= 2
if (!c.check(detail::debug::context_ptr))
{
print_mutate_stats();
throw std::runtime_error("Adjust Tree check failed");
}
++mutate_adjust_counter;
#endif
} }
} }
@ -285,7 +264,7 @@ namespace blt::gp
for (ptrdiff_t i = static_cast<ptrdiff_t>(replacement_func_info.argc.argc) - 1; for (ptrdiff_t i = static_cast<ptrdiff_t>(replacement_func_info.argc.argc) - 1;
i >= current_func_info.argc.argc; i--) i >= current_func_info.argc.argc; i--)
{ {
auto& tree = get_static_tree_tl(program); auto& tree = tree_t::get_thread_local(program);
config.generator.get().generate(tree, config.generator.get().generate(tree,
{ {
program, replacement_func_info.argument_types[i].id, config.replacement_min_depth, program, replacement_func_info.argument_types[i].id, config.replacement_min_depth,
@ -308,6 +287,8 @@ namespace blt::gp
print_mutate_stats(); print_mutate_stats();
BLT_ABORT("Adjust Tree Check Failed."); BLT_ABORT("Adjust Tree Check Failed.");
} }
#endif
#if defined(BLT_TRACK_ALLOCATIONS) || BLT_DEBUG_LEVEL >= 2
++mutate_adjust_counter; ++mutate_adjust_counter;
#endif #endif
} }
@ -358,7 +339,7 @@ namespace blt::gp
size_t start_index = c_node; size_t start_index = c_node;
for (ptrdiff_t i = new_argc - 1; i > static_cast<ptrdiff_t>(arg_position); i--) for (ptrdiff_t i = new_argc - 1; i > static_cast<ptrdiff_t>(arg_position); i--)
{ {
auto& tree = get_static_tree_tl(program); auto& tree = tree_t::get_thread_local(program);
config.generator.get().generate(tree, config.generator.get().generate(tree,
{ {
program, replacement_func_info.argument_types[i].id, config.replacement_min_depth, program, replacement_func_info.argument_types[i].id, config.replacement_min_depth,
@ -370,7 +351,7 @@ namespace blt::gp
// vals.copy_from(combined_ptr, for_bytes); // vals.copy_from(combined_ptr, for_bytes);
for (blt::ptrdiff_t i = static_cast<blt::ptrdiff_t>(arg_position) - 1; i >= 0; i--) for (blt::ptrdiff_t i = static_cast<blt::ptrdiff_t>(arg_position) - 1; i >= 0; i--)
{ {
auto& tree = get_static_tree_tl(program); auto& tree = tree_t::get_thread_local(program);
config.generator.get().generate(tree, config.generator.get().generate(tree,
{ {
program, replacement_func_info.argument_types[i].id, config.replacement_min_depth, program, replacement_func_info.argument_types[i].id, config.replacement_min_depth,
@ -397,6 +378,8 @@ namespace blt::gp
print_mutate_stats(); print_mutate_stats();
BLT_ABORT("SUB_FUNC Tree Check Failed."); BLT_ABORT("SUB_FUNC Tree Check Failed.");
} }
#endif
#if defined(BLT_TRACK_ALLOCATIONS) || BLT_DEBUG_LEVEL >= 2
++mutate_sub_func_counter; ++mutate_sub_func_counter;
#endif #endif
} }
@ -469,6 +452,8 @@ namespace blt::gp
print_mutate_stats(); print_mutate_stats();
BLT_ABORT("JUMP_FUNC Tree Check Failed."); BLT_ABORT("JUMP_FUNC Tree Check Failed.");
} }
#endif
#if defined(BLT_TRACK_ALLOCATIONS) || BLT_DEBUG_LEVEL >= 2
++mutate_jump_counter; ++mutate_jump_counter;
#endif #endif
} }
@ -476,55 +461,36 @@ namespace blt::gp
case mutation_operator::COPY: case mutation_operator::COPY:
{ {
auto& info = program.get_operator_info(c.get_operator(c_node).id()); auto& info = program.get_operator_info(c.get_operator(c_node).id());
size_t pt = -1ul; if (c.get_operator(c_node).is_value())
size_t pf = -1ul;
for (const auto& [index, v] : blt::enumerate(info.argument_types))
{
for (size_t i = index + 1; i < info.argument_types.size(); i++)
{
auto& v1 = info.argument_types[i];
if (v == v1)
{
if (pt == -1ul)
pt = index;
else
pf = index;
break;
}
}
if (pt != -1ul && pf != -1ul)
break;
}
if (pt == -1ul || pf == -1ul)
continue; continue;
thread_local tracked_vector<size_t> potential_indexes;
potential_indexes.clear();
size_t from = 0; const auto from_index = program.get_random().get_u64(0, info.argument_types.size());
size_t to = 0; for (const auto [index, type] : enumerate(info.argument_types))
if (program.get_random().choice())
{ {
from = pt; if (index == from_index)
to = pf; continue;
} if (info.argument_types[from_index] == type)
else potential_indexes.push_back(index);
{
from = pf;
to = pt;
} }
if (potential_indexes.empty())
continue;
const auto to_index = program.get_random().select(potential_indexes);
thread_local tracked_vector<tree_t::child_t> child_data; thread_local tracked_vector<tree_t::child_t> child_data;
child_data.clear(); child_data.clear();
c.find_child_extends(child_data, c_node, info.argument_types.size()); c.find_child_extends(child_data, c_node, info.argument_types.size());
auto from_index = child_data.size() - 1 - from; const auto child_from_index = child_data.size() - 1 - from_index;
auto to_index = child_data.size() - 1 - to; const auto child_to_index = child_data.size() - 1 - to_index;
auto& from_child = child_data[from_index]; const auto& [from_start, from_end] = child_data[child_from_index];
auto& to_child = child_data[to_index]; const auto& [to_start, to_end] = child_data[child_to_index];
thread_local tree_t copy_tree{program}; thread_local tree_t copy_tree{program};
c.copy_subtree(tree_t::subtree_point_t{from_child.start}, from_child.end, copy_tree); c.copy_subtree(tree_t::subtree_point_t{from_start}, from_end, copy_tree);
c.replace_subtree(tree_t::subtree_point_t{to_child.start}, to_child.end, copy_tree); c.replace_subtree(tree_t::subtree_point_t{to_start}, to_end, copy_tree);
copy_tree.clear(program); copy_tree.clear(program);
#if BLT_DEBUG_LEVEL >= 2 #if BLT_DEBUG_LEVEL >= 2
@ -538,6 +504,8 @@ namespace blt::gp
print_mutate_stats(); print_mutate_stats();
BLT_ABORT("COPY Tree Check Failed."); BLT_ABORT("COPY Tree Check Failed.");
} }
#endif
#if defined(BLT_TRACK_ALLOCATIONS) || BLT_DEBUG_LEVEL >= 2
++mutate_copy_counter; ++mutate_copy_counter;
#endif #endif

View File

@ -501,7 +501,6 @@ namespace blt::gp
tree_t& tree_t::get_thread_local(gp_program& program) tree_t& tree_t::get_thread_local(gp_program& program)
{ {
thread_local tree_t tree{program}; thread_local tree_t tree{program};
tree.clear(program);
return tree; return tree;
} }