add ability to do steady state GPs. Untested currently.

main
Brett 2025-04-01 20:55:40 -04:00
parent 577f3d613c
commit 08e44fb4bd
4 changed files with 1007 additions and 893 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.34) project(blt-gp VERSION 0.4.0)
include(CTest) include(CTest)

View File

@ -26,191 +26,201 @@
namespace blt::gp::example namespace blt::gp::example
{ {
class symbolic_regression_t : public example_base_t class symbolic_regression_t : public example_base_t
{ {
public: public:
struct context struct context
{ {
float x, y; float x, y;
}; };
private: private:
bool fitness_function(const tree_t& current_tree, fitness_t& fitness, size_t) const bool fitness_function(const tree_t& current_tree, fitness_t& fitness, size_t) const
{ {
constexpr static double value_cutoff = 1.e15; constexpr static double value_cutoff = 1.e15;
for (auto& fitness_case : training_cases) for (auto& fitness_case : training_cases)
{ {
const auto diff = std::abs(fitness_case.y - current_tree.get_evaluation_value<float>(fitness_case)); const auto diff = std::abs(fitness_case.y - current_tree.get_evaluation_value<float>(fitness_case));
if (diff < value_cutoff) if (diff < value_cutoff)
{ {
fitness.raw_fitness += diff; fitness.raw_fitness += diff;
if (diff <= 0.01) if (diff <= 0.01)
fitness.hits++; fitness.hits++;
} } else
else fitness.raw_fitness += value_cutoff;
fitness.raw_fitness += value_cutoff; }
} fitness.standardized_fitness = fitness.raw_fitness;
fitness.standardized_fitness = fitness.raw_fitness; fitness.adjusted_fitness = (1.0 / (1.0 + fitness.standardized_fitness));
fitness.adjusted_fitness = (1.0 / (1.0 + fitness.standardized_fitness)); return static_cast<size_t>(fitness.hits) == training_cases.size();
return static_cast<size_t>(fitness.hits) == training_cases.size(); }
}
static float example_function(const float x) static float example_function(const float x)
{ {
return x * x * x * x + x * x * x + x * x + x; return x * x * x * x + x * x * x + x * x + x;
} }
public: public:
template <typename SEED> template <typename SEED>
symbolic_regression_t(SEED seed, const prog_config_t& config): example_base_t{std::forward<SEED>(seed), config} symbolic_regression_t(SEED seed, const prog_config_t& config): example_base_t{std::forward<SEED>(seed), config}
{ {
BLT_INFO("Starting BLT-GP Symbolic Regression Example"); BLT_INFO("Starting BLT-GP Symbolic Regression Example");
BLT_DEBUG("Setup Fitness cases"); BLT_DEBUG("Setup Fitness cases");
for (auto& fitness_case : training_cases) for (auto& fitness_case : training_cases)
{ {
constexpr float range = 10; constexpr float range = 10;
constexpr float half_range = range / 2.0; constexpr float half_range = range / 2.0;
const auto x = program.get_random().get_float(-half_range, half_range); const auto x = program.get_random().get_float(-half_range, half_range);
const auto y = example_function(x); const auto y = example_function(x);
fitness_case = {x, y}; fitness_case = {x, y};
} }
fitness_function_ref = [this](const tree_t& t, fitness_t& f, const size_t i) fitness_function_ref = [this](const tree_t& t, fitness_t& f, const size_t i) {
{ return fitness_function(t, f, i);
return fitness_function(t, f, i); };
}; }
}
void setup_operations() void setup_operations()
{ {
BLT_DEBUG("Setup Types and Operators"); BLT_DEBUG("Setup Types and Operators");
static operation_t add{ static operation_t add{
// this is the function used by the operation // this is the function used by the operation
[](const float a, const float b) [](const float a, const float b) {
{ return a + b;
return a + b; },
}, // this name is optional and is used if you print an individual
// this name is optional and is used if you print an individual "add"
"add" };
}; static operation_t sub([](const float a, const float b) {
static operation_t sub([](const float a, const float b) { return a - b; }, "sub"); return a - b;
static operation_t mul([](const float a, const float b) { return a * b; }, "mul"); }, "sub");
static operation_t pro_div([](const float a, const float b) { return b == 0.0f ? 0.0f : a / b; }, "div"); static operation_t mul([](const float a, const float b) {
static operation_t op_sin([](const float a) { return std::sin(a); }, "sin"); return a * b;
static operation_t op_cos([](const float a) { return std::cos(a); }, "cos"); }, "mul");
static operation_t op_exp([](const float a) { return std::exp(a); }, "exp"); static operation_t pro_div([](const float a, const float b) {
static operation_t op_log([](const float a) { return a <= 0.0f ? 0.0f : std::log(a); }, "log"); return b == 0.0f ? 0.0f : a / b;
static auto lit = operation_t([this]() }, "div");
{ static operation_t op_sin([](const float a) {
return program.get_random().get_float(-1.0f, 1.0f); return std::sin(a);
}, "lit").set_ephemeral(); }, "sin");
static operation_t op_cos([](const float a) {
return std::cos(a);
}, "cos");
static operation_t op_exp([](const float a) {
return std::exp(a);
}, "exp");
static operation_t op_log([](const float a) {
return a <= 0.0f ? 0.0f : std::log(a);
}, "log");
static auto lit = operation_t([this]() {
return program.get_random().get_float(-1.0f, 1.0f);
}, "lit").set_ephemeral();
static operation_t op_x([](const context& context) static operation_t op_x([](const context& context) {
{ return context.x;
return context.x; }, "x");
}, "x");
operator_builder<context> builder{}; operator_builder<context> builder{};
builder.build(add, sub, mul, pro_div, op_sin, op_cos, op_exp, op_log, lit, op_x); builder.build(add, sub, mul, pro_div, op_sin, op_cos, op_exp, op_log, lit, op_x);
program.set_operations(builder.grab()); program.set_operations(builder.grab());
} }
void generate_initial_population() void generate_initial_population()
{ {
BLT_DEBUG("Generate Initial Population"); BLT_DEBUG("Generate Initial Population");
static auto sel = select_tournament_t{}; static auto sel = select_tournament_t{};
if (crossover_sel == nullptr) if (crossover_sel == nullptr)
crossover_sel = &sel; crossover_sel = &sel;
if (mutation_sel == nullptr) if (mutation_sel == nullptr)
mutation_sel = &sel; mutation_sel = &sel;
if (reproduction_sel == nullptr) if (reproduction_sel == nullptr)
reproduction_sel = &sel; reproduction_sel = &sel;
program.generate_population(program.get_typesystem().get_type<float>().id(), fitness_function_ref, *crossover_sel, *mutation_sel, program.generate_initial_population(program.get_typesystem().get_type<float>().id());
*reproduction_sel); program.setup_generational_evaluation(fitness_function_ref, *crossover_sel,
} *mutation_sel, *reproduction_sel);
}
void run_generation_loop() void run_generation_loop()
{ {
BLT_DEBUG("Begin Generation Loop"); BLT_DEBUG("Begin Generation Loop");
while (!program.should_terminate()) while (!program.should_terminate())
{ {
#ifdef BLT_TRACK_ALLOCATIONS #ifdef BLT_TRACK_ALLOCATIONS
auto cross = crossover_calls.start_measurement(); auto cross = crossover_calls.start_measurement();
auto mut = mutation_calls.start_measurement(); auto mut = mutation_calls.start_measurement();
auto repo = reproduction_calls.start_measurement(); auto repo = reproduction_calls.start_measurement();
#endif #endif
BLT_TRACE("------------{Begin Generation %ld}------------", program.get_current_generation()); BLT_TRACE("------------\\{Begin Generation {}}------------", program.get_current_generation());
BLT_TRACE("Creating next generation"); BLT_TRACE("Creating next generation");
program.create_next_generation(); program.create_next_generation();
BLT_TRACE("Move to next generation"); BLT_TRACE("Move to next generation");
program.next_generation(); program.next_generation();
BLT_TRACE("Evaluate Fitness"); BLT_TRACE("Evaluate Fitness");
program.evaluate_fitness(); program.evaluate_fitness();
const auto& stats = program.get_population_stats(); const auto& stats = program.get_population_stats();
BLT_TRACE("Avg Fit: %lf, Best Fit: %lf, Worst Fit: %lf, Overall Fit: %lf", BLT_TRACE("Avg Fit: {:0.6f}, Best Fit: {:0.6f}, Worst Fit: {:0.6f}, Overall Fit: {:0.6f}", stats.average_fitness.load(std::memory_order_relaxed),
stats.average_fitness.load(std::memory_order_relaxed), stats.best_fitness.load(std::memory_order_relaxed), stats.best_fitness.load(std::memory_order_relaxed), stats.worst_fitness.load(std::memory_order_relaxed),
stats.worst_fitness.load(std::memory_order_relaxed), stats.overall_fitness.load(std::memory_order_relaxed)); stats.overall_fitness.load(std::memory_order_relaxed));
#ifdef BLT_TRACK_ALLOCATIONS #ifdef BLT_TRACK_ALLOCATIONS
crossover_calls.stop_measurement(cross); crossover_calls.stop_measurement(cross);
mutation_calls.stop_measurement(mut); mutation_calls.stop_measurement(mut);
reproduction_calls.stop_measurement(repo); reproduction_calls.stop_measurement(repo);
const auto total = (cross.get_call_difference() * 2) + mut.get_call_difference() + repo.get_call_difference(); const auto total = (cross.get_call_difference() * 2) + mut.get_call_difference() + repo.get_call_difference();
BLT_TRACE("Calls Crossover: %ld, Mutation %ld, Reproduction %ld; %ld", cross.get_call_difference(), mut.get_call_difference(), repo.get_call_difference(), total); BLT_TRACE("Calls Crossover: {}, Mutation {}, Reproduction {}; {}", cross.get_call_difference(), mut.get_call_difference(), repo.get_call_difference(), total);
BLT_TRACE("Value Crossover: %ld, Mutation %ld, Reproduction %ld; %ld", cross.get_value_difference(), mut.get_value_difference(), repo.get_value_difference(), (cross.get_value_difference() * 2 + mut.get_value_difference() + repo.get_value_difference()) - total); BLT_TRACE("Value Crossover: {}, Mutation {}, Reproduction {}; {}", cross.get_value_difference(), mut.get_value_difference(), repo.get_value_difference(), (cross.get_value_difference() * 2 + mut.get_value_difference() + repo.get_value_difference()) - total);
#endif #endif
BLT_TRACE("----------------------------------------------"); BLT_TRACE("----------------------------------------------");
std::cout << std::endl; std::cout << std::endl;
} }
} }
auto get_and_print_best() auto get_and_print_best()
{ {
const auto best = program.get_best_individuals<3>(); const auto best = program.get_best_individuals<3>();
BLT_INFO("Best approximations:"); BLT_INFO("Best approximations:");
for (auto& i_ref : best) for (auto& i_ref : best)
{ {
auto& i = i_ref.get(); auto& i = i_ref.get();
BLT_DEBUG("Fitness: %lf, stand: %lf, raw: %lf", i.fitness.adjusted_fitness, i.fitness.standardized_fitness, i.fitness.raw_fitness); BLT_DEBUG("Fitness: {:0.6f}, stand: {:0.6f}, raw: {:0.6f}", i.fitness.adjusted_fitness, i.fitness.standardized_fitness, i.fitness.raw_fitness);
i.tree.print(std::cout); i.tree.print(std::cout);
std::cout << "\n"; std::cout << "\n";
} }
return best; return best;
} }
void print_stats() const void print_stats() const
{ {
// TODO: make stats helper // TODO: make stats helper
const auto& stats = program.get_population_stats(); const auto& stats = program.get_population_stats();
BLT_INFO("Stats:"); BLT_INFO("Stats:");
BLT_INFO("Average fitness: %lf", stats.average_fitness.load()); BLT_INFO("Average fitness: %lf", stats.average_fitness.load());
BLT_INFO("Best fitness: %lf", stats.best_fitness.load()); BLT_INFO("Best fitness: %lf", stats.best_fitness.load());
BLT_INFO("Worst fitness: %lf", stats.worst_fitness.load()); BLT_INFO("Worst fitness: %lf", stats.worst_fitness.load());
BLT_INFO("Overall fitness: %lf", stats.overall_fitness.load()); BLT_INFO("Overall fitness: %lf", stats.overall_fitness.load());
} }
void execute() void execute()
{ {
setup_operations(); setup_operations();
generate_initial_population(); generate_initial_population();
run_generation_loop(); run_generation_loop();
get_and_print_best(); get_and_print_best();
print_stats(); print_stats();
} }
[[nodiscard]] const auto& get_training_cases() const [[nodiscard]] const auto& get_training_cases() const
{ {
return training_cases; return training_cases;
} }
private: private:
std::array<context, 200> training_cases{}; std::array<context, 200> training_cases{};
}; };
} }
#endif //BLT_GP_EXAMPLE_SYMBOLIC_REGRESSION_H #endif //BLT_GP_EXAMPLE_SYMBOLIC_REGRESSION_H

File diff suppressed because it is too large Load Diff

View File

@ -26,36 +26,36 @@ namespace blt::gp
static advanced_mutation_t s_mutator; static advanced_mutation_t s_mutator;
static crossover_t s_crossover; static crossover_t s_crossover;
static ramped_half_initializer_t s_init; static ramped_half_initializer_t s_init;
prog_config_t::prog_config_t(): mutator(s_mutator), crossover(s_crossover), pop_initializer(s_init) prog_config_t::prog_config_t(): mutator(s_mutator), crossover(s_crossover), pop_initializer(s_init)
{ {
} }
prog_config_t::prog_config_t(const std::reference_wrapper<population_initializer_t>& popInitializer): prog_config_t::prog_config_t(const std::reference_wrapper<population_initializer_t>& popInitializer):
mutator(s_mutator), crossover(s_crossover), pop_initializer(popInitializer) mutator(s_mutator), crossover(s_crossover), pop_initializer(popInitializer)
{} {}
prog_config_t::prog_config_t(size_t populationSize, const std::reference_wrapper<population_initializer_t>& popInitializer): prog_config_t::prog_config_t(size_t populationSize, const std::reference_wrapper<population_initializer_t>& popInitializer):
population_size(populationSize), mutator(s_mutator), crossover(s_crossover), pop_initializer(popInitializer) population_size(populationSize), mutator(s_mutator), crossover(s_crossover), pop_initializer(popInitializer)
{} {}
prog_config_t::prog_config_t(size_t populationSize): prog_config_t::prog_config_t(size_t populationSize):
population_size(populationSize), mutator(s_mutator), crossover(s_crossover), pop_initializer(s_init) population_size(populationSize), mutator(s_mutator), crossover(s_crossover), pop_initializer(s_init)
{} {}
random_t& gp_program::get_random() const random_t& gp_program::get_random() const
{ {
thread_local static blt::gp::random_t random_engine{seed_func()}; thread_local static blt::gp::random_t random_engine{seed_func()};
return random_engine; return random_engine;
} }
stack_allocator::Allocator& stack_allocator::get_allocator() stack_allocator::Allocator& stack_allocator::get_allocator()
{ {
static Allocator allocator; static Allocator allocator;
return allocator; return allocator;
} }
void gp_program::create_threads() void gp_program::create_threads()
{ {
#ifdef BLT_TRACK_ALLOCATIONS #ifdef BLT_TRACK_ALLOCATIONS