Begin working on adding drop function, add tests for many symbolic regression configs + performance
parent
a497a006ee
commit
ca0f10b410
|
@ -27,7 +27,7 @@ macro(compile_options target_name)
|
|||
sanitizers(${target_name})
|
||||
endmacro()
|
||||
|
||||
project(blt-gp VERSION 0.2.10)
|
||||
project(blt-gp VERSION 0.3.0)
|
||||
|
||||
include(CTest)
|
||||
|
||||
|
@ -87,9 +87,9 @@ macro(blt_add_project name source type)
|
|||
target_compile_definitions(${name}-${type} PRIVATE BLT_TRACK_ALLOCATIONS=1)
|
||||
endif ()
|
||||
|
||||
add_test(NAME ${name} COMMAND ${name}-${type})
|
||||
add_test(NAME ${name}-${type} COMMAND ${name}-${type})
|
||||
|
||||
set_property(TEST ${name} PROPERTY FAIL_REGULAR_EXPRESSION "FAIL;ERROR;FATAL;exception")
|
||||
set_property(TEST ${name}-${type} PROPERTY FAIL_REGULAR_EXPRESSION "FAIL;ERROR;FATAL;exception")
|
||||
|
||||
project(blt-gp)
|
||||
endmacro()
|
||||
|
@ -115,4 +115,6 @@ if (${BUILD_GP_TESTS})
|
|||
blt_add_project(blt-gp6 tests/old/gp_test_6.cpp test)
|
||||
blt_add_project(blt-gp7 tests/old/gp_test_7.cpp test)
|
||||
|
||||
blt_add_project(blt-symbolic-regression tests/symbolic_regression_test.cpp test)
|
||||
|
||||
endif ()
|
|
@ -124,6 +124,7 @@ bool blt::gp::example::rice_classification_t::fitness_function(const tree_t& cur
|
|||
{
|
||||
for (auto& training_case : training_cases)
|
||||
{
|
||||
BLT_GP_UPDATE_CONTEXT(training_case);
|
||||
const auto v = current_tree.get_evaluation_value<float>(training_case);
|
||||
switch (training_case.type)
|
||||
{
|
||||
|
|
|
@ -62,48 +62,3 @@ int main()
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool blt::gp::example::symbolic_regression_t::fitness_function(const tree_t& current_tree, fitness_t& fitness, size_t) const
|
||||
{
|
||||
constexpr static double value_cutoff = 1.e15;
|
||||
for (auto& fitness_case : training_cases)
|
||||
{
|
||||
const auto diff = std::abs(fitness_case.y - current_tree.get_evaluation_value<float>(fitness_case));
|
||||
if (diff < value_cutoff)
|
||||
{
|
||||
fitness.raw_fitness += diff;
|
||||
if (diff <= 0.01)
|
||||
fitness.hits++;
|
||||
}
|
||||
else
|
||||
fitness.raw_fitness += value_cutoff;
|
||||
}
|
||||
fitness.standardized_fitness = fitness.raw_fitness;
|
||||
fitness.adjusted_fitness = (1.0 / (1.0 + fitness.standardized_fitness));
|
||||
return static_cast<size_t>(fitness.hits) == training_cases.size();
|
||||
}
|
||||
|
||||
void blt::gp::example::symbolic_regression_t::setup_operations()
|
||||
{
|
||||
BLT_DEBUG("Setup Types and Operators");
|
||||
static operation_t add{[](const float a, const float b) { return a + b; }, "add"};
|
||||
static operation_t sub([](const float a, const float b) { return a - b; }, "sub");
|
||||
static operation_t mul([](const float a, const float b) { return a * b; }, "mul");
|
||||
static operation_t pro_div([](const float a, const float b) { return b == 0.0f ? 0.0f : a / b; }, "div");
|
||||
static operation_t op_sin([](const float a) { return std::sin(a); }, "sin");
|
||||
static operation_t op_cos([](const float a) { return std::cos(a); }, "cos");
|
||||
static operation_t op_exp([](const float a) { return std::exp(a); }, "exp");
|
||||
static operation_t op_log([](const float a) { return a == 0.0f ? 0.0f : std::log(a); }, "log");
|
||||
static auto lit = operation_t([this]()
|
||||
{
|
||||
return program.get_random().get_float(-1.0f, 1.0f);
|
||||
}, "lit").set_ephemeral();
|
||||
|
||||
static operation_t op_x([](const context& context)
|
||||
{
|
||||
return context.x;
|
||||
}, "x");
|
||||
|
||||
operator_builder<context> builder{};
|
||||
builder.build(add, sub, mul, pro_div, op_sin, op_cos, op_exp, op_log, lit, op_x);
|
||||
program.set_operations(builder.grab());
|
||||
}
|
||||
|
|
|
@ -35,7 +35,25 @@ namespace blt::gp::example
|
|||
};
|
||||
|
||||
private:
|
||||
bool fitness_function(const tree_t& current_tree, fitness_t& fitness, size_t) const;
|
||||
bool fitness_function(const tree_t& current_tree, fitness_t& fitness, size_t) const
|
||||
{
|
||||
constexpr static double value_cutoff = 1.e15;
|
||||
for (auto& fitness_case : training_cases)
|
||||
{
|
||||
const auto diff = std::abs(fitness_case.y - current_tree.get_evaluation_value<float>(fitness_case));
|
||||
if (diff < value_cutoff)
|
||||
{
|
||||
fitness.raw_fitness += diff;
|
||||
if (diff <= 0.01)
|
||||
fitness.hits++;
|
||||
}
|
||||
else
|
||||
fitness.raw_fitness += value_cutoff;
|
||||
}
|
||||
fitness.standardized_fitness = fitness.raw_fitness;
|
||||
fitness.adjusted_fitness = (1.0 / (1.0 + fitness.standardized_fitness));
|
||||
return static_cast<size_t>(fitness.hits) == training_cases.size();
|
||||
}
|
||||
|
||||
static float example_function(const float x)
|
||||
{
|
||||
|
@ -63,7 +81,31 @@ namespace blt::gp::example
|
|||
};
|
||||
}
|
||||
|
||||
void setup_operations();
|
||||
void setup_operations()
|
||||
{
|
||||
BLT_DEBUG("Setup Types and Operators");
|
||||
static operation_t add{[](const float a, const float b) { return a + b; }, "add"};
|
||||
static operation_t sub([](const float a, const float b) { return a - b; }, "sub");
|
||||
static operation_t mul([](const float a, const float b) { return a * b; }, "mul");
|
||||
static operation_t pro_div([](const float a, const float b) { return b == 0.0f ? 0.0f : a / b; }, "div");
|
||||
static operation_t op_sin([](const float a) { return std::sin(a); }, "sin");
|
||||
static operation_t op_cos([](const float a) { return std::cos(a); }, "cos");
|
||||
static operation_t op_exp([](const float a) { return std::exp(a); }, "exp");
|
||||
static operation_t op_log([](const float a) { return a == 0.0f ? 0.0f : std::log(a); }, "log");
|
||||
static auto lit = operation_t([this]()
|
||||
{
|
||||
return program.get_random().get_float(-1.0f, 1.0f);
|
||||
}, "lit").set_ephemeral();
|
||||
|
||||
static operation_t op_x([](const context& context)
|
||||
{
|
||||
return context.x;
|
||||
}, "x");
|
||||
|
||||
operator_builder<context> builder{};
|
||||
builder.build(add, sub, mul, pro_div, op_sin, op_cos, op_exp, op_log, lit, op_x);
|
||||
program.set_operations(builder.grab());
|
||||
}
|
||||
|
||||
void generate_initial_population()
|
||||
{
|
||||
|
|
|
@ -20,12 +20,29 @@
|
|||
#define BLT_GP_ALLOCATOR_H
|
||||
|
||||
#include <blt/std/types.h>
|
||||
#include <blt/std/logging.h>
|
||||
#include <blt/gp/util/trackers.h>
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
|
||||
namespace blt::gp
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
static constexpr inline size_t MAX_ALIGNMENT = 8;
|
||||
|
||||
#if BLT_DEBUG_LEVEL > 0
|
||||
static void check_alignment(const size_t bytes, const std::string& message = "Invalid alignment")
|
||||
{
|
||||
if (bytes % MAX_ALIGNMENT != 0)
|
||||
{
|
||||
BLT_ABORT((message + ", expected multiple of " + std::to_string(detail::MAX_ALIGNMENT) + " got "
|
||||
+ std::to_string(bytes)).c_str());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef BLT_TRACK_ALLOCATIONS
|
||||
inline allocation_tracker_t tracker;
|
||||
|
||||
|
@ -47,11 +64,14 @@ namespace blt::gp
|
|||
public:
|
||||
void* allocate(blt::size_t bytes) // NOLINT
|
||||
{
|
||||
#ifdef BLT_TRACK_ALLOCATIONS
|
||||
#ifdef BLT_TRACK_ALLOCATIONSS
|
||||
tracker.allocate(bytes);
|
||||
// std::cout << "Hey our aligned allocator allocated " << bytes << " bytes!\n";
|
||||
#endif
|
||||
return std::aligned_alloc(8, bytes);
|
||||
#if (BLT_DEBUG_LEVEL > 0)
|
||||
detail::check_alignment(bytes);
|
||||
#endif
|
||||
return std::aligned_alloc(detail::MAX_ALIGNMENT, bytes);
|
||||
}
|
||||
|
||||
void deallocate(void* ptr, blt::size_t bytes) // NOLINT
|
||||
|
@ -62,7 +82,7 @@ namespace blt::gp
|
|||
tracker.deallocate(bytes);
|
||||
// std::cout << "[Hey our aligned allocator deallocated " << bytes << " bytes!]\n";
|
||||
#else
|
||||
(void) bytes;
|
||||
(void)bytes;
|
||||
#endif
|
||||
std::free(ptr);
|
||||
}
|
||||
|
@ -108,7 +128,7 @@ namespace blt::gp
|
|||
::blt::gp::tracker.deallocate(n * sizeof(T));
|
||||
// std::cout << "[Hey our tracked allocator deallocated " << (n * sizeof(T)) << " bytes!]\n";
|
||||
#else
|
||||
(void) n;
|
||||
(void)n;
|
||||
#endif
|
||||
std::free(p);
|
||||
}
|
||||
|
@ -147,7 +167,7 @@ namespace blt::gp
|
|||
template<typename T>
|
||||
using tracked_vector = std::vector<T, tracked_allocator_t<T>>;
|
||||
#else
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
using tracked_vector = std::vector<T>;
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -85,6 +85,18 @@ namespace blt::gp
|
|||
using const_op_iter_t = tracked_vector<op_container_t>::const_iterator;
|
||||
using op_iter_t = tracked_vector<op_container_t>::iterator;
|
||||
}
|
||||
|
||||
#if BLT_DEBUG_LEVEL > 0
|
||||
|
||||
namespace detail::debug
|
||||
{
|
||||
inline void* context_ptr;
|
||||
}
|
||||
|
||||
#define BLT_GP_UPDATE_CONTEXT(context) blt::gp::detail::debug::context_ptr = const_cast<void*>(static_cast<const void*>(&context))
|
||||
#else
|
||||
#define BLT_GP_UPDATE_CONTEXT(context)
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -143,13 +143,13 @@ namespace blt::gp
|
|||
for (const auto& operation : iterate(ops).rev())
|
||||
{
|
||||
op_pos++;
|
||||
if (operation.is_value)
|
||||
if (operation.is_value())
|
||||
{
|
||||
total_so_far += stack_allocator::aligned_size(operation.type_size);
|
||||
results.values.copy_from(vals.from(total_so_far), stack_allocator::aligned_size(operation.type_size));
|
||||
total_so_far += operation.type_size();
|
||||
results.values.copy_from(vals.from(total_so_far), operation.type_size());
|
||||
continue;
|
||||
}
|
||||
call_jmp_table(operation.id, context, results.values, results.values, operators...);
|
||||
call_jmp_table(operation.id(), context, results.values, results.values, operators...);
|
||||
}
|
||||
|
||||
return results;
|
||||
|
@ -258,9 +258,9 @@ namespace blt::gp
|
|||
operator_metadata_t meta;
|
||||
if constexpr (sizeof...(Args) != 0)
|
||||
{
|
||||
meta.arg_size_bytes = (stack_allocator::aligned_size(sizeof(Args)) + ...);
|
||||
meta.arg_size_bytes = (stack_allocator::aligned_size<Args>() + ...);
|
||||
}
|
||||
meta.return_size_bytes = sizeof(Return);
|
||||
meta.return_size_bytes = stack_allocator::aligned_size<Return>();
|
||||
meta.argc = info.argc;
|
||||
|
||||
storage.operator_metadata.push_back(meta);
|
||||
|
|
|
@ -43,6 +43,10 @@ namespace blt::gp
|
|||
{
|
||||
auto& [program, current_pop, current_stats, config, random] = args;
|
||||
|
||||
BLT_ASSERT_MSG(config.elites <= current_pop.get_individuals().size(), ("Not enough individuals in population (" +
|
||||
std::to_string(current_pop.get_individuals().size()) +
|
||||
") for requested amount of elites (" + std::to_string(config.elites) + ")").c_str());
|
||||
|
||||
if (config.elites > 0 && current_pop.get_individuals().size() >= config.elites)
|
||||
{
|
||||
static thread_local tracked_vector<std::pair<std::size_t, double>> values;
|
||||
|
@ -132,13 +136,13 @@ namespace blt::gp
|
|||
|
||||
// auto old_parent_val = parent_fitness.load(std::memory_order_relaxed);
|
||||
// while (!parent_fitness.compare_exchange_weak(old_parent_val, old_parent_val + parent_val, std::memory_order_relaxed,
|
||||
// std::memory_order_relaxed))
|
||||
// std::memory_order_relaxed))
|
||||
// {
|
||||
// }
|
||||
|
||||
// auto old_child_val = child_fitness.load(std::memory_order_relaxed);
|
||||
// while (!child_fitness.compare_exchange_weak(old_child_val, old_child_val + child_val, std::memory_order_relaxed,
|
||||
// std::memory_order_relaxed))
|
||||
// std::memory_order_relaxed))
|
||||
// {
|
||||
// }
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <blt/meta/meta.h>
|
||||
#include <blt/gp/fwdecl.h>
|
||||
#include <blt/gp/util/trackers.h>
|
||||
#include <blt/gp/allocator.h>
|
||||
#include <utility>
|
||||
#include <stdexcept>
|
||||
#include <cstdlib>
|
||||
|
@ -46,11 +47,19 @@ namespace blt::gp
|
|||
class stack_allocator
|
||||
{
|
||||
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>>;
|
||||
using Allocator = aligned_allocator;
|
||||
|
||||
// todo remove this once i fix all the broken references
|
||||
struct detail
|
||||
{
|
||||
static constexpr size_t aligned_size(const size_t size) noexcept
|
||||
{
|
||||
return (size + (gp::detail::MAX_ALIGNMENT - 1)) & ~(gp::detail::MAX_ALIGNMENT - 1);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
static Allocator& get_allocator();
|
||||
|
||||
|
@ -74,19 +83,14 @@ namespace blt::gp
|
|||
};
|
||||
|
||||
template <typename T>
|
||||
static inline constexpr size_t aligned_size() noexcept
|
||||
static constexpr size_t aligned_size() noexcept
|
||||
{
|
||||
const auto bytes = aligned_size(sizeof(NO_REF_T<T>));
|
||||
const auto bytes = detail::aligned_size(sizeof(NO_REF_T<T>));
|
||||
if constexpr (blt::gp::detail::has_func_drop_v<T>)
|
||||
return bytes + sizeof(size_t*);
|
||||
return bytes + aligned_size<size_t*>();
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static inline constexpr blt::size_t aligned_size(blt::size_t size) noexcept
|
||||
{
|
||||
return (size + (MAX_ALIGNMENT - 1)) & ~(MAX_ALIGNMENT - 1);
|
||||
}
|
||||
|
||||
stack_allocator() = default;
|
||||
|
||||
stack_allocator(const stack_allocator& copy)
|
||||
|
@ -138,7 +142,7 @@ namespace blt::gp
|
|||
bytes_stored += bytes;
|
||||
}
|
||||
|
||||
void copy_from(blt::u8* data, blt::size_t bytes)
|
||||
void copy_from(const u8* data, const size_t bytes)
|
||||
{
|
||||
if (bytes == 0 || data == nullptr)
|
||||
return;
|
||||
|
@ -148,7 +152,7 @@ namespace blt::gp
|
|||
bytes_stored += bytes;
|
||||
}
|
||||
|
||||
void copy_to(blt::u8* data, blt::size_t bytes)
|
||||
void copy_to(u8* data, const size_t bytes) const
|
||||
{
|
||||
if (bytes == 0 || data == nullptr)
|
||||
return;
|
||||
|
@ -158,64 +162,65 @@ namespace blt::gp
|
|||
template <typename T, typename NO_REF = NO_REF_T<T>>
|
||||
void push(const T& t)
|
||||
{
|
||||
static_assert(std::is_trivially_copyable_v<NO_REF> && "Type must be bitwise copyable!");
|
||||
static_assert(alignof(NO_REF) <= MAX_ALIGNMENT && "Type alignment must not be greater than the max alignment!");
|
||||
auto ptr = allocate_bytes_for_size(sizeof(NO_REF));
|
||||
static_assert(std::is_trivially_copyable_v<NO_REF>, "Type must be bitwise copyable!");
|
||||
static_assert(alignof(NO_REF) <= gp::detail::MAX_ALIGNMENT, "Type alignment must not be greater than the max alignment!");
|
||||
const auto ptr = allocate_bytes_for_size(aligned_size<NO_REF>());
|
||||
std::memcpy(ptr, &t, sizeof(NO_REF));
|
||||
}
|
||||
|
||||
template <typename T, typename NO_REF = NO_REF_T<T>>
|
||||
T pop()
|
||||
{
|
||||
static_assert(std::is_trivially_copyable_v<NO_REF> && "Type must be bitwise copyable!");
|
||||
static_assert(alignof(NO_REF) <= MAX_ALIGNMENT && "Type alignment must not be greater than the max alignment!");
|
||||
constexpr auto size = aligned_size(sizeof(NO_REF));
|
||||
static_assert(std::is_trivially_copyable_v<NO_REF>, "Type must be bitwise copyable!");
|
||||
static_assert(alignof(NO_REF) <= gp::detail::MAX_ALIGNMENT, "Type alignment must not be greater than the max alignment!");
|
||||
constexpr auto size = aligned_size<NO_REF>();
|
||||
#if BLT_DEBUG_LEVEL > 0
|
||||
if (bytes_stored < size)
|
||||
BLT_ABORT("Not enough bytes left to pop!");
|
||||
if (bytes_stored < size)
|
||||
BLT_ABORT("Not enough bytes left to pop!");
|
||||
#endif
|
||||
bytes_stored -= size;
|
||||
return *reinterpret_cast<T*>(data_ + bytes_stored);
|
||||
}
|
||||
|
||||
[[nodiscard]] blt::u8* from(blt::size_t bytes) const
|
||||
[[nodiscard]] u8* from(const size_t bytes) const
|
||||
{
|
||||
#if BLT_DEBUG_LEVEL > 0
|
||||
if (bytes_stored < bytes)
|
||||
BLT_ABORT(("Not enough bytes in stack to reference " + std::to_string(bytes) + " bytes requested but " + std::to_string(bytes) +
|
||||
" bytes stored!").c_str());
|
||||
if (bytes_stored < bytes)
|
||||
BLT_ABORT(("Not enough bytes in stack to reference " + std::to_string(bytes) + " bytes requested but " + std::to_string(bytes) +
|
||||
" bytes stored!").c_str());
|
||||
#endif
|
||||
return data_ + (bytes_stored - bytes);
|
||||
}
|
||||
|
||||
template <typename T, typename NO_REF = NO_REF_T<T>>
|
||||
T& from(blt::size_t bytes)
|
||||
T& from(const size_t bytes)
|
||||
{
|
||||
static_assert(std::is_trivially_copyable_v<NO_REF> && "Type must be bitwise copyable!");
|
||||
static_assert(alignof(NO_REF) <= MAX_ALIGNMENT && "Type alignment must not be greater than the max alignment!");
|
||||
return *reinterpret_cast<NO_REF*>(from(aligned_size(sizeof(NO_REF)) + bytes));
|
||||
static_assert(alignof(NO_REF) <= gp::detail::MAX_ALIGNMENT && "Type alignment must not be greater than the max alignment!");
|
||||
return *reinterpret_cast<NO_REF*>(from(aligned_size<NO_REF>() + bytes));
|
||||
}
|
||||
|
||||
void pop_bytes(blt::size_t bytes)
|
||||
void pop_bytes(const size_t bytes)
|
||||
{
|
||||
#if BLT_DEBUG_LEVEL > 0
|
||||
if (bytes_stored < bytes)
|
||||
BLT_ABORT(("Not enough bytes in stack to pop " + std::to_string(bytes) + " bytes requested but " + std::to_string(bytes) +
|
||||
" bytes stored!").c_str());
|
||||
if (bytes_stored < bytes)
|
||||
BLT_ABORT(("Not enough bytes in stack to pop " + std::to_string(bytes) + " bytes requested but " + std::to_string(bytes) +
|
||||
" bytes stored!").c_str());
|
||||
gp::detail::check_alignment(bytes);
|
||||
#endif
|
||||
bytes_stored -= bytes;
|
||||
}
|
||||
|
||||
void transfer_bytes(stack_allocator& to, blt::size_t bytes)
|
||||
void transfer_bytes(stack_allocator& to, const size_t aligned_bytes)
|
||||
{
|
||||
#if BLT_DEBUG_LEVEL > 0
|
||||
if (bytes_stored < bytes)
|
||||
BLT_ABORT(("Not enough bytes in stack to transfer " + std::to_string(bytes) + " bytes requested but " + std::to_string(bytes) +
|
||||
" bytes stored!").c_str());
|
||||
if (bytes_stored < aligned_bytes)
|
||||
BLT_ABORT(("Not enough bytes in stack to transfer " + std::to_string(aligned_bytes) + " bytes requested but " + std::to_string(aligned_bytes) +
|
||||
" bytes stored!").c_str());
|
||||
gp::detail::check_alignment(aligned_bytes);
|
||||
#endif
|
||||
auto alg = aligned_size(bytes);
|
||||
to.copy_from(*this, alg);
|
||||
pop_bytes(alg);
|
||||
to.copy_from(*this, aligned_bytes);
|
||||
pop_bytes(aligned_bytes);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
|
@ -223,9 +228,8 @@ namespace blt::gp
|
|||
{
|
||||
if constexpr (sizeof...(Args) > 0)
|
||||
{
|
||||
blt::size_t offset = (stack_allocator::aligned_size(sizeof(NO_REF_T<Args>)) + ...) -
|
||||
stack_allocator::aligned_size(sizeof(NO_REF_T<typename blt::meta::arg_helper<Args...>::First>));
|
||||
((call_drop<Args>(offset), offset -= stack_allocator::aligned_size(sizeof(NO_REF_T<Args>))), ...);
|
||||
size_t offset = (aligned_size<NO_REF_T<Args>>() + ...) - aligned_size<NO_REF_T<typename meta::arg_helper<Args...>::First>>();
|
||||
((call_drop<Args>(offset), offset -= aligned_size<NO_REF_T<Args>>()), ...);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,14 +238,14 @@ namespace blt::gp
|
|||
return bytes_stored == 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] blt::ptrdiff_t remaining_bytes_in_block() const noexcept
|
||||
[[nodiscard]] ptrdiff_t remaining_bytes_in_block() const noexcept
|
||||
{
|
||||
return static_cast<blt::ptrdiff_t>(size_ - bytes_stored);
|
||||
return static_cast<ptrdiff_t>(size_ - bytes_stored);
|
||||
}
|
||||
|
||||
[[nodiscard]] blt::ptrdiff_t bytes_in_head() const noexcept
|
||||
[[nodiscard]] ptrdiff_t bytes_in_head() const noexcept
|
||||
{
|
||||
return static_cast<blt::ptrdiff_t>(bytes_stored);
|
||||
return static_cast<ptrdiff_t>(bytes_stored);
|
||||
}
|
||||
|
||||
[[nodiscard]] size_data_t size() const noexcept
|
||||
|
@ -255,18 +259,18 @@ namespace blt::gp
|
|||
return data;
|
||||
}
|
||||
|
||||
void reserve(blt::size_t bytes)
|
||||
void reserve(const size_t bytes)
|
||||
{
|
||||
if (bytes > size_)
|
||||
expand_raw(bytes);
|
||||
}
|
||||
|
||||
[[nodiscard]] blt::size_t stored() const
|
||||
[[nodiscard]] size_t stored() const
|
||||
{
|
||||
return bytes_stored;
|
||||
}
|
||||
|
||||
[[nodiscard]] blt::size_t internal_storage_size() const
|
||||
[[nodiscard]] size_t internal_storage_size() const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
@ -277,15 +281,16 @@ namespace blt::gp
|
|||
}
|
||||
|
||||
private:
|
||||
void expand(blt::size_t bytes)
|
||||
void expand(const size_t bytes)
|
||||
{
|
||||
//bytes = to_nearest_page_size(bytes);
|
||||
expand_raw(bytes);
|
||||
}
|
||||
|
||||
void expand_raw(blt::size_t bytes)
|
||||
void expand_raw(const size_t bytes)
|
||||
{
|
||||
auto new_data = static_cast<blt::u8*>(get_allocator().allocate(bytes));
|
||||
// auto aligned = detail::aligned_size(bytes);
|
||||
const auto new_data = static_cast<u8*>(get_allocator().allocate(bytes));
|
||||
if (bytes_stored > 0)
|
||||
std::memcpy(new_data, data_, bytes_stored);
|
||||
get_allocator().deallocate(data_, size_);
|
||||
|
@ -293,40 +298,42 @@ namespace blt::gp
|
|||
size_ = bytes;
|
||||
}
|
||||
|
||||
static size_t to_nearest_page_size(blt::size_t bytes) noexcept
|
||||
static size_t to_nearest_page_size(const size_t bytes) noexcept
|
||||
{
|
||||
constexpr static blt::size_t MASK = ~(PAGE_SIZE - 1);
|
||||
constexpr static size_t MASK = ~(PAGE_SIZE - 1);
|
||||
return (bytes & MASK) + PAGE_SIZE;
|
||||
}
|
||||
|
||||
void* get_aligned_pointer(blt::size_t bytes) noexcept
|
||||
[[nodiscard]] void* get_aligned_pointer(const size_t bytes) const noexcept
|
||||
{
|
||||
if (data_ == nullptr)
|
||||
return nullptr;
|
||||
blt::size_t remaining_bytes = remaining_bytes_in_block();
|
||||
size_t remaining_bytes = remaining_bytes_in_block();
|
||||
auto* pointer = static_cast<void*>(data_ + bytes_stored);
|
||||
return std::align(MAX_ALIGNMENT, bytes, pointer, remaining_bytes);
|
||||
return std::align(gp::detail::MAX_ALIGNMENT, bytes, pointer, remaining_bytes);
|
||||
}
|
||||
|
||||
void* allocate_bytes_for_size(blt::size_t bytes)
|
||||
void* allocate_bytes_for_size(const size_t aligned_bytes)
|
||||
{
|
||||
auto used_bytes = aligned_size(bytes);
|
||||
auto aligned_ptr = get_aligned_pointer(used_bytes);
|
||||
#if BLT_DEBUG_LEVEL > 0
|
||||
gp::detail::check_alignment(aligned_bytes);
|
||||
#endif
|
||||
auto aligned_ptr = get_aligned_pointer(aligned_bytes);
|
||||
if (aligned_ptr == nullptr)
|
||||
{
|
||||
expand(size_ + used_bytes);
|
||||
aligned_ptr = get_aligned_pointer(used_bytes);
|
||||
expand(size_ + aligned_bytes);
|
||||
aligned_ptr = get_aligned_pointer(aligned_bytes);
|
||||
}
|
||||
if (aligned_ptr == nullptr)
|
||||
throw std::bad_alloc();
|
||||
bytes_stored += used_bytes;
|
||||
bytes_stored += aligned_bytes;
|
||||
return aligned_ptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void call_drop(blt::size_t offset)
|
||||
void call_drop(const size_t offset)
|
||||
{
|
||||
if constexpr (detail::has_func_drop_v<T>)
|
||||
if constexpr (blt::gp::detail::has_func_drop_v<T>)
|
||||
{
|
||||
from<NO_REF_T<T>>(offset).drop();
|
||||
}
|
||||
|
|
|
@ -35,17 +35,37 @@ namespace blt::gp
|
|||
struct op_container_t
|
||||
{
|
||||
op_container_t(const size_t type_size, const operator_id id, const bool is_value):
|
||||
type_size(type_size), id(id), is_value(is_value), has_drop(false)
|
||||
m_type_size(type_size), m_id(id), m_is_value(is_value), m_has_drop(false)
|
||||
{}
|
||||
|
||||
op_container_t(const size_t type_size, const operator_id id, const bool is_value, const bool has_drop):
|
||||
type_size(type_size), id(id), is_value(is_value), has_drop(has_drop)
|
||||
m_type_size(type_size), m_id(id), m_is_value(is_value), m_has_drop(has_drop)
|
||||
{}
|
||||
|
||||
size_t type_size;
|
||||
operator_id id;
|
||||
bool is_value;
|
||||
bool has_drop;
|
||||
|
||||
[[nodiscard]] auto type_size() const
|
||||
{
|
||||
return m_type_size;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto id() const
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto is_value() const
|
||||
{
|
||||
return m_is_value;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool has_drop() const
|
||||
{
|
||||
return m_has_drop;
|
||||
}
|
||||
private:
|
||||
size_t m_type_size;
|
||||
operator_id m_id;
|
||||
bool m_is_value;
|
||||
bool m_has_drop;
|
||||
};
|
||||
|
||||
class evaluation_context
|
||||
|
@ -83,42 +103,37 @@ namespace blt::gp
|
|||
values.reset();
|
||||
values.insert(copy.values);
|
||||
|
||||
// operations.reserve(copy.operations.size());
|
||||
//
|
||||
// auto copy_it = copy.operations.begin();
|
||||
// auto op_it = operations.begin();
|
||||
//
|
||||
// for (; op_it != operations.end(); ++op_it)
|
||||
// {
|
||||
// if (op_it->has_drop)
|
||||
// {
|
||||
//
|
||||
// }
|
||||
// if (copy_it == copy.operations.end())
|
||||
// break;
|
||||
// *op_it = *copy_it;
|
||||
// if (copy_it->has_drop)
|
||||
// {
|
||||
//
|
||||
// }
|
||||
// ++copy_it;
|
||||
// }
|
||||
// const auto op_it_cpy = op_it;
|
||||
// for (;op_it != operations.end(); ++op_it)
|
||||
// {
|
||||
// if (op_it->has_drop)
|
||||
// {
|
||||
//
|
||||
// }
|
||||
// }
|
||||
// operations.erase(op_it_cpy, operations.end());
|
||||
// for (; copy_it != copy.operations.end(); ++copy_it)
|
||||
// operations.emplace_back(*copy_it);
|
||||
|
||||
|
||||
operations.clear();
|
||||
operations.reserve(copy.operations.size());
|
||||
operations.insert(operations.begin(), copy.operations.begin(), copy.operations.end());
|
||||
|
||||
auto copy_it = copy.operations.begin();
|
||||
auto op_it = operations.begin();
|
||||
|
||||
for (; op_it != operations.end(); ++op_it)
|
||||
{
|
||||
if (op_it->has_drop())
|
||||
{
|
||||
|
||||
}
|
||||
if (copy_it == copy.operations.end())
|
||||
break;
|
||||
*op_it = *copy_it;
|
||||
if (copy_it->has_drop())
|
||||
{
|
||||
|
||||
}
|
||||
++copy_it;
|
||||
}
|
||||
const auto op_it_cpy = op_it;
|
||||
for (;op_it != operations.end(); ++op_it)
|
||||
{
|
||||
if (op_it->has_drop())
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
operations.erase(op_it_cpy, operations.end());
|
||||
for (; copy_it != copy.operations.end(); ++copy_it)
|
||||
operations.emplace_back(*copy_it);
|
||||
}
|
||||
|
||||
tree_t(tree_t&& move) = default;
|
||||
|
@ -213,36 +228,36 @@ 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(detail::const_op_iter_t begin, detail::const_op_iter_t end)
|
||||
static size_t total_value_bytes(const detail::const_op_iter_t begin, const detail::const_op_iter_t end)
|
||||
{
|
||||
blt::size_t total = 0;
|
||||
for (auto it = begin; it != end; it++)
|
||||
size_t total = 0;
|
||||
for (auto it = begin; it != end; ++it)
|
||||
{
|
||||
if (it->is_value)
|
||||
total += stack_allocator::aligned_size(it->type_size);
|
||||
if (it->is_value())
|
||||
total += it->type_size();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
[[nodiscard]] blt::size_t total_value_bytes(blt::size_t begin, blt::size_t end) const
|
||||
[[nodiscard]] size_t total_value_bytes(const size_t begin, const size_t end) const
|
||||
{
|
||||
return total_value_bytes(operations.begin() + static_cast<blt::ptrdiff_t>(begin),
|
||||
operations.begin() + static_cast<blt::ptrdiff_t>(end));
|
||||
}
|
||||
|
||||
[[nodiscard]] blt::size_t total_value_bytes(blt::size_t begin) const
|
||||
[[nodiscard]] size_t total_value_bytes(const size_t begin) const
|
||||
{
|
||||
return total_value_bytes(operations.begin() + static_cast<blt::ptrdiff_t>(begin), operations.end());
|
||||
}
|
||||
|
||||
[[nodiscard]] blt::size_t total_value_bytes() const
|
||||
[[nodiscard]] size_t total_value_bytes() const
|
||||
{
|
||||
return total_value_bytes(operations.begin(), operations.end());
|
||||
}
|
||||
|
||||
private:
|
||||
tracked_vector<op_container_t> operations;
|
||||
blt::gp::stack_allocator values;
|
||||
stack_allocator values;
|
||||
detail::eval_func_t* func;
|
||||
};
|
||||
|
||||
|
|
|
@ -46,9 +46,9 @@ namespace blt::gp
|
|||
type() = default;
|
||||
|
||||
template<typename T>
|
||||
static type make_type(type_id id)
|
||||
static type make_type(const type_id id)
|
||||
{
|
||||
return type(sizeof(T), id, blt::type_string<T>());
|
||||
return type(stack_allocator::aligned_size<T>(), id, blt::type_string<T>());
|
||||
}
|
||||
|
||||
[[nodiscard]] inline blt::size_t size() const
|
||||
|
|
2
lib/blt
2
lib/blt
|
@ -1 +1 @@
|
|||
Subproject commit 1798980ac6829d5d79c162325a2162aa42917958
|
||||
Subproject commit 8133553ed87f705c890c5becda710f72f3ddccb9
|
|
@ -60,7 +60,7 @@ namespace blt::gp
|
|||
tree_generator.pop();
|
||||
|
||||
auto& info = args.program.get_operator_info(top.id);
|
||||
|
||||
|
||||
tree.get_operations().emplace_back(
|
||||
args.program.get_typesystem().get_type(info.return_type).size(),
|
||||
top.id,
|
||||
|
|
|
@ -40,8 +40,8 @@ namespace blt::gp
|
|||
size_t total = 0;
|
||||
for (auto it = begin; it != end; ++it)
|
||||
{
|
||||
if (it->is_value)
|
||||
total += stack_allocator::aligned_size(it->type_size);
|
||||
if (it->is_value())
|
||||
total += it->type_size();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
@ -157,14 +157,14 @@ namespace blt::gp
|
|||
blt::size_t c2_found_bytes = c2.get_values().size().total_used_bytes;
|
||||
blt::size_t c1_expected_bytes = std::accumulate(c1.get_operations().begin(), c1.get_operations().end(), 0ul,
|
||||
[](const auto& v1, const auto& v2) {
|
||||
if (v2.is_value)
|
||||
return v1 + stack_allocator::aligned_size(v2.type_size);
|
||||
if (v2.is_value())
|
||||
return v1 + v2.type_size();
|
||||
return v1;
|
||||
});
|
||||
blt::size_t c2_expected_bytes = std::accumulate(c2.get_operations().begin(), c2.get_operations().end(), 0ul,
|
||||
[](const auto& v1, const auto& v2) {
|
||||
if (v2.is_value)
|
||||
return v1 + stack_allocator::aligned_size(v2.type_size);
|
||||
if (v2.is_value())
|
||||
return v1 + v2.type_size();
|
||||
return v1;
|
||||
});
|
||||
if (c1_found_bytes != c1_expected_bytes || c2_found_bytes != c2_expected_bytes)
|
||||
|
@ -191,7 +191,7 @@ namespace blt::gp
|
|||
const bool allow_terminal_selection = program.get_random().choice(config.terminal_chance);
|
||||
|
||||
blt::size_t counter = 0;
|
||||
while (!allow_terminal_selection && program.get_operator_info(c1_ops[crossover_point].id).argc.is_terminal())
|
||||
while (!allow_terminal_selection && program.get_operator_info(c1_ops[crossover_point].id()).argc.is_terminal())
|
||||
{
|
||||
if (counter >= config.max_crossover_tries)
|
||||
return {};
|
||||
|
@ -201,13 +201,13 @@ namespace blt::gp
|
|||
|
||||
size_t attempted_point = 0;
|
||||
|
||||
const auto& crossover_point_type = program.get_operator_info(c1_ops[crossover_point].id);
|
||||
const auto& crossover_point_type = program.get_operator_info(c1_ops[crossover_point].id());
|
||||
const operator_info_t* attempted_point_type = nullptr;
|
||||
|
||||
for (counter = 0; counter < config.max_crossover_tries; counter++)
|
||||
{
|
||||
attempted_point = program.get_random().get_size_t(1ul, c2_ops.size());
|
||||
attempted_point_type = &program.get_operator_info(c2_ops[attempted_point].id);
|
||||
attempted_point_type = &program.get_operator_info(c2_ops[attempted_point].id());
|
||||
if (!allow_terminal_selection && attempted_point_type->argc.is_terminal())
|
||||
continue;
|
||||
if (crossover_point_type.return_type == attempted_point_type->return_type)
|
||||
|
@ -239,14 +239,14 @@ namespace blt::gp
|
|||
ptrdiff_t point = 0;
|
||||
while (true)
|
||||
{
|
||||
auto& current_op_type = program.get_operator_info(t.get_operations()[point].id);
|
||||
auto& current_op_type = program.get_operator_info(t.get_operations()[point].id());
|
||||
if (current_op_type.argc.is_terminal())
|
||||
{
|
||||
if (!should_select_terminal)
|
||||
{
|
||||
if (parent == -1)
|
||||
return {};
|
||||
auto& parent_type = program.get_operator_info(t.get_operations()[parent].id);
|
||||
auto& parent_type = program.get_operator_info(t.get_operations()[parent].id());
|
||||
if (type && *type != parent_type.return_type)
|
||||
return {};
|
||||
return {{parent, parent_type}};
|
||||
|
@ -299,7 +299,7 @@ namespace blt::gp
|
|||
|
||||
auto begin_point = static_cast<blt::ptrdiff_t>(node);
|
||||
auto end_point = c.find_endpoint(program, begin_point);
|
||||
auto begin_operator_id = ops_r[begin_point].id;
|
||||
auto begin_operator_id = ops_r[begin_point].id();
|
||||
const auto& type_info = program.get_operator_info(begin_operator_id);
|
||||
|
||||
auto begin_itr = ops_r.begin() + begin_point;
|
||||
|
@ -337,8 +337,8 @@ namespace blt::gp
|
|||
|
||||
for (const auto& op : c.get_operations())
|
||||
{
|
||||
if (op.is_value)
|
||||
bytes_expected += stack_allocator::aligned_size(op.type_size);
|
||||
if (op.is_value())
|
||||
bytes_expected += op.type_size();
|
||||
}
|
||||
|
||||
if (bytes_expected != bytes_size)
|
||||
|
@ -352,14 +352,14 @@ namespace blt::gp
|
|||
auto copy = c;
|
||||
try
|
||||
{
|
||||
const auto& result = copy.evaluate();
|
||||
const auto& result = copy.evaluate(*static_cast<char*>(detail::debug::context_ptr));
|
||||
blt::black_box(result);
|
||||
} catch (...)
|
||||
{
|
||||
std::cout << "This occurred at point " << begin_point << " ending at (old) " << end_point << "\n";
|
||||
std::cout << "our root type is " << ops_r[begin_point].id << " with size " << stack_allocator::aligned_size(ops_r[begin_point].type_size)
|
||||
std::cout << "our root type is " << ops_r[begin_point].id() << " with size " << ops_r[begin_point].type_size()
|
||||
<< "\n";
|
||||
std::cout << "now Named: " << (program.get_name(ops_r[begin_point].id) ? *program.get_name(ops_r[begin_point].id) : "Unnamed") << "\n";
|
||||
std::cout << "now Named: " << (program.get_name(ops_r[begin_point].id()) ? *program.get_name(ops_r[begin_point].id()) : "Unnamed") << "\n";
|
||||
std::cout << "Was named: " << (program.get_name(begin_operator_id) ? *program.get_name(begin_operator_id) : "Unnamed") << "\n";
|
||||
//std::cout << "Parent:" << std::endl;
|
||||
//p.print(program, std::cout, false, true);
|
||||
|
@ -422,9 +422,9 @@ namespace blt::gp
|
|||
{
|
||||
// this is going to be evil >:3
|
||||
const auto& node = ops[c_node];
|
||||
if (!node.is_value)
|
||||
if (!node.is_value())
|
||||
{
|
||||
auto& current_func_info = program.get_operator_info(ops[c_node].id);
|
||||
auto& current_func_info = program.get_operator_info(ops[c_node].id());
|
||||
operator_id random_replacement = program.get_random().select(
|
||||
program.get_type_non_terminals(current_func_info.return_type.id));
|
||||
auto& replacement_func_info = program.get_operator_info(random_replacement);
|
||||
|
@ -483,8 +483,8 @@ namespace blt::gp
|
|||
blt::size_t expected_bytes = std::accumulate(ops.begin(),
|
||||
ops.end(), 0ul,
|
||||
[](const auto& v1, const auto& v2) {
|
||||
if (v2.is_value)
|
||||
return v1 + stack_allocator::aligned_size(v2.type_size);
|
||||
if (v2.is_value())
|
||||
return v1 + v2.type_size();
|
||||
return v1;
|
||||
});
|
||||
if (found_bytes != expected_bytes)
|
||||
|
@ -545,7 +545,7 @@ namespace blt::gp
|
|||
};
|
||||
}
|
||||
#if BLT_DEBUG_LEVEL >= 2
|
||||
if (!c.check(program, nullptr))
|
||||
if (!c.check(program, detail::debug::context_ptr))
|
||||
{
|
||||
std::cout << "Parent: " << std::endl;
|
||||
c_copy.print(program, std::cout, false, true);
|
||||
|
@ -559,7 +559,7 @@ namespace blt::gp
|
|||
break;
|
||||
case mutation_operator::SUB_FUNC:
|
||||
{
|
||||
auto& current_func_info = program.get_operator_info(ops[c_node].id);
|
||||
auto& current_func_info = program.get_operator_info(ops[c_node].id());
|
||||
|
||||
// need to:
|
||||
// mutate the current function.
|
||||
|
@ -640,7 +640,7 @@ namespace blt::gp
|
|||
});
|
||||
|
||||
#if BLT_DEBUG_LEVEL >= 2
|
||||
if (!c.check(program, nullptr))
|
||||
if (!c.check(program, detail::debug::context_ptr))
|
||||
{
|
||||
std::cout << "Parent: " << std::endl;
|
||||
p.print(program, std::cout, false, true);
|
||||
|
@ -656,7 +656,7 @@ namespace blt::gp
|
|||
break;
|
||||
case mutation_operator::JUMP_FUNC:
|
||||
{
|
||||
auto& info = program.get_operator_info(ops[c_node].id);
|
||||
auto& info = program.get_operator_info(ops[c_node].id());
|
||||
blt::size_t argument_index = -1ul;
|
||||
for (const auto& [index, v] : blt::enumerate(info.argument_types))
|
||||
{
|
||||
|
@ -703,7 +703,7 @@ namespace blt::gp
|
|||
vals.copy_from(storage_ptr, for_bytes + after_bytes);
|
||||
|
||||
#if BLT_DEBUG_LEVEL >= 2
|
||||
if (!c.check(program, nullptr))
|
||||
if (!c.check(program, detail::debug::context_ptr))
|
||||
{
|
||||
std::cout << "Parent: " << std::endl;
|
||||
p.print(program, std::cout, false, true);
|
||||
|
@ -717,7 +717,7 @@ namespace blt::gp
|
|||
break;
|
||||
case mutation_operator::COPY:
|
||||
{
|
||||
auto& info = program.get_operator_info(ops[c_node].id);
|
||||
auto& info = program.get_operator_info(ops[c_node].id());
|
||||
blt::size_t pt = -1ul;
|
||||
blt::size_t pf = -1ul;
|
||||
for (const auto& [index, v] : blt::enumerate(info.argument_types))
|
||||
|
@ -803,7 +803,7 @@ namespace blt::gp
|
|||
}
|
||||
|
||||
#if BLT_DEBUG_LEVEL >= 2
|
||||
if (!c.check(program, nullptr))
|
||||
if (!c.check(program, detail::debug::context_ptr))
|
||||
{
|
||||
std::cout << "Parent: " << std::endl;
|
||||
p.print(program, std::cout, false, true);
|
||||
|
|
74
src/tree.cpp
74
src/tree.cpp
|
@ -72,14 +72,14 @@ namespace blt::gp
|
|||
// reverse the order of the stack
|
||||
for (const auto& v : operations)
|
||||
{
|
||||
if (v.is_value)
|
||||
copy.transfer_bytes(reversed, v.type_size);
|
||||
if (v.is_value())
|
||||
copy.transfer_bytes(reversed, v.type_size());
|
||||
}
|
||||
}
|
||||
for (const auto& v : operations)
|
||||
{
|
||||
auto info = program.get_operator_info(v.id);
|
||||
auto name = program.get_name(v.id) ? program.get_name(v.id).value() : "NULL";
|
||||
auto info = program.get_operator_info(v.id());
|
||||
const auto name = program.get_name(v.id()) ? program.get_name(v.id()).value() : "NULL";
|
||||
auto return_type = get_return_type(program, info.return_type, include_types);
|
||||
if (info.argc.argc > 0)
|
||||
{
|
||||
|
@ -92,10 +92,10 @@ namespace blt::gp
|
|||
if (print_literals)
|
||||
{
|
||||
create_indent(out, indent, pretty_print);
|
||||
if (program.is_operator_ephemeral(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));
|
||||
program.get_print_func(v.id())(out, reversed);
|
||||
reversed.pop_bytes(v.type_size());
|
||||
} else
|
||||
out << name;
|
||||
out << return_type << end_indent(pretty_print);
|
||||
|
@ -153,7 +153,7 @@ namespace blt::gp
|
|||
|
||||
for (const auto& op : operations_stack)
|
||||
{
|
||||
if (op.is_value)
|
||||
if (op.is_value())
|
||||
value_stack.push_back(1);
|
||||
}
|
||||
|
||||
|
@ -162,7 +162,7 @@ namespace blt::gp
|
|||
auto operation = operations_stack.back();
|
||||
// keep the last value in the stack on the process stack stored in the eval context, this way it can be accessed easily.
|
||||
operations_stack.pop_back();
|
||||
if (operation.is_value)
|
||||
if (operation.is_value())
|
||||
{
|
||||
auto d = value_stack.back();
|
||||
depth = std::max(depth, d);
|
||||
|
@ -171,54 +171,54 @@ namespace blt::gp
|
|||
continue;
|
||||
}
|
||||
size_t local_depth = 0;
|
||||
for (size_t i = 0; i < program.get_operator_info(operation.id).argc.argc; i++)
|
||||
for (size_t i = 0; i < program.get_operator_info(operation.id()).argc.argc; i++)
|
||||
{
|
||||
local_depth = std::max(local_depth, values_process.back());
|
||||
values_process.pop_back();
|
||||
}
|
||||
value_stack.push_back(local_depth + 1);
|
||||
operations_stack.emplace_back(operation.type_size, operation.id, true);
|
||||
operations_stack.emplace_back(operation.type_size(), operation.id(), true);
|
||||
}
|
||||
|
||||
return depth;
|
||||
}
|
||||
|
||||
blt::ptrdiff_t tree_t::find_endpoint(gp_program& program, blt::ptrdiff_t index) const
|
||||
ptrdiff_t tree_t::find_endpoint(gp_program& program, ptrdiff_t start) const
|
||||
{
|
||||
blt::i64 children_left = 0;
|
||||
i64 children_left = 0;
|
||||
|
||||
do
|
||||
{
|
||||
const auto& type = program.get_operator_info(operations[index].id);
|
||||
const auto& type = program.get_operator_info(operations[start].id());
|
||||
// this is a child to someone
|
||||
if (children_left != 0)
|
||||
children_left--;
|
||||
if (type.argc.argc > 0)
|
||||
children_left += type.argc.argc;
|
||||
index++;
|
||||
start++;
|
||||
} while (children_left > 0);
|
||||
|
||||
return index;
|
||||
return start;
|
||||
}
|
||||
|
||||
// this function doesn't work!
|
||||
blt::ptrdiff_t tree_t::find_parent(gp_program& program, blt::ptrdiff_t index) const
|
||||
ptrdiff_t tree_t::find_parent(gp_program& program, ptrdiff_t start) const
|
||||
{
|
||||
blt::i64 children_left = 0;
|
||||
i64 children_left = 0;
|
||||
do
|
||||
{
|
||||
if (index == 0)
|
||||
if (start == 0)
|
||||
return 0;
|
||||
const auto& type = program.get_operator_info(operations[index].id);
|
||||
const auto& type = program.get_operator_info(operations[start].id());
|
||||
if (type.argc.argc > 0)
|
||||
children_left -= type.argc.argc;
|
||||
children_left++;
|
||||
if (children_left <= 0)
|
||||
break;
|
||||
--index;
|
||||
--start;
|
||||
} while (true);
|
||||
|
||||
return index;
|
||||
return start;
|
||||
}
|
||||
|
||||
bool tree_t::check(gp_program& program, void* context) const
|
||||
|
@ -228,8 +228,8 @@ namespace blt::gp
|
|||
|
||||
for (const auto& op : get_operations())
|
||||
{
|
||||
if (op.is_value)
|
||||
bytes_expected += stack_allocator::aligned_size(op.type_size);
|
||||
if (op.is_value())
|
||||
bytes_expected += op.type_size();
|
||||
}
|
||||
|
||||
if (bytes_expected != bytes_size)
|
||||
|
@ -252,24 +252,24 @@ namespace blt::gp
|
|||
|
||||
for (const auto& operation : iterate(operations).rev())
|
||||
{
|
||||
if (operation.is_value)
|
||||
if (operation.is_value())
|
||||
{
|
||||
value_stack.transfer_bytes(values_process, operation.type_size);
|
||||
total_produced += stack_allocator::aligned_size(operation.type_size);
|
||||
value_stack.transfer_bytes(values_process, operation.type_size());
|
||||
total_produced += operation.type_size();
|
||||
continue;
|
||||
}
|
||||
auto& info = program.get_operator_info(operation.id);
|
||||
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());
|
||||
total_consumed += program.get_typesystem().get_type(arg).size();
|
||||
program.get_operator_info(operation.id()).func(context, values_process, values_process);
|
||||
total_produced += 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));
|
||||
const auto v1 = results.values.bytes_in_head();
|
||||
const auto v2 = static_cast<ptrdiff_t>(operations.front().type_size());
|
||||
if (v1 != v2)
|
||||
{
|
||||
auto vd = std::abs(v1 - v2);
|
||||
const auto vd = std::abs(v1 - v2);
|
||||
BLT_ERROR("found %ld bytes expected %ld bytes, total difference: %ld", v1, v2, vd);
|
||||
BLT_ERROR("Total Produced %ld || Total Consumed %ld || Total Difference %ld", total_produced, total_consumed,
|
||||
std::abs(static_cast<blt::ptrdiff_t>(total_produced) - static_cast<blt::ptrdiff_t>(total_consumed)));
|
||||
|
@ -278,7 +278,7 @@ namespace blt::gp
|
|||
return true;
|
||||
}
|
||||
|
||||
void tree_t::find_child_extends(gp_program& program, tracked_vector<child_t>& vec, blt::size_t parent_node, blt::size_t argc) const
|
||||
void tree_t::find_child_extends(gp_program& program, tracked_vector<child_t>& vec, const size_t parent_node, const size_t argc) const
|
||||
{
|
||||
while (vec.size() < argc)
|
||||
{
|
||||
|
@ -287,8 +287,8 @@ namespace blt::gp
|
|||
if (current_point == 0)
|
||||
{
|
||||
// first child.
|
||||
prev = {static_cast<blt::ptrdiff_t>(parent_node + 1),
|
||||
find_endpoint(program, static_cast<blt::ptrdiff_t>(parent_node + 1))};
|
||||
prev = {static_cast<ptrdiff_t>(parent_node + 1),
|
||||
find_endpoint(program, static_cast<ptrdiff_t>(parent_node + 1))};
|
||||
vec.push_back(prev);
|
||||
continue;
|
||||
} else
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* <Short Description>
|
||||
* Copyright (C) 2025 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 "../examples/symbolic_regression.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <array>
|
||||
#include <blt/profiling/profiler_v2.h>
|
||||
|
||||
static const auto SEED_FUNC = [] { return std::random_device()(); };
|
||||
|
||||
std::array crossover_chances = {1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0};
|
||||
std::array mutation_chances = {0.0, 0.1, 0.2, 0.9, 1.0};
|
||||
std::array reproduction_chances = {0.0, 1.0, 0.1, 0.9};
|
||||
std::array elite_amounts = {0, 2, 10, 50};
|
||||
std::array population_sizes = {50, 500, 5000};
|
||||
|
||||
blt::gp::prog_config_t best_config;
|
||||
double best_fitness = 0;
|
||||
|
||||
void run(const blt::gp::prog_config_t& config)
|
||||
{
|
||||
// the config is copied into the gp_system so changing the config will not change the runtime of the program.
|
||||
blt::gp::example::symbolic_regression_t regression{SEED_FUNC, config};
|
||||
|
||||
|
||||
BLT_START_INTERVAL("Symbolic Regression", "Setup Operations");
|
||||
regression.setup_operations();
|
||||
BLT_END_INTERVAL("Symbolic Regression", "Setup Operations");
|
||||
|
||||
BLT_START_INTERVAL("Symbolic Regression", "Generate Initial Population");
|
||||
regression.generate_initial_population();
|
||||
BLT_END_INTERVAL("Symbolic Regression", "Generate Initial Population");
|
||||
|
||||
BLT_START_INTERVAL("Symbolic Regression", "Total Generation Loop");
|
||||
BLT_DEBUG("Begin Generation Loop");
|
||||
auto& program = regression.get_program();
|
||||
while (!program.should_terminate())
|
||||
{
|
||||
#ifdef BLT_TRACK_ALLOCATIONS
|
||||
auto cross = crossover_calls.start_measurement();
|
||||
auto mut = mutation_calls.start_measurement();
|
||||
auto repo = reproduction_calls.start_measurement();
|
||||
#endif
|
||||
BLT_TRACE("------------{Begin Generation %ld}------------", program.get_current_generation());
|
||||
BLT_TRACE("Creating next generation");
|
||||
BLT_START_INTERVAL("Symbolic Regression", "Create Next Generation");
|
||||
program.create_next_generation();
|
||||
BLT_END_INTERVAL("Symbolic Regression", "Create Next Generation");
|
||||
BLT_TRACE("Move to next generation");
|
||||
BLT_START_INTERVAL("Symbolic Regress", "Move Next Generation");
|
||||
program.next_generation();
|
||||
BLT_END_INTERVAL("Symbolic Regress", "Move Next Generation");
|
||||
BLT_TRACE("Evaluate Fitness");
|
||||
BLT_START_INTERVAL("Symbolic Regress", "Evaluate Fitness");
|
||||
program.evaluate_fitness();
|
||||
BLT_END_INTERVAL("Symbolic Regress", "Evaluate Fitness");
|
||||
BLT_START_INTERVAL("Symbolic Regress", "Fitness Print");
|
||||
const auto& stats = program.get_population_stats();
|
||||
BLT_TRACE("Avg Fit: %lf, Best Fit: %lf, Worst Fit: %lf, Overall Fit: %lf",
|
||||
stats.average_fitness.load(std::memory_order_relaxed), stats.best_fitness.load(std::memory_order_relaxed),
|
||||
stats.worst_fitness.load(std::memory_order_relaxed), stats.overall_fitness.load(std::memory_order_relaxed));
|
||||
BLT_END_INTERVAL("Symbolic Regress", "Fitness Print");
|
||||
#ifdef BLT_TRACK_ALLOCATIONS
|
||||
crossover_calls.stop_measurement(cross);
|
||||
mutation_calls.stop_measurement(mut);
|
||||
reproduction_calls.stop_measurement(repo);
|
||||
const auto total = (cross.get_call_difference() * 2) + mut.get_call_difference() + repo.get_call_difference();
|
||||
BLT_TRACE("Calls Crossover: %ld, Mutation %ld, Reproduction %ld; %ld", cross.get_call_difference(), mut.get_call_difference(), repo.get_call_difference(), total);
|
||||
BLT_TRACE("Value Crossover: %ld, Mutation %ld, Reproduction %ld; %ld", cross.get_value_difference(), mut.get_value_difference(), repo.get_value_difference(), (cross.get_value_difference() * 2 + mut.get_value_difference() + repo.get_value_difference()) - total);
|
||||
#endif
|
||||
BLT_TRACE("----------------------------------------------");
|
||||
std::cout << std::endl;
|
||||
}
|
||||
BLT_END_INTERVAL("Symbolic Regression", "Total Generation Loop");
|
||||
|
||||
const auto best = program.get_best_individuals<1>();
|
||||
|
||||
if (best[0].get().fitness.adjusted_fitness > best_fitness)
|
||||
{
|
||||
best_fitness = best[0].get().fitness.adjusted_fitness;
|
||||
best_config = config;
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
std::stringstream results;
|
||||
for (const auto crossover_chance : crossover_chances)
|
||||
{
|
||||
for (const auto mutation_chance : mutation_chances)
|
||||
{
|
||||
for (const auto reproduction_chance : reproduction_chances)
|
||||
{
|
||||
for (const auto elite_amount : elite_amounts)
|
||||
{
|
||||
for (const auto population_sizes : population_sizes)
|
||||
{
|
||||
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(elite_amount)
|
||||
.set_crossover_chance(crossover_chance)
|
||||
.set_mutation_chance(mutation_chance)
|
||||
.set_reproduction_chance(reproduction_chance)
|
||||
.set_max_generations(50)
|
||||
.set_pop_size(population_sizes)
|
||||
.set_thread_count(0);
|
||||
|
||||
BLT_INFO_STREAM << "Run: Crossover (";
|
||||
BLT_INFO_STREAM << crossover_chance;
|
||||
BLT_INFO_STREAM << ") Mutation (";
|
||||
BLT_INFO_STREAM << mutation_chance;
|
||||
BLT_INFO_STREAM << ") Reproduction (";
|
||||
BLT_INFO_STREAM << reproduction_chance;
|
||||
BLT_INFO_STREAM << ") Elite (";
|
||||
BLT_INFO_STREAM << elite_amount;
|
||||
BLT_INFO_STREAM << ") Population Size (";
|
||||
BLT_INFO_STREAM << population_sizes;
|
||||
BLT_INFO_STREAM << ")" << "\n";
|
||||
run(config);
|
||||
|
||||
results << "Run: Crossover (";
|
||||
results << crossover_chance;
|
||||
results << ") Mutation (";
|
||||
results << mutation_chance;
|
||||
results << ") Reproduction (";
|
||||
results << reproduction_chance;
|
||||
results << ") Elite (";
|
||||
results << elite_amount;
|
||||
results << ") Population Size (";
|
||||
results << population_sizes;
|
||||
results << ")" << std::endl;
|
||||
BLT_WRITE_PROFILE(results, "Symbolic Regression");
|
||||
results << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
std::cout << results.str() << std::endl;
|
||||
|
||||
std::cout << "Best Configuration is: " << std::endl;
|
||||
std::cout << "\tCrossover: " << best_config.crossover_chance << std::endl;
|
||||
std::cout << "\tMutation: " << best_config.mutation_chance << std::endl;
|
||||
std::cout << "\tReproduction: " << best_config.reproduction_chance << std::endl;
|
||||
std::cout << "\tElites: " << best_config.elites << std::endl;
|
||||
std::cout << "\tPopulation Size: " << best_config.population_size << std::endl;
|
||||
std::cout << std::endl;
|
||||
}
|
Loading…
Reference in New Issue