From 5d8f4c00cd162b7a663e7a8b1d5fd81343447eaa Mon Sep 17 00:00:00 2001 From: Brett Date: Sun, 23 Jun 2024 14:13:50 -0400 Subject: [PATCH] generators --- CMakeLists.txt | 4 +- examples/gp_test_2.cpp | 45 +++++++ examples/main.cpp | 231 +++++++++++++++++++----------------- include/blt/gp/program.h | 39 +----- include/blt/gp/stack.h | 4 +- include/blt/gp/typesystem.h | 70 +++++++++++ src/generators.cpp | 49 +++++++- 7 files changed, 290 insertions(+), 152 deletions(-) create mode 100644 examples/gp_test_2.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 17b1998..f3941d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(blt-gp VERSION 0.0.25) +project(blt-gp VERSION 0.0.26) option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF) option(ENABLE_UBSAN "Enable the ub sanitizer" OFF) @@ -38,7 +38,7 @@ endif () if (${BUILD_EXAMPLES}) project(blt-gp-example) - add_executable(blt-gp-example examples/main.cpp) + add_executable(blt-gp-example examples/main.cpp examples/gp_test_2.cpp) target_link_libraries(blt-gp-example PUBLIC BLT blt-gp) diff --git a/examples/gp_test_2.cpp b/examples/gp_test_2.cpp new file mode 100644 index 0000000..55c1eb3 --- /dev/null +++ b/examples/gp_test_2.cpp @@ -0,0 +1,45 @@ +/* + * + * 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 + +static constexpr long SEED = 41912; + +blt::gp::type_system type_system; +std::random_device dev; +blt::gp::gp_program program(type_system, std::mt19937_64{SEED}); // NOLINT + +blt::gp::operation_t add([](float a, float b) { return a + b; }); +blt::gp::operation_t sub([](float a, float b) { return a - b; }); +blt::gp::operation_t mul([](float a, float b) { return a * b; }); +blt::gp::operation_t pro_div([](float a, float b) { return b == 0 ? 0.0f : a / b; }); +blt::gp::operation_t lit([]() { + static std::uniform_real_distribution dist(-32000, 32000); + return dist(program.get_random()); +}); + +int main() +{ + type_system.register_type(); + type_system.register_type(); + + type_system.add_operator(add); + type_system.add_operator(sub); + type_system.add_operator(mul); + type_system.add_operator(pro_div); + type_system.add_operator(lit); +} \ No newline at end of file diff --git a/examples/main.cpp b/examples/main.cpp index c01a72b..10b3db5 100644 --- a/examples/main.cpp +++ b/examples/main.cpp @@ -272,121 +272,130 @@ void test() } -blt::gp::type_system type_system; -blt::gp::gp_program program(type_system); -std::random_device dev; -std::mt19937_64 engine{dev()}; - -blt::gp::operation_t add([](float a, float b) { return a + b; }); -blt::gp::operation_t sub([](float a, float b) { return a - b; }); -blt::gp::operation_t mul([](float a, float b) { return a * b; }); -blt::gp::operation_t pro_div([](float a, float b) { return b == 0 ? 0.0f : a / b; }); -blt::gp::operation_t lit([]() { - static std::uniform_real_distribution dist(-32000, 32000); - return dist(engine); -}); - - -int main() +float nyah(float a, int b, bool c) { - type_system.register_type(); - type_system.register_type(); + return a + static_cast(b) * c; +} + +struct silly +{ + unsigned long bruh; + int nya; - program.add_operator(add); - program.add_operator(sub); - program.add_operator(mul); - program.add_operator(pro_div); - program.add_operator(lit); + friend std::ostream& operator<<(std::ostream& out, const silly& s) + { + out << "[" << s.bruh << " " << s.nya << "]"; + return out; + } +}; -// constexpr blt::size_t MAX_ALIGNMENT = 8; -// test(); -// std::cout << alignof(silly) << " " << sizeof(silly) << std::endl; -// std::cout << alignof(super_large) << " " << sizeof(super_large) << " " << ((sizeof(super_large) + (MAX_ALIGNMENT - 1)) & ~(MAX_ALIGNMENT - 1)) -// << std::endl; -// std::cout << ((sizeof(char) + (MAX_ALIGNMENT - 1)) & ~(MAX_ALIGNMENT - 1)) << " " -// << ((sizeof(short) + (MAX_ALIGNMENT - 1)) & ~(MAX_ALIGNMENT - 1)) << std::endl; -// std::cout << ((sizeof(int) + (MAX_ALIGNMENT - 1)) & ~(MAX_ALIGNMENT - 1)) << " " << ((sizeof(long) + (MAX_ALIGNMENT - 1)) & ~(MAX_ALIGNMENT - 1)) -// << std::endl; -// std::cout << alignof(void*) << " " << sizeof(void*) << std::endl; -// std::cout << blt::type_string() << std::endl; -// -// alloc.push(50); -// alloc.push(550.3f); -// alloc.push(20.1230345); -// alloc.push(true); -// alloc.push(false); -// alloc.push(std::string("SillyString")); -// alloc.push(&"SillyString"); -// -// std::cout << std::endl; -// std::cout << *alloc.pop() << std::endl; -// std::cout << alloc.pop() << std::endl; -// std::cout << alloc.pop() << std::endl; -// std::cout << alloc.pop() << std::endl; -// std::cout << alloc.pop() << std::endl; -// std::cout << alloc.pop() << std::endl; -// std::cout << alloc.pop() << std::endl; -// std::cout << std::endl; -// -// std::cout << "Is empty? " << alloc.empty() << std::endl; -// alloc.push(silly{}); -// alloc.push(large{}); -// alloc.push(super_large{}); -// alloc.push(silly{25, 24}); -// alloc.push(large{}); -// -// std::cout << std::endl; -// std::cout << "Is empty? " << alloc.empty() << std::endl; -// alloc.pop(); -// std::cout << "Is empty? " << alloc.empty() << std::endl; -// std::cout << alloc.pop() << std::endl; -// std::cout << "Is empty? " << alloc.empty() << std::endl; -// alloc.pop(); -// std::cout << "Is empty? " << alloc.empty() << std::endl; -// alloc.pop(); -// std::cout << "Is empty? " << alloc.empty() << std::endl; -// std::cout << alloc.pop() << std::endl; -// std::cout << std::endl; -// -// std::cout << "Is empty? " << alloc.empty() << " bytes left: " << alloc.bytes_in_head() << std::endl; -// std::cout << std::endl; -// -// alloc.push(silly{2, 5}); -// alloc.push(large{}); -// alloc.push(super_large{}); -// alloc.push(silly{80, 10}); -// alloc.push(large{}); -// alloc.push(50); -// alloc.push(550.3f); -// alloc.push(20.1230345); -// alloc.push(std::string("SillyString")); -// alloc.push(33.22f); -// alloc.push(120); -// alloc.push(true); -// -// blt::gp::operation_t silly_op(nyah); -// blt::gp::operation_t silly_op_2([](float f, float g) { -// return f + g; -// }); +struct large +{ + unsigned char data[256]; +}; -// std::cout << silly_op(alloc) << std::endl; -// -// std::cout << "Is empty? " << alloc.empty() << std::endl; +struct super_large +{ + unsigned char data[5129]; +}; -// std::cout << std::endl; -// -// //auto* pointer = static_cast(head->metadata.offset); -// //return std::align(alignment, bytes, pointer, remaining_bytes); -// -// float f = 10.5; -// int i = 412; -// bool b = true; -// -// std::array arr{reinterpret_cast(&f), reinterpret_cast(&i), reinterpret_cast(&b)}; -// -// blt::span spv{arr}; -// -// std::cout << silly_op.operator()(spv) << std::endl; +blt::gp::stack_allocator alloc; + +int main_old() +{ + constexpr blt::size_t MAX_ALIGNMENT = 8; + test(); + std::cout << alignof(silly) << " " << sizeof(silly) << std::endl; + std::cout << alignof(super_large) << " " << sizeof(super_large) << " " << ((sizeof(super_large) + (MAX_ALIGNMENT - 1)) & ~(MAX_ALIGNMENT - 1)) + << std::endl; + std::cout << ((sizeof(char) + (MAX_ALIGNMENT - 1)) & ~(MAX_ALIGNMENT - 1)) << " " + << ((sizeof(short) + (MAX_ALIGNMENT - 1)) & ~(MAX_ALIGNMENT - 1)) << std::endl; + std::cout << ((sizeof(int) + (MAX_ALIGNMENT - 1)) & ~(MAX_ALIGNMENT - 1)) << " " << ((sizeof(long) + (MAX_ALIGNMENT - 1)) & ~(MAX_ALIGNMENT - 1)) + << std::endl; + std::cout << alignof(void*) << " " << sizeof(void*) << std::endl; + std::cout << blt::type_string() << std::endl; - //std::cout << "Hello World!" << std::endl; + alloc.push(50); + alloc.push(550.3f); + alloc.push(20.1230345); + alloc.push(true); + alloc.push(false); + alloc.push(std::string("SillyString")); + alloc.push(&"SillyString"); + + std::cout << std::endl; + std::cout << *alloc.pop() << std::endl; + std::cout << alloc.pop() << std::endl; + std::cout << alloc.pop() << std::endl; + std::cout << alloc.pop() << std::endl; + std::cout << alloc.pop() << std::endl; + std::cout << alloc.pop() << std::endl; + std::cout << alloc.pop() << std::endl; + std::cout << std::endl; + + std::cout << "Is empty? " << alloc.empty() << std::endl; + alloc.push(silly{}); + alloc.push(large{}); + alloc.push(super_large{}); + alloc.push(silly{25, 24}); + alloc.push(large{}); + + std::cout << std::endl; + std::cout << "Is empty? " << alloc.empty() << std::endl; + alloc.pop(); + std::cout << "Is empty? " << alloc.empty() << std::endl; + std::cout << alloc.pop() << std::endl; + std::cout << "Is empty? " << alloc.empty() << std::endl; + alloc.pop(); + std::cout << "Is empty? " << alloc.empty() << std::endl; + alloc.pop(); + std::cout << "Is empty? " << alloc.empty() << std::endl; + std::cout << alloc.pop() << std::endl; + std::cout << std::endl; + + std::cout << "Is empty? " << alloc.empty() << " bytes left: " << alloc.bytes_in_head() << std::endl; + std::cout << std::endl; + + alloc.push(silly{2, 5}); + alloc.push(large{}); + alloc.push(super_large{}); + alloc.push(silly{80, 10}); + alloc.push(large{}); + alloc.push(50); + alloc.push(550.3f); + alloc.push(20.1230345); + alloc.push(std::string("SillyString")); + alloc.push(33.22f); + alloc.push(120); + alloc.push(true); + + blt::gp::operation_t silly_op(nyah); + blt::gp::operation_t silly_op_2([](float f, float g) { + return f + g; + }); + + std::cout << silly_op(alloc) << std::endl; + + std::cout << "Is empty? " << alloc.empty() << std::endl; + + std::cout << std::endl; + + //auto* pointer = static_cast(head->metadata.offset); + //return std::align(alignment, bytes, pointer, remaining_bytes); + + float f = 10.5; + int i = 412; + bool b = true; + + alloc.push(f); + alloc.push(i); + alloc.push(b); + + //std::array arr{reinterpret_cast(&f), reinterpret_cast(&i), reinterpret_cast(&b)}; + + //blt::span spv{arr}; + + std::cout << silly_op.operator()(alloc) << std::endl; + + std::cout << "Hello World!" << std::endl; } \ No newline at end of file diff --git a/include/blt/gp/program.h b/include/blt/gp/program.h index 285d47e..b56e4d6 100644 --- a/include/blt/gp/program.h +++ b/include/blt/gp/program.h @@ -45,21 +45,9 @@ namespace blt::gp class gp_program { public: - explicit gp_program(type_system system): system(std::move(system)) + explicit gp_program(type_system& system, std::mt19937_64 engine): system(system), engine(engine) {} - template - void add_operator(const operation_t& op) - { - auto return_type_id = system.get_type().id(); - auto& operator_list = op.get_argc() == 0 ? terminals : non_terminals; - operator_list[return_type_id].push_back(operators.size()); - - auto operator_index = operators.size(); - (argument_types[operator_index].push_back(system.get_type()), ...); - operators.push_back(op.make_callable()); - } - [[nodiscard]] inline type_system& get_typesystem() { return system; @@ -67,32 +55,15 @@ namespace blt::gp void generate_tree(); - inline operator_id select_terminal(type_id id, std::mt19937_64& engine) + inline std::mt19937_64& get_random() { - std::uniform_int_distribution dist(0, terminals[id].size() - 1); - return terminals[id][dist(engine)]; - } - - inline operator_id select_non_terminal(type_id id, std::mt19937_64& engine) - { - std::uniform_int_distribution dist(0, non_terminals[id].size() - 1); - return non_terminals[id][dist(engine)]; - } - - inline std::vector& get_argument_types(operator_id id) - { - return argument_types[id]; + return engine; } private: - type_system system; + type_system& system; blt::gp::stack_allocator alloc; - // indexed from return TYPE ID, returns index of operator - blt::expanding_buffer> terminals; - blt::expanding_buffer> non_terminals; - // indexed from OPERATOR ID (operator number) - blt::expanding_buffer> argument_types; - std::vector> operators; + std::mt19937_64 engine; }; } diff --git a/include/blt/gp/stack.h b/include/blt/gp/stack.h index 1b216f9..ce05dff 100644 --- a/include/blt/gp/stack.h +++ b/include/blt/gp/stack.h @@ -24,6 +24,7 @@ #include #include #include +#include namespace blt::gp { @@ -40,9 +41,10 @@ namespace blt::gp template void push(T&& value) { + using NO_REF_T = std::remove_reference_t; auto ptr = allocate_bytes(); head->metadata.offset = static_cast(ptr) + aligned_size(); - new(ptr) T(std::forward(value)); + new(ptr) NO_REF_T(std::forward(value)); } template diff --git a/include/blt/gp/typesystem.h b/include/blt/gp/typesystem.h index 6c3f0d4..1be619f 100644 --- a/include/blt/gp/typesystem.h +++ b/include/blt/gp/typesystem.h @@ -21,7 +21,13 @@ #include #include +#include +#include +#include #include +#include +#include +#include namespace blt::gp { @@ -70,6 +76,10 @@ namespace blt::gp std::string name_{}; }; + /** + * Is a provider for the set of types possible in a GP program + * also provides a set of functions for converting between C++ types and BLT GP types + */ class type_system { public: @@ -87,9 +97,69 @@ namespace blt::gp { return types[blt::type_string_raw()]; } + + /** + * This function is slow btw + * @param engine + * @return + */ + inline type select_type(std::mt19937_64& engine) + { + std::uniform_int_distribution dist(0ul, types.size() - 1); + auto offset = dist(engine); + auto itr = types.begin(); + for ([[maybe_unused]] auto _ : blt::range(0ul, offset)) + itr = itr++; + return itr->second; + } + + inline operator_id select_terminal(std::mt19937_64& engine, type_id id) + { + std::uniform_int_distribution dist(0, terminals[id].size() - 1); + return terminals[id][dist(engine)]; + } + + inline operator_id select_non_terminal(std::mt19937_64& engine, type_id id) + { + std::uniform_int_distribution dist(0, non_terminals[id].size() - 1); + return non_terminals[id][dist(engine)]; + } + + inline std::vector& get_argument_types(operator_id id) + { + return argument_types[id]; + } + + inline std::vector& get_type_terminals(type_id id) + { + return terminals[id]; + } + + inline std::vector& get_type_non_terminals(type_id id) + { + return non_terminals[id]; + } + + template + void add_operator(const operation_t& op) + { + auto return_type_id = get_type().id(); + auto& operator_list = op.get_argc() == 0 ? terminals : non_terminals; + operator_list[return_type_id].push_back(operators.size()); + + auto operator_index = operators.size(); + (argument_types[operator_index].push_back(get_type()), ...); + operators.push_back(op.make_callable()); + } private: blt::hashmap_t types; + // indexed from return TYPE ID, returns index of operator + blt::expanding_buffer> terminals; + blt::expanding_buffer> non_terminals; + // indexed from OPERATOR ID (operator number) + blt::expanding_buffer> argument_types; + std::vector> operators; }; } diff --git a/src/generators.cpp b/src/generators.cpp index 406e039..9bbc46d 100644 --- a/src/generators.cpp +++ b/src/generators.cpp @@ -16,19 +16,60 @@ * along with this program. If not, see . */ #include +#include +#include namespace blt::gp { + struct stack + { + blt::gp::operator_id id; + blt::size_t depth; + }; + + static inline std::stack get_base_generator(gp_program& program) + { + std::stack tree_generator; + + auto& system = program.get_typesystem(); + // select a type which has a non-empty set of non-terminals + type base_type; + do + { + base_type = system.select_type(program.get_random()); + } while (system.get_type_non_terminals(base_type.id()).empty()); + + tree_generator.emplace(system.select_non_terminal(program.get_random(), base_type.id()), 0); + + return tree_generator; + } + + template + tree_t create_tree(Func func, gp_program& program, blt::size_t min_depth, blt::size_t max_depth) + { + std::stack tree_generator = get_base_generator(program); + tree_t tree; + + while (!tree_generator.empty()) + { + + } + + return tree; + } + tree_t grow_generator_t::generate(gp_program& program, blt::size_t min_depth, blt::size_t max_depth) { - tree_t tree; - return tree; + return create_tree([]() { + + }, program, min_depth, max_depth); } tree_t full_generator_t::generate(gp_program& program, blt::size_t min_depth, blt::size_t max_depth) { - tree_t tree; - return tree; + return create_tree([]() { + + }, program, min_depth, max_depth); } } \ No newline at end of file