this is a breaking patch

dev-0.2.1
Brett 2024-12-23 00:17:02 -05:00
parent e4ce3739f0
commit c6a2b5d324
13 changed files with 1267 additions and 1231 deletions

View File

@ -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 ()

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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>();
}

View File

@ -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");
}
}
}