diff --git a/CMakeLists.txt b/CMakeLists.txt index f056e7e..e8c27b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(blt-gp VERSION 0.0.31) +project(blt-gp VERSION 0.0.33) option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF) option(ENABLE_UBSAN "Enable the ub sanitizer" OFF) diff --git a/examples/main.cpp b/examples/main.cpp index 212a53f..58cd793 100644 --- a/examples/main.cpp +++ b/examples/main.cpp @@ -345,12 +345,12 @@ int main_old() alloc.push(20.1230345); alloc.push(true); alloc.push(false); - alloc.push(std::string("SillyString")); + //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; @@ -389,7 +389,7 @@ int main_old() alloc.push(50); alloc.push(550.3f); alloc.push(20.1230345); - alloc.push(std::string("SillyString")); + //alloc.push(std::string("SillyString")); alloc.push(33.22f); alloc.push(120); alloc.push(true); diff --git a/include/blt/gp/fwdecl.h b/include/blt/gp/fwdecl.h index 90a87a7..2981220 100644 --- a/include/blt/gp/fwdecl.h +++ b/include/blt/gp/fwdecl.h @@ -29,6 +29,10 @@ namespace blt::gp class type_system; + struct op_container_t; + + class evaluation_context; + class tree_t; class population_t; diff --git a/include/blt/gp/generators.h b/include/blt/gp/generators.h index 51dcfd1..68e8c4c 100644 --- a/include/blt/gp/generators.h +++ b/include/blt/gp/generators.h @@ -25,53 +25,89 @@ namespace blt::gp { + struct generator_arguments + { + gp_program& program; + type_id root_type; + blt::size_t min_depth; + blt::size_t max_depth; + }; + + struct initializer_arguments + { + gp_program& program; + type_id root_type; + blt::size_t size; + blt::size_t min_depth; + blt::size_t max_depth; + + [[nodiscard]] generator_arguments to_gen_args() const + { + return {program, root_type, min_depth, max_depth}; + } + }; + // base class for any kind of tree generator class tree_generator_t { public: - virtual tree_t generate(gp_program& program, blt::size_t min_depth, blt::size_t max_depth) = 0; + virtual tree_t generate(const generator_arguments& args) = 0; }; class grow_generator_t : public tree_generator_t { public: - tree_t generate(gp_program& program, blt::size_t min_depth, blt::size_t max_depth) final; + tree_t generate(const generator_arguments& args) final; }; class full_generator_t : public tree_generator_t { public: - tree_t generate(gp_program& program, blt::size_t min_depth, blt::size_t max_depth) final; + tree_t generate(const generator_arguments& args) final; }; class population_initializer_t { public: - virtual population_t generate(gp_program& program, blt::size_t size, blt::size_t min_depth, blt::size_t max_depth) = 0; + virtual population_t generate(const initializer_arguments& args) = 0; }; - class grow_initializer_t + class grow_initializer_t : public population_initializer_t { public: - population_t generate(gp_program& program, blt::size_t size, blt::size_t min_depth, blt::size_t max_depth) final; + population_t generate(const initializer_arguments& args) final; + + private: + grow_generator_t grow; }; - class full_initializer_t + class full_initializer_t : public population_initializer_t { public: - population_t generate(gp_program& program, blt::size_t size, blt::size_t min_depth, blt::size_t max_depth) final; + population_t generate(const initializer_arguments& args) final; + + private: + full_generator_t full; }; - class half_half_initializer_t + class half_half_initializer_t : public population_initializer_t { public: - population_t generate(gp_program& program, blt::size_t size, blt::size_t min_depth, blt::size_t max_depth) final; + population_t generate(const initializer_arguments& args) final; + + private: + grow_generator_t grow; + full_generator_t full; }; - class ramped_half_initializer_t + class ramped_half_initializer_t : public population_initializer_t { public: - population_t generate(gp_program& program, blt::size_t size, blt::size_t min_depth, blt::size_t max_depth) final; + population_t generate(const initializer_arguments& args) final; + + private: + grow_generator_t grow; + full_generator_t full; }; } diff --git a/include/blt/gp/stack.h b/include/blt/gp/stack.h index 5d9f96d..94b899d 100644 --- a/include/blt/gp/stack.h +++ b/include/blt/gp/stack.h @@ -41,8 +41,8 @@ namespace blt::gp template void push(T&& value) { - static_assert(std::is_trivially_copyable_v && "Type must be bitwise copyable!"); using NO_REF_T = std::remove_reference_t; + static_assert(std::is_trivially_copyable_v && "Type must be bitwise copyable!"); auto ptr = allocate_bytes(); head->metadata.offset = static_cast(ptr) + aligned_size(); new(ptr) NO_REF_T(std::forward(value)); @@ -51,6 +51,7 @@ namespace blt::gp template T pop() { + static_assert(std::is_trivially_copyable_v> && "Type must be bitwise copyable!"); constexpr static auto TYPE_SIZE = aligned_size(); if (head == nullptr) throw std::runtime_error("Silly boi the stack is empty!"); diff --git a/include/blt/gp/tree.h b/include/blt/gp/tree.h index ff53c9e..700eeeb 100644 --- a/include/blt/gp/tree.h +++ b/include/blt/gp/tree.h @@ -24,13 +24,34 @@ #include #include +#include +#include + namespace blt::gp { struct op_container_t { - blt::gp::operator_id op_id; + op_container_t(detail::callable_t& func, u16 depth, bool isStatic, u16 argc, u8 argcContext): + func(func), depth(depth), is_static(isStatic), argc(argc), argc_context(argcContext) + {} + + detail::callable_t& func; blt::u16 depth; + bool is_static: 1; + blt::u16 argc: 15; + blt::u8 argc_context; + }; + + class evaluation_context + { + friend class tree_t; + + private: + explicit evaluation_context(stack_allocator values): values(std::move(values)) + {} + + blt::gp::stack_allocator values; }; class tree_t @@ -65,6 +86,36 @@ namespace blt::gp valid = true; return 0; } + + evaluation_context evaluate(void* context); + + /** + * Helper template for returning the result of the last evaluation + */ + template + T get_evaluation_value(evaluation_context& context) + { + return context.values.pop(); + } + + /** + * Helper template for returning the result of the last evaluation + */ + template + T& get_evaluation_ref(evaluation_context& context) + { + return context.values.from(0); + } + + /** + * Helper template for returning the result of evalutation (this calls it) + */ + template + T get_evaluation_value(void* context) + { + auto results = evaluate(context); + return results.values.pop(); + } private: bool valid = false; @@ -76,6 +127,10 @@ namespace blt::gp class population_t { public: + std::vector& getIndividuals() + { + return individuals; + } private: std::vector individuals; diff --git a/src/generators.cpp b/src/generators.cpp index c1e971b..59c5f87 100644 --- a/src/generators.cpp +++ b/src/generators.cpp @@ -21,6 +21,8 @@ namespace blt::gp { + // TODO: change how the generators work, but keep how nice everything is within the C++ file. less headers! + // maybe have tree call the generate functions with out variables as the members of tree_t struct stack { @@ -28,27 +30,27 @@ namespace blt::gp blt::size_t depth; }; - inline std::stack get_initial_stack(gp_program& program) + inline std::stack get_initial_stack(gp_program& program, type_id root_type) { 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 (program.get_type_non_terminals(base_type.id()).empty()); - - tree_generator.push(stack{program.select_non_terminal(base_type.id()), 1}); +// +// 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 (program.get_type_non_terminals(base_type.id()).empty()); +// + tree_generator.push(stack{program.select_non_terminal(root_type), 1}); return tree_generator; } template - inline tree_t create_tree(Func&& perChild, gp_program& program) + inline tree_t create_tree(Func&& perChild, const generator_arguments& args) { - std::stack tree_generator = get_initial_stack(program); + std::stack tree_generator = get_initial_stack(args.program, args.root_type); blt::size_t max_depth = 0; tree_t tree; @@ -57,18 +59,24 @@ namespace blt::gp auto top = tree_generator.top(); tree_generator.pop(); - tree.get_operations().push_back({top.id, static_cast(top.depth)}); + tree.get_operations().emplace_back( + args.program.get_operation(top.id), + static_cast(top.depth), + args.program.is_static(top.id), + static_cast(args.program.get_argc(top.id).argc), + static_cast(args.program.get_argc(top.id).argc_context) + ); max_depth = std::max(max_depth, top.depth); - if (program.is_static(top.id)) + if (args.program.is_static(top.id)) { - program.get_operation(top.id)(nullptr, tree.get_values()); + args.program.get_operation(top.id)(nullptr, tree.get_values()); continue; } - for (const auto& child : program.get_argument_types(top.id)) + for (const auto& child : args.program.get_argument_types(top.id)) { - std::forward(perChild)(program, tree_generator, child, top.depth + 1); + std::forward(perChild)(args.program, tree_generator, child, top.depth + 1); } } @@ -77,50 +85,96 @@ namespace blt::gp return tree; } - tree_t grow_generator_t::generate(gp_program& program, blt::size_t min_depth, blt::size_t max_depth) + tree_t grow_generator_t::generate(const generator_arguments& args) { - return create_tree([min_depth, max_depth](gp_program& program, std::stack& tree_generator, const type& type, blt::size_t new_depth) { - if (new_depth >= max_depth) + return create_tree([args](gp_program& program, std::stack& tree_generator, const type& type, blt::size_t new_depth) { + if (new_depth >= args.max_depth) { tree_generator.push({program.select_terminal(type.id()), new_depth}); return; } - if (program.choice() || new_depth < min_depth) + if (program.choice() || new_depth < args.min_depth) tree_generator.push({program.select_non_terminal(type.id()), new_depth}); else tree_generator.push({program.select_terminal(type.id()), new_depth}); - }, program); + }, args); } - tree_t full_generator_t::generate(gp_program& program, blt::size_t, blt::size_t max_depth) + tree_t full_generator_t::generate(const generator_arguments& args) { - return create_tree([max_depth](gp_program& program, std::stack& tree_generator, const type& type, blt::size_t new_depth) { - if (new_depth >= max_depth) + return create_tree([args](gp_program& program, std::stack& tree_generator, const type& type, blt::size_t new_depth) { + if (new_depth >= args.max_depth) { tree_generator.push({program.select_terminal(type.id()), new_depth}); return; } tree_generator.push({program.select_non_terminal(type.id()), new_depth}); - }, program); + }, args); } - population_t grow_initializer_t::generate(gp_program& program, blt::size_t size, blt::size_t min_depth, blt::size_t max_depth) + population_t grow_initializer_t::generate(const initializer_arguments& args) { - return population_t(); + population_t pop; + + for (auto i = 0ul; i < args.size; i++) + pop.getIndividuals().push_back(grow.generate(args.to_gen_args())); + + return pop; } - population_t full_initializer_t::generate(gp_program& program, blt::size_t size, blt::size_t min_depth, blt::size_t max_depth) + population_t full_initializer_t::generate(const initializer_arguments& args) { - return population_t(); + population_t pop; + + for (auto i = 0ul; i < args.size; i++) + pop.getIndividuals().push_back(full.generate(args.to_gen_args())); + + return pop; } - population_t half_half_initializer_t::generate(gp_program& program, blt::size_t size, blt::size_t min_depth, blt::size_t max_depth) + population_t half_half_initializer_t::generate(const initializer_arguments& args) { - return population_t(); + population_t pop; + + for (auto i = 0ul; i < args.size; i++) + { + if (args.program.choice()) + pop.getIndividuals().push_back(full.generate(args.to_gen_args())); + else + pop.getIndividuals().push_back(grow.generate(args.to_gen_args())); + } + + return pop; } - population_t ramped_half_initializer_t::generate(gp_program& program, blt::size_t size, blt::size_t min_depth, blt::size_t max_depth) + population_t ramped_half_initializer_t::generate(const initializer_arguments& args) { - return population_t(); + auto steps = args.max_depth - args.min_depth; + auto per_step = args.size / steps; + auto remainder = args.size % steps; + population_t pop; + + for (auto depth : blt::range(args.min_depth, args.max_depth)) + { + for (auto i = 0ul; i < per_step; i++) + { + if (args.program.choice()) + pop.getIndividuals().push_back(full.generate({args.program, args.root_type, args.min_depth, depth})); + else + pop.getIndividuals().push_back(grow.generate({args.program, args.root_type, args.min_depth, depth})); + } + } + + for (auto i = 0ul; i < remainder; i++) + { + if (args.program.choice()) + pop.getIndividuals().push_back(full.generate(args.to_gen_args())); + else + pop.getIndividuals().push_back(grow.generate(args.to_gen_args())); + } + + blt_assert(pop.getIndividuals().size() == args.size); + + return pop; } } \ No newline at end of file diff --git a/src/tree.cpp b/src/tree.cpp new file mode 100644 index 0000000..54b3408 --- /dev/null +++ b/src/tree.cpp @@ -0,0 +1,33 @@ +/* + * + * 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 + +namespace blt::gp +{ + evaluation_context tree_t::evaluate(void* context) + { + evaluation_context results {values}; + + auto& value_stack = results.values; + std::stack operations_stack; + + + + return results; + } +} \ No newline at end of file