generators

thread
Brett 2024-06-23 14:13:50 -04:00
parent 0ecfa3750c
commit 5d8f4c00cd
7 changed files with 290 additions and 152 deletions

View File

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

45
examples/gp_test_2.cpp Normal file
View File

@ -0,0 +1,45 @@
/*
* <Short Description>
* Copyright (C) 2024 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <blt/gp/program.h>
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<float> dist(-32000, 32000);
return dist(program.get_random());
});
int main()
{
type_system.register_type<float>();
type_system.register_type<bool>();
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);
}

View File

@ -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<float> dist(-32000, 32000);
return dist(engine);
});
int main()
float nyah(float a, int b, bool c)
{
type_system.register_type<float>();
type_system.register_type<bool>();
return a + static_cast<float>(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<decltype(&"SillString")>() << 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<decltype(&"SillString")>() << std::endl;
// std::cout << alloc.pop<std::string>() << std::endl;
// std::cout << alloc.pop<bool>() << std::endl;
// std::cout << alloc.pop<bool>() << std::endl;
// std::cout << alloc.pop<double>() << std::endl;
// std::cout << alloc.pop<float>() << std::endl;
// std::cout << alloc.pop<int>() << 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<large>();
// std::cout << "Is empty? " << alloc.empty() << std::endl;
// std::cout << alloc.pop<silly>() << std::endl;
// std::cout << "Is empty? " << alloc.empty() << std::endl;
// alloc.pop<super_large>();
// std::cout << "Is empty? " << alloc.empty() << std::endl;
// alloc.pop<large>();
// std::cout << "Is empty? " << alloc.empty() << std::endl;
// std::cout << alloc.pop<silly>() << 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<float(float, int, bool)> silly_op(nyah);
// blt::gp::operation_t<float(float, float)> 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<void*>(head->metadata.offset);
// //return std::align(alignment, bytes, pointer, remaining_bytes);
//
// float f = 10.5;
// int i = 412;
// bool b = true;
//
// std::array<void*, 3> arr{reinterpret_cast<void*>(&f), reinterpret_cast<void*>(&i), reinterpret_cast<void*>(&b)};
//
// blt::span<void*, 3> 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<decltype(&"SillString")>() << 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<decltype(&"SillString")>() << std::endl;
std::cout << alloc.pop<std::string>() << std::endl;
std::cout << alloc.pop<bool>() << std::endl;
std::cout << alloc.pop<bool>() << std::endl;
std::cout << alloc.pop<double>() << std::endl;
std::cout << alloc.pop<float>() << std::endl;
std::cout << alloc.pop<int>() << 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<large>();
std::cout << "Is empty? " << alloc.empty() << std::endl;
std::cout << alloc.pop<silly>() << std::endl;
std::cout << "Is empty? " << alloc.empty() << std::endl;
alloc.pop<super_large>();
std::cout << "Is empty? " << alloc.empty() << std::endl;
alloc.pop<large>();
std::cout << "Is empty? " << alloc.empty() << std::endl;
std::cout << alloc.pop<silly>() << 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<void*>(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<void*, 3> arr{reinterpret_cast<void*>(&f), reinterpret_cast<void*>(&i), reinterpret_cast<void*>(&b)};
//blt::span<void*, 3> spv{arr};
std::cout << silly_op.operator()(alloc) << std::endl;
std::cout << "Hello World!" << std::endl;
}

View File

@ -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<typename Return, typename... Args>
void add_operator(const operation_t<Return(Args...)>& op)
{
auto return_type_id = system.get_type<Return>().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<Args>()), ...);
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<blt::size_t> 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<blt::size_t> dist(0, non_terminals[id].size() - 1);
return non_terminals[id][dist(engine)];
}
inline std::vector<type>& 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<std::vector<operator_id>> terminals;
blt::expanding_buffer<std::vector<operator_id>> non_terminals;
// indexed from OPERATOR ID (operator number)
blt::expanding_buffer<std::vector<type>> argument_types;
std::vector<std::function<void(stack_allocator&)>> operators;
std::mt19937_64 engine;
};
}

View File

@ -24,6 +24,7 @@
#include <stdexcept>
#include <cstdlib>
#include <memory>
#include <type_traits>
namespace blt::gp
{
@ -40,9 +41,10 @@ namespace blt::gp
template<typename T>
void push(T&& value)
{
using NO_REF_T = std::remove_reference_t<T>;
auto ptr = allocate_bytes<T>();
head->metadata.offset = static_cast<blt::u8*>(ptr) + aligned_size<T>();
new(ptr) T(std::forward<T>(value));
new(ptr) NO_REF_T(std::forward<T>(value));
}
template<typename T>

View File

@ -21,7 +21,13 @@
#include <blt/std/hashmap.h>
#include <blt/std/types.h>
#include <blt/std/ranges.h>
#include <blt/std/utility.h>
#include <blt/std/memory.h>
#include <blt/gp/fwdecl.h>
#include <blt/gp/stack.h>
#include <blt/gp/operations.h>
#include <random>
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<T>()];
}
/**
* 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<blt::size_t> 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<blt::size_t> dist(0, non_terminals[id].size() - 1);
return non_terminals[id][dist(engine)];
}
inline std::vector<type>& get_argument_types(operator_id id)
{
return argument_types[id];
}
inline std::vector<operator_id>& get_type_terminals(type_id id)
{
return terminals[id];
}
inline std::vector<operator_id>& get_type_non_terminals(type_id id)
{
return non_terminals[id];
}
template<typename Return, typename... Args>
void add_operator(const operation_t<Return(Args...)>& op)
{
auto return_type_id = get_type<Return>().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<Args>()), ...);
operators.push_back(op.make_callable());
}
private:
blt::hashmap_t<std::string, type> types;
// 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;
// indexed from OPERATOR ID (operator number)
blt::expanding_buffer<std::vector<type>> argument_types;
std::vector<std::function<void(stack_allocator&)>> operators;
};
}

View File

@ -16,19 +16,60 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <blt/gp/generators.h>
#include <blt/gp/program.h>
#include <stack>
namespace blt::gp
{
struct stack
{
blt::gp::operator_id id;
blt::size_t depth;
};
static inline std::stack<stack> get_base_generator(gp_program& program)
{
std::stack<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<typename Func>
tree_t create_tree(Func func, gp_program& program, blt::size_t min_depth, blt::size_t max_depth)
{
std::stack<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);
}
}