selection, add lambdas to operators, few tests, mutation
parent
f37582ab4a
commit
f983c0fb62
|
@ -0,0 +1,3 @@
|
|||
<component name="DependencyValidationManager">
|
||||
<scope name="gp progject" pattern="file[blt-gp]:include//*||file[blt-gp]:examples//*||file[blt-gp]:src//*" />
|
||||
</component>
|
|
@ -1,5 +1,5 @@
|
|||
cmake_minimum_required(VERSION 3.25)
|
||||
project(blt-gp VERSION 0.0.50)
|
||||
project(blt-gp VERSION 0.0.51)
|
||||
|
||||
include(CTest)
|
||||
|
||||
|
@ -77,5 +77,7 @@ if (${BUILD_EXAMPLES})
|
|||
blt_add_example(blt-gp3 examples/gp_test_3.cpp)
|
||||
blt_add_example(blt-gp4 examples/gp_test_4.cpp)
|
||||
blt_add_example(blt-gp5 examples/gp_test_5.cpp)
|
||||
blt_add_example(blt-gp6 examples/gp_test_6.cpp)
|
||||
blt_add_example(blt-gp7 examples/gp_test_7.cpp)
|
||||
|
||||
endif ()
|
|
@ -78,7 +78,7 @@ int main()
|
|||
|
||||
auto pop = pop_init.generate(blt::gp::initializer_arguments{program, type_system.get_type<float>().id(), 500, 3, 10});
|
||||
|
||||
for (auto& tree : pop.getIndividuals())
|
||||
for (auto& tree : pop.for_each_tree())
|
||||
{
|
||||
auto value = tree.get_evaluation_value<float>(nullptr);
|
||||
|
||||
|
|
|
@ -116,7 +116,7 @@ int main()
|
|||
BLT_INFO("Pre-Crossover:");
|
||||
for (auto& tree : pop.getIndividuals())
|
||||
{
|
||||
auto f = tree.get_evaluation_value<float>(nullptr);
|
||||
auto f = tree.tree.get_evaluation_value<float>(nullptr);
|
||||
pre.push_back(f);
|
||||
BLT_TRACE(f);
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ int main()
|
|||
second = dist(random);
|
||||
} while (second == first);
|
||||
|
||||
auto results = crossover.apply(program, ind[first], ind[second]);
|
||||
auto results = crossover.apply(program, ind[first].tree, ind[second].tree);
|
||||
if (results.has_value())
|
||||
{
|
||||
// bool print_literals = true;
|
||||
|
@ -149,8 +149,8 @@ int main()
|
|||
// results->child1.print(program, std::cout, print_literals, pretty_print, print_returns);
|
||||
// BLT_TRACE("Child 2: %f", results->child2.get_evaluation_value<float>(nullptr));
|
||||
// results->child2.print(program, std::cout, print_literals, pretty_print, print_returns);
|
||||
new_pop.getIndividuals().push_back(std::move(results->child1));
|
||||
new_pop.getIndividuals().push_back(std::move(results->child2));
|
||||
new_pop.getIndividuals().push_back({std::move(results->child1)});
|
||||
new_pop.getIndividuals().push_back({std::move(results->child2)});
|
||||
} else
|
||||
{
|
||||
switch (results.error())
|
||||
|
@ -169,7 +169,7 @@ int main()
|
|||
}
|
||||
|
||||
BLT_INFO("Post-Crossover:");
|
||||
for (auto& tree : new_pop.getIndividuals())
|
||||
for (auto& tree : new_pop.for_each_tree())
|
||||
{
|
||||
auto f = tree.get_evaluation_value<float>(nullptr);
|
||||
pos.push_back(f);
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* <Short Description>
|
||||
* Copyright (C) 2024 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/*
|
||||
* <Short Description>
|
||||
* Copyright (C) 2024 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <blt/gp/program.h>
|
||||
#include <blt/gp/generators.h>
|
||||
#include <blt/gp/tree.h>
|
||||
#include <blt/std/logging.h>
|
||||
#include <blt/gp/transformers.h>
|
||||
|
||||
static constexpr long SEED = 41912;
|
||||
|
||||
|
||||
blt::gp::type_provider type_system;
|
||||
blt::gp::gp_program program(type_system, std::mt19937_64{SEED}); // NOLINT
|
||||
|
||||
blt::gp::operation_t add([](float a, float b) { return a + b; }, "add"); // 0
|
||||
blt::gp::operation_t sub([](float a, float b) { return a - b; }, "sub"); // 1
|
||||
blt::gp::operation_t mul([](float a, float b) { return a * b; }, "mul"); // 2
|
||||
blt::gp::operation_t pro_div([](float a, float b) { return b == 0 ? 0.0f : a / b; }, "div"); // 3
|
||||
|
||||
blt::gp::operation_t op_if([](bool b, float a, float c) { return b ? a : c; }, "if"); // 4
|
||||
blt::gp::operation_t eq_f([](float a, float b) { return a == b; }, "eq_f"); // 5
|
||||
blt::gp::operation_t eq_b([](bool a, bool b) { return a == b; }, "eq_b"); // 6
|
||||
blt::gp::operation_t lt([](float a, float b) { return a < b; }, "lt"); // 7
|
||||
blt::gp::operation_t gt([](float a, float b) { return a > b; }, "gt"); // 8
|
||||
blt::gp::operation_t op_and([](bool a, bool b) { return a && b; }, "and"); // 9
|
||||
blt::gp::operation_t op_or([](bool a, bool b) { return a || b; }, "or"); // 10
|
||||
blt::gp::operation_t op_xor([](bool a, bool b) { return static_cast<bool>(a ^ b); }, "xor"); // 11
|
||||
blt::gp::operation_t op_not([](bool b) { return !b; }, "not"); // 12
|
||||
|
||||
blt::gp::operation_t lit([]() { // 13
|
||||
//static std::uniform_real_distribution<float> dist(-32000, 32000);
|
||||
static std::uniform_real_distribution<float> dist(0.0f, 10.0f);
|
||||
return dist(program.get_random());
|
||||
}, "lit");
|
||||
|
||||
/**
|
||||
* This is a test using multiple types with blt::gp
|
||||
*/
|
||||
int main()
|
||||
{
|
||||
type_system.register_type<float>();
|
||||
type_system.register_type<bool>();
|
||||
|
||||
blt::gp::operator_builder builder{type_system};
|
||||
builder.add_operator(add);
|
||||
builder.add_operator(sub);
|
||||
builder.add_operator(mul);
|
||||
builder.add_operator(pro_div);
|
||||
|
||||
builder.add_operator(op_if);
|
||||
builder.add_operator(eq_f);
|
||||
builder.add_operator(eq_b);
|
||||
builder.add_operator(lt);
|
||||
builder.add_operator(gt);
|
||||
builder.add_operator(op_and);
|
||||
builder.add_operator(op_or);
|
||||
builder.add_operator(op_xor);
|
||||
builder.add_operator(op_not);
|
||||
|
||||
builder.add_operator(lit, true);
|
||||
|
||||
program.set_operations(builder.build());
|
||||
|
||||
blt::gp::ramped_half_initializer_t pop_init;
|
||||
|
||||
auto pop = pop_init.generate(blt::gp::initializer_arguments{program, type_system.get_type<float>().id(), 500, 3, 10});
|
||||
|
||||
blt::gp::population_t new_pop;
|
||||
blt::gp::mutation_t mutator;
|
||||
blt::gp::grow_generator_t generator;
|
||||
|
||||
std::vector<float> pre;
|
||||
std::vector<float> pos;
|
||||
|
||||
BLT_INFO("Pre-Mutation:");
|
||||
for (auto& tree : pop.for_each_tree())
|
||||
{
|
||||
auto f = tree.get_evaluation_value<float>(nullptr);
|
||||
pre.push_back(f);
|
||||
BLT_TRACE(f);
|
||||
}
|
||||
BLT_INFO("Mutation:");
|
||||
for (auto& tree : pop.for_each_tree())
|
||||
{
|
||||
new_pop.getIndividuals().push_back({mutator.apply(program, generator, tree)});
|
||||
}
|
||||
BLT_INFO("Post-Mutation");
|
||||
for (auto& tree : new_pop.for_each_tree())
|
||||
{
|
||||
auto f = tree.get_evaluation_value<float>(nullptr);
|
||||
pos.push_back(f);
|
||||
BLT_TRACE(f);
|
||||
}
|
||||
|
||||
BLT_INFO("Stats:");
|
||||
blt::size_t eq = 0;
|
||||
for (const auto& v : pos)
|
||||
{
|
||||
for (const auto m : pre)
|
||||
{
|
||||
if (v == m)
|
||||
{
|
||||
eq++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
BLT_INFO("Equal values: %ld", eq);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* <Short Description>
|
||||
* Copyright (C) 2024 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <blt/gp/program.h>
|
||||
#include <blt/gp/generators.h>
|
||||
#include <blt/gp/tree.h>
|
||||
#include <blt/std/logging.h>
|
||||
#include <blt/gp/transformers.h>
|
||||
|
||||
static constexpr long SEED = 41912;
|
||||
|
||||
|
||||
blt::gp::type_provider type_system;
|
||||
blt::gp::gp_program program(type_system, std::mt19937_64{SEED}); // NOLINT
|
||||
|
||||
blt::gp::operation_t add([](float a, float b) { return a + b; }, "add"); // 0
|
||||
blt::gp::operation_t sub([](float a, float b) { return a - b; }, "sub"); // 1
|
||||
blt::gp::operation_t mul([](float a, float b) { return a * b; }, "mul"); // 2
|
||||
blt::gp::operation_t pro_div([](float a, float b) { return b == 0 ? 0.0f : a / b; }, "div"); // 3
|
||||
|
||||
blt::gp::operation_t op_if([](bool b, float a, float c) { return b ? a : c; }, "if"); // 4
|
||||
blt::gp::operation_t eq_f([](float a, float b) { return a == b; }, "eq_f"); // 5
|
||||
blt::gp::operation_t eq_b([](bool a, bool b) { return a == b; }, "eq_b"); // 6
|
||||
blt::gp::operation_t lt([](float a, float b) { return a < b; }, "lt"); // 7
|
||||
blt::gp::operation_t gt([](float a, float b) { return a > b; }, "gt"); // 8
|
||||
blt::gp::operation_t op_and([](bool a, bool b) { return a && b; }, "and"); // 9
|
||||
blt::gp::operation_t op_or([](bool a, bool b) { return a || b; }, "or"); // 10
|
||||
blt::gp::operation_t op_xor([](bool a, bool b) { return static_cast<bool>(a ^ b); }, "xor"); // 11
|
||||
blt::gp::operation_t op_not([](bool b) { return !b; }, "not"); // 12
|
||||
|
||||
blt::gp::operation_t lit([]() { // 13
|
||||
//static std::uniform_real_distribution<float> dist(-32000, 32000);
|
||||
static std::uniform_real_distribution<float> dist(0.0f, 10.0f);
|
||||
return dist(program.get_random());
|
||||
}, "lit");
|
||||
|
||||
/**
|
||||
* This is a test using multiple types with blt::gp
|
||||
*/
|
||||
int main()
|
||||
{
|
||||
type_system.register_type<float>();
|
||||
type_system.register_type<bool>();
|
||||
|
||||
blt::gp::operator_builder builder{type_system};
|
||||
builder.add_operator(add);
|
||||
builder.add_operator(sub);
|
||||
builder.add_operator(mul);
|
||||
builder.add_operator(pro_div);
|
||||
|
||||
builder.add_operator(op_if);
|
||||
builder.add_operator(eq_f);
|
||||
builder.add_operator(eq_b);
|
||||
builder.add_operator(lt);
|
||||
builder.add_operator(gt);
|
||||
builder.add_operator(op_and);
|
||||
builder.add_operator(op_or);
|
||||
builder.add_operator(op_xor);
|
||||
builder.add_operator(op_not);
|
||||
|
||||
builder.add_operator(lit, true);
|
||||
|
||||
program.set_operations(builder.build());
|
||||
|
||||
blt::gp::ramped_half_initializer_t pop_init;
|
||||
|
||||
auto pop = pop_init.generate(blt::gp::initializer_arguments{program, type_system.get_type<float>().id(), 500, 3, 10});
|
||||
|
||||
blt::gp::population_t new_pop;
|
||||
blt::gp::mutation_t mutator;
|
||||
blt::gp::grow_generator_t generator;
|
||||
|
||||
std::vector<float> pre;
|
||||
std::vector<float> pos;
|
||||
|
||||
BLT_INFO("Pre-Mutation:");
|
||||
for (auto& tree : pop.for_each_tree())
|
||||
{
|
||||
auto f = tree.get_evaluation_value<float>(nullptr);
|
||||
pre.push_back(f);
|
||||
BLT_TRACE(f);
|
||||
}
|
||||
BLT_INFO("Mutation:");
|
||||
for (auto& tree : pop.for_each_tree())
|
||||
{
|
||||
new_pop.getIndividuals().push_back({mutator.apply(program, generator, tree)});
|
||||
}
|
||||
BLT_INFO("Post-Mutation");
|
||||
for (auto& tree : new_pop.for_each_tree())
|
||||
{
|
||||
auto f = tree.get_evaluation_value<float>(nullptr);
|
||||
pos.push_back(f);
|
||||
BLT_TRACE(f);
|
||||
}
|
||||
|
||||
BLT_INFO("Stats:");
|
||||
blt::size_t eq = 0;
|
||||
for (const auto& v : pos)
|
||||
{
|
||||
for (const auto m : pre)
|
||||
{
|
||||
if (v == m)
|
||||
{
|
||||
eq++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
BLT_INFO("Equal values: %ld", eq);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -23,6 +23,7 @@
|
|||
#include <blt/std/logging.h>
|
||||
#include <blt/std/types.h>
|
||||
#include <ostream>
|
||||
#include <optional>
|
||||
|
||||
namespace blt::gp
|
||||
{
|
||||
|
@ -55,7 +56,7 @@ namespace blt::gp
|
|||
// context*, read stack, write stack
|
||||
using callable_t = std::function<void(void*, stack_allocator&, stack_allocator&)>;
|
||||
// to, from
|
||||
using transfer_t = std::function<void(stack_allocator&, stack_allocator&)>;
|
||||
using transfer_t = std::function<void(std::optional<std::reference_wrapper<stack_allocator>>, stack_allocator&)>;
|
||||
// debug function,
|
||||
using print_func_t = std::function<void(std::ostream&, stack_allocator&)>;
|
||||
}
|
||||
|
|
|
@ -52,6 +52,8 @@ namespace blt::gp
|
|||
{
|
||||
public:
|
||||
virtual tree_t generate(const generator_arguments& args) = 0;
|
||||
|
||||
virtual ~tree_generator_t() = default;
|
||||
};
|
||||
|
||||
class grow_generator_t : public tree_generator_t
|
||||
|
@ -70,6 +72,8 @@ namespace blt::gp
|
|||
{
|
||||
public:
|
||||
virtual population_t generate(const initializer_arguments& args) = 0;
|
||||
|
||||
virtual ~population_initializer_t() = default;
|
||||
};
|
||||
|
||||
class grow_initializer_t : public population_initializer_t
|
||||
|
|
|
@ -107,14 +107,14 @@ namespace blt::gp
|
|||
using call_with<Return, Args...>::call_with;
|
||||
};
|
||||
|
||||
template<typename>
|
||||
template<typename, typename>
|
||||
class operation_t;
|
||||
|
||||
template<typename Return, typename... Args>
|
||||
class operation_t<Return(Args...)>
|
||||
template<typename ArgType, typename Return, typename... Args>
|
||||
class operation_t<ArgType, Return(Args...)>
|
||||
{
|
||||
public:
|
||||
using function_t = std::function<Return(Args...)>;
|
||||
using function_t = ArgType;
|
||||
|
||||
constexpr operation_t(const operation_t& copy) = default;
|
||||
|
||||
|
@ -183,24 +183,24 @@ namespace blt::gp
|
|||
std::optional<std::string_view> name;
|
||||
};
|
||||
|
||||
template<typename Return, typename Class, typename... Args>
|
||||
class operation_t<Return (Class::*)(Args...) const> : public operation_t<Return(Args...)>
|
||||
template<typename ArgType, typename Return, typename Class, typename... Args>
|
||||
class operation_t<ArgType, Return (Class::*)(Args...) const> : public operation_t<ArgType, Return(Args...)>
|
||||
{
|
||||
public:
|
||||
using operation_t<Return(Args...)>::operation_t;
|
||||
using operation_t<ArgType, Return(Args...)>::operation_t;
|
||||
};
|
||||
|
||||
template<typename Lambda>
|
||||
operation_t(Lambda) -> operation_t<decltype(&Lambda::operator())>;
|
||||
operation_t(Lambda) -> operation_t<Lambda, decltype(&Lambda::operator())>;
|
||||
|
||||
template<typename Return, typename... Args>
|
||||
operation_t(Return(*)(Args...)) -> operation_t<Return(Args...)>;
|
||||
operation_t(Return(*)(Args...)) -> operation_t<Return(*)(Args...), Return(Args...)>;
|
||||
|
||||
template<typename Lambda>
|
||||
operation_t(Lambda, std::optional<std::string_view>) -> operation_t<decltype(&Lambda::operator())>;
|
||||
operation_t(Lambda, std::optional<std::string_view>) -> operation_t<Lambda, decltype(&Lambda::operator())>;
|
||||
|
||||
template<typename Return, typename... Args>
|
||||
operation_t(Return(*)(Args...), std::optional<std::string_view>) -> operation_t<Return(Args...)>;
|
||||
operation_t(Return(*)(Args...), std::optional<std::string_view>) -> operation_t<Return(*)(Args...), Return(Args...)>;
|
||||
|
||||
// templat\e<typename Return, typename Class, typename... Args>
|
||||
// operation_t<Return(Args...)> make_operator(Return (Class::*)(Args...) const lambda)
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <iostream>
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include <blt/std/ranges.h>
|
||||
#include <blt/std/hashmap.h>
|
||||
|
@ -38,6 +39,7 @@
|
|||
#include <blt/gp/fwdecl.h>
|
||||
#include <blt/gp/typesystem.h>
|
||||
#include <blt/gp/operations.h>
|
||||
#include <blt/gp/transformers.h>
|
||||
#include <blt/gp/tree.h>
|
||||
#include <blt/gp/stack.h>
|
||||
|
||||
|
@ -55,16 +57,6 @@ 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;
|
||||
// if we fail to find a point in the tree, should we search forward from the last point to the end of the operators?
|
||||
bool should_crossover_try_forward = false;
|
||||
// avoid selecting terminals when doing crossover
|
||||
bool avoid_terminals = false;
|
||||
};
|
||||
|
||||
struct operator_info
|
||||
{
|
||||
// types of the arguments
|
||||
|
@ -87,10 +79,6 @@ namespace blt::gp
|
|||
blt::expanding_buffer<std::vector<std::pair<operator_id, blt::size_t>>> operators_ordered_terminals;
|
||||
// indexed from OPERATOR ID (operator number)
|
||||
blt::hashset_t<operator_id> static_types;
|
||||
// blt::expanding_buffer<std::vector<type>> argument_types;
|
||||
// blt::expanding_buffer<argc_t> operator_argc;
|
||||
// std::vector<detail::callable_t> operators;
|
||||
// std::vector<detail::transfer_t> transfer_funcs;
|
||||
std::vector<operator_info> operators;
|
||||
std::vector<detail::print_func_t> print_funcs;
|
||||
std::vector<std::optional<std::string_view>> names;
|
||||
|
@ -107,8 +95,8 @@ namespace blt::gp
|
|||
explicit operator_builder(type_provider& system): system(system)
|
||||
{}
|
||||
|
||||
template<typename Return, typename... Args>
|
||||
operator_builder& add_operator(const operation_t<Return(Args...)>& op, bool is_static = false)
|
||||
template<typename ArgType, typename Return, typename... Args>
|
||||
operator_builder& add_operator(const operation_t<ArgType, Return(Args...)>& op, bool is_static = false)
|
||||
{
|
||||
auto return_type_id = system.get_type<Return>().id();
|
||||
auto operator_id = blt::gp::operator_id(storage.operators.size());
|
||||
|
@ -131,13 +119,21 @@ namespace blt::gp
|
|||
BLT_ASSERT(info.argc.argc_context - info.argc.argc <= 1 && "Cannot pass multiple context as arguments!");
|
||||
|
||||
info.function = op.template make_callable<Context>();
|
||||
info.transfer = [](stack_allocator& to, stack_allocator& from) {
|
||||
info.transfer = [](std::optional<std::reference_wrapper<stack_allocator>> to, stack_allocator& from) {
|
||||
#if BLT_DEBUG_LEVEL >= 3
|
||||
auto value = from.pop<Return>();
|
||||
//BLT_TRACE_STREAM << value << "\n";
|
||||
to.push(value);
|
||||
if (to){
|
||||
to->get().push(value);
|
||||
}
|
||||
#else
|
||||
to.push(from.pop<Return>());
|
||||
if (to)
|
||||
{
|
||||
to->get().push(from.pop<Return>());
|
||||
} else
|
||||
{
|
||||
from.pop<Return>();
|
||||
}
|
||||
#endif
|
||||
|
||||
};
|
||||
|
@ -231,6 +227,68 @@ namespace blt::gp
|
|||
class gp_program
|
||||
{
|
||||
public:
|
||||
struct config_t
|
||||
{
|
||||
blt::size_t population_size = 500;
|
||||
blt::size_t initial_min_tree_size = 3;
|
||||
blt::size_t initial_max_tree_size = 10;
|
||||
|
||||
std::reference_wrapper<mutation_t> mutator;
|
||||
std::reference_wrapper<crossover_t> crossover;
|
||||
std::reference_wrapper<population_initializer_t> pop_initializer;
|
||||
|
||||
// default config (ramped half-and-half init) or for buildering
|
||||
config_t();
|
||||
|
||||
// default config with a user specified initializer
|
||||
config_t(const std::reference_wrapper<population_initializer_t>& popInitializer); // NOLINT
|
||||
|
||||
config_t(size_t populationSize, size_t initialMinTreeSize, size_t initialMaxTreeSize);
|
||||
|
||||
config_t(size_t populationSize, size_t initialMinTreeSize, size_t initialMaxTreeSize,
|
||||
const std::reference_wrapper<population_initializer_t>& popInitializer);
|
||||
|
||||
config_t(size_t populationSize, size_t initialMinTreeSize, size_t initialMaxTreeSize,
|
||||
const std::reference_wrapper<mutation_t>& mutator, const std::reference_wrapper<crossover_t>& crossover,
|
||||
const std::reference_wrapper<population_initializer_t>& popInitializer);
|
||||
|
||||
config_t& set_pop_size(blt::size_t pop)
|
||||
{
|
||||
population_size = pop;
|
||||
return *this;
|
||||
}
|
||||
|
||||
config_t& set_initial_min_tree_size(blt::size_t size)
|
||||
{
|
||||
initial_min_tree_size = size;
|
||||
return *this;
|
||||
}
|
||||
|
||||
config_t& set_initial_max_tree_size(blt::size_t size)
|
||||
{
|
||||
initial_max_tree_size = size;
|
||||
return *this;
|
||||
}
|
||||
|
||||
config_t& set_crossover(crossover_t& ref)
|
||||
{
|
||||
crossover = ref;
|
||||
return *this;
|
||||
}
|
||||
|
||||
config_t& set_mutation(mutation_t& ref)
|
||||
{
|
||||
mutator = ref;
|
||||
return *this;
|
||||
}
|
||||
|
||||
config_t& set_initializer(population_initializer_t& ref)
|
||||
{
|
||||
pop_initializer = ref;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Note about context size: This is required as context is passed to every operator in the GP tree, this context will be provided by your
|
||||
* call to one of the evaluator functions. This was the nicest way to provide this as C++ lacks reflection
|
||||
|
@ -243,7 +301,69 @@ namespace blt::gp
|
|||
system(system), engine(engine)
|
||||
{}
|
||||
|
||||
void generate_tree();
|
||||
explicit gp_program(type_provider& system, std::mt19937_64 engine, config_t config):
|
||||
system(system), engine(engine), config(config)
|
||||
{}
|
||||
|
||||
void generate_population(type_id root_type);
|
||||
|
||||
|
||||
/**
|
||||
* takes in a lambda for the fitness evaluation function (must return a value convertable to double)
|
||||
* The lambda must accept a tree for evaluation, container for evaluation context, and a index into that container (current tree)
|
||||
*
|
||||
* Container must be concurrently accessible from multiple threads using operator[]
|
||||
*
|
||||
* NOTE: 0 is considered the best, in terms of standardized and adjusted fitness
|
||||
*/
|
||||
template<typename Return, typename Class, typename Container, typename Lambda = Return(Class::*)(tree_t, Container, blt::size_t) const>
|
||||
void evaluate_fitness(Lambda&& fitness_function, Container& result_storage)
|
||||
{
|
||||
for (const auto& ind : blt::enumerate(current_pop.getIndividuals()))
|
||||
ind.second.raw_fitness = static_cast<double>(fitness_function(ind.second.tree, result_storage, ind.first));
|
||||
double min = 0;
|
||||
for (auto& ind : current_pop.getIndividuals())
|
||||
{
|
||||
if (ind.raw_fitness < min)
|
||||
min = ind.raw_fitness;
|
||||
}
|
||||
|
||||
double overall_fitness = 0;
|
||||
double best_fitness = 2;
|
||||
double worst_fitness = 0;
|
||||
individual* best = nullptr;
|
||||
individual* worst = nullptr;
|
||||
|
||||
auto diff = -min;
|
||||
for (auto& ind : current_pop.getIndividuals())
|
||||
{
|
||||
ind.standardized_fitness = ind.raw_fitness + diff;
|
||||
ind.adjusted_fitness = 1.0 / (1.0 + ind.standardized_fitness);
|
||||
|
||||
if (ind.adjusted_fitness > worst_fitness)
|
||||
{
|
||||
worst_fitness = ind.adjusted_fitness;
|
||||
worst = &ind;
|
||||
}
|
||||
|
||||
if (ind.adjusted_fitness < best_fitness)
|
||||
{
|
||||
best_fitness = ind.adjusted_fitness;
|
||||
best = &ind;
|
||||
}
|
||||
|
||||
overall_fitness += ind.adjusted_fitness;
|
||||
}
|
||||
|
||||
current_stats = {overall_fitness, overall_fitness / static_cast<double>(config.population_size), best_fitness, worst_fitness, best,
|
||||
worst};
|
||||
}
|
||||
|
||||
void next_generation()
|
||||
{
|
||||
current_pop = next_pop;
|
||||
current_generation++;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline std::mt19937_64& get_random()
|
||||
{
|
||||
|
@ -257,7 +377,7 @@ namespace blt::gp
|
|||
}
|
||||
|
||||
/**
|
||||
* @param cutoff precent in floating point form chance of the event happening.
|
||||
* @param cutoff percent in floating point form chance of the event happening.
|
||||
* @return
|
||||
*/
|
||||
[[nodiscard]] inline bool choice(double cutoff)
|
||||
|
@ -327,19 +447,19 @@ namespace blt::gp
|
|||
storage = std::move(op);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline const config_t& get_config() const
|
||||
{
|
||||
return config;
|
||||
}
|
||||
|
||||
private:
|
||||
type_provider& system;
|
||||
|
||||
blt::gp::stack_allocator alloc;
|
||||
config_t config;
|
||||
|
||||
operator_storage storage;
|
||||
population_t current_pop;
|
||||
population_stats current_stats;
|
||||
population_t next_pop;
|
||||
blt::size_t current_generation = 0;
|
||||
|
||||
std::mt19937_64 engine;
|
||||
config_t config;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
#pragma once
|
||||
/*
|
||||
* Copyright (C) 2024 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BLT_GP_SELECTION_H
|
||||
#define BLT_GP_SELECTION_H
|
||||
|
||||
#include <blt/gp/fwdecl.h>
|
||||
#include <blt/gp/tree.h>
|
||||
#include <blt/std/assert.h>
|
||||
|
||||
namespace blt::gp
|
||||
{
|
||||
|
||||
class selection_t
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @param program gp program to select with, used in randoms
|
||||
* @param pop population to select from
|
||||
* @param stats the populations statistics
|
||||
* @return
|
||||
*/
|
||||
virtual tree_t& select(gp_program& program, population_t& pop, population_stats& stats) = 0;
|
||||
|
||||
virtual ~selection_t() = default;
|
||||
};
|
||||
|
||||
class select_best_t : public selection_t
|
||||
{
|
||||
public:
|
||||
tree_t& select(gp_program& program, population_t& pop, population_stats& stats) final;
|
||||
};
|
||||
|
||||
class select_worst_t : public selection_t
|
||||
{
|
||||
public:
|
||||
tree_t& select(gp_program& program, population_t& pop, population_stats& stats) final;
|
||||
};
|
||||
|
||||
class select_random_t : public selection_t
|
||||
{
|
||||
public:
|
||||
tree_t& select(gp_program& program, population_t& pop, population_stats& stats) final;
|
||||
};
|
||||
|
||||
class select_tournament_t : public selection_t
|
||||
{
|
||||
public:
|
||||
explicit select_tournament_t(blt::size_t selection_size = 3): selection_size(selection_size)
|
||||
{
|
||||
if (selection_size < 1)
|
||||
BLT_ABORT("Unable to select with this size. Must select at least 1 individual!");
|
||||
}
|
||||
|
||||
tree_t& select(gp_program& program, population_t& pop, population_stats& stats) final;
|
||||
|
||||
private:
|
||||
blt::size_t selection_size;
|
||||
};
|
||||
|
||||
class select_fitness_proportionate_t : public selection_t
|
||||
{
|
||||
public:
|
||||
tree_t& select(gp_program& program, population_t& pop, population_stats& stats) final;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //BLT_GP_SELECTION_H
|
|
@ -22,6 +22,7 @@
|
|||
#include <blt/std/utility.h>
|
||||
#include <blt/gp/fwdecl.h>
|
||||
#include <blt/gp/tree.h>
|
||||
#include <blt/gp/generators.h>
|
||||
#include <blt/std/expected.h>
|
||||
|
||||
namespace blt::gp
|
||||
|
@ -40,6 +41,20 @@ namespace blt::gp
|
|||
tree_t child1;
|
||||
tree_t child2;
|
||||
};
|
||||
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;
|
||||
// if we fail to find a point in the tree, should we search forward from the last point to the end of the operators?
|
||||
bool should_crossover_try_forward = false;
|
||||
// avoid selecting terminals when doing crossover
|
||||
bool avoid_terminals = false;
|
||||
};
|
||||
|
||||
crossover_t() = default;
|
||||
|
||||
explicit crossover_t(const config_t& config): config(config)
|
||||
{}
|
||||
|
||||
/**
|
||||
* child1 and child2 are copies of the parents, the result of selecting a crossover point and performing standard subtree crossover.
|
||||
|
@ -50,6 +65,32 @@ namespace blt::gp
|
|||
* @return expected pair of child otherwise returns error enum
|
||||
*/
|
||||
virtual blt::expected<result_t, error_t> apply(gp_program& program, const tree_t& p1, const tree_t& p2); // NOLINT
|
||||
|
||||
virtual ~crossover_t() = default;
|
||||
|
||||
private:
|
||||
config_t config;
|
||||
};
|
||||
|
||||
class mutation_t
|
||||
{
|
||||
public:
|
||||
struct config_t
|
||||
{
|
||||
blt::size_t replacement_min_depth = 3;
|
||||
blt::size_t replacement_max_depth = 7;
|
||||
};
|
||||
|
||||
mutation_t() = default;
|
||||
|
||||
explicit mutation_t(const config_t& config): config(config)
|
||||
{}
|
||||
|
||||
virtual tree_t apply(gp_program& program, tree_generator_t& generator, const tree_t& p); // NOLINT
|
||||
|
||||
virtual ~mutation_t() = default;
|
||||
private:
|
||||
config_t config;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -110,16 +110,92 @@ namespace blt::gp
|
|||
blt::size_t depth;
|
||||
};
|
||||
|
||||
struct individual
|
||||
{
|
||||
tree_t tree;
|
||||
double raw_fitness = 0;
|
||||
double standardized_fitness = 0;
|
||||
double adjusted_fitness = 0;
|
||||
};
|
||||
|
||||
struct population_stats
|
||||
{
|
||||
double overall_fitness = 0;
|
||||
double average_fitness = 0;
|
||||
double best_fitness = 1;
|
||||
double worst_fitness = 0;
|
||||
// these will never be null unless your pop is not initialized / fitness eval was not called!
|
||||
individual* best_individual = nullptr;
|
||||
individual* worst_individual = nullptr;
|
||||
};
|
||||
|
||||
class population_t
|
||||
{
|
||||
public:
|
||||
std::vector<tree_t>& getIndividuals()
|
||||
class population_tree_iterator
|
||||
{
|
||||
public:
|
||||
population_tree_iterator(std::vector<individual>& ind, blt::size_t pos): ind(ind), pos(pos)
|
||||
{}
|
||||
|
||||
auto begin()
|
||||
{
|
||||
return population_tree_iterator(ind, 0);
|
||||
}
|
||||
|
||||
auto end()
|
||||
{
|
||||
return population_tree_iterator(ind, ind.size());
|
||||
}
|
||||
|
||||
population_tree_iterator operator++(int)
|
||||
{
|
||||
auto prev = pos++;
|
||||
return {ind, prev};
|
||||
}
|
||||
|
||||
population_tree_iterator operator++()
|
||||
{
|
||||
return {ind, ++pos};
|
||||
}
|
||||
|
||||
tree_t& operator*()
|
||||
{
|
||||
return ind[pos].tree;
|
||||
}
|
||||
|
||||
tree_t& operator->()
|
||||
{
|
||||
return ind[pos].tree;
|
||||
}
|
||||
|
||||
friend bool operator==(population_tree_iterator a, population_tree_iterator b)
|
||||
{
|
||||
return a.pos == b.pos;
|
||||
}
|
||||
|
||||
friend bool operator!=(population_tree_iterator a, population_tree_iterator b)
|
||||
{
|
||||
return a.pos != b.pos;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<individual>& ind;
|
||||
blt::size_t pos;
|
||||
};
|
||||
|
||||
std::vector<individual>& getIndividuals()
|
||||
{
|
||||
return individuals;
|
||||
}
|
||||
|
||||
population_tree_iterator for_each_tree()
|
||||
{
|
||||
return population_tree_iterator{individuals, 0};
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<tree_t> individuals;
|
||||
std::vector<individual> individuals;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -120,7 +120,7 @@ namespace blt::gp
|
|||
population_t pop;
|
||||
|
||||
for (auto i = 0ul; i < args.size; i++)
|
||||
pop.getIndividuals().push_back(grow.generate(args.to_gen_args()));
|
||||
pop.getIndividuals().push_back({grow.generate(args.to_gen_args())});
|
||||
|
||||
return pop;
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ namespace blt::gp
|
|||
population_t pop;
|
||||
|
||||
for (auto i = 0ul; i < args.size; i++)
|
||||
pop.getIndividuals().push_back(full.generate(args.to_gen_args()));
|
||||
pop.getIndividuals().push_back({full.generate(args.to_gen_args())});
|
||||
|
||||
return pop;
|
||||
}
|
||||
|
@ -142,9 +142,9 @@ namespace blt::gp
|
|||
for (auto i = 0ul; i < args.size; i++)
|
||||
{
|
||||
if (args.program.choice())
|
||||
pop.getIndividuals().push_back(full.generate(args.to_gen_args()));
|
||||
pop.getIndividuals().push_back({full.generate(args.to_gen_args())});
|
||||
else
|
||||
pop.getIndividuals().push_back(grow.generate(args.to_gen_args()));
|
||||
pop.getIndividuals().push_back({grow.generate(args.to_gen_args())});
|
||||
}
|
||||
|
||||
return pop;
|
||||
|
@ -162,18 +162,18 @@ namespace blt::gp
|
|||
for (auto i = 0ul; i < per_step; i++)
|
||||
{
|
||||
if (args.program.choice())
|
||||
pop.getIndividuals().push_back(full.generate({args.program, args.root_type, args.min_depth, depth}));
|
||||
pop.getIndividuals().push_back({full.generate({args.program, args.root_type, args.min_depth, depth})});
|
||||
else
|
||||
pop.getIndividuals().push_back(grow.generate({args.program, args.root_type, args.min_depth, depth}));
|
||||
pop.getIndividuals().push_back({grow.generate({args.program, args.root_type, args.min_depth, depth})});
|
||||
}
|
||||
}
|
||||
|
||||
for (auto i = 0ul; i < remainder; i++)
|
||||
{
|
||||
if (args.program.choice())
|
||||
pop.getIndividuals().push_back(full.generate(args.to_gen_args()));
|
||||
pop.getIndividuals().push_back({full.generate(args.to_gen_args())});
|
||||
else
|
||||
pop.getIndividuals().push_back(grow.generate(args.to_gen_args()));
|
||||
pop.getIndividuals().push_back({grow.generate(args.to_gen_args())});
|
||||
}
|
||||
|
||||
blt_assert(pop.getIndividuals().size() == args.size);
|
||||
|
|
|
@ -18,6 +18,41 @@
|
|||
#include <blt/gp/program.h>
|
||||
|
||||
namespace blt::gp
|
||||
{
|
||||
|
||||
// default static references for mutation, crossover, and initializer
|
||||
// this is largely to not break the tests :3
|
||||
// it's also to allow for quick setup of a gp program if you don't care how crossover or mutation is handled
|
||||
static mutation_t s_mutator;
|
||||
static crossover_t s_crossover;
|
||||
static ramped_half_initializer_t s_init;
|
||||
|
||||
gp_program::config_t::config_t(size_t populationSize, size_t initialMinTreeSize, size_t initialMaxTreeSize):
|
||||
population_size(populationSize), initial_min_tree_size(initialMinTreeSize), initial_max_tree_size(initialMaxTreeSize), mutator(s_mutator),
|
||||
crossover(s_crossover), pop_initializer(s_init)
|
||||
{}
|
||||
|
||||
gp_program::config_t::config_t(size_t populationSize, size_t initialMinTreeSize, size_t initialMaxTreeSize,
|
||||
const std::reference_wrapper<mutation_t>& mutator, const std::reference_wrapper<crossover_t>& crossover,
|
||||
const std::reference_wrapper<population_initializer_t>& popInitializer):
|
||||
population_size(populationSize), initial_min_tree_size(initialMinTreeSize), initial_max_tree_size(initialMaxTreeSize), mutator(mutator),
|
||||
crossover(crossover), pop_initializer(popInitializer)
|
||||
{}
|
||||
|
||||
gp_program::config_t::config_t(size_t populationSize, size_t initialMinTreeSize, size_t initialMaxTreeSize,
|
||||
const std::reference_wrapper<population_initializer_t>& popInitializer):
|
||||
population_size(populationSize), initial_min_tree_size(initialMinTreeSize), initial_max_tree_size(initialMaxTreeSize),
|
||||
mutator(s_mutator), crossover(s_crossover), pop_initializer(popInitializer)
|
||||
{}
|
||||
|
||||
gp_program::config_t::config_t(): mutator(s_mutator), crossover(s_crossover), pop_initializer(s_init)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
gp_program::config_t::config_t(const std::reference_wrapper<population_initializer_t>& popInitializer):
|
||||
mutator(s_mutator), crossover(s_crossover), pop_initializer(popInitializer)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* <Short Description>
|
||||
* Copyright (C) 2024 Brett Terpstra
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <blt/gp/selection.h>
|
||||
#include <blt/gp/program.h>
|
||||
|
||||
namespace blt::gp
|
||||
{
|
||||
|
||||
tree_t& select_best_t::select(gp_program&, population_t& pop, population_stats& stats)
|
||||
{
|
||||
auto& first = pop.getIndividuals()[0];
|
||||
double best_fitness = first.adjusted_fitness;
|
||||
tree_t* tree = &first.tree;
|
||||
for (auto& ind : pop.getIndividuals())
|
||||
{
|
||||
if (ind.adjusted_fitness < best_fitness)
|
||||
{
|
||||
best_fitness = ind.adjusted_fitness;
|
||||
tree = &ind.tree;
|
||||
}
|
||||
}
|
||||
return *tree;
|
||||
}
|
||||
|
||||
tree_t& select_worst_t::select(gp_program&, population_t& pop, population_stats& stats)
|
||||
{
|
||||
auto& first = pop.getIndividuals()[0];
|
||||
double worst_fitness = first.adjusted_fitness;
|
||||
tree_t* tree = &first.tree;
|
||||
for (auto& ind : pop.getIndividuals())
|
||||
{
|
||||
if (ind.adjusted_fitness > worst_fitness)
|
||||
{
|
||||
worst_fitness = ind.adjusted_fitness;
|
||||
tree = &ind.tree;
|
||||
}
|
||||
}
|
||||
return *tree;
|
||||
}
|
||||
|
||||
tree_t& select_random_t::select(gp_program& program, population_t& pop, population_stats& stats)
|
||||
{
|
||||
// TODO: use a more generic randomness solution.
|
||||
std::uniform_int_distribution dist(0ul, pop.getIndividuals().size());
|
||||
return pop.getIndividuals()[dist(program.get_random())].tree;
|
||||
}
|
||||
|
||||
tree_t& select_tournament_t::select(gp_program& program, population_t& pop, population_stats& stats)
|
||||
{
|
||||
std::uniform_int_distribution dist(0ul, pop.getIndividuals().size());
|
||||
|
||||
auto& first = pop.getIndividuals()[dist(program.get_random())];
|
||||
individual* ind = &first;
|
||||
double best_guy = first.adjusted_fitness;
|
||||
for (blt::size_t i = 0; i < selection_size - 1; i++)
|
||||
{
|
||||
auto& sel = pop.getIndividuals()[dist(program.get_random())];
|
||||
if (sel.adjusted_fitness < best_guy)
|
||||
{
|
||||
best_guy = sel.adjusted_fitness;
|
||||
ind = &sel;
|
||||
}
|
||||
}
|
||||
|
||||
return ind->tree;
|
||||
}
|
||||
|
||||
// https://www.google.com/search?client=firefox-b-d&sca_esv=71668abf73626b35&sca_upv=1&biw=1916&bih=940&sxsrf=ADLYWIJehgPtkALJDoTgHCiO4GNeQppSeA:1720490607140&q=roulette+wheel+selection+pseudocode&uds=ADvngMgiq8uozSRb4WPAa_ESRaBJz-G_Xhk1OLU3QFjqc3o31P4ECuIkKJxHd-cR3WUe9U7VQGpI6NRaMgYiWTMd4wNofAAaNq6X4eHYpN8cR9HmTfTw0KgYC6gI4dgu-s-5mXivdsv4QxrkVAL7yMoXacJngsiMBg&udm=2&sa=X&ved=2ahUKEwig7Oj77piHAxU3D1kFHS1lAIsQxKsJegQIDBAB&ictx=0#vhid=6iCOymnPvtyy-M&vssid=mosaic
|
||||
tree_t& select_fitness_proportionate_t::select(gp_program& program, population_t& pop, population_stats& stats)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -24,7 +24,6 @@ namespace blt::gp
|
|||
{
|
||||
blt::expected<crossover_t::result_t, crossover_t::error_t> crossover_t::apply(gp_program& program, const tree_t& p1, const tree_t& p2) // NOLINT
|
||||
{
|
||||
const auto& config = program.get_config();
|
||||
result_t result{p1, p2};
|
||||
|
||||
#if BLT_DEBUG_LEVEL > 0
|
||||
|
@ -254,4 +253,80 @@ namespace blt::gp
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
tree_t mutation_t::apply(gp_program& program, tree_generator_t& generator, const tree_t& p)
|
||||
{
|
||||
auto c = p;
|
||||
|
||||
auto& ops = c.get_operations();
|
||||
auto& vals = c.get_values();
|
||||
|
||||
std::uniform_int_distribution point_sel_dist(0ul, ops.size() - 1);
|
||||
auto point = point_sel_dist(program.get_random());
|
||||
const auto& type_info = program.get_operator_info(ops[point].id);
|
||||
|
||||
blt::i64 children_left = 0;
|
||||
blt::size_t index = point;
|
||||
|
||||
do
|
||||
{
|
||||
const auto& type = program.get_operator_info(ops[index].id);
|
||||
|
||||
// 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 begin_p = ops.begin() + static_cast<blt::ptrdiff_t>(point);
|
||||
auto end_p = ops.begin() + static_cast<blt::ptrdiff_t>(index);
|
||||
|
||||
stack_allocator after_stack;
|
||||
//std::vector<op_container_t> after_ops;
|
||||
|
||||
for (auto it = ops.end() - 1; it != end_p - 1; it--)
|
||||
{
|
||||
if (it->is_value)
|
||||
{
|
||||
it->transfer(after_stack, vals);
|
||||
//after_ops.push_back(*it);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = end_p - 1; it != begin_p - 1; it--)
|
||||
{
|
||||
if (it->is_value)
|
||||
it->transfer(std::optional<std::reference_wrapper<stack_allocator>>{}, vals);
|
||||
}
|
||||
|
||||
auto before = begin_p - 1;
|
||||
|
||||
ops.erase(begin_p, end_p);
|
||||
|
||||
auto new_tree = generator.generate({program, type_info.return_type, config.replacement_min_depth, config.replacement_max_depth});
|
||||
|
||||
auto& new_ops = new_tree.get_operations();
|
||||
auto& new_vals = new_tree.get_values();
|
||||
|
||||
ops.insert(++before, new_ops.begin(), new_ops.end());
|
||||
|
||||
for (const auto& op : new_ops)
|
||||
{
|
||||
if (op.is_value)
|
||||
op.transfer(vals, new_vals);
|
||||
}
|
||||
|
||||
auto new_end_point = point + new_ops.size();
|
||||
auto new_end_p = ops.begin() + static_cast<blt::ptrdiff_t>(new_end_point);
|
||||
|
||||
for (auto it = new_end_p; it != ops.end(); it++)
|
||||
{
|
||||
if (it->is_value)
|
||||
it->transfer(vals, after_stack);
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue