Merge branch 'shared'

main
Brett 2024-08-31 22:15:59 -04:00
commit 89ed1bd116
24 changed files with 1256 additions and 527 deletions

4
.gitignore vendored
View File

@ -7,7 +7,5 @@ out/
massif.*
callgrind.*
*.out.*
<<<<<<< HEAD
heaptrack.*
=======
>>>>>>> refs/remotes/origin/main
Rice_Cammeo_Osmancik.arff

View File

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.25)
project(blt-gp VERSION 0.1.20)
project(blt-gp VERSION 0.1.36)
include(CTest)
@ -9,12 +9,15 @@ option(ENABLE_TSAN "Enable the thread data race sanitizer" OFF)
option(BUILD_EXAMPLES "Build example programs. This will build with CTest" OFF)
option(BUILD_GP_TESTS "Build test programs." OFF)
option(DEBUG_LEVEL "Enable debug features which prints extra information to the console, might slow processing down. [0, 3)" 0)
option(TRACK_ALLOCATIONS "Track total allocations. Can be accessed with blt::gp::tracker" OFF)
set(CMAKE_CXX_STANDARD 17)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
#SET(CMAKE_CXX_FLAGS_RELEASE "-O3 -g")
if (NOT TARGET BLT)
add_subdirectory(lib/blt)
endif ()
@ -38,6 +41,10 @@ target_include_directories(blt-gp PUBLIC include/)
target_link_libraries(blt-gp PRIVATE BLT Threads::Threads)
target_compile_definitions(blt-gp PRIVATE BLT_DEBUG_LEVEL=${DEBUG_LEVEL})
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)
@ -67,6 +74,10 @@ macro(blt_add_project name source type)
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)
@ -97,6 +108,7 @@ 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)
endif ()

View File

@ -1302,7 +1302,7 @@
,"0x4845DA0: memalign (in /usr/libexec/valgrind/vgpreload_dhat-amd64-linux.so)"
,"0x129674: std::_Function_handler<void (void*, blt::gp::stack_allocator&, blt::gp::stack_allocator&), blt::gp::operation_t<lit::{lambda()#1}, float ()>::make_callable<context>() const::{lambda(void*, blt::gp::stack_allocator&, blt::gp::stack_allocator&)#1}>::_M_invoke(std::_Any_data const&, void*&&, blt::gp::stack_allocator&, blt::gp::stack_allocator&) (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)"
,"0x158664: blt::gp::tree_t blt::gp::create_tree<blt::gp::grow_generator_t::generate(blt::gp::generator_arguments const&)::{lambda(blt::gp::gp_program&, std::stack<blt::gp::stack, std::deque<blt::gp::stack, std::allocator<blt::gp::stack> > >&, blt::gp::type_id, unsigned long)#1}>(blt::gp::grow_generator_t::generate(blt::gp::generator_arguments const&)::{lambda(blt::gp::gp_program&, std::stack<blt::gp::stack, std::deque<blt::gp::stack, std::allocator<blt::gp::stack> > >&, blt::gp::type_id, unsigned long)#1}&&, blt::gp::generator_arguments const&) (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)"
,"0x15D0C0: void std::vector<blt::gp::individual, std::allocator<blt::gp::individual> >::_M_realloc_insert<blt::gp::tree_t>(__gnu_cxx::__normal_iterator<blt::gp::individual*, std::vector<blt::gp::individual, std::allocator<blt::gp::individual> > >, blt::gp::tree_t&&) (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)"
,"0x15D0C0: void std::vector<blt::gp::individual_t, std::allocator<blt::gp::individual_t> >::_M_realloc_insert<blt::gp::tree_t>(__gnu_cxx::__normal_iterator<blt::gp::individual_t*, std::vector<blt::gp::individual_t, std::allocator<blt::gp::individual_t> > >, blt::gp::tree_t&&) (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)"
,"0x15CCFA: blt::gp::ramped_half_initializer_t::generate(blt::gp::initializer_arguments const&) (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)"
,"0x15A9F0: blt::gp::tree_t blt::gp::create_tree<blt::gp::full_generator_t::generate(blt::gp::generator_arguments const&)::{lambda(blt::gp::gp_program&, std::stack<blt::gp::stack, std::deque<blt::gp::stack, std::allocator<blt::gp::stack> > >&, blt::gp::type_id, unsigned long)#1}>(blt::gp::full_generator_t::generate(blt::gp::generator_arguments const&)::{lambda(blt::gp::gp_program&, std::stack<blt::gp::stack, std::deque<blt::gp::stack, std::allocator<blt::gp::stack> > >&, blt::gp::type_id, unsigned long)#1}&&, blt::gp::generator_arguments const&) (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)"
,"0x15C977: blt::gp::ramped_half_initializer_t::generate(blt::gp::initializer_arguments const&) (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)"
@ -1330,7 +1330,7 @@
,"0x1548AB: blt::gp::crossover_t::apply(blt::gp::gp_program&, blt::gp::tree_t const&, blt::gp::tree_t const&) (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)"
,"0x1549A7: blt::gp::crossover_t::apply(blt::gp::gp_program&, blt::gp::tree_t const&, blt::gp::tree_t const&) (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)"
,"0x127C77: blt::gp::default_next_pop_creator<blt::gp::select_tournament_t, blt::gp::select_tournament_t, blt::gp::select_tournament_t>::{lambda(blt::gp::selector_args&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&)#1}::operator()(blt::gp::selector_args&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&) const [clone .isra.0] (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)"
,"0x1268EF: blt::gp::individual& std::vector<blt::gp::individual, std::allocator<blt::gp::individual> >::emplace_back<blt::gp::individual>(blt::gp::individual&&) [clone .isra.0] (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)"
,"0x1268EF: blt::gp::individual_t& std::vector<blt::gp::individual_t, std::allocator<blt::gp::individual_t> >::emplace_back<blt::gp::individual_t>(blt::gp::individual_t&&) [clone .isra.0] (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)"
,"0x127DDE: blt::gp::default_next_pop_creator<blt::gp::select_tournament_t, blt::gp::select_tournament_t, blt::gp::select_tournament_t>::{lambda(blt::gp::selector_args&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&)#1}::operator()(blt::gp::selector_args&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&) const [clone .isra.0] (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)"
,"0x127F26: blt::gp::default_next_pop_creator<blt::gp::select_tournament_t, blt::gp::select_tournament_t, blt::gp::select_tournament_t>::{lambda(blt::gp::selector_args&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&)#1}::operator()(blt::gp::selector_args&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&) const [clone .isra.0] (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)"
,"0x127FE6: blt::gp::default_next_pop_creator<blt::gp::select_tournament_t, blt::gp::select_tournament_t, blt::gp::select_tournament_t>::{lambda(blt::gp::selector_args&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&)#1}::operator()(blt::gp::selector_args&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&) const [clone .isra.0] (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)"
@ -1366,7 +1366,7 @@
,"0x1247AC: main (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)"
,"0x12497D: main (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)"
,"0x124A4A: main (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)"
,"0x126AFC: blt::gp::individual& std::vector<blt::gp::individual, std::allocator<blt::gp::individual> >::emplace_back<blt::gp::tree_t>(blt::gp::tree_t&&) [clone .isra.0] (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)"
,"0x126AFC: blt::gp::individual_t& std::vector<blt::gp::individual_t, std::allocator<blt::gp::individual_t> >::emplace_back<blt::gp::tree_t>(blt::gp::tree_t&&) [clone .isra.0] (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)"
,"0x127AE9: blt::gp::default_next_pop_creator<blt::gp::select_tournament_t, blt::gp::select_tournament_t, blt::gp::select_tournament_t>::{lambda(blt::gp::selector_args&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&)#1}::operator()(blt::gp::selector_args&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&) const [clone .isra.0] (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)"
,"0x127E76: blt::gp::default_next_pop_creator<blt::gp::select_tournament_t, blt::gp::select_tournament_t, blt::gp::select_tournament_t>::{lambda(blt::gp::selector_args&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&)#1}::operator()(blt::gp::selector_args&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&) const [clone .isra.0] (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)"
,"0x127EAF: blt::gp::default_next_pop_creator<blt::gp::select_tournament_t, blt::gp::select_tournament_t, blt::gp::select_tournament_t>::{lambda(blt::gp::selector_args&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&)#1}::operator()(blt::gp::selector_args&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&) const [clone .isra.0] (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)"

View File

@ -0,0 +1,33 @@
#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,353 @@
/*
* This rice classification example uses data from the UC Irvine Machine Learning repository.
* The data for this example can be found at:
* https://archive.ics.uci.edu/dataset/545/rice+cammeo+and+osmancik
*
* 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/std/format.h>
#include <blt/parse/argparse.h>
#include <iostream>
#include "operations_common.h"
#include "blt/fs/loader.h"
static const auto SEED_FUNC = [] { return std::random_device()(); };
enum class rice_type_t
{
Cammeo,
Osmancik
};
struct rice_record
{
float area;
float perimeter;
float major_axis_length;
float minor_axis_length;
float eccentricity;
float convex_area;
float extent;
rice_type_t type;
};
std::vector<rice_record> training_cases;
std::vector<rice_record> testing_cases;
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(5000)
.set_thread_count(0);
blt::gp::gp_program program{SEED_FUNC, config};
auto lit = blt::gp::operation_t([]() {
return program.get_random().get_float(-32000.0f, 32000.0f);
}, "lit").set_ephemeral();
blt::gp::operation_t op_area([](const rice_record& rice_data) {
return rice_data.area;
}, "area");
blt::gp::operation_t op_perimeter([](const rice_record& rice_data) {
return rice_data.perimeter;
}, "perimeter");
blt::gp::operation_t op_major_axis_length([](const rice_record& rice_data) {
return rice_data.major_axis_length;
}, "major_axis_length");
blt::gp::operation_t op_minor_axis_length([](const rice_record& rice_data) {
return rice_data.minor_axis_length;
}, "minor_axis_length");
blt::gp::operation_t op_eccentricity([](const rice_record& rice_data) {
return rice_data.eccentricity;
}, "eccentricity");
blt::gp::operation_t op_convex_area([](const rice_record& rice_data) {
return rice_data.convex_area;
}, "convex_area");
blt::gp::operation_t op_extent([](const rice_record& rice_data) {
return rice_data.extent;
}, "extent");
constexpr auto fitness_function = [](blt::gp::tree_t& current_tree, blt::gp::fitness_t& fitness, blt::size_t) {
for (auto& training_case : training_cases)
{
auto v = current_tree.get_evaluation_value<float>(&training_case);
switch (training_case.type)
{
case rice_type_t::Cammeo:
if (v >= 0)
fitness.hits++;
break;
case rice_type_t::Osmancik:
if (v < 0)
fitness.hits++;
break;
}
}
fitness.raw_fitness = static_cast<double>(fitness.hits);
fitness.standardized_fitness = fitness.raw_fitness;
fitness.adjusted_fitness = 1.0 - (1.0 / (1.0 + fitness.standardized_fitness));
return static_cast<blt::size_t>(fitness.hits) == training_cases.size();
};
void load_rice_data(std::string_view rice_file_path)
{
auto rice_file_data = blt::fs::getLinesFromFile(rice_file_path);
size_t index = 0;
while (!blt::string::contains(rice_file_data[index++], "@DATA"))
{}
std::vector<rice_record> c;
std::vector<rice_record> o;
for (std::string_view v : blt::itr_offset(rice_file_data, index))
{
auto data = blt::string::split(v, ',');
rice_record r{std::stof(data[0]), std::stof(data[1]), std::stof(data[2]), std::stof(data[3]), std::stof(data[4]), std::stof(data[5]),
std::stof(data[6]), blt::string::contains(data[7], "Cammeo") ? rice_type_t::Cammeo : rice_type_t::Osmancik};
switch (r.type)
{
case rice_type_t::Cammeo:
c.push_back(r);
break;
case rice_type_t::Osmancik:
o.push_back(r);
break;
}
}
blt::size_t total_records = c.size() + o.size();
blt::size_t training_size = std::min(total_records / 3, 1000ul);
for (blt::size_t i = 0; i < training_size; i++)
{
auto& random = program.get_random();
auto& vec = random.choice() ? c : o;
auto pos = random.get_i64(0, static_cast<blt::i64>(vec.size()));
training_cases.push_back(vec[pos]);
vec.erase(vec.begin() + pos);
}
testing_cases.insert(testing_cases.end(), c.begin(), c.end());
testing_cases.insert(testing_cases.end(), o.begin(), o.end());
std::shuffle(testing_cases.begin(), testing_cases.end(), program.get_random());
BLT_INFO("Created training set of size %ld, testing set is of size %ld", training_size, testing_cases.size());
}
struct test_results_t
{
blt::size_t cc = 0;
blt::size_t co = 0;
blt::size_t oo = 0;
blt::size_t oc = 0;
blt::size_t hits = 0;
blt::size_t size = 0;
double percent_hit = 0;
test_results_t& operator+=(const test_results_t& a)
{
cc += a.cc;
co += a.co;
oo += a.oo;
oc += a.oc;
hits += a.hits;
size += a.size;
percent_hit += a.percent_hit;
return *this;
}
test_results_t& operator/=(blt::size_t s)
{
cc /= s;
co /= s;
oo /= s;
oc /= s;
hits /= s;
size /= s;
percent_hit /= static_cast<double>(s);
return *this;
}
friend bool operator<(const test_results_t& a, const test_results_t& b)
{
return a.hits < b.hits;
}
friend bool operator>(const test_results_t& a, const test_results_t& b)
{
return a.hits > b.hits;
}
};
test_results_t test_individual(blt::gp::individual_t& i)
{
test_results_t results;
for (auto& testing_case : testing_cases)
{
auto result = i.tree.get_evaluation_value<float>(&testing_case);
switch (testing_case.type)
{
case rice_type_t::Cammeo:
if (result >= 0)
results.cc++; // cammeo cammeo
else
results.co++; // cammeo osmancik
break;
case rice_type_t::Osmancik:
if (result < 0)
results.oo++; // osmancik osmancik
else
results.oc++; // osmancik cammeo
break;
}
}
results.hits = results.cc + results.oo;
results.size = testing_cases.size();
results.percent_hit = static_cast<double>(results.hits) / static_cast<double>(results.size) * 100;
return results;
}
int main(int argc, const char** argv)
{
blt::arg_parse parser;
parser.addArgument(blt::arg_builder{"-f", "--file"}.setHelp("File for rice data. Should be in .arff format.").setRequired().build());
auto args = parser.parse_args(argc, argv);
if (!args.contains("file"))
{
BLT_WARN("Please provide path to file with -f or --file");
return 1;
}
auto rice_file_path = args.get<std::string>("file");
BLT_INFO("Starting BLT-GP Rice Classification Example");
BLT_START_INTERVAL("Rice Classification", "Main");
BLT_DEBUG("Setup Fitness cases");
load_rice_data(rice_file_path);
BLT_DEBUG("Setup Types and Operators");
blt::gp::operator_builder<rice_record> builder{};
program.set_operations(builder.build(add, sub, mul, pro_div, op_exp, op_log, lit, op_area, op_perimeter, op_major_axis_length,
op_minor_axis_length, op_eccentricity, op_convex_area, op_extent));
BLT_DEBUG("Generate Initial Population");
auto sel = blt::gp::select_tournament_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("Rice Classification", "Gen");
program.create_next_generation();
BLT_END_INTERVAL("Rice Classification", "Gen");
BLT_TRACE("Move to next generation");
BLT_START_INTERVAL("Rice Classification", "Fitness");
program.next_generation();
BLT_TRACE("Evaluate Fitness");
program.evaluate_fitness();
BLT_END_INTERVAL("Rice Classification", "Fitness");
auto& stats = program.get_population_stats();
BLT_TRACE("Stats:");
BLT_TRACE("Average fitness: %lf", stats.average_fitness.load());
BLT_TRACE("Best fitness: %lf", stats.best_fitness.load());
BLT_TRACE("Worst fitness: %lf", stats.worst_fitness.load());
BLT_TRACE("Overall fitness: %lf", stats.overall_fitness.load());
BLT_TRACE("----------------------------------------------");
std::cout << std::endl;
}
BLT_END_INTERVAL("Rice Classification", "Main");
std::vector<std::pair<test_results_t, blt::gp::individual_t*>> results;
for (auto& i : program.get_current_pop().get_individuals())
results.emplace_back(test_individual(i), &i);
std::sort(results.begin(), results.end(), [](const auto& a, const auto& b) {
return a.first > b.first;
});
BLT_INFO("Best results:");
for (blt::size_t index = 0; index < 3; index++)
{
const auto& record = results[index].first;
const auto& i = *results[index].second;
BLT_INFO("Hits %ld, Total Cases %ld, Percent Hit: %lf", record.hits, record.size, record.percent_hit);
BLT_DEBUG("Cammeo Cammeo: %ld", record.cc);
BLT_DEBUG("Cammeo Osmancik: %ld", record.co);
BLT_DEBUG("Osmancik Osmancik: %ld", record.oo);
BLT_DEBUG("Osmancik Cammeo: %ld", record.oc);
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";
}
BLT_INFO("Worst Results:");
for (blt::size_t index = 0; index < 3; index++)
{
const auto& record = results[results.size() - 1 - index].first;
const auto& i = *results[results.size() - 1 - index].second;
BLT_INFO("Hits %ld, Total Cases %ld, Percent Hit: %lf", record.hits, record.size, record.percent_hit);
BLT_DEBUG("Cammeo Cammeo: %ld", record.cc);
BLT_DEBUG("Cammeo Osmancik: %ld", record.co);
BLT_DEBUG("Osmancik Osmancik: %ld", record.oo);
BLT_DEBUG("Osmancik Cammeo: %ld", record.oc);
BLT_DEBUG("Fitness: %lf, stand: %lf, raw: %lf", i.fitness.adjusted_fitness, i.fitness.standardized_fitness, i.fitness.raw_fitness);
std::cout << "\n";
}
BLT_INFO("Average Results");
test_results_t avg{};
for (const auto& v : results)
avg += v.first;
avg /= results.size();
BLT_INFO("Hits %ld, Total Cases %ld, Percent Hit: %lf", avg.hits, avg.size, avg.percent_hit);
BLT_DEBUG("Cammeo Cammeo: %ld", avg.cc);
BLT_DEBUG("Cammeo Osmancik: %ld", avg.co);
BLT_DEBUG("Osmancik Osmancik: %ld", avg.oo);
BLT_DEBUG("Osmancik Cammeo: %ld", avg.oc);
std::cout << "\n";
BLT_PRINT_PROFILE("Rice Classification", blt::PRINT_CYCLES | blt::PRINT_THREAD | blt::PRINT_WALL);
#ifdef BLT_TRACK_ALLOCATIONS
BLT_TRACE("Total Allocations: %ld times with a total of %s", blt::gp::tracker.getAllocations(),
blt::byte_convert_t(blt::gp::tracker.getAllocatedBytes()).convert_to_nearest_type().to_pretty_string().c_str());
#endif
return 0;
}

View File

@ -19,7 +19,9 @@
#include <blt/profiling/profiler_v2.h>
#include <blt/gp/tree.h>
#include <blt/std/logging.h>
#include <blt/std/format.h>
#include <iostream>
#include "operations_common.h"
//static constexpr long SEED = 41912;
static const unsigned long SEED = std::random_device()();
@ -29,35 +31,25 @@ struct context
float x, y;
};
std::array<context, 200> fitness_cases;
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_elite_count(200)
.set_crossover_chance(0.9)
.set_mutation_chance(0.1)
.set_reproduction_chance(0)
.set_max_generations(50)
.set_pop_size(5000)
.set_pop_size(20000)
.set_thread_count(0);
blt::gp::type_provider type_system;
blt::gp::gp_program program{type_system, SEED, config};
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");
blt::gp::gp_program program{SEED, config};
auto lit = blt::gp::operation_t([]() {
return program.get_random().get_float(-320.0f, 320.0f);
return program.get_random().get_float(-1.0f, 1.0f);
}, "lit").set_ephemeral();
blt::gp::operation_t op_x([](const context& context) {
@ -66,7 +58,7 @@ blt::gp::operation_t op_x([](const context& context) {
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 : fitness_cases)
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)
@ -79,7 +71,7 @@ constexpr auto fitness_function = [](blt::gp::tree_t& current_tree, blt::gp::fit
}
fitness.standardized_fitness = fitness.raw_fitness;
fitness.adjusted_fitness = (1.0 / (1.0 + fitness.standardized_fitness));
return static_cast<blt::size_t>(fitness.hits) == fitness_cases.size();
return static_cast<blt::size_t>(fitness.hits) == training_cases.size();
};
float example_function(float x)
@ -92,7 +84,7 @@ 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 : fitness_cases)
for (auto& fitness_case : training_cases)
{
constexpr float range = 10;
constexpr float half_range = range / 2.0;
@ -102,28 +94,47 @@ int main()
}
BLT_DEBUG("Setup Types and Operators");
type_system.register_type<float>();
blt::gp::operator_builder<context> builder{type_system};
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(type_system.get_type<float>().id(), fitness_function, sel, sel, sel);
auto sel = blt::gp::select_tournament_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");
#ifdef BLT_TRACK_ALLOCATIONS
auto gen_alloc = blt::gp::tracker.start_measurement();
#endif
BLT_START_INTERVAL("Symbolic Regression", "Gen");
program.create_next_generation();
BLT_END_INTERVAL("Symbolic Regression", "Gen");
#ifdef BLT_TRACK_ALLOCATIONS
blt::gp::tracker.stop_measurement(gen_alloc);
BLT_TRACE("Generation Allocated %ld times with a total of %s", gen_alloc.getAllocationDifference(),
blt::byte_convert_t(gen_alloc.getAllocatedByteDifference()).convert_to_nearest_type().to_pretty_string().c_str());
auto fitness_alloc = blt::gp::tracker.start_measurement();
#endif
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");
#ifdef BLT_TRACK_ALLOCATIONS
blt::gp::tracker.stop_measurement(fitness_alloc);
BLT_TRACE("Fitness Allocated %ld times with a total of %s", fitness_alloc.getAllocationDifference(),
blt::byte_convert_t(fitness_alloc.getAllocatedByteDifference()).convert_to_nearest_type().to_pretty_string().c_str());
#endif
BLT_TRACE("----------------------------------------------");
std::cout << std::endl;
}
@ -150,5 +161,10 @@ int main()
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", blt::gp::tracker.getAllocations(),
blt::byte_convert_t(blt::gp::tracker.getAllocatedBytes()).convert_to_nearest_type().to_pretty_string().c_str());
#endif
return 0;
}

View File

@ -22,16 +22,22 @@
#include <functional>
#include <blt/std/logging.h>
#include <blt/std/types.h>
#include <blt/gp/stats.h>
#include <ostream>
#include <optional>
#include <cstdlib>
namespace blt::gp
{
inline allocation_tracker_t tracker;
class gp_program;
class type;
struct operator_id;
struct type_id;
class type_provider;
struct op_container_t;
@ -40,6 +46,8 @@ namespace blt::gp
class tree_t;
struct individual_t;
class population_t;
class tree_generator_t;
@ -50,12 +58,118 @@ namespace blt::gp
class stack_allocator;
template<typename T>
class tracked_allocator_t;
template<typename T>
using tracked_vector = std::vector<T, tracked_allocator_t<T>>;
// using operation_vector_t = tracked_vector<op_container_t>;
// using individual_vector_t = tracked_vector<individual_t, tracked_allocator_t<individual_t>>;
// using tree_vector_t = tracked_vector<tree_t>;
class aligned_allocator
{
public:
void* allocate(blt::size_t bytes) // NOLINT
{
#ifdef BLT_TRACK_ALLOCATIONS
tracker.allocate(bytes);
#endif
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);
#else
(void) bytes;
#endif
std::free(ptr);
}
};
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)
{
#ifdef BLT_TRACK_ALLOCATIONS
tracker.allocate(n * sizeof(T));
#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)
{
#ifdef BLT_TRACK_ALLOCATIONS
tracker.deallocate(n * sizeof(T));
#else
(void) n;
#endif
std::free(p);
}
template<class U, class... Args>
void construct(U* p, Args&& ... args)
{
new(p) T(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>
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>
inline static bool operator!=(const tracked_allocator_t<T1>& lhs, const tracked_allocator_t<T2>& rhs) noexcept
{
return &lhs != &rhs;
}
namespace detail
{
class operator_storage_test;
// context*, read stack, write stack
using operator_func_t = std::function<void(void*, stack_allocator&, stack_allocator&)>;
using eval_func_t = std::function<evaluation_context(const tree_t& tree, void* context)>;
using eval_func_t = std::function<evaluation_context&(const tree_t& tree, void* context)>;
// debug function,
using print_func_t = std::function<void(std::ostream&, stack_allocator&)>;
@ -66,6 +180,9 @@ namespace blt::gp
};
using destroy_func_t = std::function<void(destroy_t, stack_allocator&)>;
using const_op_iter_t = tracked_vector<op_container_t>::const_iterator;
using op_iter_t = tracked_vector<op_container_t>::iterator;
}
}

View File

@ -51,7 +51,7 @@ namespace blt::gp
class tree_generator_t
{
public:
virtual tree_t generate(const generator_arguments& args) = 0;
virtual void generate(tree_t& out, const generator_arguments& args) = 0;
virtual ~tree_generator_t() = default;
};
@ -59,13 +59,13 @@ namespace blt::gp
class grow_generator_t : public tree_generator_t
{
public:
tree_t generate(const generator_arguments& args) final;
void generate(tree_t& out, const generator_arguments& args) final;
};
class full_generator_t : public tree_generator_t
{
public:
tree_t generate(const generator_arguments& args) final;
void generate(tree_t& out, const generator_arguments& args) final;
};
class population_initializer_t

View File

@ -52,10 +52,10 @@
#include <blt/gp/stack.h>
#include <blt/gp/config.h>
#include <blt/gp/random.h>
#include "blt/std/format.h"
namespace blt::gp
{
struct argc_t
{
blt::u32 argc = 0;
@ -79,20 +79,22 @@ namespace blt::gp
detail::operator_func_t func;
};
struct operator_storage
struct program_operator_storage_t
{
// indexed from return TYPE ID, returns index of operator
blt::expanding_buffer<std::vector<operator_id>> terminals;
blt::expanding_buffer<std::vector<operator_id>> non_terminals;
blt::expanding_buffer<std::vector<std::pair<operator_id, blt::size_t>>> operators_ordered_terminals;
// indexed from OPERATOR ID (operator number)
blt::hashset_t<operator_id> static_types;
blt::hashset_t<operator_id> ephemeral_leaf_operators;
std::vector<operator_info> operators;
std::vector<detail::print_func_t> print_funcs;
std::vector<detail::destroy_func_t> destroy_funcs;
std::vector<std::optional<std::string_view>> names;
detail::eval_func_t eval_func;
type_provider system;
};
template<typename Context = detail::empty_t>
@ -103,11 +105,10 @@ namespace blt::gp
friend class blt::gp::detail::operator_storage_test;
public:
explicit operator_builder(type_provider& system): system(system)
{}
explicit operator_builder() = default;
template<typename... Operators>
operator_storage& build(Operators& ... operators)
program_operator_storage_t& build(Operators& ... operators)
{
std::vector<blt::size_t> sizes;
(sizes.push_back(add_operator(operators)), ...);
@ -115,11 +116,12 @@ namespace blt::gp
for (auto v : sizes)
largest = std::max(v, largest);
storage.eval_func = [&operators..., largest](const tree_t& tree, void* context) {
storage.eval_func = [&operators..., largest](const tree_t& tree, void* context) -> evaluation_context& {
const auto& ops = tree.get_operations();
const auto& vals = tree.get_values();
evaluation_context results{};
static thread_local evaluation_context results{};
results.values.reset();
results.values.reserve(largest);
blt::size_t total_so_far = 0;
@ -199,7 +201,7 @@ namespace blt::gp
return storage;
}
operator_storage&& grab()
program_operator_storage_t&& grab()
{
return std::move(storage);
}
@ -208,10 +210,14 @@ namespace blt::gp
template<typename RawFunction, typename Return, typename... Args>
auto add_operator(operation_t<RawFunction, Return(Args...)>& op)
{
// check for types we can register
(storage.system.register_type<Args>(), ...);
storage.system.register_type<Return>();
auto total_size_required = stack_allocator::aligned_size(sizeof(Return));
((total_size_required += stack_allocator::aligned_size(sizeof(Args))), ...);
auto return_type_id = system.get_type<Return>().id();
auto return_type_id = storage.system.get_type<Return>().id();
auto operator_id = blt::gp::operator_id(storage.operators.size());
op.id = operator_id;
@ -260,8 +266,8 @@ namespace blt::gp
});
storage.names.push_back(op.get_name());
if (op.is_ephemeral())
storage.static_types.insert(operator_id);
return total_size_required;
storage.ephemeral_leaf_operators.insert(operator_id);
return total_size_required * 2;
}
template<typename T>
@ -269,7 +275,7 @@ namespace blt::gp
{
if constexpr (!std::is_same_v<Context, detail::remove_cv_ref<T>>)
{
types.push_back(system.get_type<T>().id());
types.push_back(storage.system.get_type<T>().id());
}
}
@ -298,7 +304,7 @@ namespace blt::gp
template<typename... Operators, size_t... operator_ids>
static inline void call_jmp_table_internal(size_t op, void* context, stack_allocator& write_stack, stack_allocator& read_stack,
std::integer_sequence<size_t, operator_ids...>, Operators&... operators)
std::integer_sequence<size_t, operator_ids...>, Operators& ... operators)
{
if (op >= sizeof...(operator_ids))
{
@ -314,8 +320,7 @@ namespace blt::gp
call_jmp_table_internal(op, context, write_stack, read_stack, std::index_sequence_for<Operators...>(), operators...);
}
type_provider& system;
operator_storage storage;
program_operator_storage_t storage;
};
class gp_program
@ -325,29 +330,89 @@ namespace blt::gp
* Note about context size: This is required as context is passed to every operator in the GP tree, this context will be provided by your
* call to one of the evaluator functions. This was the nicest way to provide this as C++ lacks reflection
*
* @param system type system to use in tree generation
* @param engine random engine to use throughout the program.
* @param context_size number of arguments which are always present as "context" to the GP system / operators
*/
explicit gp_program(type_provider& system, blt::u64 seed):
system(system), seed(seed)
explicit gp_program(blt::u64 seed): seed_func([seed] { return seed; })
{ create_threads(); }
explicit gp_program(type_provider& system, blt::u64 seed, prog_config_t config):
system(system), seed(seed), config(config)
explicit gp_program(blt::u64 seed, prog_config_t config): seed_func([seed] { return seed; }), config(config)
{ create_threads(); }
explicit gp_program(std::function<blt::u64()> seed_func): seed_func(std::move(seed_func))
{ create_threads(); }
explicit gp_program(std::function<blt::u64()> seed_func, prog_config_t config): seed_func(std::move(seed_func)), config(config)
{ create_threads(); }
~gp_program()
{
thread_helper.lifetime_over = true;
thread_helper.barrier.notify_all();
thread_helper.thread_function_condition.notify_all();
for (auto& thread : thread_helper.threads)
{
if (thread->joinable())
thread->join();
}
}
void create_next_generation()
{
#ifdef BLT_TRACK_ALLOCATIONS
auto gen_alloc = blt::gp::tracker.start_measurement();
#endif
BLT_ASSERT_MSG(current_pop.get_individuals().size() == config.population_size,
("cur pop size: " + std::to_string(current_pop.get_individuals().size())).c_str());
BLT_ASSERT_MSG(next_pop.get_individuals().size() == config.population_size,
("next pop size: " + std::to_string(next_pop.get_individuals().size())).c_str());
// should already be empty
next_pop.clear();
thread_helper.next_gen_left.store(config.population_size, std::memory_order_release);
(*thread_execution_service)(0);
#ifdef BLT_TRACK_ALLOCATIONS
blt::gp::tracker.stop_measurement(gen_alloc);
BLT_TRACE("Generation Allocated %ld times with a total of %s", gen_alloc.getAllocationDifference(),
blt::byte_convert_t(gen_alloc.getAllocatedByteDifference()).convert_to_nearest_type().to_pretty_string().c_str());
#endif
}
void next_generation()
{
BLT_ASSERT_MSG(current_pop.get_individuals().size() == config.population_size,
("cur pop size: " + std::to_string(current_pop.get_individuals().size())).c_str());
BLT_ASSERT_MSG(next_pop.get_individuals().size() == config.population_size,
("next pop size: " + std::to_string(next_pop.get_individuals().size())).c_str());
std::swap(current_pop, next_pop);
current_generation++;
}
void evaluate_fitness()
{
#ifdef BLT_TRACK_ALLOCATIONS
auto fitness_alloc = blt::gp::tracker.start_measurement();
#endif
evaluate_fitness_internal();
#ifdef BLT_TRACK_ALLOCATIONS
blt::gp::tracker.stop_measurement(fitness_alloc);
BLT_TRACE("Fitness Allocated %ld times with a total of %s", fitness_alloc.getAllocationDifference(),
blt::byte_convert_t(fitness_alloc.getAllocatedByteDifference()).convert_to_nearest_type().to_pretty_string().c_str());
#endif
}
void reset_program(type_id root_type, bool eval_fitness_now = true)
{
current_generation = 0;
current_pop = config.pop_initializer.get().generate(
{*this, root_type, config.population_size, config.initial_min_tree_size, config.initial_max_tree_size});
next_pop = population_t(current_pop);
if (eval_fitness_now)
evaluate_fitness_internal();
}
void kill()
{
thread_helper.lifetime_over = true;
}
/**
@ -368,61 +433,71 @@ namespace blt::gp
using LambdaReturn = typename decltype(blt::meta::lambda_helper(fitness_function))::Return;
current_pop = config.pop_initializer.get().generate(
{*this, root_type, config.population_size, config.initial_min_tree_size, config.initial_max_tree_size});
next_pop = population_t(current_pop);
if (config.threads == 1)
{
BLT_INFO("Starting with single thread variant!");
thread_execution_service = new std::function(
thread_execution_service = std::unique_ptr<std::function<void(blt::size_t)>>(new std::function(
[this, &fitness_function, &crossover_selection, &mutation_selection, &reproduction_selection, &func](blt::size_t) {
if (thread_helper.evaluation_left > 0)
{
for (const auto& ind : blt::enumerate(current_pop.get_individuals()))
current_stats.normalized_fitness.clear();
double sum_of_prob = 0;
for (const auto& [index, ind] : blt::enumerate(current_pop.get_individuals()))
{
if constexpr (std::is_same_v<LambdaReturn, bool> || std::is_convertible_v<LambdaReturn, bool>)
{
auto result = fitness_function(ind.second.tree, ind.second.fitness, ind.first);
auto result = fitness_function(ind.tree, ind.fitness, index);
if (result)
fitness_should_exit = true;
} else
{
fitness_function(ind.second.tree, ind.second.fitness, ind.first);
fitness_function(ind.tree, ind.fitness, index);
if (ind.fitness.adjusted_fitness > current_stats.best_fitness)
current_stats.best_fitness = ind.fitness.adjusted_fitness;
if (ind.fitness.adjusted_fitness < current_stats.worst_fitness)
current_stats.worst_fitness = ind.fitness.adjusted_fitness;
current_stats.overall_fitness = current_stats.overall_fitness + ind.fitness.adjusted_fitness;
}
if (ind.second.fitness.adjusted_fitness > current_stats.best_fitness)
current_stats.best_fitness = ind.second.fitness.adjusted_fitness;
if (ind.second.fitness.adjusted_fitness < current_stats.worst_fitness)
current_stats.worst_fitness = ind.second.fitness.adjusted_fitness;
current_stats.overall_fitness = current_stats.overall_fitness + ind.second.fitness.adjusted_fitness;
for (auto& ind : current_pop)
{
auto prob = (ind.fitness.adjusted_fitness / current_stats.overall_fitness);
current_stats.normalized_fitness.push_back(sum_of_prob + prob);
sum_of_prob += prob;
}
thread_helper.evaluation_left = 0;
}
if (thread_helper.next_gen_left > 0)
{
static thread_local std::vector<tree_t> new_children;
new_children.clear();
auto args = get_selector_args(new_children);
auto args = get_selector_args();
crossover_selection.pre_process(*this, current_pop, current_stats);
mutation_selection.pre_process(*this, current_pop, current_stats);
reproduction_selection.pre_process(*this, current_pop, current_stats);
crossover_selection.pre_process(*this, current_pop);
mutation_selection.pre_process(*this, current_pop);
reproduction_selection.pre_process(*this, current_pop);
perform_elitism(args);
perform_elitism(args, next_pop);
while (new_children.size() < config.population_size)
func(args, crossover_selection, mutation_selection, reproduction_selection);
blt::size_t start = config.elites;
for (auto& i : new_children)
next_pop.get_individuals().emplace_back(std::move(i));
while (start < config.population_size)
{
tree_t& c1 = next_pop.get_individuals()[start].tree;
tree_t* c2 = nullptr;
if (start + 1 < config.population_size)
c2 = &next_pop.get_individuals()[start + 1].tree;
start += func(args, crossover_selection, mutation_selection, reproduction_selection, c1, c2);
}
thread_helper.next_gen_left = 0;
}
});
}));
} else
{
BLT_INFO("Starting thread execution service!");
std::scoped_lock lock(thread_helper.thread_function_control);
thread_execution_service = new std::function(
thread_execution_service = std::unique_ptr<std::function<void(blt::size_t)>>(new std::function(
[this, &fitness_function, &crossover_selection, &mutation_selection, &reproduction_selection, &func](blt::size_t id) {
thread_helper.barrier.wait();
if (thread_helper.evaluation_left > 0)
@ -476,23 +551,27 @@ namespace blt::gp
}
if (thread_helper.next_gen_left > 0)
{
static thread_local std::vector<tree_t> new_children;
new_children.clear();
auto args = get_selector_args(new_children);
auto args = get_selector_args();
if (id == 0)
{
crossover_selection.pre_process(*this, current_pop, current_stats);
current_stats.normalized_fitness.clear();
double sum_of_prob = 0;
for (auto& ind : current_pop)
{
auto prob = (ind.fitness.adjusted_fitness / current_stats.overall_fitness);
current_stats.normalized_fitness.push_back(sum_of_prob + prob);
sum_of_prob += prob;
}
crossover_selection.pre_process(*this, current_pop);
if (&crossover_selection != &mutation_selection)
mutation_selection.pre_process(*this, current_pop, current_stats);
mutation_selection.pre_process(*this, current_pop);
if (&crossover_selection != &reproduction_selection)
reproduction_selection.pre_process(*this, current_pop, current_stats);
reproduction_selection.pre_process(*this, current_pop);
perform_elitism(args);
perform_elitism(args, next_pop);
for (auto& i : new_children)
next_pop.get_individuals().emplace_back(std::move(i));
thread_helper.next_gen_left -= new_children.size();
new_children.clear();
thread_helper.next_gen_left -= config.elites;
}
thread_helper.barrier.wait();
@ -509,88 +588,26 @@ namespace blt::gp
std::memory_order::memory_order_relaxed,
std::memory_order::memory_order_relaxed));
for (blt::size_t i = begin; i < end; i++)
func(args, crossover_selection, mutation_selection, reproduction_selection);
while (begin != end)
{
auto index = config.elites + begin;
tree_t& c1 = next_pop.get_individuals()[index].tree;
tree_t* c2 = nullptr;
if (index + 1 < end)
c2 = &next_pop.get_individuals()[index + 1].tree;
begin += func(args, crossover_selection, mutation_selection, reproduction_selection, c1, c2);
}
{
std::scoped_lock lock(thread_helper.thread_generation_lock);
for (auto& i : new_children)
{
if (next_pop.get_individuals().size() < config.population_size)
next_pop.get_individuals().emplace_back(i);
}
}
}
}
thread_helper.barrier.wait();
});
}));
thread_helper.thread_function_condition.notify_all();
}
if (eval_fitness_now)
evaluate_fitness_internal();
}
void reset_program(type_id root_type, bool eval_fitness_now = true)
{
current_generation = 0;
current_pop = config.pop_initializer.get().generate(
{*this, root_type, config.population_size, config.initial_min_tree_size, config.initial_max_tree_size});
if (eval_fitness_now)
evaluate_fitness_internal();
}
void next_generation()
{
current_pop = std::move(next_pop);
current_generation++;
}
inline auto& get_current_pop()
{
return current_pop;
}
template<blt::size_t size>
std::array<blt::size_t, size> get_best_indexes()
{
std::array<blt::size_t, size> arr;
std::vector<std::pair<blt::size_t, double>> values;
values.reserve(current_pop.get_individuals().size());
for (const auto& ind : blt::enumerate(current_pop.get_individuals()))
values.emplace_back(ind.first, ind.second.fitness.adjusted_fitness);
std::sort(values.begin(), values.end(), [](const auto& a, const auto& b) {
return a.second > b.second;
});
for (blt::size_t i = 0; i < size; i++)
arr[i] = values[i].first;
return arr;
}
template<blt::size_t size>
auto get_best_trees()
{
return convert_array<std::array<std::reference_wrapper<tree_t>, size>>(get_best_indexes<size>(),
[this](auto&& arr, blt::size_t index) -> tree_t& {
return current_pop.get_individuals()[arr[index]].tree;
},
std::make_integer_sequence<blt::size_t, size>());
}
template<blt::size_t size>
auto get_best_individuals()
{
return convert_array<std::array<std::reference_wrapper<individual>, size>>(get_best_indexes<size>(),
[this](auto&& arr, blt::size_t index) -> individual& {
return current_pop.get_individuals()[arr[index]];
},
std::make_integer_sequence<blt::size_t, size>());
}
[[nodiscard]] bool should_terminate() const
{
return current_generation >= config.max_generations || fitness_should_exit;
@ -601,13 +618,6 @@ namespace blt::gp
return thread_helper.lifetime_over;
}
[[nodiscard]] random_t& get_random() const;
[[nodiscard]] inline type_provider& get_typesystem()
{
return system;
}
inline operator_id select_terminal(type_id id)
{
// we wanted a terminal, but could not find one, so we will select from a function that has a terminal
@ -633,47 +643,49 @@ namespace blt::gp
return get_random().select(storage.operators_ordered_terminals[id]).first;
}
inline operator_info& get_operator_info(operator_id id)
inline auto& get_current_pop()
{
return current_pop;
}
[[nodiscard]] random_t& get_random() const;
[[nodiscard]] inline type_provider& get_typesystem()
{
return storage.system;
}
[[nodiscard]] inline operator_info& get_operator_info(operator_id id)
{
return storage.operators[id];
}
inline detail::print_func_t& get_print_func(operator_id id)
[[nodiscard]] inline detail::print_func_t& get_print_func(operator_id id)
{
return storage.print_funcs[id];
}
inline detail::destroy_func_t& get_destroy_func(operator_id id)
[[nodiscard]] inline detail::destroy_func_t& get_destroy_func(operator_id id)
{
return storage.destroy_funcs[id];
}
inline std::optional<std::string_view> get_name(operator_id id)
[[nodiscard]] inline std::optional<std::string_view> get_name(operator_id id)
{
return storage.names[id];
}
inline std::vector<operator_id>& get_type_terminals(type_id id)
[[nodiscard]] inline std::vector<operator_id>& get_type_terminals(type_id id)
{
return storage.terminals[id];
}
inline std::vector<operator_id>& get_type_non_terminals(type_id id)
[[nodiscard]] inline std::vector<operator_id>& get_type_non_terminals(type_id id)
{
return storage.non_terminals[id];
}
inline bool is_static(operator_id id)
{
return storage.static_types.contains(static_cast<blt::size_t>(id));
}
inline void set_operations(operator_storage op)
{
storage = std::move(op);
}
inline detail::eval_func_t& get_eval_func()
[[nodiscard]] inline detail::eval_func_t& get_eval_func()
{
return storage.eval_func;
}
@ -683,68 +695,66 @@ namespace blt::gp
return current_generation.load();
}
[[nodiscard]] inline auto& get_population_stats()
[[nodiscard]] inline const auto& get_population_stats() const
{
return current_stats;
}
~gp_program()
[[nodiscard]] inline bool is_operator_ephemeral(operator_id id)
{
thread_helper.lifetime_over = true;
thread_helper.barrier.notify_all();
thread_helper.thread_function_condition.notify_all();
for (auto& thread : thread_helper.threads)
{
if (thread->joinable())
thread->join();
}
auto* cpy = thread_execution_service.load(std::memory_order_acquire);
thread_execution_service = nullptr;
delete cpy;
return storage.ephemeral_leaf_operators.contains(static_cast<blt::size_t>(id));
}
void kill()
inline void set_operations(program_operator_storage_t op)
{
thread_helper.lifetime_over = true;
storage = std::move(op);
}
template<blt::size_t size>
std::array<blt::size_t, size> get_best_indexes()
{
std::array<blt::size_t, size> arr;
std::vector<std::pair<blt::size_t, double>> values;
values.reserve(current_pop.get_individuals().size());
for (const auto& ind : blt::enumerate(current_pop.get_individuals()))
values.emplace_back(ind.first, ind.second.fitness.adjusted_fitness);
std::sort(values.begin(), values.end(), [](const auto& a, const auto& b) {
return a.second > b.second;
});
for (blt::size_t i = 0; i < size; i++)
arr[i] = values[i].first;
return arr;
}
template<blt::size_t size>
auto get_best_trees()
{
return convert_array<std::array<std::reference_wrapper<individual_t>, size>>(get_best_indexes<size>(),
[this](auto&& arr, blt::size_t index) -> tree_t& {
return current_pop.get_individuals()[arr[index]].tree;
},
std::make_integer_sequence<blt::size_t, size>());
}
template<blt::size_t size>
auto get_best_individuals()
{
return convert_array<std::array<std::reference_wrapper<individual_t>, size>>(get_best_indexes<size>(),
[this](auto&& arr, blt::size_t index) -> individual_t& {
return current_pop.get_individuals()[arr[index]];
},
std::make_integer_sequence<blt::size_t, size>());
}
private:
type_provider& system;
operator_storage storage;
population_t current_pop;
population_stats current_stats{};
population_t next_pop;
std::atomic_uint64_t current_generation = 0;
std::atomic_bool fitness_should_exit = false;
blt::u64 seed;
prog_config_t config{};
struct concurrency_storage
inline selector_args get_selector_args()
{
std::vector<std::unique_ptr<std::thread>> threads;
std::mutex thread_function_control;
std::mutex thread_generation_lock;
std::condition_variable thread_function_condition{};
std::atomic_uint64_t evaluation_left = 0;
std::atomic_uint64_t next_gen_left = 0;
std::atomic_bool lifetime_over = false;
blt::barrier barrier;
explicit concurrency_storage(blt::size_t threads): barrier(threads, lifetime_over)
{}
} thread_helper{config.threads == 0 ? std::thread::hardware_concurrency() : config.threads};
// for convenience, shouldn't decrease performance too much
std::atomic<std::function<void(blt::size_t)>*> thread_execution_service = nullptr;
inline selector_args get_selector_args(std::vector<tree_t>& next_pop_trees)
{
return {*this, next_pop_trees, current_pop, current_stats, config, get_random()};
return {*this, current_pop, current_stats, config, get_random()};
}
template<typename Return, blt::size_t size, typename Accessor, blt::size_t... indexes>
@ -758,12 +768,47 @@ namespace blt::gp
void evaluate_fitness_internal()
{
statistic_history.push_back(current_stats);
current_stats.clear();
thread_helper.evaluation_left.store(current_pop.get_individuals().size(), std::memory_order_release);
thread_helper.evaluation_left.store(config.population_size, std::memory_order_release);
(*thread_execution_service)(0);
current_stats.average_fitness = current_stats.overall_fitness / static_cast<double>(config.population_size);
}
private:
program_operator_storage_t storage;
std::function<blt::u64()> seed_func;
prog_config_t config{};
population_t current_pop;
population_t next_pop;
std::atomic_uint64_t current_generation = 0;
std::atomic_bool fitness_should_exit = false;
population_stats current_stats{};
std::vector<population_stats> statistic_history;
struct concurrency_storage
{
std::vector<std::unique_ptr<std::thread>> threads;
std::mutex thread_function_control{};
std::condition_variable thread_function_condition{};
std::atomic_uint64_t evaluation_left = 0;
std::atomic_uint64_t next_gen_left = 0;
std::atomic_bool lifetime_over = false;
blt::barrier barrier;
explicit concurrency_storage(blt::size_t threads): barrier(threads, lifetime_over)
{}
} thread_helper{config.threads == 0 ? std::thread::hardware_concurrency() : config.threads};
std::unique_ptr<std::function<void(blt::size_t)>> thread_execution_service = nullptr;
};
}

View File

@ -25,93 +25,7 @@
namespace blt::gp
{
#define BLT_RANDOM_FUNCTION blt::random::murmur_random64
#define BLT_RANDOM_FLOAT blt::random::murmur_float64
#define BLT_RANDOM_DOUBLE blt::random::murmur_double64
class random_t
{
public:
explicit random_t(blt::u64 seed): seed(seed)
{}
void set_seed(blt::u64 s)
{
seed = s;
}
float get_float()
{
return BLT_RANDOM_FLOAT(seed);
}
double get_double()
{
return BLT_RANDOM_DOUBLE(seed);
}
// [min, max)
double get_double(double min, double max)
{
return BLT_RANDOM_FUNCTION(seed, min, max);
}
// [min, max)
float get_float(float min, float max)
{
return BLT_RANDOM_FUNCTION(seed, min, max);
}
i32 get_i32(i32 min, i32 max)
{
return BLT_RANDOM_FUNCTION(seed, min, max);
}
u32 get_u32(u32 min, u32 max)
{
return BLT_RANDOM_FUNCTION(seed, min, max);
}
i64 get_i64(i64 min, i64 max)
{
return BLT_RANDOM_FUNCTION(seed, min, max);
}
u64 get_u64(u64 min, u64 max)
{
return BLT_RANDOM_FUNCTION(seed, min, max);
}
blt::size_t get_size_t(blt::size_t min, blt::size_t max)
{
return BLT_RANDOM_FUNCTION(seed, min, max);
}
bool choice()
{
return BLT_RANDOM_DOUBLE(seed) < 0.5;
}
bool choice(double cutoff)
{
return BLT_RANDOM_DOUBLE(seed) <= cutoff;
}
template<typename Container>
auto& select(Container& container)
{
return container[get_u64(0, container.size())];
}
template<typename Container>
const auto& select(const Container& container)
{
return container[get_u64(0, container.size())];
}
private:
blt::u64 seed;
};
using random_t = blt::random::random_t;
}

View File

@ -24,6 +24,7 @@
#include <blt/gp/config.h>
#include <blt/gp/random.h>
#include <blt/std/assert.h>
#include "blt/std/format.h"
namespace blt::gp
{
@ -31,19 +32,19 @@ namespace blt::gp
struct selector_args
{
gp_program& program;
std::vector<tree_t>& next_pop;
population_t& current_pop;
const population_t& current_pop;
population_stats& current_stats;
prog_config_t& config;
random_t& random;
};
constexpr inline auto perform_elitism = [](const selector_args& args) {
auto& [program, next_pop, current_pop, current_stats, config, random] = args;
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)
{
std::vector<std::pair<std::size_t, double>> values;
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);
@ -68,50 +69,66 @@ namespace blt::gp
}
for (blt::size_t i = 0; i < config.elites; i++)
next_pop.push_back(current_pop.get_individuals()[values[i].first].tree);
next_pop.get_individuals()[i].copy_fast(current_pop.get_individuals()[values[i].first].tree);
}
};
template<typename Crossover, typename Mutation, typename Reproduction>
constexpr inline auto default_next_pop_creator = [](
blt::gp::selector_args& args, Crossover& crossover_selection, Mutation& mutation_selection, Reproduction& reproduction_selection) {
auto& [program, next_pop, current_pop, current_stats, config, random] = args;
blt::gp::selector_args& args, Crossover& crossover_selection, Mutation& mutation_selection, Reproduction& reproduction_selection,
tree_t& c1, tree_t* c2) {
auto& [program, current_pop, current_stats, config, random] = args;
int sel = random.get_i32(0, 3);
switch (sel)
{
case 0:
if (c2 == nullptr)
return 0;
// everyone gets a chance once per loop.
if (random.choice(config.crossover_chance))
{
// auto state = tracker.start_measurement();
// crossover
auto& p1 = crossover_selection.select(program, current_pop, current_stats);
auto& p2 = crossover_selection.select(program, current_pop, current_stats);
auto results = config.crossover.get().apply(program, p1, p2);
// if crossover fails, we can check for mutation on these guys. otherwise straight copy them into the next pop
if (results)
const tree_t* p1;
const tree_t* p2;
do
{
next_pop.push_back(std::move(results->child1));
next_pop.push_back(std::move(results->child2));
}
p1 = &crossover_selection.select(program, current_pop);
p2 = &crossover_selection.select(program, current_pop);
} while (!config.crossover.get().apply(program, *p1, *p2, c1, *c2));
// tracker.stop_measurement(state);
// BLT_TRACE("Crossover Allocated %ld times with a total of %s", state.getAllocationDifference(),
// blt::byte_convert_t(state.getAllocatedByteDifference()).convert_to_nearest_type().to_pretty_string().c_str());
return 2;
}
break;
case 1:
if (random.choice(config.mutation_chance))
{
// auto state = tracker.start_measurement();
// mutation
auto& p = mutation_selection.select(program, current_pop, current_stats);
next_pop.push_back(std::move(config.mutator.get().apply(program, p)));
const tree_t* p;
do
{
p = &mutation_selection.select(program, current_pop);
} while (!config.mutator.get().apply(program, *p, c1));
// tracker.stop_measurement(state);
// BLT_TRACE("Mutation Allocated %ld times with a total of %s", state.getAllocationDifference(),
// blt::byte_convert_t(state.getAllocatedByteDifference()).convert_to_nearest_type().to_pretty_string().c_str());
return 1;
}
break;
case 2:
if (config.reproduction_chance > 0 && random.choice(config.reproduction_chance))
{
// auto state = tracker.start_measurement();
// reproduction
auto& p = reproduction_selection.select(program, current_pop, current_stats);
next_pop.push_back(p);
c1 = reproduction_selection.select(program, current_pop);
// tracker.stop_measurement(state);
// BLT_TRACE("Reproduction Allocated %ld times with a total of %s", state.getAllocationDifference(),
// blt::byte_convert_t(state.getAllocatedByteDifference()).convert_to_nearest_type().to_pretty_string().c_str());
return 1;
}
break;
default:
@ -121,6 +138,7 @@ namespace blt::gp
BLT_UNREACHABLE;
#endif
}
return 0;
};
class selection_t
@ -132,9 +150,9 @@ namespace blt::gp
* @param stats the populations statistics
* @return
*/
virtual tree_t& select(gp_program& program, population_t& pop, population_stats& stats) = 0;
virtual const tree_t& select(gp_program& program, const population_t& pop) = 0;
virtual void pre_process(gp_program&, population_t&, population_stats&)
virtual void pre_process(gp_program&, population_t&)
{}
virtual ~selection_t() = default;
@ -143,19 +161,19 @@ namespace blt::gp
class select_best_t : public selection_t
{
public:
tree_t& select(gp_program& program, population_t& pop, population_stats& stats) final;
const tree_t& select(gp_program& program, const population_t& pop) final;
};
class select_worst_t : public selection_t
{
public:
tree_t& select(gp_program& program, population_t& pop, population_stats& stats) final;
const tree_t& select(gp_program& program, const population_t& pop) final;
};
class select_random_t : public selection_t
{
public:
tree_t& select(gp_program& program, population_t& pop, population_stats& stats) final;
const tree_t& select(gp_program& program, const population_t& pop) final;
};
class select_tournament_t : public selection_t
@ -163,11 +181,11 @@ namespace blt::gp
public:
explicit select_tournament_t(blt::size_t selection_size = 3): selection_size(selection_size)
{
if (selection_size < 1)
BLT_ABORT("Unable to select with this size. Must select at least 1 individual!");
if (selection_size == 0)
BLT_ABORT("Unable to select with this size. Must select at least 1 individual_t!");
}
tree_t& select(gp_program& program, population_t& pop, population_stats& stats) final;
const tree_t& select(gp_program& program, const population_t& pop) final;
private:
const blt::size_t selection_size;
@ -176,9 +194,7 @@ namespace blt::gp
class select_fitness_proportionate_t : public selection_t
{
public:
void pre_process(gp_program& program, population_t& pop, population_stats& stats) final;
tree_t& select(gp_program& program, population_t& pop, population_stats& stats) final;
const tree_t& select(gp_program& program, const population_t& pop) final;
};
}

View File

@ -20,12 +20,14 @@
#define BLT_GP_STACK_H
#include <blt/std/types.h>
#include <blt/std/bump_allocator.h>
#include <blt/std/assert.h>
#include <blt/std/logging.h>
#include <blt/std/allocator.h>
#include <blt/std/ranges.h>
#include <blt/std/meta.h>
#include <blt/gp/fwdecl.h>
#include <blt/gp/stats.h>
#include <utility>
#include <stdexcept>
#include <cstdlib>
@ -36,29 +38,14 @@
namespace blt::gp
{
namespace detail
{
BLT_META_MAKE_FUNCTION_CHECK(drop);
}
class aligned_allocator
{
public:
void* allocate(blt::size_t bytes) // NOLINT
{
return std::aligned_alloc(8, bytes);
}
void deallocate(void* ptr, blt::size_t) // NOLINT
{
std::free(ptr);
}
};
class stack_allocator
{
constexpr static blt::size_t PAGE_SIZE = 0x1000;
constexpr static blt::size_t PAGE_SIZE = 0x100;
constexpr static blt::size_t MAX_ALIGNMENT = 8;
template<typename T>
using NO_REF_T = std::remove_cv_t<std::remove_reference_t<T>>;
@ -106,7 +93,7 @@ namespace blt::gp
}
stack_allocator(stack_allocator&& move) noexcept:
data_(std::exchange(move.data_, nullptr)), bytes_stored(move.bytes_stored), size_(move.size_)
data_(std::exchange(move.data_, nullptr)), bytes_stored(std::exchange(move.bytes_stored, 0)), size_(std::exchange(move.size_, 0))
{}
stack_allocator& operator=(const stack_allocator& copy) = delete;
@ -121,7 +108,6 @@ namespace blt::gp
~stack_allocator()
{
//std::free(data_);
get_allocator().deallocate(data_, size_);
}
@ -265,13 +251,33 @@ namespace blt::gp
void reserve(blt::size_t bytes)
{
if (bytes > size_)
expand(bytes);
expand_raw(bytes);
}
[[nodiscard]] blt::size_t stored() const
{
return bytes_stored;
}
[[nodiscard]] blt::size_t internal_storage_size() const
{
return size_;
}
void reset()
{
bytes_stored = 0;
}
private:
void expand(blt::size_t bytes)
{
bytes = to_nearest_page_size(bytes);
//bytes = to_nearest_page_size(bytes);
expand_raw(bytes);
}
void expand_raw(blt::size_t bytes)
{
auto new_data = static_cast<blt::u8*>(get_allocator().allocate(bytes));
if (bytes_stored > 0)
std::memcpy(new_data, data_, bytes_stored);

134
include/blt/gp/stats.h Normal file
View File

@ -0,0 +1,134 @@
#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_STATS_H
#define BLT_GP_STATS_H
#include <blt/std/types.h>
#include <atomic>
#include <cmath>
namespace blt::gp
{
class allocation_tracker_t
{
public:
struct allocation_data_t
{
blt::u64 start_allocations = 0;
blt::u64 start_deallocations = 0;
blt::u64 start_allocated_bytes = 0;
blt::u64 start_deallocated_bytes = 0;
blt::u64 end_allocations = 0;
blt::u64 end_deallocations = 0;
blt::u64 end_allocated_bytes = 0;
blt::u64 end_deallocated_bytes = 0;
[[nodiscard]] blt::u64 getAllocationDifference() const
{
return end_allocations - start_allocations;
}
[[nodiscard]] blt::u64 getDeallocationDifference() const
{
return end_deallocations - start_deallocations;
}
[[nodiscard]] blt::u64 getAllocatedByteDifference() const
{
return end_allocated_bytes - start_allocated_bytes;
}
[[nodiscard]] blt::u64 getDeallocatedByteDifference() const
{
return end_deallocated_bytes - start_deallocated_bytes;
}
};
void allocate(blt::size_t bytes)
{
allocations++;
allocated_bytes += bytes;
}
void deallocate(blt::size_t bytes)
{
deallocations++;
deallocated_bytes += bytes;
}
[[nodiscard]] blt::u64 getAllocations() const
{
return allocations;
}
[[nodiscard]] blt::u64 getDeallocations() const
{
return deallocations;
}
[[nodiscard]] blt::u64 getAllocatedBytes() const
{
return allocated_bytes;
}
[[nodiscard]] blt::u64 getDeallocatedBytes() const
{
return deallocated_bytes;
}
[[nodiscard]] blt::u64 getAllocationDifference() const
{
return std::abs(static_cast<blt::i64>(getAllocations()) - static_cast<blt::i64>(getDeallocations()));
}
[[nodiscard]] blt::u64 getCurrentlyAllocatedBytes() const
{
return getAllocatedBytes() - getDeallocatedBytes();
}
[[nodiscard]] allocation_data_t start_measurement() const
{
allocation_data_t data{};
data.start_allocations = allocations;
data.start_deallocations = deallocations;
data.start_allocated_bytes = allocated_bytes;
data.start_deallocated_bytes = deallocated_bytes;
return data;
}
void stop_measurement(allocation_data_t& data) const
{
data.end_allocations = allocations;
data.end_deallocations = deallocations;
data.end_allocated_bytes = allocated_bytes;
data.end_deallocated_bytes = deallocated_bytes;
}
private:
std::atomic_uint64_t allocations = 0;
std::atomic_uint64_t deallocations = 0;
std::atomic_uint64_t allocated_bytes = 0;
std::atomic_uint64_t deallocated_bytes = 0;
};
}
#endif //BLT_GP_STATS_H

View File

@ -29,8 +29,6 @@ namespace blt::gp
{
namespace detail
{
using op_iter = std::vector<blt::gp::op_container_t>::iterator;
template<typename T>
inline static constexpr double sum(const T& array)
{
@ -59,16 +57,6 @@ namespace blt::gp
class crossover_t
{
public:
enum class error_t
{
NO_VALID_TYPE,
TREE_TOO_SMALL
};
struct result_t
{
tree_t child1;
tree_t child2;
};
struct crossover_point_t
{
blt::ptrdiff_t p1_crossover_point;
@ -89,7 +77,7 @@ namespace blt::gp
explicit crossover_t(const config_t& config): config(config)
{}
blt::expected<crossover_t::crossover_point_t, error_t> get_crossover_point(gp_program& program, const tree_t& c1, const tree_t& c2) const;
std::optional<crossover_t::crossover_point_t> get_crossover_point(gp_program& program, const tree_t& c1, const tree_t& c2) const;
/**
* child1 and child2 are copies of the parents, the result of selecting a crossover point and performing standard subtree crossover.
@ -99,7 +87,7 @@ namespace blt::gp
* @param p2 reference to the second parent
* @return expected pair of child otherwise returns error enum
*/
virtual blt::expected<result_t, error_t> apply(gp_program& program, const tree_t& p1, const tree_t& p2); // NOLINT
virtual bool apply(gp_program& program, const tree_t& p1, const tree_t& p2, tree_t& c1, tree_t& c2); // NOLINT
virtual ~crossover_t() = default;
@ -128,7 +116,7 @@ namespace blt::gp
explicit mutation_t(const config_t& config): config(config)
{}
virtual tree_t apply(gp_program& program, const tree_t& p);
virtual bool apply(gp_program& program, const tree_t& p, tree_t& c);
// returns the point after the mutation
blt::size_t mutate_point(gp_program& program, tree_t& c, blt::size_t node);
@ -157,7 +145,7 @@ namespace blt::gp
explicit advanced_mutation_t(const config_t& config): mutation_t(config)
{}
tree_t apply(gp_program& program, const tree_t& p) final;
bool apply(gp_program& program, const tree_t& p, tree_t& c) final;
advanced_mutation_t& set_per_node_mutation_chance(double v)
{
@ -172,11 +160,11 @@ namespace blt::gp
double per_node_mutation_chance = 5.0;
static constexpr std::array<double, operators_size> mutation_operator_chances = detail::aggregate_array<operators_size>(
0.1, // EXPRESSION
0.25, // ADJUST
0.25, // EXPRESSION
0.15, // ADJUST
0.01, // SUB_FUNC
0.25, // JUMP_FUNC
0.12 // COPY
0.01, // JUMP_FUNC
0.05 // COPY
);
};

View File

@ -53,10 +53,42 @@ namespace blt::gp
class tree_t
{
using iter_type = std::vector<op_container_t>::const_iterator;
public:
explicit tree_t(gp_program& program);
tree_t(const tree_t& copy) = default;
tree_t& operator=(const tree_t& copy)
{
if (this == &copy)
return *this;
copy_fast(copy);
return *this;
}
/**
* This function copies the data from the provided tree, will attempt to reserve and copy in one step.
* will avoid reallocation if enough space is already present.
*/
void copy_fast(const tree_t& copy)
{
if (this == &copy)
return;
values.reserve(copy.values.internal_storage_size());
values.reset();
values.insert(copy.values);
operations.clear();
operations.reserve(copy.operations.size());
operations.insert(operations.begin(), copy.operations.begin(), copy.operations.end());
}
tree_t(tree_t&& move) = default;
tree_t& operator=(tree_t&& move) = default;
void clear(gp_program& program);
struct child_t
{
blt::ptrdiff_t start;
@ -64,12 +96,12 @@ namespace blt::gp
blt::ptrdiff_t end;
};
[[nodiscard]] inline std::vector<op_container_t>& get_operations()
[[nodiscard]] inline tracked_vector<op_container_t>& get_operations()
{
return operations;
}
[[nodiscard]] inline const std::vector<op_container_t>& get_operations() const
[[nodiscard]] inline const tracked_vector<op_container_t>& get_operations() const
{
return operations;
}
@ -84,7 +116,7 @@ namespace blt::gp
return values;
}
evaluation_context evaluate(void* context) const
evaluation_context& evaluate(void* context) const
{
return (*func)(*this, context);
}
@ -115,7 +147,7 @@ namespace blt::gp
template<typename T>
T get_evaluation_value(void* context)
{
auto results = evaluate(context);
auto& results = evaluate(context);
return results.values.pop<T>();
}
@ -131,7 +163,7 @@ namespace blt::gp
blt::ptrdiff_t find_parent(blt::gp::gp_program& program, blt::ptrdiff_t start) const;
// valid for [begin, end)
static blt::size_t total_value_bytes(iter_type begin, iter_type end)
static blt::size_t total_value_bytes(detail::const_op_iter_t begin, detail::const_op_iter_t end)
{
blt::size_t total = 0;
for (auto it = begin; it != end; it++)
@ -159,7 +191,7 @@ namespace blt::gp
}
private:
std::vector<op_container_t> operations;
tracked_vector<op_container_t> operations;
blt::gp::stack_allocator values;
detail::eval_func_t* func;
};
@ -172,30 +204,49 @@ namespace blt::gp
blt::i64 hits = 0;
};
struct individual
struct individual_t
{
tree_t tree;
fitness_t fitness;
individual() = default;
void copy_fast(const tree_t& copy)
{
// fast copy of the tree
tree.copy_fast(copy);
// reset fitness
fitness = {};
}
explicit individual(tree_t&& tree): tree(std::move(tree))
individual_t() = delete;
explicit individual_t(tree_t&& tree): tree(std::move(tree))
{}
explicit individual(const tree_t& tree): tree(tree)
explicit individual_t(const tree_t& tree): tree(tree)
{}
individual(const individual&) = default;
individual_t(const individual_t&) = default;
individual(individual&&) = default;
individual_t(individual_t&&) = default;
individual& operator=(const individual&) = delete;
individual_t& operator=(const individual_t&) = delete;
individual& operator=(individual&&) = default;
individual_t& operator=(individual_t&&) = default;
};
struct population_stats
{
population_stats() = default;
population_stats(const population_stats& copy):
overall_fitness(copy.overall_fitness.load()), average_fitness(copy.average_fitness.load()), best_fitness(copy.best_fitness.load()),
worst_fitness(copy.worst_fitness.load())
{
normalized_fitness.reserve(copy.normalized_fitness.size());
for (auto v : copy.normalized_fitness)
normalized_fitness.push_back(v);
}
std::atomic<double> overall_fitness = 0;
std::atomic<double> average_fitness = 0;
std::atomic<double> best_fitness = 0;
@ -218,7 +269,7 @@ namespace blt::gp
class population_tree_iterator
{
public:
population_tree_iterator(std::vector<individual>& ind, blt::size_t pos): ind(ind), pos(pos)
population_tree_iterator(tracked_vector<individual_t>& ind, blt::size_t pos): ind(ind), pos(pos)
{}
auto begin()
@ -263,11 +314,16 @@ namespace blt::gp
}
private:
std::vector<individual>& ind;
tracked_vector<individual_t>& ind;
blt::size_t pos;
};
std::vector<individual>& get_individuals()
tracked_vector<individual_t>& get_individuals()
{
return individuals;
}
[[nodiscard]] const tracked_vector<individual_t>& get_individuals() const
{
return individuals;
}
@ -313,7 +369,7 @@ namespace blt::gp
population_t& operator=(population_t&&) = default;
private:
std::vector<individual> individuals;
tracked_vector<individual_t> individuals;
};
}

View File

@ -85,12 +85,13 @@ namespace blt::gp
type_provider() = default;
template<typename T>
inline type register_type()
inline void register_type()
{
if (has_type<T>())
return;
auto t = type::make_type<T>(types.size());
types.insert({blt::type_string_raw<T>(), t});
types_from_id[t.id()] = t;
return t;
}
template<typename T>
@ -99,6 +100,11 @@ namespace blt::gp
return types[blt::type_string_raw<T>()];
}
template<typename T>
inline bool has_type(){
return types.find(blt::type_string_raw<T>()) != types.end();
}
inline type get_type(type_id id)
{
return types_from_id[id];

@ -1 +1 @@
Subproject commit 6632d045286b42d257eb3783e96256c13b588186
Subproject commit 79e080cfd34fb47342f67f19b95ffa27efb0f715

View File

@ -49,11 +49,10 @@ namespace blt::gp
}
template<typename Func>
inline tree_t create_tree(Func&& perChild, const generator_arguments& args)
inline void create_tree(tree_t& tree, Func&& perChild, const generator_arguments& args)
{
std::stack<stack> tree_generator = get_initial_stack(args.program, args.root_type);
blt::size_t max_depth = 0;
tree_t tree{args.program};
while (!tree_generator.empty())
{
@ -65,10 +64,10 @@ namespace blt::gp
tree.get_operations().emplace_back(
args.program.get_typesystem().get_type(info.return_type).size(),
top.id,
args.program.is_static(top.id));
args.program.is_operator_ephemeral(top.id));
max_depth = std::max(max_depth, top.depth);
if (args.program.is_static(top.id))
if (args.program.is_operator_ephemeral(top.id))
{
info.func(nullptr, tree.get_values(), tree.get_values());
continue;
@ -77,13 +76,11 @@ namespace blt::gp
for (const auto& child : info.argument_types)
std::forward<Func>(perChild)(args.program, tree_generator, child, top.depth + 1);
}
return tree;
}
tree_t grow_generator_t::generate(const generator_arguments& args)
void grow_generator_t::generate(tree_t& tree, const generator_arguments& args)
{
return create_tree([args](gp_program& program, std::stack<stack>& tree_generator, type_id type, blt::size_t new_depth) {
return create_tree(tree, [args](gp_program& program, std::stack<stack>& tree_generator, type_id type, blt::size_t new_depth) {
if (new_depth >= args.max_depth)
{
if (program.get_type_terminals(type).empty())
@ -99,9 +96,9 @@ namespace blt::gp
}, args);
}
tree_t full_generator_t::generate(const generator_arguments& args)
void full_generator_t::generate(tree_t& tree, const generator_arguments& args)
{
return create_tree([args](gp_program& program, std::stack<stack>& tree_generator, type_id type, blt::size_t new_depth) {
return create_tree(tree, [args](gp_program& program, std::stack<stack>& tree_generator, type_id type, blt::size_t new_depth) {
if (new_depth >= args.max_depth)
{
if (program.get_type_terminals(type).empty())
@ -119,7 +116,11 @@ namespace blt::gp
population_t pop;
for (auto i = 0ul; i < args.size; i++)
pop.get_individuals().emplace_back(grow.generate(args.to_gen_args()));
{
tree_t tree{args.program};
grow.generate(tree, args.to_gen_args());
pop.get_individuals().emplace_back(tree);
}
return pop;
}
@ -129,7 +130,11 @@ namespace blt::gp
population_t pop;
for (auto i = 0ul; i < args.size; i++)
pop.get_individuals().emplace_back(full.generate(args.to_gen_args()));
{
tree_t tree{args.program};
full.generate(tree, args.to_gen_args());
pop.get_individuals().emplace_back(tree);
}
return pop;
}
@ -140,10 +145,12 @@ namespace blt::gp
for (auto i = 0ul; i < args.size; i++)
{
tree_t tree{args.program};
if (args.program.get_random().choice())
pop.get_individuals().emplace_back(full.generate(args.to_gen_args()));
full.generate(tree, args.to_gen_args());
else
pop.get_individuals().emplace_back(grow.generate(args.to_gen_args()));
grow.generate(tree, args.to_gen_args());
pop.get_individuals().emplace_back(tree);
}
return pop;
@ -160,22 +167,26 @@ namespace blt::gp
{
for (auto i = 0ul; i < per_step; i++)
{
tree_t tree{args.program};
if (args.program.get_random().choice())
pop.get_individuals().emplace_back(full.generate({args.program, args.root_type, args.min_depth, depth}));
full.generate(tree, {args.program, args.root_type, args.min_depth, depth});
else
pop.get_individuals().emplace_back(grow.generate({args.program, args.root_type, args.min_depth, depth}));
grow.generate(tree, {args.program, args.root_type, args.min_depth, depth});
pop.get_individuals().emplace_back(tree);
}
}
for (auto i = 0ul; i < remainder; i++)
{
tree_t tree{args.program};
if (args.program.get_random().choice())
pop.get_individuals().emplace_back(full.generate(args.to_gen_args()));
full.generate(tree, args.to_gen_args());
else
pop.get_individuals().emplace_back(grow.generate(args.to_gen_args()));
grow.generate(tree, args.to_gen_args());
pop.get_individuals().emplace_back(tree);
}
blt_assert(pop.get_individuals().size() == args.size);
BLT_ASSERT(pop.get_individuals().size() == args.size);
return pop;
}

View File

@ -46,7 +46,7 @@ namespace blt::gp
random_t& gp_program::get_random() const
{
thread_local static blt::gp::random_t random_engine{seed};
thread_local static blt::gp::random_t random_engine{seed_func()};
return random_engine;
}
@ -76,7 +76,7 @@ namespace blt::gp
if (should_thread_terminate())
return;
}
execution_function = thread_execution_service.load(std::memory_order_acquire);
execution_function = thread_execution_service.get();
}
if (execution_function != nullptr)
(*execution_function)(i);

View File

@ -21,11 +21,11 @@
namespace blt::gp
{
tree_t& select_best_t::select(gp_program&, population_t& pop, population_stats&)
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;
tree_t* tree = &first.tree;
const tree_t* tree = &first.tree;
for (auto& ind : pop.get_individuals())
{
if (ind.fitness.adjusted_fitness > best_fitness)
@ -37,11 +37,11 @@ namespace blt::gp
return *tree;
}
tree_t& select_worst_t::select(gp_program&, population_t& pop, population_stats&)
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;
tree_t* tree = &first.tree;
const tree_t* tree = &first.tree;
for (auto& ind : pop.get_individuals())
{
if (ind.fitness.adjusted_fitness < worst_fitness)
@ -53,32 +53,27 @@ namespace blt::gp
return *tree;
}
tree_t& select_random_t::select(gp_program& program, population_t& pop, population_stats&)
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;
}
tree_t& select_tournament_t::select(gp_program& program, population_t& pop, population_stats&)
const tree_t& select_tournament_t::select(gp_program& program, const population_t& pop)
{
auto& first = pop.get_individuals()[program.get_random().get_size_t(0ul, pop.get_individuals().size())];
individual* ind = &first;
double best_guy = first.fitness.adjusted_fitness;
for (blt::size_t i = 0; i < selection_size - 1; i++)
blt::u64 best = program.get_random().get_u64(0, pop.get_individuals().size());
auto& i_ref = pop.get_individuals();
for (blt::size_t i = 0; i < selection_size; i++)
{
auto& sel = pop.get_individuals()[program.get_random().get_size_t(0ul, pop.get_individuals().size())];
if (sel.fitness.adjusted_fitness > best_guy)
{
best_guy = sel.fitness.adjusted_fitness;
ind = &sel;
auto sel_point = program.get_random().get_u64(0ul, pop.get_individuals().size());
if (i_ref[sel_point].fitness.adjusted_fitness > i_ref[best].fitness.adjusted_fitness)
best = sel_point;
}
return i_ref[best].tree;
}
return ind->tree;
}
tree_t& select_fitness_proportionate_t::select(gp_program& program, population_t& pop, population_stats& stats)
const tree_t& select_fitness_proportionate_t::select(gp_program& program, const population_t& pop)
{
auto& stats = program.get_population_stats();
auto choice = program.get_random().get_double();
for (const auto& [index, ref] : blt::enumerate(pop))
{
@ -92,20 +87,8 @@ namespace blt::gp
return ref.tree;
}
}
BLT_WARN("Unable to find individual with fitness proportionate. This should not be a possible code path! (%lf)", choice);
BLT_WARN("Unable to find individual_t with fitness proportionate. This should not be a possible code path! (%lf)", choice);
return pop.get_individuals()[0].tree;
//BLT_ABORT("Unable to find individual");
}
void select_fitness_proportionate_t::pre_process(gp_program&, population_t& pop, population_stats& stats)
{
stats.normalized_fitness.clear();
double sum_of_prob = 0;
for (auto& ind : pop)
{
auto prob = (ind.fitness.adjusted_fitness / stats.overall_fitness);
stats.normalized_fitness.push_back(sum_of_prob + prob);
sum_of_prob += prob;
}
}
}

24
src/stats.cpp Normal file
View File

@ -0,0 +1,24 @@
/*
* <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/stats.h>
#include <blt/std/logging.h>
namespace blt::gp
{
}

View File

@ -27,7 +27,16 @@
namespace blt::gp
{
inline blt::size_t accumulate_type_sizes(detail::op_iter begin, detail::op_iter end)
grow_generator_t grow_generator;
inline tree_t& get_static_tree_tl(gp_program& program)
{
static thread_local tree_t new_tree{program};
new_tree.clear(program);
return new_tree;
}
inline blt::size_t accumulate_type_sizes(detail::op_iter_t begin, detail::op_iter_t end)
{
blt::size_t total = 0;
for (auto it = begin; it != end; ++it)
@ -47,28 +56,24 @@ namespace blt::gp
return buffer.data();
}
grow_generator_t grow_generator;
mutation_t::config_t::config_t(): generator(grow_generator)
{}
blt::expected<crossover_t::result_t, crossover_t::error_t> crossover_t::apply(gp_program& program, const tree_t& p1, const tree_t& p2) // NOLINT
bool crossover_t::apply(gp_program& program, const tree_t& p1, const tree_t& p2, tree_t& c1, tree_t& c2) // NOLINT
{
result_t result{p1, p2};
auto& c1 = result.child1;
auto& c2 = result.child2;
c1.copy_fast(p1);
c2.copy_fast(p2);
auto& c1_ops = c1.get_operations();
auto& c2_ops = c2.get_operations();
if (c1_ops.size() < 5 || c2_ops.size() < 5)
return blt::unexpected(error_t::TREE_TOO_SMALL);
return false;
auto point = get_crossover_point(program, c1, c2);
auto point = get_crossover_point(program, p1, p2);
if (!point)
return blt::unexpected(point.error());
return false;
auto crossover_point_begin_itr = c1_ops.begin() + point->p1_crossover_point;
auto crossover_point_end_itr = c1_ops.begin() + c1.find_endpoint(program, point->p1_crossover_point);
@ -97,8 +102,8 @@ namespace blt::gp
blt::size_t c2_stack_for_bytes = accumulate_type_sizes(found_point_begin_itr, found_point_end_itr);
auto c1_total = static_cast<blt::ptrdiff_t>(c1_stack_after_bytes + c1_stack_for_bytes);
auto c2_total = static_cast<blt::ptrdiff_t>(c2_stack_after_bytes + c2_stack_for_bytes);
auto copy_ptr_c1 = get_thread_pointer_for_size<struct c1>(c1_total);
auto copy_ptr_c2 = get_thread_pointer_for_size<struct c2>(c2_total);
auto copy_ptr_c1 = get_thread_pointer_for_size<struct c1_t>(c1_total);
auto copy_ptr_c2 = get_thread_pointer_for_size<struct c2_t>(c2_total);
c1_stack.copy_to(copy_ptr_c1, c1_total);
c1_stack.pop_bytes(c1_total);
@ -124,8 +129,8 @@ namespace blt::gp
c2_ops.insert(++insert_point_c2, c1_operators.begin(), c1_operators.end());
#if BLT_DEBUG_LEVEL >= 2
blt::size_t c1_found_bytes = result.child1.get_values().size().total_used_bytes;
blt::size_t c2_found_bytes = result.child2.get_values().size().total_used_bytes;
blt::size_t c1_found_bytes = c1.get_values().size().total_used_bytes;
blt::size_t c2_found_bytes = c2.get_values().size().total_used_bytes;
blt::size_t c1_expected_bytes = std::accumulate(result.child1.get_operations().begin(), result.child1.get_operations().end(), 0ul,
[](const auto& v1, const auto& v2) {
if (v2.is_value)
@ -146,10 +151,10 @@ namespace blt::gp
}
#endif
return result;
return true;
}
blt::expected<crossover_t::crossover_point_t, crossover_t::error_t> crossover_t::get_crossover_point(gp_program& program, const tree_t& c1,
std::optional<crossover_t::crossover_point_t> crossover_t::get_crossover_point(gp_program& program, const tree_t& c1,
const tree_t& c2) const
{
auto& c1_ops = c1.get_operations();
@ -187,10 +192,10 @@ namespace blt::gp
}
}
if (!found)
return blt::unexpected(error_t::NO_VALID_TYPE);
return {};
}
// should we try again over the whole tree? probably not.
return blt::unexpected(error_t::NO_VALID_TYPE);
return {};
} else
{
attempted_point = program.get_random().get_size_t(1ul, c2_ops.size());
@ -206,13 +211,13 @@ namespace blt::gp
return crossover_point_t{static_cast<blt::ptrdiff_t>(crossover_point), static_cast<blt::ptrdiff_t>(attempted_point)};
}
tree_t mutation_t::apply(gp_program& program, const tree_t& p)
bool mutation_t::apply(gp_program& program, const tree_t& p, tree_t& c)
{
auto c = p;
c.copy_fast(p);
mutate_point(program, c, program.get_random().get_size_t(0ul, c.get_operations().size()));
return c;
return true;
}
blt::size_t mutation_t::mutate_point(gp_program& program, tree_t& c, blt::size_t node)
@ -228,7 +233,8 @@ namespace blt::gp
auto begin_itr = ops_r.begin() + begin_point;
auto end_itr = ops_r.begin() + end_point;
auto new_tree = config.generator.get().generate({program, type_info.return_type, config.replacement_min_depth, config.replacement_max_depth});
auto& new_tree = get_static_tree_tl(program);
config.generator.get().generate(new_tree, {program, type_info.return_type, config.replacement_min_depth, config.replacement_max_depth});
auto& new_ops_r = new_tree.get_operations();
auto& new_vals_r = new_tree.get_values();
@ -296,10 +302,10 @@ namespace blt::gp
return begin_point + new_ops_r.size();
}
tree_t advanced_mutation_t::apply(gp_program& program, const tree_t& p)
bool advanced_mutation_t::apply(gp_program& program, const tree_t& p, tree_t& c)
{
// child tree
tree_t c = p;
c.copy_fast(p);
auto& ops = c.get_operations();
auto& vals = c.get_values();
@ -364,7 +370,8 @@ namespace blt::gp
if (index < current_func_info.argument_types.size() && val.id != current_func_info.argument_types[index].id)
{
// TODO: new config?
auto tree = config.generator.get().generate(
auto& tree = get_static_tree_tl(program);
config.generator.get().generate(tree,
{program, val.id, config.replacement_min_depth, config.replacement_max_depth});
auto& child = children_data[children_data.size() - 1 - index];
@ -445,7 +452,8 @@ namespace blt::gp
for (blt::ptrdiff_t i = static_cast<blt::ptrdiff_t>(replacement_func_info.argc.argc) - 1;
i >= current_func_info.argc.argc; i--)
{
auto tree = config.generator.get().generate(
auto& tree = get_static_tree_tl(program);
config.generator.get().generate(tree,
{program, replacement_func_info.argument_types[i].id, config.replacement_min_depth,
config.replacement_max_depth});
blt::size_t total_bytes_for = tree.total_value_bytes();
@ -458,7 +466,7 @@ namespace blt::gp
}
// now finally update the type.
ops[c_node] = {program.get_typesystem().get_type(replacement_func_info.return_type).size(), random_replacement,
program.is_static(random_replacement)};
program.is_operator_ephemeral(random_replacement)};
}
#if BLT_DEBUG_LEVEL >= 2
if (!c.check(program, nullptr))
@ -518,7 +526,8 @@ namespace blt::gp
blt::size_t start_index = c_node;
for (blt::ptrdiff_t i = new_argc - 1; i > static_cast<blt::ptrdiff_t>(arg_position); i--)
{
auto tree = config.generator.get().generate(
auto& tree = get_static_tree_tl(program);
config.generator.get().generate(tree,
{program, replacement_func_info.argument_types[i].id, config.replacement_min_depth,
config.replacement_max_depth});
blt::size_t total_bytes_for = tree.total_value_bytes();
@ -531,7 +540,8 @@ namespace blt::gp
vals.copy_from(combined_ptr, for_bytes);
for (blt::ptrdiff_t i = static_cast<blt::ptrdiff_t>(arg_position) - 1; i >= 0; i--)
{
auto tree = config.generator.get().generate(
auto& tree = get_static_tree_tl(program);
config.generator.get().generate(tree,
{program, replacement_func_info.argument_types[i].id, config.replacement_min_depth,
config.replacement_max_depth});
blt::size_t total_bytes_for = tree.total_value_bytes();
@ -544,7 +554,7 @@ namespace blt::gp
ops.insert(ops.begin() + static_cast<blt::ptrdiff_t>(c_node),
{program.get_typesystem().get_type(replacement_func_info.return_type).size(),
random_replacement, program.is_static(random_replacement)});
random_replacement, program.is_operator_ephemeral(random_replacement)});
#if BLT_DEBUG_LEVEL >= 2
if (!c.check(program, nullptr))
@ -720,6 +730,6 @@ namespace blt::gp
}
#endif
return c;
return true;
}
}

View File

@ -92,7 +92,7 @@ namespace blt::gp
if (print_literals)
{
create_indent(out, indent, pretty_print);
if (program.is_static(v.id))
if (program.is_operator_ephemeral(v.id))
{
program.get_print_func(v.id)(out, reversed);
reversed.pop_bytes(stack_allocator::aligned_size(v.type_size));
@ -247,22 +247,20 @@ namespace blt::gp
blt::size_t total_produced = 0;
blt::size_t total_consumed = 0;
// for (const auto& operation : blt::reverse_iterate(operations.begin(), operations.end()))
// {
// if (operation.is_value)
// {
// value_stack.transfer_bytes(values_process, operation.type_size);
// total_produced += stack_allocator::aligned_size(operation.type_size);
// bitfield.push_back(false);
// continue;
// }
// auto& info = program.get_operator_info(operation.id);
// for (auto& arg : info.argument_types)
// total_consumed += stack_allocator::aligned_size(program.get_typesystem().get_type(arg).size());
// operation.func(context, values_process, values_process, &bitfield);
// bitfield.push_back(true);
// total_produced += stack_allocator::aligned_size(program.get_typesystem().get_type(info.return_type).size());
// }
for (const auto& operation : blt::reverse_iterate(operations.begin(), operations.end()))
{
if (operation.is_value)
{
value_stack.transfer_bytes(values_process, operation.type_size);
total_produced += stack_allocator::aligned_size(operation.type_size);
continue;
}
auto& info = program.get_operator_info(operation.id);
for (auto& arg : info.argument_types)
total_consumed += stack_allocator::aligned_size(program.get_typesystem().get_type(arg).size());
program.get_operator_info(operation.id).func(context, values_process, values_process);
total_produced += stack_allocator::aligned_size(program.get_typesystem().get_type(info.return_type).size());
}
auto v1 = results.values.bytes_in_head();
auto v2 = static_cast<blt::ptrdiff_t>(stack_allocator::aligned_size(operations.front().type_size));
@ -301,4 +299,13 @@ namespace blt::gp
{
}
void tree_t::clear(gp_program& program)
{
auto* f = &program.get_eval_func();
if (f != func)
func = f;
operations.clear();
values.reset();
}
}

View File

@ -107,7 +107,7 @@ struct context
float x, y;
};
std::array<context, 200> fitness_cases;
std::array<context, 200> training_cases;
blt::gp::prog_config_t config = blt::gp::prog_config_t()
.set_initial_min_tree_size(2)
@ -141,7 +141,7 @@ blt::gp::operation_t op_x([](const context& context) {
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 : fitness_cases)
for (auto& fitness_case : training_cases)
{
auto ctx = current_tree.evaluate(&fitness_case);
auto diff = std::abs(fitness_case.y - *current_tree.get_evaluation_ref<move_float>(ctx));
@ -157,7 +157,7 @@ constexpr auto fitness_function = [](blt::gp::tree_t& current_tree, blt::gp::fit
}
fitness.standardized_fitness = fitness.raw_fitness;
fitness.adjusted_fitness = (1.0 / (1.0 + fitness.standardized_fitness));
return static_cast<blt::size_t>(fitness.hits) == fitness_cases.size();
return static_cast<blt::size_t>(fitness.hits) == training_cases.size();
};
float example_function(float x)
@ -170,7 +170,7 @@ 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 : fitness_cases)
for (auto& fitness_case : training_cases)
{
constexpr float range = 10;
constexpr float half_range = range / 2.0;