diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d9aa74..3f66556 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(blt-gp VERSION 0.0.133) +project(blt-gp VERSION 0.0.134) include(CTest) @@ -97,6 +97,7 @@ if (${BUILD_GP_TESTS}) blt_add_project(blt-stack tests/stack_tests.cpp test) blt_add_project(blt-eval tests/evaluation_tests.cpp test) blt_add_project(blt-order tests/order_tests.cpp test) + blt_add_project(blt-destructor tests/destructor_test.cpp test) blt_add_project(blt-gp1 tests/gp_test_1.cpp test) blt_add_project(blt-gp2 tests/gp_test_2.cpp test) blt_add_project(blt-gp3 tests/gp_test_3.cpp test) diff --git a/examples/symbolic_regression.cpp b/examples/symbolic_regression.cpp index e098cd7..5dd751b 100644 --- a/examples/symbolic_regression.cpp +++ b/examples/symbolic_regression.cpp @@ -20,7 +20,6 @@ #include #include #include -#include //static constexpr long SEED = 41912; static const unsigned long SEED = std::random_device()(); @@ -77,7 +76,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 fitness.hits == fitness_cases.size(); + return static_cast(fitness.hits) == fitness_cases.size(); }; float example_function(float x) diff --git a/include/blt/gp/operations.h b/include/blt/gp/operations.h index a542c05..c2ed5a9 100644 --- a/include/blt/gp/operations.h +++ b/include/blt/gp/operations.h @@ -181,11 +181,11 @@ namespace blt::gp if constexpr (detail::is_same_v::type>>) { // first arg is context - write_allocator.push(std::move(this->operator()(context, read_allocator))); + write_allocator.push(this->operator()(context, read_allocator)); } else { // first arg isn't context - write_allocator.push(std::move(this->operator()(read_allocator))); + write_allocator.push(this->operator()(read_allocator)); } }; } diff --git a/include/blt/gp/stack.h b/include/blt/gp/stack.h index cea68fb..e7ecefc 100644 --- a/include/blt/gp/stack.h +++ b/include/blt/gp/stack.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,11 @@ namespace blt::gp { + namespace detail + { + BLT_META_MAKE_FUNCTION_CHECK(drop); + } + class stack_allocator { constexpr static blt::size_t PAGE_SIZE = 0x1000; @@ -226,7 +232,8 @@ namespace blt::gp // make copy NO_REF_T t = *reinterpret_cast(head->metadata.offset - TYPE_SIZE); // call destructor - reinterpret_cast(head->metadata.offset - TYPE_SIZE)->~NO_REF_T(); + if constexpr (detail::has_func_drop_v) + reinterpret_cast(head->metadata.offset - TYPE_SIZE)->~NO_REF_T(); // move offset back head->metadata.offset -= TYPE_SIZE; // moving back allows us to allocate with other data, if there is room. @@ -313,7 +320,7 @@ namespace blt::gp /** * Warning this function should be used to transfer types, not arrays of types! It will produce an error if you attempt to pass more - * than one type # of bytes at a time~! + * than one type # of bytes at a time! * @param to stack to push to * @param bytes number of bytes to transfer out. */ @@ -343,8 +350,7 @@ namespace blt::gp { blt::size_t offset = 0; - ((from>(offset).~NO_REF_T(), offset += stack_allocator::aligned_size> - ()), ...); + ((call_drop(offset), offset += stack_allocator::aligned_size>()), ...); } [[nodiscard]] bool empty() const noexcept @@ -544,6 +550,15 @@ namespace blt::gp blt::u8* start_point; }; + template + inline void call_drop(blt::size_t offset) + { + if constexpr (detail::has_func_drop_v) + { + from>(offset).drop(); + } + } + template void* allocate_bytes() { diff --git a/tests/destructor_test.cpp b/tests/destructor_test.cpp new file mode 100644 index 0000000..c8e27b9 --- /dev/null +++ b/tests/destructor_test.cpp @@ -0,0 +1,220 @@ +/* + * + * 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 . + */ +/* + * + * 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 . + */ +#include +#include +#include +#include +#include +#include +#include + +//static constexpr long SEED = 41912; +static const unsigned long SEED = std::random_device()(); + +inline std::atomic_uint64_t constructions = 0; +inline std::atomic_uint64_t destructions = 0; + +class move_float +{ + public: + move_float(): f(new float()) + { constructions++; } + + move_float(float f): f(new float(f)) // NOLINT + { + constructions++; + // BLT_TRACE("Value Constructed"); + } + + operator float() // NOLINT + { + return *f; + } + + void drop() // NOLINT + { + //BLT_TRACE("Drop Called"); + delete f; + f = nullptr; + destructions++; + } + + private: + float* f = nullptr; +}; + +static_assert(std::is_trivially_copyable_v); +//static_assert(std::is_standard_layout_v); + +struct context +{ + move_float x, y; +}; + +std::array fitness_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(1) + .set_pop_size(1) + .set_thread_count(0); + +blt::gp::type_provider type_system; +blt::gp::gp_program program{type_system, SEED, config}; + +blt::gp::operation_t add([](move_float a, move_float b) { return move_float(a + b); }, "add"); +blt::gp::operation_t sub([](move_float a, move_float b) { return move_float(a - b); }, "sub"); +blt::gp::operation_t mul([](move_float a, move_float b) { return move_float(a * b); }, "mul"); +blt::gp::operation_t pro_div([](move_float a, move_float b) { return move_float(b == 0.0f ? 1.0f : a / b); }, "div"); +blt::gp::operation_t op_sin([](move_float a) { return move_float(std::sin(a)); }, "sin"); +blt::gp::operation_t op_cos([](move_float a) { return move_float(std::cos(a)); }, "cos"); +blt::gp::operation_t op_exp([](move_float a) { return move_float(std::exp(a)); }, "exp"); +blt::gp::operation_t op_log([](move_float a) { return move_float(a == 0.0f ? 0.0f : std::log(a)); }, "log"); + +blt::gp::operation_t lit([]() { + return move_float(program.get_random().get_float(-320.0f, 320.0f)); +}, "lit"); +blt::gp::operation_t op_x([](const context& context) { + return move_float(context.x); +}, "x"); + +constexpr auto fitness_function = [](blt::gp::tree_t& current_tree, blt::gp::fitness_t& fitness, blt::size_t) { + constexpr double value_cutoff = 1.e15; + for (auto& fitness_case : fitness_cases) + { + auto diff = std::abs(fitness_case.y - current_tree.get_evaluation_value(&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(fitness.hits) == fitness_cases.size(); +}; + +float example_function(float x) +{ + return x * x * x * x + x * x * x + x * x + x; +} + +int main() +{ + BLT_INFO("Starting BLT-GP Symbolic Regression Example"); + BLT_START_INTERVAL("Symbolic Regression", "Main"); + BLT_DEBUG("Setup Fitness cases"); + for (auto& fitness_case : fitness_cases) + { + constexpr float range = 10; + constexpr float half_range = range / 2.0; + auto x = program.get_random().get_float(-half_range, half_range); + auto y = example_function(x); + fitness_case = {move_float(x), move_float(y)}; + } + + BLT_DEBUG("Setup Types and Operators"); + type_system.register_type(); + + blt::gp::operator_builder builder{type_system}; + builder.add_operator(add); + builder.add_operator(sub); + builder.add_operator(mul); + builder.add_operator(pro_div); + builder.add_operator(op_sin); + builder.add_operator(op_cos); + builder.add_operator(op_exp); + builder.add_operator(op_log); + + builder.add_operator(lit, true); + builder.add_operator(op_x); + + program.set_operations(builder.build()); + + BLT_DEBUG("Generate Initial Population"); + program.generate_population(type_system.get_type().id(), fitness_function); + + BLT_DEBUG("Begin Generation Loop"); + while (!program.should_terminate()) + { + BLT_TRACE("------------{Begin Generation %ld}------------", program.get_current_generation()); + BLT_START_INTERVAL("Symbolic Regression", "Gen"); + auto sel = blt::gp::select_fitness_proportionate_t{}; + program.create_next_generation(sel, sel, sel); + BLT_END_INTERVAL("Symbolic Regression", "Gen"); + BLT_TRACE("Move to next generation"); + BLT_START_INTERVAL("Symbolic Regression", "Fitness"); + program.next_generation(); + BLT_TRACE("Evaluate Fitness"); + program.evaluate_fitness(); + BLT_END_INTERVAL("Symbolic Regression", "Fitness"); + BLT_TRACE("----------------------------------------------"); + std::cout << std::endl; + } + + BLT_END_INTERVAL("Symbolic Regression", "Main"); + + auto best = program.get_best_individuals<3>(); + + BLT_INFO("Best approximations:"); + for (auto& i_ref : best) + { + auto& i = i_ref.get(); + BLT_DEBUG("Fitness: %lf, stand: %lf, raw: %lf", i.fitness.adjusted_fitness, i.fitness.standardized_fitness, i.fitness.raw_fitness); + i.tree.print(program, std::cout); + std::cout << "\n"; + } + auto& stats = program.get_population_stats(); + BLT_INFO("Stats:"); + BLT_INFO("Average fitness: %lf", stats.average_fitness.load()); + BLT_INFO("Best fitness: %lf", stats.best_fitness.load()); + BLT_INFO("Worst fitness: %lf", stats.worst_fitness.load()); + BLT_INFO("Overall fitness: %lf", stats.overall_fitness.load()); + // TODO: make stats helper + + BLT_PRINT_PROFILE("Symbolic Regression", blt::PRINT_CYCLES | blt::PRINT_THREAD | blt::PRINT_WALL); + + BLT_TRACE("Constructions %ld Destructions %ld Difference %ld", constructions.load(), destructions.load(), + std::abs(static_cast(constructions) - static_cast(destructions))); + + return 0; +} \ No newline at end of file