this is a breaking patch
parent
e4ce3739f0
commit
c6a2b5d324
105
CMakeLists.txt
105
CMakeLists.txt
|
@ -1,8 +1,4 @@
|
|||
cmake_minimum_required(VERSION 3.25)
|
||||
macro(compile_options target_name)
|
||||
target_compile_options(${target_name} PRIVATE -Wall -Wextra -Wpedantic -Wno-comment)
|
||||
target_link_options(${target_name} PRIVATE -Wall -Wextra -Wpedantic -Wno-comment)
|
||||
endmacro()
|
||||
|
||||
macro(sanitizers target_name)
|
||||
if (${ENABLE_ADDRSAN} MATCHES ON)
|
||||
|
@ -21,7 +17,39 @@ macro(sanitizers target_name)
|
|||
endif ()
|
||||
endmacro()
|
||||
|
||||
project(blt-gp VERSION 0.2.1)
|
||||
macro(compile_options target_name)
|
||||
if (NOT ${MOLD} STREQUAL MOLD-NOTFOUND)
|
||||
target_compile_options(${target_name} PUBLIC -fuse-ld=mold)
|
||||
endif ()
|
||||
|
||||
target_compile_options(${target_name} PRIVATE -Wall -Wextra -Wpedantic -Wno-comment)
|
||||
target_link_options(${target_name} PRIVATE -Wall -Wextra -Wpedantic -Wno-comment)
|
||||
sanitizers(${target_name})
|
||||
endmacro()
|
||||
|
||||
macro(blt_add_project name source type)
|
||||
|
||||
project(${name}-${type})
|
||||
|
||||
add_executable(${name}-${type} ${source})
|
||||
|
||||
target_link_libraries(${name}-${type} PRIVATE BLT blt-gp Threads::Threads)
|
||||
|
||||
compile_options(${name}-${type})
|
||||
target_compile_definitions(${name}-${type} PRIVATE BLT_DEBUG_LEVEL=${DEBUG_LEVEL})
|
||||
|
||||
if (${TRACK_ALLOCATIONS})
|
||||
target_compile_definitions(${name}-${type} PRIVATE BLT_TRACK_ALLOCATIONS=1)
|
||||
endif ()
|
||||
|
||||
add_test(NAME ${name} COMMAND ${name}-${type})
|
||||
|
||||
set_property(TEST ${name} PROPERTY FAIL_REGULAR_EXPRESSION "FAIL;ERROR;FATAL;exception")
|
||||
|
||||
project(blt-gp)
|
||||
endmacro()
|
||||
|
||||
project(blt-gp VERSION 0.2.2)
|
||||
|
||||
include(CTest)
|
||||
|
||||
|
@ -49,8 +77,7 @@ file(GLOB_RECURSE PROJECT_BUILD_FILES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
|
|||
|
||||
add_library(blt-gp ${PROJECT_BUILD_FILES})
|
||||
|
||||
target_compile_options(blt-gp PRIVATE -Wall -Wextra -Wpedantic -Wno-comment)
|
||||
target_link_options(blt-gp PRIVATE -Wall -Wextra -Wpedantic -Wno-comment)
|
||||
compile_options(blt-gp)
|
||||
|
||||
find_program(MOLD "mold")
|
||||
|
||||
|
@ -67,70 +94,10 @@ if (${TRACK_ALLOCATIONS})
|
|||
target_compile_definitions(blt-gp PRIVATE BLT_TRACK_ALLOCATIONS=1)
|
||||
endif ()
|
||||
|
||||
if (${ENABLE_ADDRSAN} MATCHES ON)
|
||||
target_compile_options(blt-gp PRIVATE -fsanitize=address)
|
||||
target_link_options(blt-gp PRIVATE -fsanitize=address)
|
||||
endif ()
|
||||
|
||||
if (${ENABLE_UBSAN} MATCHES ON)
|
||||
target_compile_options(blt-gp PRIVATE -fsanitize=undefined)
|
||||
target_link_options(blt-gp PRIVATE -fsanitize=undefined)
|
||||
endif ()
|
||||
|
||||
if (${ENABLE_TSAN} MATCHES ON)
|
||||
target_compile_options(blt-gp PRIVATE -fsanitize=thread)
|
||||
target_link_options(blt-gp PRIVATE -fsanitize=thread)
|
||||
endif ()
|
||||
|
||||
macro(blt_add_project name source type)
|
||||
|
||||
project(${name}-${type})
|
||||
|
||||
#add_compile_options(-fuse-ld=mold)
|
||||
|
||||
add_executable(${name}-${type} ${source})
|
||||
|
||||
target_link_libraries(${name}-${type} PRIVATE BLT blt-gp Threads::Threads)
|
||||
|
||||
target_compile_options(${name}-${type} PRIVATE -Wall -Wextra -Wpedantic -Wno-comment)
|
||||
target_link_options(${name}-${type} PRIVATE -Wall -Wextra -Wpedantic -Wno-comment)
|
||||
target_compile_definitions(${name}-${type} PRIVATE BLT_DEBUG_LEVEL=${DEBUG_LEVEL})
|
||||
|
||||
if (${TRACK_ALLOCATIONS})
|
||||
target_compile_definitions(${name}-${type} PRIVATE BLT_TRACK_ALLOCATIONS=1)
|
||||
endif ()
|
||||
|
||||
if (${ENABLE_ADDRSAN} MATCHES ON)
|
||||
target_compile_options(${name}-${type} PRIVATE -fsanitize=address)
|
||||
target_link_options(${name}-${type} PRIVATE -fsanitize=address)
|
||||
endif ()
|
||||
|
||||
if (${ENABLE_UBSAN} MATCHES ON)
|
||||
target_compile_options(${name}-${type} PRIVATE -fsanitize=undefined)
|
||||
target_link_options(${name}-${type} PRIVATE -fsanitize=undefined)
|
||||
endif ()
|
||||
|
||||
if (${ENABLE_TSAN} MATCHES ON)
|
||||
target_compile_options(${name}-${type} PRIVATE -fsanitize=thread)
|
||||
target_link_options(${name}-${type} PRIVATE -fsanitize=thread)
|
||||
endif ()
|
||||
|
||||
add_test(NAME ${name} COMMAND ${name}-${type})
|
||||
|
||||
# set (passRegex "Pass" "Passed" "PASS" "PASSED")
|
||||
# set (failRegex "WARN" "FAIL" "ERROR" "FATAL")
|
||||
set(failRegex "\\[WARN\\]" "FAIL" "ERROR" "FATAL" "exception")
|
||||
|
||||
# set_property (TEST ${name} PROPERTY PASS_REGULAR_EXPRESSION "${passRegex}")
|
||||
set_property(TEST ${name} PROPERTY FAIL_REGULAR_EXPRESSION "${failRegex}")
|
||||
|
||||
project(blt-gp)
|
||||
endmacro()
|
||||
|
||||
if (${BUILD_EXAMPLES})
|
||||
|
||||
blt_add_project(blt-symbolic-regression examples/symbolic_regression.cpp example)
|
||||
blt_add_project(blt-rice-classification examples/rice_classification.cpp example)
|
||||
blt_add_project(blt-symbolic-regression examples/src/symbolic_regression.cpp example)
|
||||
blt_add_project(blt-rice-classification examples/src/rice_classification.cpp example)
|
||||
|
||||
endif ()
|
||||
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
#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_OPERATIONS_COMMON_H
|
||||
#define BLT_GP_OPERATIONS_COMMON_H
|
||||
|
||||
#include <blt/gp/program.h>
|
||||
|
||||
blt::gp::operation_t add([](float a, float b) { return a + b; }, "add");
|
||||
blt::gp::operation_t sub([](float a, float b) { return a - b; }, "sub");
|
||||
blt::gp::operation_t mul([](float a, float b) { return a * b; }, "mul");
|
||||
blt::gp::operation_t pro_div([](float a, float b) { return b == 0.0f ? 1.0f : a / b; }, "div");
|
||||
blt::gp::operation_t op_sin([](float a) { return std::sin(a); }, "sin");
|
||||
blt::gp::operation_t op_cos([](float a) { return std::cos(a); }, "cos");
|
||||
blt::gp::operation_t op_exp([](float a) { return std::exp(a); }, "exp");
|
||||
blt::gp::operation_t op_log([](float a) { return a == 0.0f ? 0.0f : std::log(a); }, "log");
|
||||
|
||||
#endif //BLT_GP_OPERATIONS_COMMON_H
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* <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 "../symbolic_regression.h"
|
||||
|
||||
static const unsigned long SEED = std::random_device()();
|
||||
|
||||
blt::gp::prog_config_t config = blt::gp::prog_config_t()
|
||||
.set_initial_min_tree_size(2)
|
||||
.set_initial_max_tree_size(6)
|
||||
.set_elite_count(2)
|
||||
.set_crossover_chance(0.9)
|
||||
.set_mutation_chance(0.0)
|
||||
.set_reproduction_chance(0.25)
|
||||
.set_max_generations(50)
|
||||
.set_pop_size(500)
|
||||
.set_thread_count(0);
|
||||
|
||||
int main()
|
||||
{
|
||||
blt::gp::example::symbolic_regression_t regression{config, SEED};
|
||||
regression.execute();
|
||||
|
||||
BLT_TRACE("%lf vs %lf", blt::gp::parent_fitness.load(std::memory_order_relaxed), blt::gp::child_fitness.load(std::memory_order_relaxed));
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,196 +0,0 @@
|
|||
/*
|
||||
* <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/profiling/profiler_v2.h>
|
||||
#include <blt/gp/tree.h>
|
||||
#include <blt/std/logging.h>
|
||||
#include <blt/format/format.h>
|
||||
#include <iostream>
|
||||
#include "operations_common.h"
|
||||
#include "blt/math/averages.h"
|
||||
|
||||
//static constexpr long SEED = 41912;
|
||||
static const unsigned long SEED = std::random_device()();
|
||||
|
||||
struct context
|
||||
{
|
||||
float x, y;
|
||||
};
|
||||
|
||||
std::array<context, 200> training_cases;
|
||||
|
||||
blt::gp::mutation_t mut;
|
||||
|
||||
blt::gp::prog_config_t config = blt::gp::prog_config_t()
|
||||
.set_initial_min_tree_size(2)
|
||||
.set_initial_max_tree_size(6)
|
||||
.set_elite_count(2)
|
||||
.set_crossover_chance(0.9)
|
||||
.set_mutation_chance(0.1)
|
||||
.set_reproduction_chance(0)
|
||||
.set_max_generations(50)
|
||||
.set_pop_size(500)
|
||||
.set_thread_count(0);
|
||||
//blt::gp::prog_config_t config = blt::gp::prog_config_t()
|
||||
// .set_initial_min_tree_size(2)
|
||||
// .set_initial_max_tree_size(6)
|
||||
// .set_elite_count(2)
|
||||
// .set_crossover_chance(0.9)
|
||||
// .set_mutation_chance(0.1)
|
||||
// .set_reproduction_chance(0)
|
||||
// .set_max_generations(50)
|
||||
// .set_pop_size(500)
|
||||
// .set_thread_count(0);
|
||||
|
||||
blt::gp::gp_program program{SEED, config};
|
||||
|
||||
auto lit = blt::gp::operation_t([]() {
|
||||
return program.get_random().get_float(-1.0f, 1.0f);
|
||||
}, "lit").set_ephemeral();
|
||||
|
||||
blt::gp::operation_t op_x([](const context& context) {
|
||||
return context.x;
|
||||
}, "x");
|
||||
|
||||
constexpr auto fitness_function = [](blt::gp::tree_t& current_tree, blt::gp::fitness_t& fitness, blt::size_t) {
|
||||
constexpr double value_cutoff = 1.e15;
|
||||
for (auto& fitness_case : training_cases)
|
||||
{
|
||||
auto diff = std::abs(fitness_case.y - current_tree.get_evaluation_value<float>(fitness_case));
|
||||
if (diff < value_cutoff)
|
||||
{
|
||||
fitness.raw_fitness += diff;
|
||||
if (diff <= 0.01)
|
||||
fitness.hits++;
|
||||
} else
|
||||
fitness.raw_fitness += value_cutoff;
|
||||
}
|
||||
fitness.standardized_fitness = fitness.raw_fitness;
|
||||
fitness.adjusted_fitness = (1.0 / (1.0 + fitness.standardized_fitness));
|
||||
return static_cast<blt::size_t>(fitness.hits) == training_cases.size();
|
||||
};
|
||||
|
||||
float example_function(float x)
|
||||
{
|
||||
return x * x * x * x + x * x * x + x * x + x;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
BLT_INFO("Starting BLT-GP Symbolic Regression Example");
|
||||
BLT_START_INTERVAL("Symbolic Regression", "Main");
|
||||
BLT_DEBUG("Setup Fitness cases");
|
||||
for (auto& fitness_case : training_cases)
|
||||
{
|
||||
constexpr float range = 10;
|
||||
constexpr float half_range = range / 2.0;
|
||||
auto x = program.get_random().get_float(-half_range, half_range);
|
||||
auto y = example_function(x);
|
||||
fitness_case = {x, y};
|
||||
}
|
||||
|
||||
BLT_DEBUG("Setup Types and Operators");
|
||||
blt::gp::operator_builder<context> builder{};
|
||||
program.set_operations(builder.build(add, sub, mul, pro_div, op_sin, op_cos, op_exp, op_log, lit, op_x));
|
||||
|
||||
BLT_DEBUG("Generate Initial Population");
|
||||
auto sel = blt::gp::select_fitness_proportionate_t{};
|
||||
program.generate_population(program.get_typesystem().get_type<float>().id(), fitness_function, sel, sel, sel);
|
||||
|
||||
BLT_DEBUG("Begin Generation Loop");
|
||||
while (!program.should_terminate())
|
||||
{
|
||||
BLT_TRACE("------------{Begin Generation %ld}------------", program.get_current_generation());
|
||||
BLT_TRACE("Creating next generation");
|
||||
BLT_START_INTERVAL("Symbolic Regression", "Gen");
|
||||
program.create_next_generation();
|
||||
BLT_END_INTERVAL("Symbolic Regression", "Gen");
|
||||
BLT_TRACE("Move to next generation");
|
||||
BLT_START_INTERVAL("Symbolic Regression", "Fitness");
|
||||
program.next_generation();
|
||||
BLT_TRACE("Evaluate Fitness");
|
||||
program.evaluate_fitness();
|
||||
BLT_END_INTERVAL("Symbolic Regression", "Fitness");
|
||||
BLT_TRACE("----------------------------------------------");
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
BLT_END_INTERVAL("Symbolic Regression", "Main");
|
||||
|
||||
auto best = program.get_best_individuals<3>();
|
||||
|
||||
BLT_INFO("Best approximations:");
|
||||
for (auto& i_ref : best)
|
||||
{
|
||||
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);
|
||||
i.tree.print(program, std::cout);
|
||||
std::cout << "\n";
|
||||
}
|
||||
auto& stats = program.get_population_stats();
|
||||
BLT_INFO("Stats:");
|
||||
BLT_INFO("Average fitness: %lf", stats.average_fitness.load());
|
||||
BLT_INFO("Best fitness: %lf", stats.best_fitness.load());
|
||||
BLT_INFO("Worst fitness: %lf", stats.worst_fitness.load());
|
||||
BLT_INFO("Overall fitness: %lf", stats.overall_fitness.load());
|
||||
// TODO: make stats helper
|
||||
|
||||
BLT_PRINT_PROFILE("Symbolic Regression", blt::PRINT_CYCLES | blt::PRINT_THREAD | blt::PRINT_WALL);
|
||||
|
||||
#ifdef BLT_TRACK_ALLOCATIONS
|
||||
BLT_TRACE("Total Allocations: %ld times with a total of %s, peak allocated bytes %s", blt::gp::tracker.getAllocations(),
|
||||
blt::byte_convert_t(blt::gp::tracker.getAllocatedBytes()).convert_to_nearest_type().to_pretty_string().c_str(),
|
||||
blt::byte_convert_t(blt::gp::tracker.getPeakAllocatedBytes()).convert_to_nearest_type().to_pretty_string().c_str());
|
||||
BLT_TRACE("------------------------------------------------------");
|
||||
auto evaluation_calls_v = blt::gp::evaluation_calls.get_calls();
|
||||
auto evaluation_allocations_v = blt::gp::evaluation_allocations.get_calls();
|
||||
BLT_TRACE("Total Evaluation Calls: %ld; Peak Bytes Allocated %s", evaluation_calls_v,
|
||||
blt::string::bytes_to_pretty(blt::gp::evaluation_calls.get_value()).c_str());
|
||||
BLT_TRACE("Total Evaluation Allocations: %ld; Bytes %s; Average %s", evaluation_allocations_v,
|
||||
blt::string::bytes_to_pretty(blt::gp::evaluation_allocations.get_value()).c_str(),
|
||||
blt::string::bytes_to_pretty(blt::average(blt::gp::evaluation_allocations.get_value(), evaluation_allocations_v)).c_str());
|
||||
BLT_TRACE("Percent Evaluation calls allocate? %lf%%", blt::average(evaluation_allocations_v, evaluation_calls_v) * 100);
|
||||
BLT_TRACE("------------------------------------------------------");
|
||||
auto crossover_calls_v = blt::gp::crossover_calls.get_calls();
|
||||
auto crossover_allocations_v = blt::gp::crossover_allocations.get_calls();
|
||||
auto mutation_calls_v = blt::gp::mutation_calls.get_calls();
|
||||
auto mutation_allocations_v = blt::gp::mutation_allocations.get_calls();
|
||||
auto reproduction_calls_v = blt::gp::reproduction_calls.get_calls();
|
||||
auto reproduction_allocations_v = blt::gp::reproduction_allocations.get_calls();
|
||||
BLT_TRACE("Total Crossover Calls: %ld; Peak Bytes Allocated %s", crossover_calls_v,
|
||||
blt::string::bytes_to_pretty(blt::gp::crossover_calls.get_value()).c_str());
|
||||
BLT_TRACE("Total Mutation Calls: %ld; Peak Bytes Allocated %s", mutation_calls_v,
|
||||
blt::string::bytes_to_pretty(blt::gp::mutation_calls.get_value()).c_str());
|
||||
BLT_TRACE("Total Reproduction Calls: %ld; Peak Bytes Allocated %s", reproduction_calls_v,
|
||||
blt::string::bytes_to_pretty(blt::gp::reproduction_calls.get_value()).c_str());
|
||||
BLT_TRACE("Total Crossover Allocations: %ld; Bytes %s; Average %s", crossover_allocations_v,
|
||||
blt::string::bytes_to_pretty(blt::gp::crossover_allocations.get_value()).c_str(),
|
||||
blt::string::bytes_to_pretty(blt::average(blt::gp::crossover_allocations.get_value(), crossover_allocations_v)).c_str());
|
||||
BLT_TRACE("Total Mutation Allocations: %ld; Bytes %s; Average %s", mutation_allocations_v,
|
||||
blt::string::bytes_to_pretty(blt::gp::mutation_allocations.get_value()).c_str(),
|
||||
blt::string::bytes_to_pretty(blt::average(blt::gp::mutation_allocations.get_value(), mutation_allocations_v)).c_str());
|
||||
BLT_TRACE("Total Reproduction Allocations: %ld; Bytes %s; Average %s", reproduction_allocations_v,
|
||||
blt::string::bytes_to_pretty(blt::gp::reproduction_allocations.get_value()).c_str(),
|
||||
blt::string::bytes_to_pretty(blt::average(blt::gp::reproduction_allocations.get_value(), reproduction_allocations_v)).c_str());
|
||||
BLT_TRACE("Percent Crossover calls allocate? %lf%%", blt::average(crossover_allocations_v, crossover_calls_v) * 100);
|
||||
BLT_TRACE("Percent Mutation calls allocate? %lf%%", blt::average(mutation_allocations_v, mutation_calls_v) * 100);
|
||||
BLT_TRACE("Percent Reproduction calls allocate? %lf%%", blt::average(reproduction_allocations_v, reproduction_calls_v) * 100);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,167 @@
|
|||
#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_EXAMPLE_SYMBOLIC_REGRESSION_H
|
||||
#define BLT_GP_EXAMPLE_SYMBOLIC_REGRESSION_H
|
||||
|
||||
#include <blt/gp/program.h>
|
||||
#include <blt/gp/tree.h>
|
||||
#include <blt/std/logging.h>
|
||||
#include <blt/format/format.h>
|
||||
#include <iostream>
|
||||
|
||||
namespace blt::gp::example
|
||||
{
|
||||
class symbolic_regression_t
|
||||
{
|
||||
public:
|
||||
struct context
|
||||
{
|
||||
float x, y;
|
||||
};
|
||||
|
||||
private:
|
||||
bool fitness_function(const tree_t& current_tree, fitness_t& fitness, size_t) const
|
||||
{
|
||||
constexpr static double value_cutoff = 1.e15;
|
||||
for (auto& fitness_case : training_cases)
|
||||
{
|
||||
const auto diff = std::abs(fitness_case.y - current_tree.get_evaluation_value<float>(fitness_case));
|
||||
if (diff < value_cutoff)
|
||||
{
|
||||
fitness.raw_fitness += diff;
|
||||
if (diff <= 0.01)
|
||||
fitness.hits++;
|
||||
}
|
||||
else
|
||||
fitness.raw_fitness += value_cutoff;
|
||||
}
|
||||
fitness.standardized_fitness = fitness.raw_fitness;
|
||||
fitness.adjusted_fitness = (1.0 / (1.0 + fitness.standardized_fitness));
|
||||
return static_cast<size_t>(fitness.hits) == training_cases.size();
|
||||
}
|
||||
|
||||
static float example_function(const float x)
|
||||
{
|
||||
return x * x * x * x + x * x * x + x * x + x;
|
||||
}
|
||||
|
||||
public:
|
||||
symbolic_regression_t(const prog_config_t& config, const size_t seed): program{seed, config}
|
||||
{
|
||||
BLT_INFO("Starting BLT-GP Symbolic Regression Example");
|
||||
BLT_DEBUG("Setup Fitness cases");
|
||||
for (auto& fitness_case : training_cases)
|
||||
{
|
||||
constexpr float range = 10;
|
||||
constexpr float half_range = range / 2.0;
|
||||
const auto x = program.get_random().get_float(-half_range, half_range);
|
||||
const auto y = example_function(x);
|
||||
fitness_case = {x, y};
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Ctx>
|
||||
auto make_operations(operator_builder<Ctx>& builder)
|
||||
{
|
||||
static operation_t add{[](const float a, const float b) { return a + b; }, "add"};
|
||||
static operation_t sub([](const float a, const float b) { return a - b; }, "sub");
|
||||
static operation_t mul([](const float a, const float b) { return a * b; }, "mul");
|
||||
static operation_t pro_div([](const float a, const float b) { return b == 0.0f ? 1.0f : a / b; }, "div");
|
||||
static operation_t op_sin([](const float a) { return std::sin(a); }, "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)
|
||||
{
|
||||
return context.x;
|
||||
}, "x");
|
||||
|
||||
return builder.build(add, sub, mul, pro_div, op_sin, op_cos, op_exp, op_log, lit, op_x);
|
||||
}
|
||||
|
||||
void execute()
|
||||
{
|
||||
BLT_DEBUG("Setup Types and Operators");
|
||||
operator_builder<context> builder{};
|
||||
program.set_operations(make_operations(builder));
|
||||
|
||||
BLT_DEBUG("Generate Initial Population");
|
||||
auto sel = select_tournament_t{};
|
||||
auto fitness = [this](const tree_t& c, fitness_t& f, const size_t i) { return fitness_function(c, f, i); };
|
||||
program.generate_population(program.get_typesystem().get_type<float>().id(), fitness, sel, sel, sel);
|
||||
|
||||
BLT_DEBUG("Begin Generation Loop");
|
||||
while (!program.should_terminate())
|
||||
{
|
||||
auto cross = crossover_calls.start_measurement();
|
||||
auto mut = mutation_calls.start_measurement();
|
||||
auto repo = reproduction_calls.start_measurement();
|
||||
BLT_TRACE("------------{Begin Generation %ld}------------", program.get_current_generation());
|
||||
BLT_TRACE("Creating next generation");
|
||||
program.create_next_generation();
|
||||
BLT_TRACE("Move to next generation");
|
||||
program.next_generation();
|
||||
BLT_TRACE("Evaluate Fitness");
|
||||
program.evaluate_fitness();
|
||||
const auto& stats = program.get_population_stats();
|
||||
BLT_TRACE("Avg Fit: %lf, Best Fit: %lf, Worst Fit: %lf, Overall Fit: %lf",
|
||||
stats.average_fitness.load(std::memory_order_relaxed), stats.best_fitness.load(std::memory_order_relaxed),
|
||||
stats.worst_fitness.load(std::memory_order_relaxed), stats.overall_fitness.load(std::memory_order_relaxed));
|
||||
crossover_calls.stop_measurement(cross);
|
||||
mutation_calls.stop_measurement(mut);
|
||||
reproduction_calls.stop_measurement(repo);
|
||||
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("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("----------------------------------------------");
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
const auto best = program.get_best_individuals<3>();
|
||||
|
||||
BLT_INFO("Best approximations:");
|
||||
for (auto& i_ref : best)
|
||||
{
|
||||
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);
|
||||
i.tree.print(program, std::cout);
|
||||
std::cout << "\n";
|
||||
}
|
||||
const auto& stats = program.get_population_stats();
|
||||
BLT_INFO("Stats:");
|
||||
BLT_INFO("Average fitness: %lf", stats.average_fitness.load());
|
||||
BLT_INFO("Best fitness: %lf", stats.best_fitness.load());
|
||||
BLT_INFO("Worst fitness: %lf", stats.worst_fitness.load());
|
||||
BLT_INFO("Overall fitness: %lf", stats.overall_fitness.load());
|
||||
// TODO: make stats helper
|
||||
}
|
||||
|
||||
private:
|
||||
gp_program program;
|
||||
mutation_t mut;
|
||||
std::array<context, 200> training_cases{};
|
||||
};
|
||||
}
|
||||
|
||||
#endif //BLT_GP_EXAMPLE_SYMBOLIC_REGRESSION_H
|
|
@ -21,105 +21,123 @@
|
|||
|
||||
#include <blt/std/types.h>
|
||||
#include <blt/gp/stats.h>
|
||||
#include <blt/gp/fwdecl.h>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace blt::gp
|
||||
{
|
||||
#ifdef BLT_TRACK_ALLOCATIONS
|
||||
inline allocation_tracker_t tracker;
|
||||
|
||||
// population gen specifics
|
||||
inline call_tracker_t crossover_calls;
|
||||
inline call_tracker_t mutation_calls;
|
||||
inline call_tracker_t reproduction_calls;
|
||||
inline call_tracker_t crossover_allocations;
|
||||
inline call_tracker_t mutation_allocations;
|
||||
inline call_tracker_t reproduction_allocations;
|
||||
|
||||
// for evaluating fitness
|
||||
inline call_tracker_t evaluation_calls;
|
||||
inline call_tracker_t evaluation_allocations;
|
||||
#endif
|
||||
|
||||
class aligned_allocator
|
||||
{
|
||||
public:
|
||||
void* allocate(blt::size_t bytes) // NOLINT
|
||||
{
|
||||
public:
|
||||
void* allocate(blt::size_t bytes) // NOLINT
|
||||
{
|
||||
#ifdef BLT_TRACK_ALLOCATIONS
|
||||
tracker.allocate(bytes);
|
||||
// std::cout << "Hey our aligned allocator allocated " << bytes << " bytes!\n";
|
||||
tracker.allocate(bytes);
|
||||
// std::cout << "Hey our aligned allocator allocated " << bytes << " bytes!\n";
|
||||
#endif
|
||||
return std::aligned_alloc(8, bytes);
|
||||
}
|
||||
|
||||
void deallocate(void* ptr, blt::size_t bytes) // NOLINT
|
||||
{
|
||||
if (ptr == nullptr)
|
||||
return;
|
||||
return std::aligned_alloc(8, bytes);
|
||||
}
|
||||
|
||||
void deallocate(void* ptr, blt::size_t bytes) // NOLINT
|
||||
{
|
||||
if (ptr == nullptr)
|
||||
return;
|
||||
#ifdef BLT_TRACK_ALLOCATIONS
|
||||
tracker.deallocate(bytes);
|
||||
// std::cout << "[Hey our aligned allocator deallocated " << bytes << " bytes!]\n";
|
||||
tracker.deallocate(bytes);
|
||||
// std::cout << "[Hey our aligned allocator deallocated " << bytes << " bytes!]\n";
|
||||
#else
|
||||
(void) bytes;
|
||||
#endif
|
||||
std::free(ptr);
|
||||
}
|
||||
std::free(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
||||
template <typename T>
|
||||
class tracked_allocator_t
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
using void_pointer = void*;
|
||||
using const_void_pointer = const void*;
|
||||
using difference_type = blt::ptrdiff_t;
|
||||
using size_type = blt::size_t;
|
||||
template<class U>
|
||||
struct rebind
|
||||
{
|
||||
typedef tracked_allocator_t<U> other;
|
||||
};
|
||||
|
||||
pointer allocate(size_type n)
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
using void_pointer = void*;
|
||||
using const_void_pointer = const void*;
|
||||
using difference_type = blt::ptrdiff_t;
|
||||
using size_type = blt::size_t;
|
||||
|
||||
template <class U>
|
||||
struct rebind
|
||||
{
|
||||
typedef tracked_allocator_t<U> other;
|
||||
};
|
||||
|
||||
pointer allocate(size_type n)
|
||||
{
|
||||
#ifdef BLT_TRACK_ALLOCATIONS
|
||||
tracker.allocate(n * sizeof(T));
|
||||
// std::cout << "Hey our tracked allocator allocated " << (n * sizeof(T)) << " bytes!\n";
|
||||
tracker.allocate(n * sizeof(T));
|
||||
// std::cout << "Hey our tracked allocator allocated " << (n * sizeof(T)) << " bytes!\n";
|
||||
#endif
|
||||
return static_cast<pointer>(std::malloc(n * sizeof(T)));
|
||||
}
|
||||
|
||||
pointer allocate(size_type n, const_void_pointer)
|
||||
{
|
||||
return allocate(n);
|
||||
}
|
||||
|
||||
void deallocate(pointer p, size_type n)
|
||||
{
|
||||
return static_cast<pointer>(std::malloc(n * sizeof(T)));
|
||||
}
|
||||
|
||||
pointer allocate(size_type n, const_void_pointer)
|
||||
{
|
||||
return allocate(n);
|
||||
}
|
||||
|
||||
void deallocate(pointer p, size_type n)
|
||||
{
|
||||
#ifdef BLT_TRACK_ALLOCATIONS
|
||||
tracker.deallocate(n * sizeof(T));
|
||||
// std::cout << "[Hey our tracked allocator deallocated " << (n * sizeof(T)) << " bytes!]\n";
|
||||
::blt::gp::tracker.deallocate(n * sizeof(T));
|
||||
// std::cout << "[Hey our tracked allocator deallocated " << (n * sizeof(T)) << " bytes!]\n";
|
||||
#else
|
||||
(void) n;
|
||||
#endif
|
||||
std::free(p);
|
||||
}
|
||||
|
||||
template<class U, class... Args>
|
||||
void construct(U* p, Args&& ... args)
|
||||
{
|
||||
new(p) T(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class U>
|
||||
void destroy(U* p)
|
||||
{
|
||||
p->~T();
|
||||
}
|
||||
|
||||
[[nodiscard]] size_type max_size() const noexcept
|
||||
{
|
||||
return std::numeric_limits<size_type>::max();
|
||||
}
|
||||
std::free(p);
|
||||
}
|
||||
|
||||
template <class U, class... Args>
|
||||
void construct(U* p, Args&&... args)
|
||||
{
|
||||
new(p) T(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class U>
|
||||
void destroy(U* p)
|
||||
{
|
||||
p->~T();
|
||||
}
|
||||
|
||||
[[nodiscard]] size_type max_size() const noexcept
|
||||
{
|
||||
return std::numeric_limits<size_type>::max();
|
||||
}
|
||||
};
|
||||
|
||||
template<class T1, class T2>
|
||||
|
||||
template <class T1, class T2>
|
||||
inline static bool operator==(const tracked_allocator_t<T1>& lhs, const tracked_allocator_t<T2>& rhs) noexcept
|
||||
{
|
||||
return &lhs == &rhs;
|
||||
}
|
||||
|
||||
template<class T1, class T2>
|
||||
|
||||
template <class T1, class T2>
|
||||
inline static bool operator!=(const tracked_allocator_t<T1>& lhs, const tracked_allocator_t<T2>& rhs) noexcept
|
||||
{
|
||||
return &lhs != &rhs;
|
||||
|
|
|
@ -32,22 +32,6 @@
|
|||
|
||||
namespace blt::gp
|
||||
{
|
||||
#ifdef BLT_TRACK_ALLOCATIONS
|
||||
inline allocation_tracker_t tracker;
|
||||
|
||||
// population gen specifics
|
||||
inline call_tracker_t crossover_calls;
|
||||
inline call_tracker_t mutation_calls;
|
||||
inline call_tracker_t reproduction_calls;
|
||||
inline call_tracker_t crossover_allocations;
|
||||
inline call_tracker_t mutation_allocations;
|
||||
inline call_tracker_t reproduction_allocations;
|
||||
|
||||
// for evaluating fitness
|
||||
inline call_tracker_t evaluation_calls;
|
||||
inline call_tracker_t evaluation_allocations;
|
||||
#endif
|
||||
|
||||
class gp_program;
|
||||
|
||||
class type;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -25,10 +25,10 @@
|
|||
#include <blt/gp/random.h>
|
||||
#include <blt/std/assert.h>
|
||||
#include "blt/format/format.h"
|
||||
#include <atomic>
|
||||
|
||||
namespace blt::gp
|
||||
{
|
||||
|
||||
struct selector_args
|
||||
{
|
||||
gp_program& program;
|
||||
|
@ -37,18 +37,19 @@ namespace blt::gp
|
|||
prog_config_t& config;
|
||||
random_t& random;
|
||||
};
|
||||
|
||||
constexpr inline auto perform_elitism = [](const selector_args& args, population_t& next_pop) {
|
||||
|
||||
constexpr inline auto perform_elitism = [](const selector_args& args, population_t& next_pop)
|
||||
{
|
||||
auto& [program, current_pop, current_stats, config, random] = args;
|
||||
|
||||
|
||||
if (config.elites > 0 && current_pop.get_individuals().size() >= config.elites)
|
||||
{
|
||||
static thread_local tracked_vector<std::pair<std::size_t, double>> values;
|
||||
values.clear();
|
||||
|
||||
|
||||
for (blt::size_t i = 0; i < config.elites; i++)
|
||||
values.emplace_back(i, current_pop.get_individuals()[i].fitness.adjusted_fitness);
|
||||
|
||||
|
||||
for (const auto& ind : blt::enumerate(current_pop.get_individuals()))
|
||||
{
|
||||
for (blt::size_t i = 0; i < config.elites; i++)
|
||||
|
@ -67,165 +68,218 @@ namespace blt::gp
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (blt::size_t i = 0; i < config.elites; i++)
|
||||
next_pop.get_individuals()[i].copy_fast(current_pop.get_individuals()[values[i].first].tree);
|
||||
return config.elites;
|
||||
}
|
||||
return 0ul;
|
||||
};
|
||||
|
||||
template<typename Crossover, typename Mutation, typename Reproduction>
|
||||
|
||||
inline std::atomic<double> parent_fitness = 0;
|
||||
inline std::atomic<double> child_fitness = 0;
|
||||
|
||||
template <typename Crossover, typename Mutation, typename Reproduction>
|
||||
constexpr inline auto default_next_pop_creator = [](
|
||||
blt::gp::selector_args& args, Crossover& crossover_selection, Mutation& mutation_selection, Reproduction& reproduction_selection,
|
||||
tree_t& c1, tree_t* c2) {
|
||||
selector_args& args, Crossover& crossover_selection, Mutation& mutation_selection, Reproduction& reproduction_selection,
|
||||
tree_t& c1, tree_t* c2, const std::function<bool(const tree_t&, fitness_t&, size_t)>& test_fitness_func)
|
||||
{
|
||||
auto& [program, current_pop, current_stats, config, random] = args;
|
||||
|
||||
int sel = random.get_i32(0, 3);
|
||||
switch (sel)
|
||||
|
||||
switch (random.get_i32(0, 3))
|
||||
{
|
||||
case 0:
|
||||
if (c2 == nullptr)
|
||||
return 0;
|
||||
// everyone gets a chance once per loop.
|
||||
if (random.choice(config.crossover_chance))
|
||||
case 0:
|
||||
if (c2 == nullptr)
|
||||
return 0;
|
||||
// everyone gets a chance once per loop.
|
||||
if (random.choice(config.crossover_chance))
|
||||
{
|
||||
#ifdef BLT_TRACK_ALLOCATIONS
|
||||
auto state = tracker.start_measurement_thread_local();
|
||||
#endif
|
||||
// crossover
|
||||
|
||||
const tree_t* p1;
|
||||
const tree_t* p2;
|
||||
double parent_val = 0;
|
||||
do
|
||||
{
|
||||
#ifdef BLT_TRACK_ALLOCATIONS
|
||||
auto state = tracker.start_measurement_thread_local();
|
||||
#endif
|
||||
// crossover
|
||||
const tree_t* p1;
|
||||
const tree_t* p2;
|
||||
do
|
||||
{
|
||||
p1 = &crossover_selection.select(program, current_pop);
|
||||
p2 = &crossover_selection.select(program, current_pop);
|
||||
c1.copy_fast(*p1);
|
||||
c2->copy_fast(*p2);
|
||||
} while (!config.crossover.get().apply(program, *p1, *p2, c1, *c2));
|
||||
#ifdef BLT_TRACK_ALLOCATIONS
|
||||
tracker.stop_measurement_thread_local(state);
|
||||
crossover_calls.call();
|
||||
crossover_calls.set_value(std::max(crossover_calls.get_value(), state.getAllocatedByteDifference()));
|
||||
if (state.getAllocatedByteDifference() != 0)
|
||||
{
|
||||
crossover_allocations.call(state.getAllocatedByteDifference());
|
||||
}
|
||||
#endif
|
||||
return 2;
|
||||
p1 = &crossover_selection.select(program, current_pop);
|
||||
p2 = &crossover_selection.select(program, current_pop);
|
||||
|
||||
fitness_t fitness1;
|
||||
fitness_t fitness2;
|
||||
test_fitness_func(*p1, fitness1, 0);
|
||||
test_fitness_func(*p2, fitness2, 0);
|
||||
parent_val = fitness1.adjusted_fitness + fitness2.adjusted_fitness;
|
||||
// BLT_TRACE("%ld> P1 Fit: %lf, P2 Fit: %lf", val, fitness1.adjusted_fitness, fitness2.adjusted_fitness);
|
||||
|
||||
c1.copy_fast(*p1);
|
||||
c2->copy_fast(*p2);
|
||||
|
||||
crossover_calls.value(1);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (random.choice(config.mutation_chance))
|
||||
while (!config.crossover.get().apply(program, *p1, *p2, c1, *c2));
|
||||
fitness_t fitness1;
|
||||
fitness_t fitness2;
|
||||
test_fitness_func(c1, fitness1, 0);
|
||||
test_fitness_func(*c2, fitness2, 0);
|
||||
|
||||
const auto child_val = fitness1.adjusted_fitness + fitness2.adjusted_fitness;
|
||||
|
||||
auto old_parent_val = parent_fitness.load(std::memory_order_relaxed);
|
||||
while (!parent_fitness.compare_exchange_weak(old_parent_val, old_parent_val + parent_val, std::memory_order_relaxed,
|
||||
std::memory_order_relaxed))
|
||||
{
|
||||
#ifdef BLT_TRACK_ALLOCATIONS
|
||||
auto state = tracker.start_measurement_thread_local();
|
||||
#endif
|
||||
// mutation
|
||||
const tree_t* p;
|
||||
do
|
||||
{
|
||||
p = &mutation_selection.select(program, current_pop);
|
||||
c1.copy_fast(*p);
|
||||
} while (!config.mutator.get().apply(program, *p, c1));
|
||||
#ifdef BLT_TRACK_ALLOCATIONS
|
||||
tracker.stop_measurement_thread_local(state);
|
||||
mutation_calls.call();
|
||||
mutation_calls.set_value(std::max(mutation_calls.get_value(), state.getAllocatedByteDifference()));
|
||||
if (state.getAllocationDifference() != 0)
|
||||
{
|
||||
mutation_allocations.call(state.getAllocatedByteDifference());
|
||||
}
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (config.reproduction_chance > 0 && random.choice(config.reproduction_chance))
|
||||
|
||||
auto old_child_val = child_fitness.load(std::memory_order_relaxed);
|
||||
while (!child_fitness.compare_exchange_weak(old_child_val, old_child_val + child_val, std::memory_order_relaxed,
|
||||
std::memory_order_relaxed))
|
||||
{
|
||||
#ifdef BLT_TRACK_ALLOCATIONS
|
||||
auto state = tracker.start_measurement_thread_local();
|
||||
#endif
|
||||
// reproduction
|
||||
c1.copy_fast(reproduction_selection.select(program, current_pop));
|
||||
#ifdef BLT_TRACK_ALLOCATIONS
|
||||
tracker.stop_measurement_thread_local(state);
|
||||
reproduction_calls.call();
|
||||
reproduction_calls.set_value(std::max(reproduction_calls.get_value(), state.getAllocatedByteDifference()));
|
||||
if (state.getAllocationDifference() != 0)
|
||||
{
|
||||
reproduction_allocations.call(state.getAllocatedByteDifference());
|
||||
}
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
// BLT_TRACE("%ld> C1 Fit: %lf, C2 Fit: %lf", val, fitness1.adjusted_fitness, fitness2.adjusted_fitness);
|
||||
#ifdef BLT_TRACK_ALLOCATIONS
|
||||
tracker.stop_measurement_thread_local(state);
|
||||
crossover_calls.call();
|
||||
if (state.getAllocatedByteDifference() != 0)
|
||||
{
|
||||
crossover_allocations.call(state.getAllocatedByteDifference());
|
||||
crossover_allocations.set_value(std::max(crossover_allocations.get_value(), state.getAllocatedByteDifference()));
|
||||
}
|
||||
#endif
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (random.choice(config.mutation_chance))
|
||||
{
|
||||
#ifdef BLT_TRACK_ALLOCATIONS
|
||||
auto state = tracker.start_measurement_thread_local();
|
||||
#endif
|
||||
// mutation
|
||||
const tree_t* p;
|
||||
do
|
||||
{
|
||||
p = &mutation_selection.select(program, current_pop);
|
||||
c1.copy_fast(*p);
|
||||
mutation_calls.value(1);
|
||||
}
|
||||
while (!config.mutator.get().apply(program, *p, c1));
|
||||
#ifdef BLT_TRACK_ALLOCATIONS
|
||||
tracker.stop_measurement_thread_local(state);
|
||||
mutation_calls.call();
|
||||
if (state.getAllocationDifference() != 0)
|
||||
{
|
||||
mutation_allocations.call(state.getAllocatedByteDifference());
|
||||
mutation_allocations.set_value(std::max(mutation_allocations.get_value(), state.getAllocatedByteDifference()));
|
||||
}
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (config.reproduction_chance > 0 && random.choice(config.reproduction_chance))
|
||||
{
|
||||
#ifdef BLT_TRACK_ALLOCATIONS
|
||||
auto state = tracker.start_measurement_thread_local();
|
||||
#endif
|
||||
// reproduction
|
||||
c1.copy_fast(reproduction_selection.select(program, current_pop));
|
||||
#ifdef BLT_TRACK_ALLOCATIONS
|
||||
tracker.stop_measurement_thread_local(state);
|
||||
reproduction_calls.call();
|
||||
reproduction_calls.value(1);
|
||||
if (state.getAllocationDifference() != 0)
|
||||
{
|
||||
reproduction_allocations.call(state.getAllocatedByteDifference());
|
||||
reproduction_allocations.set_value(std::max(reproduction_allocations.get_value(), state.getAllocatedByteDifference()));
|
||||
}
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
#if BLT_DEBUG_LEVEL > 0
|
||||
BLT_ABORT("This is not possible!");
|
||||
#else
|
||||
BLT_UNREACHABLE;
|
||||
BLT_UNREACHABLE;
|
||||
#endif
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
|
||||
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 const tree_t& select(gp_program& program, const population_t& pop) = 0;
|
||||
|
||||
virtual void pre_process(gp_program&, population_t&)
|
||||
{}
|
||||
|
||||
virtual ~selection_t() = default;
|
||||
public:
|
||||
/**
|
||||
* @param program gp program to select with, used in randoms
|
||||
* @param pop population to select from
|
||||
* @param stats the populations statistics
|
||||
* @return
|
||||
*/
|
||||
virtual const tree_t& select(gp_program& program, const population_t& pop) = 0;
|
||||
|
||||
/**
|
||||
* Is run once on a single thread before selection begins. allows you to preprocess the generation for fitness metrics.
|
||||
* TODO a method for parallel execution
|
||||
*/
|
||||
virtual void pre_process(gp_program&, population_t&)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~selection_t() = default;
|
||||
};
|
||||
|
||||
class select_best_t : public selection_t
|
||||
|
||||
class select_best_t final : public selection_t
|
||||
{
|
||||
public:
|
||||
const tree_t& select(gp_program& program, const population_t& pop) final;
|
||||
public:
|
||||
void pre_process(gp_program&, population_t&) override;
|
||||
|
||||
const tree_t& select(gp_program& program, const population_t& pop) override;
|
||||
|
||||
private:
|
||||
std::atomic_uint64_t index = 0;
|
||||
};
|
||||
|
||||
class select_worst_t : public selection_t
|
||||
|
||||
class select_worst_t final : public selection_t
|
||||
{
|
||||
public:
|
||||
const tree_t& select(gp_program& program, const population_t& pop) final;
|
||||
public:
|
||||
void pre_process(gp_program&, population_t&) override;
|
||||
|
||||
const tree_t& select(gp_program& program, const population_t& pop) override;
|
||||
|
||||
private:
|
||||
std::atomic_uint64_t index = 0;
|
||||
};
|
||||
|
||||
class select_random_t : public selection_t
|
||||
|
||||
class select_random_t final : public selection_t
|
||||
{
|
||||
public:
|
||||
const tree_t& select(gp_program& program, const population_t& pop) final;
|
||||
public:
|
||||
const tree_t& select(gp_program& program, const population_t& pop) override;
|
||||
};
|
||||
|
||||
class select_tournament_t : public selection_t
|
||||
|
||||
class select_tournament_t final : public selection_t
|
||||
{
|
||||
public:
|
||||
explicit select_tournament_t(blt::size_t selection_size = 3): selection_size(selection_size)
|
||||
{
|
||||
if (selection_size == 0)
|
||||
BLT_ABORT("Unable to select with this size. Must select at least 1 individual_t!");
|
||||
}
|
||||
|
||||
const tree_t& select(gp_program& program, const population_t& pop) final;
|
||||
|
||||
private:
|
||||
const blt::size_t selection_size;
|
||||
public:
|
||||
explicit select_tournament_t(const size_t selection_size = 3): selection_size(selection_size)
|
||||
{
|
||||
if (selection_size == 0)
|
||||
BLT_ABORT("Unable to select with this size. Must select at least 1 individual_t!");
|
||||
}
|
||||
|
||||
const tree_t& select(gp_program& program, const population_t& pop) override;
|
||||
|
||||
private:
|
||||
const size_t selection_size;
|
||||
};
|
||||
|
||||
class select_fitness_proportionate_t : public selection_t
|
||||
|
||||
class select_fitness_proportionate_t final : public selection_t
|
||||
{
|
||||
public:
|
||||
const tree_t& select(gp_program& program, const population_t& pop) final;
|
||||
public:
|
||||
const tree_t& select(gp_program& program, const population_t& pop) override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //BLT_GP_SELECTION_H
|
||||
|
|
|
@ -316,14 +316,15 @@ namespace blt::gp
|
|||
return secondary_value.load();
|
||||
}
|
||||
|
||||
call_data_t start_measurement()
|
||||
call_data_t start_measurement() const
|
||||
{
|
||||
return {primary_calls.load(), 0};
|
||||
return {primary_calls.load(), secondary_value.load()};
|
||||
}
|
||||
|
||||
void stop_measurement(call_data_t& data)
|
||||
void stop_measurement(call_data_t& data) const
|
||||
{
|
||||
data.end_calls = primary_calls.load();
|
||||
data.end_value = secondary_value.load();
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -133,7 +133,7 @@ namespace blt::gp
|
|||
* Helper template for returning the result of the last evaluation
|
||||
*/
|
||||
template<typename T>
|
||||
T get_evaluation_value(evaluation_context& context)
|
||||
T get_evaluation_value(evaluation_context& context) const
|
||||
{
|
||||
return context.values.pop<T>();
|
||||
}
|
||||
|
@ -142,7 +142,7 @@ namespace blt::gp
|
|||
* Helper template for returning the result of the last evaluation
|
||||
*/
|
||||
template<typename T>
|
||||
T& get_evaluation_ref(evaluation_context& context)
|
||||
T& get_evaluation_ref(evaluation_context& context) const
|
||||
{
|
||||
return context.values.from<T>(0);
|
||||
}
|
||||
|
@ -151,13 +151,13 @@ namespace blt::gp
|
|||
* Helper template for returning the result of evaluation (this calls it)
|
||||
*/
|
||||
template<typename T, typename Context>
|
||||
T get_evaluation_value(const Context& context)
|
||||
T get_evaluation_value(const Context& context) const
|
||||
{
|
||||
return evaluate(context).values.template pop<T>();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T get_evaluation_value()
|
||||
T get_evaluation_value() const
|
||||
{
|
||||
return evaluate().values.pop<T>();
|
||||
}
|
||||
|
|
|
@ -20,57 +20,63 @@
|
|||
|
||||
namespace blt::gp
|
||||
{
|
||||
|
||||
void select_best_t::pre_process(gp_program&, population_t& pop)
|
||||
{
|
||||
std::sort(pop.begin(), pop.end(), [](const auto& a, const auto& b)
|
||||
{
|
||||
return a.fitness.adjusted_fitness > b.fitness.adjusted_fitness;
|
||||
});
|
||||
index = 0;
|
||||
}
|
||||
|
||||
const tree_t& select_best_t::select(gp_program&, const population_t& pop)
|
||||
{
|
||||
auto& first = pop.get_individuals()[0];
|
||||
double best_fitness = first.fitness.adjusted_fitness;
|
||||
const tree_t* tree = &first.tree;
|
||||
for (auto& ind : pop.get_individuals())
|
||||
{
|
||||
if (ind.fitness.adjusted_fitness > best_fitness)
|
||||
{
|
||||
best_fitness = ind.fitness.adjusted_fitness;
|
||||
tree = &ind.tree;
|
||||
}
|
||||
}
|
||||
return *tree;
|
||||
const auto size = pop.get_individuals().size();
|
||||
return pop.get_individuals()[index.fetch_add(1, std::memory_order_relaxed) % size].tree;
|
||||
}
|
||||
|
||||
|
||||
void select_worst_t::pre_process(gp_program&, population_t& pop)
|
||||
{
|
||||
std::sort(pop.begin(), pop.end(), [](const auto& a, const auto& b)
|
||||
{
|
||||
return a.fitness.adjusted_fitness < b.fitness.adjusted_fitness;
|
||||
});
|
||||
index = 0;
|
||||
}
|
||||
|
||||
const tree_t& select_worst_t::select(gp_program&, const population_t& pop)
|
||||
{
|
||||
auto& first = pop.get_individuals()[0];
|
||||
double worst_fitness = first.fitness.adjusted_fitness;
|
||||
const tree_t* tree = &first.tree;
|
||||
for (auto& ind : pop.get_individuals())
|
||||
{
|
||||
if (ind.fitness.adjusted_fitness < worst_fitness)
|
||||
{
|
||||
worst_fitness = ind.fitness.adjusted_fitness;
|
||||
tree = &ind.tree;
|
||||
}
|
||||
}
|
||||
return *tree;
|
||||
const auto size = pop.get_individuals().size();
|
||||
return pop.get_individuals()[index.fetch_add(1, std::memory_order_relaxed) % size].tree;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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());
|
||||
thread_local hashset_t<u64> already_selected;
|
||||
already_selected.clear();
|
||||
auto& i_ref = pop.get_individuals();
|
||||
for (blt::size_t i = 0; i < selection_size; i++)
|
||||
|
||||
u64 best = program.get_random().get_u64(0, pop.get_individuals().size());
|
||||
for (size_t i = 0; i < selection_size; i++)
|
||||
{
|
||||
auto sel_point = program.get_random().get_u64(0ul, pop.get_individuals().size());
|
||||
u64 sel_point;
|
||||
do
|
||||
{
|
||||
sel_point = program.get_random().get_u64(0ul, pop.get_individuals().size());;
|
||||
}
|
||||
while (already_selected.contains(sel_point));
|
||||
already_selected.insert(sel_point);
|
||||
if (i_ref[sel_point].fitness.adjusted_fitness > i_ref[best].fitness.adjusted_fitness)
|
||||
best = sel_point;
|
||||
}
|
||||
return i_ref[best].tree;
|
||||
}
|
||||
|
||||
|
||||
const tree_t& select_fitness_proportionate_t::select(gp_program& program, const population_t& pop)
|
||||
{
|
||||
auto& stats = program.get_population_stats();
|
||||
|
@ -81,7 +87,8 @@ namespace blt::gp
|
|||
{
|
||||
if (choice <= stats.normalized_fitness[index])
|
||||
return ref.tree;
|
||||
} else
|
||||
}
|
||||
else
|
||||
{
|
||||
if (choice > stats.normalized_fitness[index - 1] && choice <= stats.normalized_fitness[index])
|
||||
return ref.tree;
|
||||
|
@ -91,4 +98,4 @@ namespace blt::gp
|
|||
return pop.get_individuals()[0].tree;
|
||||
//BLT_ABORT("Unable to find individual");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue