Compare commits
4 Commits
d4e6c40fe1
...
1789a9e474
Author | SHA1 | Date |
---|---|---|
Brett | 1789a9e474 | |
Brett | 3572539b74 | |
Brett | 2f3ea1b3bf | |
Brett | 91bda10b0a |
|
@ -1,5 +1,5 @@
|
||||||
cmake_minimum_required(VERSION 3.25)
|
cmake_minimum_required(VERSION 3.25)
|
||||||
project(blt-gp VERSION 0.1.35)
|
project(blt-gp VERSION 0.1.36)
|
||||||
|
|
||||||
include(CTest)
|
include(CTest)
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ set(CMAKE_CXX_STANDARD 17)
|
||||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
SET(CMAKE_CXX_FLAGS_RELEASE "-O3 -g")
|
#SET(CMAKE_CXX_FLAGS_RELEASE "-O3 -g")
|
||||||
|
|
||||||
if (NOT TARGET BLT)
|
if (NOT TARGET BLT)
|
||||||
add_subdirectory(lib/blt)
|
add_subdirectory(lib/blt)
|
||||||
|
|
|
@ -36,18 +36,18 @@ std::array<context, 200> training_cases;
|
||||||
blt::gp::prog_config_t config = blt::gp::prog_config_t()
|
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(200)
|
||||||
.set_crossover_chance(0.9)
|
.set_crossover_chance(0.9)
|
||||||
.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)
|
||||||
.set_pop_size(500)
|
.set_pop_size(20000)
|
||||||
.set_thread_count(0);
|
.set_thread_count(0);
|
||||||
|
|
||||||
blt::gp::gp_program program{SEED, config};
|
blt::gp::gp_program program{SEED, config};
|
||||||
|
|
||||||
auto lit = blt::gp::operation_t([]() {
|
auto lit = blt::gp::operation_t([]() {
|
||||||
return program.get_random().get_float(-320.0f, 320.0f);
|
return program.get_random().get_float(-1.0f, 1.0f);
|
||||||
}, "lit").set_ephemeral();
|
}, "lit").set_ephemeral();
|
||||||
|
|
||||||
blt::gp::operation_t op_x([](const context& context) {
|
blt::gp::operation_t op_x([](const context& context) {
|
||||||
|
|
|
@ -362,8 +362,11 @@ namespace blt::gp
|
||||||
#ifdef BLT_TRACK_ALLOCATIONS
|
#ifdef BLT_TRACK_ALLOCATIONS
|
||||||
auto gen_alloc = blt::gp::tracker.start_measurement();
|
auto gen_alloc = blt::gp::tracker.start_measurement();
|
||||||
#endif
|
#endif
|
||||||
|
BLT_ASSERT_MSG(current_pop.get_individuals().size() == config.population_size,
|
||||||
|
("cur pop size: " + std::to_string(current_pop.get_individuals().size())).c_str());
|
||||||
|
BLT_ASSERT_MSG(next_pop.get_individuals().size() == config.population_size,
|
||||||
|
("next pop size: " + std::to_string(next_pop.get_individuals().size())).c_str());
|
||||||
// should already be empty
|
// should already be empty
|
||||||
next_pop.clear();
|
|
||||||
thread_helper.next_gen_left.store(config.population_size, std::memory_order_release);
|
thread_helper.next_gen_left.store(config.population_size, std::memory_order_release);
|
||||||
(*thread_execution_service)(0);
|
(*thread_execution_service)(0);
|
||||||
#ifdef BLT_TRACK_ALLOCATIONS
|
#ifdef BLT_TRACK_ALLOCATIONS
|
||||||
|
@ -375,8 +378,10 @@ namespace blt::gp
|
||||||
|
|
||||||
void next_generation()
|
void next_generation()
|
||||||
{
|
{
|
||||||
|
BLT_ASSERT_MSG(current_pop.get_individuals().size() == config.population_size,
|
||||||
|
("cur pop size: " + std::to_string(current_pop.get_individuals().size())).c_str());
|
||||||
BLT_ASSERT_MSG(next_pop.get_individuals().size() == config.population_size,
|
BLT_ASSERT_MSG(next_pop.get_individuals().size() == config.population_size,
|
||||||
("pop size: " + std::to_string(next_pop.get_individuals().size())).c_str());
|
("next pop size: " + std::to_string(next_pop.get_individuals().size())).c_str());
|
||||||
std::swap(current_pop, next_pop);
|
std::swap(current_pop, next_pop);
|
||||||
current_generation++;
|
current_generation++;
|
||||||
}
|
}
|
||||||
|
@ -400,6 +405,7 @@ namespace blt::gp
|
||||||
current_generation = 0;
|
current_generation = 0;
|
||||||
current_pop = config.pop_initializer.get().generate(
|
current_pop = config.pop_initializer.get().generate(
|
||||||
{*this, root_type, config.population_size, config.initial_min_tree_size, config.initial_max_tree_size});
|
{*this, root_type, config.population_size, config.initial_min_tree_size, config.initial_max_tree_size});
|
||||||
|
next_pop = population_t(current_pop);
|
||||||
if (eval_fitness_now)
|
if (eval_fitness_now)
|
||||||
evaluate_fitness_internal();
|
evaluate_fitness_internal();
|
||||||
}
|
}
|
||||||
|
@ -465,21 +471,24 @@ namespace blt::gp
|
||||||
}
|
}
|
||||||
if (thread_helper.next_gen_left > 0)
|
if (thread_helper.next_gen_left > 0)
|
||||||
{
|
{
|
||||||
static thread_local tracked_vector<tree_t> new_children;
|
auto args = get_selector_args();
|
||||||
new_children.clear();
|
|
||||||
auto args = get_selector_args(new_children);
|
|
||||||
|
|
||||||
crossover_selection.pre_process(*this, current_pop);
|
crossover_selection.pre_process(*this, current_pop);
|
||||||
mutation_selection.pre_process(*this, current_pop);
|
mutation_selection.pre_process(*this, current_pop);
|
||||||
reproduction_selection.pre_process(*this, current_pop);
|
reproduction_selection.pre_process(*this, current_pop);
|
||||||
|
|
||||||
perform_elitism(args);
|
perform_elitism(args, next_pop);
|
||||||
|
|
||||||
while (new_children.size() < config.population_size)
|
blt::size_t start = config.elites;
|
||||||
func(args, crossover_selection, mutation_selection, reproduction_selection);
|
|
||||||
|
|
||||||
for (auto& i : new_children)
|
while (start < config.population_size)
|
||||||
next_pop.get_individuals().emplace_back(std::move(i));
|
{
|
||||||
|
tree_t& c1 = next_pop.get_individuals()[start].tree;
|
||||||
|
tree_t* c2 = nullptr;
|
||||||
|
if (start + 1 < config.population_size)
|
||||||
|
c2 = &next_pop.get_individuals()[start + 1].tree;
|
||||||
|
start += func(args, crossover_selection, mutation_selection, reproduction_selection, c1, c2);
|
||||||
|
}
|
||||||
|
|
||||||
thread_helper.next_gen_left = 0;
|
thread_helper.next_gen_left = 0;
|
||||||
}
|
}
|
||||||
|
@ -542,9 +551,7 @@ namespace blt::gp
|
||||||
}
|
}
|
||||||
if (thread_helper.next_gen_left > 0)
|
if (thread_helper.next_gen_left > 0)
|
||||||
{
|
{
|
||||||
static thread_local tracked_vector<tree_t> new_children;
|
auto args = get_selector_args();
|
||||||
new_children.clear();
|
|
||||||
auto args = get_selector_args(new_children);
|
|
||||||
if (id == 0)
|
if (id == 0)
|
||||||
{
|
{
|
||||||
current_stats.normalized_fitness.clear();
|
current_stats.normalized_fitness.clear();
|
||||||
|
@ -562,37 +569,35 @@ namespace blt::gp
|
||||||
if (&crossover_selection != &reproduction_selection)
|
if (&crossover_selection != &reproduction_selection)
|
||||||
reproduction_selection.pre_process(*this, current_pop);
|
reproduction_selection.pre_process(*this, current_pop);
|
||||||
|
|
||||||
perform_elitism(args);
|
perform_elitism(args, next_pop);
|
||||||
|
|
||||||
for (auto& i : new_children)
|
thread_helper.next_gen_left -= config.elites;
|
||||||
next_pop.get_individuals().emplace_back(std::move(i));
|
|
||||||
thread_helper.next_gen_left -= new_children.size();
|
|
||||||
new_children.clear();
|
|
||||||
}
|
}
|
||||||
thread_helper.barrier.wait();
|
thread_helper.barrier.wait();
|
||||||
|
|
||||||
while (thread_helper.next_gen_left > 0)
|
while (thread_helper.next_gen_left > 0)
|
||||||
{
|
{
|
||||||
blt::size_t size = 0;
|
blt::size_t size = 0;
|
||||||
|
blt::size_t begin = 0;
|
||||||
blt::size_t end = thread_helper.next_gen_left.load(std::memory_order_relaxed);
|
blt::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);
|
||||||
|
begin = end - size;
|
||||||
} while (!thread_helper.next_gen_left.compare_exchange_weak(end, end - size,
|
} while (!thread_helper.next_gen_left.compare_exchange_weak(end, end - size,
|
||||||
std::memory_order::memory_order_relaxed,
|
std::memory_order::memory_order_relaxed,
|
||||||
std::memory_order::memory_order_relaxed));
|
std::memory_order::memory_order_relaxed));
|
||||||
|
|
||||||
while (new_children.size() < size)
|
while (begin != end)
|
||||||
func(args, crossover_selection, mutation_selection, reproduction_selection);
|
{
|
||||||
|
auto index = config.elites + begin;
|
||||||
|
tree_t& c1 = next_pop.get_individuals()[index].tree;
|
||||||
|
tree_t* c2 = nullptr;
|
||||||
|
if (index + 1 < end)
|
||||||
|
c2 = &next_pop.get_individuals()[index + 1].tree;
|
||||||
|
begin += func(args, crossover_selection, mutation_selection, reproduction_selection, c1, c2);
|
||||||
|
}
|
||||||
|
|
||||||
{
|
|
||||||
std::scoped_lock lock(thread_helper.thread_generation_lock);
|
|
||||||
for (auto& i : new_children)
|
|
||||||
{
|
|
||||||
if (next_pop.get_individuals().size() < config.population_size)
|
|
||||||
next_pop.get_individuals().emplace_back(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
thread_helper.barrier.wait();
|
thread_helper.barrier.wait();
|
||||||
|
@ -747,9 +752,9 @@ namespace blt::gp
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
inline selector_args get_selector_args(tracked_vector<tree_t>& next_pop_trees)
|
inline selector_args get_selector_args()
|
||||||
{
|
{
|
||||||
return {*this, next_pop_trees, current_pop, current_stats, config, get_random()};
|
return {*this, current_pop, current_stats, config, get_random()};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Return, blt::size_t size, typename Accessor, blt::size_t... indexes>
|
template<typename Return, blt::size_t size, typename Accessor, blt::size_t... indexes>
|
||||||
|
@ -765,7 +770,7 @@ namespace blt::gp
|
||||||
{
|
{
|
||||||
statistic_history.push_back(current_stats);
|
statistic_history.push_back(current_stats);
|
||||||
current_stats.clear();
|
current_stats.clear();
|
||||||
thread_helper.evaluation_left.store(current_pop.get_individuals().size(), std::memory_order_release);
|
thread_helper.evaluation_left.store(config.population_size, std::memory_order_release);
|
||||||
(*thread_execution_service)(0);
|
(*thread_execution_service)(0);
|
||||||
|
|
||||||
current_stats.average_fitness = current_stats.overall_fitness / static_cast<double>(config.population_size);
|
current_stats.average_fitness = current_stats.overall_fitness / static_cast<double>(config.population_size);
|
||||||
|
@ -791,7 +796,6 @@ namespace blt::gp
|
||||||
std::vector<std::unique_ptr<std::thread>> threads;
|
std::vector<std::unique_ptr<std::thread>> threads;
|
||||||
|
|
||||||
std::mutex thread_function_control{};
|
std::mutex thread_function_control{};
|
||||||
std::mutex thread_generation_lock{};
|
|
||||||
std::condition_variable thread_function_condition{};
|
std::condition_variable thread_function_condition{};
|
||||||
|
|
||||||
std::atomic_uint64_t evaluation_left = 0;
|
std::atomic_uint64_t evaluation_left = 0;
|
||||||
|
|
|
@ -32,15 +32,14 @@ namespace blt::gp
|
||||||
struct selector_args
|
struct selector_args
|
||||||
{
|
{
|
||||||
gp_program& program;
|
gp_program& program;
|
||||||
tracked_vector<tree_t>& next_pop;
|
const population_t& current_pop;
|
||||||
population_t& current_pop;
|
|
||||||
population_stats& current_stats;
|
population_stats& current_stats;
|
||||||
prog_config_t& config;
|
prog_config_t& config;
|
||||||
random_t& random;
|
random_t& random;
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr inline auto perform_elitism = [](const selector_args& args) {
|
constexpr inline auto perform_elitism = [](const selector_args& args, population_t& next_pop) {
|
||||||
auto& [program, next_pop, current_pop, current_stats, config, random] = args;
|
auto& [program, current_pop, current_stats, config, random] = args;
|
||||||
|
|
||||||
if (config.elites > 0)
|
if (config.elites > 0)
|
||||||
{
|
{
|
||||||
|
@ -70,39 +69,38 @@ namespace blt::gp
|
||||||
}
|
}
|
||||||
|
|
||||||
for (blt::size_t i = 0; i < config.elites; i++)
|
for (blt::size_t i = 0; i < config.elites; i++)
|
||||||
next_pop.push_back(current_pop.get_individuals()[values[i].first].tree);
|
next_pop.get_individuals()[i].copy_fast(current_pop.get_individuals()[values[i].first].tree);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename Crossover, typename Mutation, typename Reproduction>
|
template<typename Crossover, typename Mutation, typename Reproduction>
|
||||||
constexpr inline auto default_next_pop_creator = [](
|
constexpr inline auto default_next_pop_creator = [](
|
||||||
blt::gp::selector_args& args, Crossover& crossover_selection, Mutation& mutation_selection, Reproduction& reproduction_selection) {
|
blt::gp::selector_args& args, Crossover& crossover_selection, Mutation& mutation_selection, Reproduction& reproduction_selection,
|
||||||
auto& [program, next_pop, current_pop, current_stats, config, random] = args;
|
tree_t& c1, tree_t* c2) {
|
||||||
|
auto& [program, current_pop, current_stats, config, random] = args;
|
||||||
|
|
||||||
int sel = random.get_i32(0, 3);
|
int sel = random.get_i32(0, 3);
|
||||||
switch (sel)
|
switch (sel)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
|
if (c2 == nullptr)
|
||||||
|
return 0;
|
||||||
// everyone gets a chance once per loop.
|
// everyone gets a chance once per loop.
|
||||||
if (random.choice(config.crossover_chance))
|
if (random.choice(config.crossover_chance))
|
||||||
{
|
{
|
||||||
// auto state = tracker.start_measurement();
|
// auto state = tracker.start_measurement();
|
||||||
// crossover
|
// crossover
|
||||||
auto& p1 = crossover_selection.select(program, current_pop);
|
const tree_t* p1;
|
||||||
auto& p2 = crossover_selection.select(program, current_pop);
|
const tree_t* p2;
|
||||||
|
do
|
||||||
auto results = config.crossover.get().apply(program, p1, p2);
|
|
||||||
|
|
||||||
// if crossover fails, we can check for mutation on these guys. otherwise straight copy them into the next pop
|
|
||||||
if (results)
|
|
||||||
{
|
{
|
||||||
next_pop.push_back(std::move(results->child1));
|
p1 = &crossover_selection.select(program, current_pop);
|
||||||
if (next_pop.size() != config.population_size)
|
p2 = &crossover_selection.select(program, current_pop);
|
||||||
next_pop.push_back(std::move(results->child2));
|
} while (!config.crossover.get().apply(program, *p1, *p2, c1, *c2));
|
||||||
}
|
|
||||||
// tracker.stop_measurement(state);
|
// tracker.stop_measurement(state);
|
||||||
// BLT_TRACE("Crossover Allocated %ld times with a total of %s", state.getAllocationDifference(),
|
// BLT_TRACE("Crossover Allocated %ld times with a total of %s", state.getAllocationDifference(),
|
||||||
// blt::byte_convert_t(state.getAllocatedByteDifference()).convert_to_nearest_type().to_pretty_string().c_str());
|
// blt::byte_convert_t(state.getAllocatedByteDifference()).convert_to_nearest_type().to_pretty_string().c_str());
|
||||||
|
return 2;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -110,11 +108,15 @@ namespace blt::gp
|
||||||
{
|
{
|
||||||
// auto state = tracker.start_measurement();
|
// auto state = tracker.start_measurement();
|
||||||
// mutation
|
// mutation
|
||||||
auto& p = mutation_selection.select(program, current_pop);
|
const tree_t* p;
|
||||||
next_pop.push_back(std::move(config.mutator.get().apply(program, p)));
|
do
|
||||||
|
{
|
||||||
|
p = &mutation_selection.select(program, current_pop);
|
||||||
|
} while (!config.mutator.get().apply(program, *p, c1));
|
||||||
// tracker.stop_measurement(state);
|
// tracker.stop_measurement(state);
|
||||||
// BLT_TRACE("Mutation Allocated %ld times with a total of %s", state.getAllocationDifference(),
|
// BLT_TRACE("Mutation Allocated %ld times with a total of %s", state.getAllocationDifference(),
|
||||||
// blt::byte_convert_t(state.getAllocatedByteDifference()).convert_to_nearest_type().to_pretty_string().c_str());
|
// blt::byte_convert_t(state.getAllocatedByteDifference()).convert_to_nearest_type().to_pretty_string().c_str());
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
|
@ -122,11 +124,11 @@ namespace blt::gp
|
||||||
{
|
{
|
||||||
// auto state = tracker.start_measurement();
|
// auto state = tracker.start_measurement();
|
||||||
// reproduction
|
// reproduction
|
||||||
auto& p = reproduction_selection.select(program, current_pop);
|
c1 = reproduction_selection.select(program, current_pop);
|
||||||
next_pop.push_back(p);
|
|
||||||
// tracker.stop_measurement(state);
|
// tracker.stop_measurement(state);
|
||||||
// BLT_TRACE("Reproduction Allocated %ld times with a total of %s", state.getAllocationDifference(),
|
// BLT_TRACE("Reproduction Allocated %ld times with a total of %s", state.getAllocationDifference(),
|
||||||
// blt::byte_convert_t(state.getAllocatedByteDifference()).convert_to_nearest_type().to_pretty_string().c_str());
|
// blt::byte_convert_t(state.getAllocatedByteDifference()).convert_to_nearest_type().to_pretty_string().c_str());
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -136,6 +138,7 @@ namespace blt::gp
|
||||||
BLT_UNREACHABLE;
|
BLT_UNREACHABLE;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class selection_t
|
class selection_t
|
||||||
|
@ -147,7 +150,7 @@ namespace blt::gp
|
||||||
* @param stats the populations statistics
|
* @param stats the populations statistics
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
virtual tree_t& select(gp_program& program, population_t& pop) = 0;
|
virtual const tree_t& select(gp_program& program, const population_t& pop) = 0;
|
||||||
|
|
||||||
virtual void pre_process(gp_program&, population_t&)
|
virtual void pre_process(gp_program&, population_t&)
|
||||||
{}
|
{}
|
||||||
|
@ -158,19 +161,19 @@ namespace blt::gp
|
||||||
class select_best_t : public selection_t
|
class select_best_t : public selection_t
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
tree_t& select(gp_program& program, population_t& pop) final;
|
const tree_t& select(gp_program& program, const population_t& pop) final;
|
||||||
};
|
};
|
||||||
|
|
||||||
class select_worst_t : public selection_t
|
class select_worst_t : public selection_t
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
tree_t& select(gp_program& program, population_t& pop) final;
|
const tree_t& select(gp_program& program, const population_t& pop) final;
|
||||||
};
|
};
|
||||||
|
|
||||||
class select_random_t : public selection_t
|
class select_random_t : public selection_t
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
tree_t& select(gp_program& program, population_t& pop) final;
|
const tree_t& select(gp_program& program, const population_t& pop) final;
|
||||||
};
|
};
|
||||||
|
|
||||||
class select_tournament_t : public selection_t
|
class select_tournament_t : public selection_t
|
||||||
|
@ -182,7 +185,7 @@ namespace blt::gp
|
||||||
BLT_ABORT("Unable to select with this size. Must select at least 1 individual_t!");
|
BLT_ABORT("Unable to select with this size. Must select at least 1 individual_t!");
|
||||||
}
|
}
|
||||||
|
|
||||||
tree_t& select(gp_program& program, population_t& pop) final;
|
const tree_t& select(gp_program& program, const population_t& pop) final;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const blt::size_t selection_size;
|
const blt::size_t selection_size;
|
||||||
|
@ -191,7 +194,7 @@ namespace blt::gp
|
||||||
class select_fitness_proportionate_t : public selection_t
|
class select_fitness_proportionate_t : public selection_t
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
tree_t& select(gp_program& program, population_t& pop) final;
|
const tree_t& select(gp_program& program, const population_t& pop) final;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,16 +57,6 @@ namespace blt::gp
|
||||||
class crossover_t
|
class crossover_t
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum class error_t
|
|
||||||
{
|
|
||||||
NO_VALID_TYPE,
|
|
||||||
TREE_TOO_SMALL
|
|
||||||
};
|
|
||||||
struct result_t
|
|
||||||
{
|
|
||||||
tree_t child1;
|
|
||||||
tree_t child2;
|
|
||||||
};
|
|
||||||
struct crossover_point_t
|
struct crossover_point_t
|
||||||
{
|
{
|
||||||
blt::ptrdiff_t p1_crossover_point;
|
blt::ptrdiff_t p1_crossover_point;
|
||||||
|
@ -87,7 +77,7 @@ namespace blt::gp
|
||||||
explicit crossover_t(const config_t& config): config(config)
|
explicit crossover_t(const config_t& config): config(config)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
blt::expected<crossover_t::crossover_point_t, error_t> get_crossover_point(gp_program& program, const tree_t& c1, const tree_t& c2) const;
|
std::optional<crossover_t::crossover_point_t> get_crossover_point(gp_program& program, 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.
|
* child1 and child2 are copies of the parents, the result of selecting a crossover point and performing standard subtree crossover.
|
||||||
|
@ -97,7 +87,7 @@ namespace blt::gp
|
||||||
* @param p2 reference to the second parent
|
* @param p2 reference to the second parent
|
||||||
* @return expected pair of child otherwise returns error enum
|
* @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 bool apply(gp_program& program, const tree_t& p1, const tree_t& p2, tree_t& c1, tree_t& c2); // NOLINT
|
||||||
|
|
||||||
virtual ~crossover_t() = default;
|
virtual ~crossover_t() = default;
|
||||||
|
|
||||||
|
@ -126,7 +116,7 @@ namespace blt::gp
|
||||||
explicit mutation_t(const config_t& config): config(config)
|
explicit mutation_t(const config_t& config): config(config)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
virtual tree_t apply(gp_program& program, const tree_t& p);
|
virtual bool apply(gp_program& program, const tree_t& p, tree_t& c);
|
||||||
|
|
||||||
// returns the point after the mutation
|
// returns the point after the mutation
|
||||||
blt::size_t mutate_point(gp_program& program, tree_t& c, blt::size_t node);
|
blt::size_t mutate_point(gp_program& program, tree_t& c, blt::size_t node);
|
||||||
|
@ -155,7 +145,7 @@ namespace blt::gp
|
||||||
explicit advanced_mutation_t(const config_t& config): mutation_t(config)
|
explicit advanced_mutation_t(const config_t& config): mutation_t(config)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
tree_t apply(gp_program& program, const tree_t& p) 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)
|
||||||
{
|
{
|
||||||
|
@ -170,11 +160,11 @@ namespace blt::gp
|
||||||
double per_node_mutation_chance = 5.0;
|
double per_node_mutation_chance = 5.0;
|
||||||
|
|
||||||
static constexpr std::array<double, operators_size> mutation_operator_chances = detail::aggregate_array<operators_size>(
|
static constexpr std::array<double, operators_size> mutation_operator_chances = detail::aggregate_array<operators_size>(
|
||||||
0.1, // EXPRESSION
|
0.25, // EXPRESSION
|
||||||
0.25, // ADJUST
|
0.15, // ADJUST
|
||||||
0.01, // SUB_FUNC
|
0.01, // SUB_FUNC
|
||||||
0.25, // JUMP_FUNC
|
0.01, // JUMP_FUNC
|
||||||
0.12 // COPY
|
0.05 // COPY
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,37 @@ namespace blt::gp
|
||||||
public:
|
public:
|
||||||
explicit tree_t(gp_program& program);
|
explicit tree_t(gp_program& program);
|
||||||
|
|
||||||
|
tree_t(const tree_t& copy) = default;
|
||||||
|
|
||||||
|
tree_t& operator=(const tree_t& copy)
|
||||||
|
{
|
||||||
|
if (this == ©)
|
||||||
|
return *this;
|
||||||
|
copy_fast(copy);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function copies the data from the provided tree, will attempt to reserve and copy in one step.
|
||||||
|
* will avoid reallocation if enough space is already present.
|
||||||
|
*/
|
||||||
|
void copy_fast(const tree_t& copy)
|
||||||
|
{
|
||||||
|
if (this == ©)
|
||||||
|
return;
|
||||||
|
values.reserve(copy.values.internal_storage_size());
|
||||||
|
values.reset();
|
||||||
|
values.insert(copy.values);
|
||||||
|
|
||||||
|
operations.clear();
|
||||||
|
operations.reserve(copy.operations.size());
|
||||||
|
operations.insert(operations.begin(), copy.operations.begin(), copy.operations.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
tree_t(tree_t&& move) = default;
|
||||||
|
|
||||||
|
tree_t& operator=(tree_t&& move) = default;
|
||||||
|
|
||||||
void clear(gp_program& program);
|
void clear(gp_program& program);
|
||||||
|
|
||||||
struct child_t
|
struct child_t
|
||||||
|
@ -178,6 +209,14 @@ namespace blt::gp
|
||||||
tree_t tree;
|
tree_t tree;
|
||||||
fitness_t fitness;
|
fitness_t fitness;
|
||||||
|
|
||||||
|
void copy_fast(const tree_t& copy)
|
||||||
|
{
|
||||||
|
// fast copy of the tree
|
||||||
|
tree.copy_fast(copy);
|
||||||
|
// reset fitness
|
||||||
|
fitness = {};
|
||||||
|
}
|
||||||
|
|
||||||
individual_t() = delete;
|
individual_t() = delete;
|
||||||
|
|
||||||
explicit individual_t(tree_t&& tree): tree(std::move(tree))
|
explicit individual_t(tree_t&& tree): tree(std::move(tree))
|
||||||
|
@ -284,6 +323,11 @@ namespace blt::gp
|
||||||
return individuals;
|
return individuals;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const tracked_vector<individual_t>& get_individuals() const
|
||||||
|
{
|
||||||
|
return individuals;
|
||||||
|
}
|
||||||
|
|
||||||
population_tree_iterator for_each_tree()
|
population_tree_iterator for_each_tree()
|
||||||
{
|
{
|
||||||
return population_tree_iterator{individuals, 0};
|
return population_tree_iterator{individuals, 0};
|
||||||
|
|
|
@ -21,11 +21,11 @@
|
||||||
namespace blt::gp
|
namespace blt::gp
|
||||||
{
|
{
|
||||||
|
|
||||||
tree_t& select_best_t::select(gp_program&, population_t& pop)
|
const tree_t& select_best_t::select(gp_program&, const population_t& pop)
|
||||||
{
|
{
|
||||||
auto& first = pop.get_individuals()[0];
|
auto& first = pop.get_individuals()[0];
|
||||||
double best_fitness = first.fitness.adjusted_fitness;
|
double best_fitness = first.fitness.adjusted_fitness;
|
||||||
tree_t* tree = &first.tree;
|
const tree_t* tree = &first.tree;
|
||||||
for (auto& ind : pop.get_individuals())
|
for (auto& ind : pop.get_individuals())
|
||||||
{
|
{
|
||||||
if (ind.fitness.adjusted_fitness > best_fitness)
|
if (ind.fitness.adjusted_fitness > best_fitness)
|
||||||
|
@ -37,11 +37,11 @@ namespace blt::gp
|
||||||
return *tree;
|
return *tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
tree_t& select_worst_t::select(gp_program&, population_t& pop)
|
const tree_t& select_worst_t::select(gp_program&, const population_t& pop)
|
||||||
{
|
{
|
||||||
auto& first = pop.get_individuals()[0];
|
auto& first = pop.get_individuals()[0];
|
||||||
double worst_fitness = first.fitness.adjusted_fitness;
|
double worst_fitness = first.fitness.adjusted_fitness;
|
||||||
tree_t* tree = &first.tree;
|
const tree_t* tree = &first.tree;
|
||||||
for (auto& ind : pop.get_individuals())
|
for (auto& ind : pop.get_individuals())
|
||||||
{
|
{
|
||||||
if (ind.fitness.adjusted_fitness < worst_fitness)
|
if (ind.fitness.adjusted_fitness < worst_fitness)
|
||||||
|
@ -53,12 +53,12 @@ namespace blt::gp
|
||||||
return *tree;
|
return *tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
tree_t& select_random_t::select(gp_program& program, population_t& pop)
|
const tree_t& select_random_t::select(gp_program& program, const population_t& pop)
|
||||||
{
|
{
|
||||||
return pop.get_individuals()[program.get_random().get_size_t(0ul, pop.get_individuals().size())].tree;
|
return pop.get_individuals()[program.get_random().get_size_t(0ul, pop.get_individuals().size())].tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
tree_t& select_tournament_t::select(gp_program& program, population_t& pop)
|
const tree_t& select_tournament_t::select(gp_program& program, const population_t& pop)
|
||||||
{
|
{
|
||||||
blt::u64 best = program.get_random().get_u64(0, pop.get_individuals().size());
|
blt::u64 best = program.get_random().get_u64(0, pop.get_individuals().size());
|
||||||
auto& i_ref = pop.get_individuals();
|
auto& i_ref = pop.get_individuals();
|
||||||
|
@ -71,7 +71,7 @@ namespace blt::gp
|
||||||
return i_ref[best].tree;
|
return i_ref[best].tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
tree_t& select_fitness_proportionate_t::select(gp_program& program, population_t& pop)
|
const tree_t& select_fitness_proportionate_t::select(gp_program& program, const population_t& pop)
|
||||||
{
|
{
|
||||||
auto& stats = program.get_population_stats();
|
auto& stats = program.get_population_stats();
|
||||||
auto choice = program.get_random().get_double();
|
auto choice = program.get_random().get_double();
|
||||||
|
|
|
@ -59,23 +59,21 @@ namespace blt::gp
|
||||||
mutation_t::config_t::config_t(): generator(grow_generator)
|
mutation_t::config_t::config_t(): generator(grow_generator)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
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
|
bool crossover_t::apply(gp_program& program, const tree_t& p1, const tree_t& p2, tree_t& c1, tree_t& c2) // NOLINT
|
||||||
{
|
{
|
||||||
result_t result{p1, p2};
|
c1.copy_fast(p1);
|
||||||
|
c2.copy_fast(p2);
|
||||||
auto& c1 = result.child1;
|
|
||||||
auto& c2 = result.child2;
|
|
||||||
|
|
||||||
auto& c1_ops = c1.get_operations();
|
auto& c1_ops = c1.get_operations();
|
||||||
auto& c2_ops = c2.get_operations();
|
auto& c2_ops = c2.get_operations();
|
||||||
|
|
||||||
if (c1_ops.size() < 5 || c2_ops.size() < 5)
|
if (c1_ops.size() < 5 || c2_ops.size() < 5)
|
||||||
return blt::unexpected(error_t::TREE_TOO_SMALL);
|
return false;
|
||||||
|
|
||||||
auto point = get_crossover_point(program, c1, c2);
|
auto point = get_crossover_point(program, p1, p2);
|
||||||
|
|
||||||
if (!point)
|
if (!point)
|
||||||
return blt::unexpected(point.error());
|
return false;
|
||||||
|
|
||||||
auto crossover_point_begin_itr = c1_ops.begin() + point->p1_crossover_point;
|
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 crossover_point_end_itr = c1_ops.begin() + c1.find_endpoint(program, point->p1_crossover_point);
|
||||||
|
@ -104,8 +102,8 @@ namespace blt::gp
|
||||||
blt::size_t c2_stack_for_bytes = accumulate_type_sizes(found_point_begin_itr, found_point_end_itr);
|
blt::size_t c2_stack_for_bytes = accumulate_type_sizes(found_point_begin_itr, found_point_end_itr);
|
||||||
auto c1_total = static_cast<blt::ptrdiff_t>(c1_stack_after_bytes + c1_stack_for_bytes);
|
auto c1_total = static_cast<blt::ptrdiff_t>(c1_stack_after_bytes + c1_stack_for_bytes);
|
||||||
auto c2_total = static_cast<blt::ptrdiff_t>(c2_stack_after_bytes + c2_stack_for_bytes);
|
auto 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>(c1_total);
|
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>(c2_total);
|
auto copy_ptr_c2 = get_thread_pointer_for_size<struct c2_t>(c2_total);
|
||||||
|
|
||||||
c1_stack.copy_to(copy_ptr_c1, c1_total);
|
c1_stack.copy_to(copy_ptr_c1, c1_total);
|
||||||
c1_stack.pop_bytes(c1_total);
|
c1_stack.pop_bytes(c1_total);
|
||||||
|
@ -131,8 +129,8 @@ namespace blt::gp
|
||||||
c2_ops.insert(++insert_point_c2, c1_operators.begin(), c1_operators.end());
|
c2_ops.insert(++insert_point_c2, c1_operators.begin(), c1_operators.end());
|
||||||
|
|
||||||
#if BLT_DEBUG_LEVEL >= 2
|
#if BLT_DEBUG_LEVEL >= 2
|
||||||
blt::size_t c1_found_bytes = result.child1.get_values().size().total_used_bytes;
|
blt::size_t c1_found_bytes = c1.get_values().size().total_used_bytes;
|
||||||
blt::size_t c2_found_bytes = result.child2.get_values().size().total_used_bytes;
|
blt::size_t c2_found_bytes = c2.get_values().size().total_used_bytes;
|
||||||
blt::size_t c1_expected_bytes = std::accumulate(result.child1.get_operations().begin(), result.child1.get_operations().end(), 0ul,
|
blt::size_t c1_expected_bytes = std::accumulate(result.child1.get_operations().begin(), result.child1.get_operations().end(), 0ul,
|
||||||
[](const auto& v1, const auto& v2) {
|
[](const auto& v1, const auto& v2) {
|
||||||
if (v2.is_value)
|
if (v2.is_value)
|
||||||
|
@ -153,10 +151,10 @@ namespace blt::gp
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return result;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
blt::expected<crossover_t::crossover_point_t, crossover_t::error_t> crossover_t::get_crossover_point(gp_program& program, const tree_t& c1,
|
std::optional<crossover_t::crossover_point_t> crossover_t::get_crossover_point(gp_program& program, const tree_t& c1,
|
||||||
const tree_t& c2) const
|
const tree_t& c2) const
|
||||||
{
|
{
|
||||||
auto& c1_ops = c1.get_operations();
|
auto& c1_ops = c1.get_operations();
|
||||||
|
@ -194,10 +192,10 @@ namespace blt::gp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found)
|
if (!found)
|
||||||
return blt::unexpected(error_t::NO_VALID_TYPE);
|
return {};
|
||||||
}
|
}
|
||||||
// should we try again over the whole tree? probably not.
|
// should we try again over the whole tree? probably not.
|
||||||
return blt::unexpected(error_t::NO_VALID_TYPE);
|
return {};
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
attempted_point = program.get_random().get_size_t(1ul, c2_ops.size());
|
attempted_point = program.get_random().get_size_t(1ul, c2_ops.size());
|
||||||
|
@ -213,13 +211,13 @@ namespace blt::gp
|
||||||
return crossover_point_t{static_cast<blt::ptrdiff_t>(crossover_point), static_cast<blt::ptrdiff_t>(attempted_point)};
|
return crossover_point_t{static_cast<blt::ptrdiff_t>(crossover_point), static_cast<blt::ptrdiff_t>(attempted_point)};
|
||||||
}
|
}
|
||||||
|
|
||||||
tree_t mutation_t::apply(gp_program& program, const tree_t& p)
|
bool mutation_t::apply(gp_program& program, const tree_t& p, tree_t& c)
|
||||||
{
|
{
|
||||||
auto c = p;
|
c.copy_fast(p);
|
||||||
|
|
||||||
mutate_point(program, c, program.get_random().get_size_t(0ul, c.get_operations().size()));
|
mutate_point(program, c, program.get_random().get_size_t(0ul, c.get_operations().size()));
|
||||||
|
|
||||||
return c;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
blt::size_t mutation_t::mutate_point(gp_program& program, tree_t& c, blt::size_t node)
|
blt::size_t mutation_t::mutate_point(gp_program& program, tree_t& c, blt::size_t node)
|
||||||
|
@ -304,10 +302,10 @@ namespace blt::gp
|
||||||
return begin_point + new_ops_r.size();
|
return begin_point + new_ops_r.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
tree_t advanced_mutation_t::apply(gp_program& program, const tree_t& p)
|
bool advanced_mutation_t::apply(gp_program& program, const tree_t& p, tree_t& c)
|
||||||
{
|
{
|
||||||
// child tree
|
// child tree
|
||||||
tree_t c = p;
|
c.copy_fast(p);
|
||||||
|
|
||||||
auto& ops = c.get_operations();
|
auto& ops = c.get_operations();
|
||||||
auto& vals = c.get_values();
|
auto& vals = c.get_values();
|
||||||
|
@ -732,6 +730,6 @@ namespace blt::gp
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return c;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue