diff --git a/CMakeLists.txt b/CMakeLists.txt index 307da13..1eab1bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(blt-gp VERSION 0.0.38) +project(blt-gp VERSION 0.0.40) include(CTest) diff --git a/examples/gp_test_1.cpp b/examples/gp_test_1.cpp index a1fa8fd..8dce123 100644 --- a/examples/gp_test_1.cpp +++ b/examples/gp_test_1.cpp @@ -311,7 +311,7 @@ namespace blt::gp::detail class operator_storage_test { public: - explicit operator_storage_test(blt::gp::gp_operations& ops): ops(ops) + explicit operator_storage_test(blt::gp::operator_builder& ops): ops(ops) {} inline blt::gp::detail::callable_t& operator[](blt::size_t index) @@ -320,7 +320,7 @@ namespace blt::gp::detail } private: - blt::gp::gp_operations& ops; + blt::gp::operator_builder& ops; }; } @@ -413,9 +413,9 @@ int main() return ctx.x; }); - blt::gp::type_system system; + blt::gp::type_provider system; system.register_type(); - blt::gp::gp_operations ops{system}; + blt::gp::operator_builder ops{system}; //BLT_TRACE(blt::type_string()); //BLT_TRACE(typeid(decltype(silly_op_3)::first::type).name()); diff --git a/examples/gp_test_2.cpp b/examples/gp_test_2.cpp index 3abbdd8..8bd0277 100644 --- a/examples/gp_test_2.cpp +++ b/examples/gp_test_2.cpp @@ -23,7 +23,7 @@ static constexpr long SEED = 41912; -blt::gp::type_system type_system; +blt::gp::type_provider type_system; blt::gp::gp_program program(type_system, std::mt19937_64{SEED}); // NOLINT blt::gp::operation_t add([](float a, float b) { @@ -52,7 +52,7 @@ int main() { type_system.register_type(); - blt::gp::gp_operations silly{type_system}; + blt::gp::operator_builder silly{type_system}; silly.add_operator(add); silly.add_operator(sub); silly.add_operator(mul); diff --git a/examples/gp_test_3.cpp b/examples/gp_test_3.cpp index 32c2e6d..699cab4 100644 --- a/examples/gp_test_3.cpp +++ b/examples/gp_test_3.cpp @@ -22,7 +22,7 @@ static constexpr long SEED = 41912; -blt::gp::type_system type_system; +blt::gp::type_provider type_system; blt::gp::gp_program program(type_system, std::mt19937_64{SEED}); // NOLINT blt::gp::operation_t add([](float a, float b) { return a + b; }); @@ -54,7 +54,7 @@ int main() type_system.register_type(); type_system.register_type(); - blt::gp::gp_operations silly{type_system}; + blt::gp::operator_builder silly{type_system}; silly.add_operator(add); silly.add_operator(sub); silly.add_operator(mul); diff --git a/examples/gp_test_4.cpp b/examples/gp_test_4.cpp index df5d389..d350006 100644 --- a/examples/gp_test_4.cpp +++ b/examples/gp_test_4.cpp @@ -22,7 +22,7 @@ static constexpr long SEED = 41912; -blt::gp::type_system type_system; +blt::gp::type_provider type_system; blt::gp::gp_program program(type_system, std::mt19937_64{SEED}); // NOLINT blt::gp::operation_t add([](float a, float b) { return a + b; }); @@ -54,25 +54,25 @@ int main() type_system.register_type(); type_system.register_type(); - blt::gp::gp_operations silly{type_system}; - silly.add_operator(add); - silly.add_operator(sub); - silly.add_operator(mul); - silly.add_operator(pro_div); + blt::gp::operator_builder builder{type_system}; + builder.add_operator(add); + builder.add_operator(sub); + builder.add_operator(mul); + builder.add_operator(pro_div); - silly.add_operator(op_if); - silly.add_operator(eq_f); - silly.add_operator(eq_b); - silly.add_operator(lt); - silly.add_operator(gt); - silly.add_operator(op_and); - silly.add_operator(op_or); - silly.add_operator(op_xor); - silly.add_operator(op_not); + builder.add_operator(op_if); + builder.add_operator(eq_f); + builder.add_operator(eq_b); + builder.add_operator(lt); + builder.add_operator(gt); + builder.add_operator(op_and); + builder.add_operator(op_or); + builder.add_operator(op_xor); + builder.add_operator(op_not); - silly.add_operator(lit, true); + builder.add_operator(lit, true); - program.set_operations(std::move(silly)); + program.set_operations(builder.build()); blt::gp::ramped_half_initializer_t pop_init; diff --git a/include/blt/gp/fwdecl.h b/include/blt/gp/fwdecl.h index 18ea465..917e45c 100644 --- a/include/blt/gp/fwdecl.h +++ b/include/blt/gp/fwdecl.h @@ -29,7 +29,7 @@ namespace blt::gp class type; - class type_system; + class type_provider; struct op_container_t; diff --git a/include/blt/gp/program.h b/include/blt/gp/program.h index 6987f75..0773137 100644 --- a/include/blt/gp/program.h +++ b/include/blt/gp/program.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -52,24 +53,37 @@ namespace blt::gp blt::size_t argc_context = 0; }; + struct operator_storage + { + // 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; + blt::expanding_buffer operator_argc; + blt::hashset_t static_types; + std::vector operators; + std::vector transfer_funcs; + }; + template - class gp_operations + class operator_builder { friend class gp_program; friend class blt::gp::detail::operator_storage_test; public: - explicit gp_operations(type_system& system): system(system) + explicit operator_builder(type_provider& system): system(system) {} template - gp_operations& add_operator(const operation_t& op, bool is_static = false) + operator_builder& add_operator(const operation_t& op, bool is_static = false) { auto return_type_id = system.get_type().id(); - auto operator_id = blt::gp::operator_id(operators.size()); + auto operator_id = blt::gp::operator_id(storage.operators.size()); - auto& operator_list = op.get_argc() == 0 ? terminals : non_terminals; + auto& operator_list = op.get_argc() == 0 ? storage.terminals : storage.non_terminals; operator_list[return_type_id].push_back(operator_id); if constexpr (sizeof...(Args) > 0) @@ -84,16 +98,68 @@ namespace blt::gp BLT_ASSERT(argc.argc_context - argc.argc <= 1 && "Cannot pass multiple context as arguments!"); - operator_argc[operator_id] = argc; + storage.operator_argc[operator_id] = argc; - operators.push_back(op.template make_callable()); - transfer_funcs.push_back([](stack_allocator& to, stack_allocator& from) { + storage.operators.push_back(op.template make_callable()); + storage.transfer_funcs.push_back([](stack_allocator& to, stack_allocator& from) { to.push(from.pop()); }); if (is_static) - static_types.insert(operator_id); + storage.static_types.insert(operator_id); return *this; } + + operator_storage&& build() + { + blt::hashset_t has_terminals; + + for (const auto& v : blt::enumerate(storage.terminals)) + { + if (!v.second.empty()) + has_terminals.insert(v.first); + } + + blt::expanding_buffer>> operators_ordered_terminals; + + for (const auto& op_r : blt::enumerate(storage.non_terminals)) + { + if (op_r.second.empty()) + continue; + auto return_type = op_r.first; + std::vector> ordered_terminals; + for (const auto& op : op_r.second) + { + // count number of terminals + blt::size_t terminals = 0; + for (const auto& type : storage.argument_types[op]) + { + if (has_terminals.contains(type.id())) + terminals++; + } + ordered_terminals.emplace_back(op, terminals); + } + bool found = false; + for (const auto& terms : ordered_terminals) + { + if (terms.second != 0) + { + found = true; + break; + } + } + if (!found) + { + BLT_ABORT(("Failed to find non-terminals ")); + } + + std::sort(ordered_terminals.begin(), ordered_terminals.end(), [](const auto& a, const auto& b) { + return a.second > b.second; + }); + operators_ordered_terminals[return_type] = ordered_terminals; + } + + return std::move(storage); + } private: template @@ -101,21 +167,12 @@ namespace blt::gp { if constexpr (!std::is_same_v>) { - argument_types[operator_id].push_back(system.get_type()); + storage.argument_types[operator_id].push_back(system.get_type()); } } - type_system& system; - - // 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; - blt::expanding_buffer operator_argc; - blt::hashset_t static_types; - std::vector operators; - std::vector transfer_funcs; + type_provider& system; + operator_storage storage; }; class gp_program @@ -129,7 +186,7 @@ namespace blt::gp * @param engine random engine to use throughout the program. TODO replace this with something better * @param context_size number of arguments which are always present as "context" to the GP system / operators */ - explicit gp_program(type_system& system, std::mt19937_64 engine): + explicit gp_program(type_provider& system, std::mt19937_64 engine): system(system), engine(engine) {} @@ -156,23 +213,23 @@ namespace blt::gp return dist(engine) < cutoff; } - [[nodiscard]] inline type_system& get_typesystem() + [[nodiscard]] inline type_provider& get_typesystem() { return system; } inline operator_id select_terminal(type_id id) { - std::uniform_int_distribution dist(0, terminals[id].size() - 1); - return terminals[id][dist(engine)]; + std::uniform_int_distribution dist(0, storage.terminals[id].size() - 1); + return storage.terminals[id][dist(engine)]; } inline operator_id select_non_terminal(type_id id) { - std::uniform_int_distribution dist(0, non_terminals[id].size() - 1); - return non_terminals[id][dist(engine)]; + std::uniform_int_distribution dist(0, storage.non_terminals[id].size() - 1); + return storage.non_terminals[id][dist(engine)]; } - + // inline operator_id select_non_terminal_too_deep(type_id id) // { // std::uniform_int_distribution dist(0, non_terminals[id].size() - 1); @@ -189,65 +246,51 @@ namespace blt::gp inline std::vector& get_argument_types(operator_id id) { - return argument_types[id]; + return storage.argument_types[id]; } inline std::vector& get_type_terminals(type_id id) { - return terminals[id]; + return storage.terminals[id]; } inline std::vector& get_type_non_terminals(type_id id) { - return non_terminals[id]; + return storage.non_terminals[id]; } inline argc_t get_argc(operator_id id) { - return operator_argc[id]; + return storage.operator_argc[id]; } inline detail::callable_t& get_operation(operator_id id) { - return operators[id]; + return storage.operators[id]; } inline detail::transfer_t& get_transfer_func(operator_id id) { - return transfer_funcs[id]; + return storage.transfer_funcs[id]; } inline bool is_static(operator_id id) { - return static_types.contains(static_cast(id)); + return storage.static_types.contains(static_cast(id)); } - template - inline void set_operations(gp_operations&& op) + inline void set_operations(operator_storage&& op) { - terminals = std::move(op.terminals); - non_terminals = std::move(op.non_terminals); - argument_types = std::move(op.argument_types); - static_types = std::move(op.static_types); - operator_argc = std::move(op.operator_argc); - operators = std::move(op.operators); - transfer_funcs = std::move(op.transfer_funcs); + storage = std::move(op); } private: - type_system& system; + type_provider& system; blt::gp::stack_allocator alloc; - std::mt19937_64 engine; - // 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; - blt::expanding_buffer operator_argc; - blt::hashset_t static_types; - std::vector operators; - std::vector transfer_funcs; + operator_storage storage; + + std::mt19937_64 engine; }; } diff --git a/include/blt/gp/typesystem.h b/include/blt/gp/typesystem.h index 84cc22e..195a642 100644 --- a/include/blt/gp/typesystem.h +++ b/include/blt/gp/typesystem.h @@ -80,10 +80,10 @@ namespace blt::gp * 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 + class type_provider { public: - type_system() = default; + type_provider() = default; template inline type register_type() @@ -98,6 +98,17 @@ namespace blt::gp return types[blt::type_string_raw()]; } + inline type get_type(type_id id) + { + for (const auto& v : types) + { + if (v.second.id() == id) + return v.second; + } + BLT_ABORT(("Type " + std::to_string(id) + " does not exist").c_str()); + std::exit(0); + } + /** * This function is slow btw * @param engine diff --git a/src/generators.cpp b/src/generators.cpp index 57016e3..bbe4e11 100644 --- a/src/generators.cpp +++ b/src/generators.cpp @@ -59,12 +59,12 @@ namespace blt::gp { auto top = tree_generator.top(); tree_generator.pop(); + //BLT_INFO("%ld D: %ld (%ld left)", top.id, top.depth, tree_generator.size()); tree.get_operations().emplace_back( args.program.get_operation(top.id), args.program.get_transfer_func(top.id), - args.program.is_static(top.id) - ); + args.program.is_static(top.id)); max_depth = std::max(max_depth, top.depth); if (args.program.is_static(top.id)) @@ -159,10 +159,16 @@ namespace blt::gp auto remainder = args.size % steps; population_t pop; + BLT_INFO(steps); + BLT_INFO(per_step); + BLT_INFO(remainder); + for (auto depth : blt::range(args.min_depth, args.max_depth)) { + BLT_TRACE(depth); for (auto i = 0ul; i < per_step; i++) { + BLT_DEBUG(i); if (args.program.choice()) pop.getIndividuals().push_back(full.generate({args.program, args.root_type, args.min_depth, depth})); else