generators
parent
0ecfa3750c
commit
5d8f4c00cd
|
@ -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)
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue