From 32a83e725c4579e44d3df7357b9933e22fc2f3b3 Mon Sep 17 00:00:00 2001 From: Brett Laptop Date: Tue, 14 Jan 2025 14:00:55 -0500 Subject: [PATCH] move evaluation function into tree class, can now call 'make_execution_lambda' to make the lambda previously found in the operator_builder class --- include/blt/gp/operations.h | 42 +- include/blt/gp/program.h | 71 +--- include/blt/gp/tree.h | 638 ++++++++++++++++------------- include/blt/gp/util/meta.h | 66 +++ src/generators.cpp | 2 +- tests/symbolic_regression_test.cpp | 28 +- 6 files changed, 446 insertions(+), 401 deletions(-) create mode 100644 include/blt/gp/util/meta.h diff --git a/include/blt/gp/operations.h b/include/blt/gp/operations.h index 9662d6a..38a7f77 100644 --- a/include/blt/gp/operations.h +++ b/include/blt/gp/operations.h @@ -22,53 +22,13 @@ #include #include #include +#include #include #include #include namespace blt::gp { - namespace detail - { - template - using remove_cv_ref = std::remove_cv_t>; - - - template - struct first_arg; - - template - struct first_arg - { - using type = First; - }; - - template<> - struct first_arg<> - { - using type = void; - }; - - template - struct is_same; - - template - struct is_same : public std::false_type - { - }; - - template - struct is_same : public std::is_same - { - }; - - template - constexpr bool is_same_v = is_same::value; - - struct empty_t - { - }; - } template struct call_with diff --git a/include/blt/gp/program.h b/include/blt/gp/program.h index 75dd657..6df6131 100644 --- a/include/blt/gp/program.h +++ b/include/blt/gp/program.h @@ -128,33 +128,9 @@ namespace blt::gp largest_returns)), ...); // largest = largest * largest_argc; - blt::size_t largest = largest_args * largest_argc * largest_returns * largest_argc; + size_t largest = largest_args * largest_argc * largest_returns * largest_argc; - storage.eval_func = [&operators..., largest](const tree_t& tree, void* context) -> evaluation_context& { - const auto& ops = tree.get_operations(); - const auto& vals = tree.get_values(); - - static thread_local evaluation_context results{}; - results.values.reset(); - results.values.reserve(largest); - - blt::size_t total_so_far = 0; - blt::size_t op_pos = 0; - - for (const auto& operation : iterate(ops).rev()) - { - op_pos++; - if (operation.is_value()) - { - total_so_far += operation.type_size(); - results.values.copy_from(vals.from(total_so_far), operation.type_size()); - continue; - } - call_jmp_table(operation.id(), context, results.values, results.values, operators...); - } - - return results; - }; + storage.eval_func = tree_t::make_execution_lambda(largest, operators...); blt::hashset_t has_terminals; @@ -306,48 +282,7 @@ namespace blt::gp types.push_back(storage.system.get_type().id()); } } - - template - static void execute(void* context, stack_allocator& write_stack, stack_allocator& read_stack, Operator& operation) - { - if constexpr (std::is_same_v, Context>) - { - write_stack.push(operation(context, read_stack)); - } - else - { - write_stack.push(operation(read_stack)); - } - } - - template - static bool call(blt::size_t op, void* context, stack_allocator& write_stack, stack_allocator& read_stack, Operator& operation) - { - if (id == op) - { - execute(context, write_stack, read_stack, operation); - return false; - } - return true; - } - - template - static void call_jmp_table_internal(size_t op, void* context, stack_allocator& write_stack, stack_allocator& read_stack, - std::integer_sequence, Operators&... operators) - { - if (op >= sizeof...(operator_ids)) - { - BLT_UNREACHABLE; - } - (call(op, context, write_stack, read_stack, operators) && ...); - } - - template - static void call_jmp_table(size_t op, void* context, stack_allocator& write_stack, stack_allocator& read_stack, - Operators&... operators) - { - call_jmp_table_internal(op, context, write_stack, read_stack, std::index_sequence_for(), operators...); - } + private: program_operator_storage_t storage; }; diff --git a/include/blt/gp/tree.h b/include/blt/gp/tree.h index f27e9b5..7ae7f4e 100644 --- a/include/blt/gp/tree.h +++ b/include/blt/gp/tree.h @@ -19,6 +19,7 @@ #ifndef BLT_GP_TREE_H #define BLT_GP_TREE_H +#include #include #include #include @@ -31,16 +32,17 @@ namespace blt::gp { - struct op_container_t { op_container_t(const size_t type_size, const operator_id id, const bool is_value): - m_type_size(type_size), m_id(id), m_is_value(is_value), m_has_drop(false) - {} + m_type_size(type_size), m_id(id), m_is_value(is_value), m_has_drop(false) + { + } op_container_t(const size_t type_size, const operator_id id, const bool is_value, const bool has_drop): - m_type_size(type_size), m_id(id), m_is_value(is_value), m_has_drop(has_drop) - {} + m_type_size(type_size), m_id(id), m_is_value(is_value), m_has_drop(has_drop) + { + } [[nodiscard]] auto type_size() const { @@ -61,206 +63,285 @@ namespace blt::gp { return m_has_drop; } + private: size_t m_type_size; operator_id m_id; bool m_is_value; bool m_has_drop; }; - + class evaluation_context { - public: - explicit evaluation_context() = default; - - blt::gp::stack_allocator values; + public: + explicit evaluation_context() = default; + + blt::gp::stack_allocator values; }; - + class tree_t { - public: - explicit tree_t(gp_program& program); - - tree_t(const tree_t& copy) = default; - - tree_t& operator=(const tree_t& copy) - { - if (this == ©) - return *this; - copy_fast(copy); + public: + explicit tree_t(gp_program& program); + + tree_t(const tree_t& copy) = default; + + tree_t& operator=(const tree_t& copy) + { + if (this == ©) return *this; - } - - /** - * This function copies the data from the provided tree, will attempt to reserve and copy in one step. - * will avoid reallocation if enough space is already present. - */ - void copy_fast(const tree_t& copy) + copy_fast(copy); + return *this; + } + + /** + * This function copies the data from the provided tree, will attempt to reserve and copy in one step. + * will avoid reallocation if enough space is already present. + */ + void copy_fast(const tree_t& copy) + { + if (this == ©) + return; + values.reserve(copy.values.stored()); + values.reset(); + values.insert(copy.values); + + operations.reserve(copy.operations.size()); + + auto copy_it = copy.operations.begin(); + auto op_it = operations.begin(); + + for (; op_it != operations.end(); ++op_it) { - if (this == ©) - return; - values.reserve(copy.values.stored()); - values.reset(); - values.insert(copy.values); - - operations.reserve(copy.operations.size()); - - auto copy_it = copy.operations.begin(); - auto op_it = operations.begin(); - - for (; op_it != operations.end(); ++op_it) + if (op_it->has_drop()) { - if (op_it->has_drop()) - { - - } - if (copy_it == copy.operations.end()) - break; - *op_it = *copy_it; - if (copy_it->has_drop()) - { - - } - ++copy_it; } - const auto op_it_cpy = op_it; - for (;op_it != operations.end(); ++op_it) + if (copy_it == copy.operations.end()) + break; + *op_it = *copy_it; + if (copy_it->has_drop()) { - if (op_it->has_drop()) - { - - } } - operations.erase(op_it_cpy, operations.end()); - for (; copy_it != copy.operations.end(); ++copy_it) - operations.emplace_back(*copy_it); + ++copy_it; } - - tree_t(tree_t&& move) = default; - - tree_t& operator=(tree_t&& move) = default; - - void clear(gp_program& program); - - struct child_t + const auto op_it_cpy = op_it; + for (; op_it != operations.end(); ++op_it) { - blt::ptrdiff_t start; - // one past the end - blt::ptrdiff_t end; + if (op_it->has_drop()) + { + } + } + operations.erase(op_it_cpy, operations.end()); + for (; copy_it != copy.operations.end(); ++copy_it) + operations.emplace_back(*copy_it); + } + + tree_t(tree_t&& move) = default; + + tree_t& operator=(tree_t&& move) = default; + + void clear(gp_program& program); + + struct child_t + { + ptrdiff_t start; + // one past the end + ptrdiff_t end; + }; + + void insert_operator(const op_container_t& container) + { + operations.emplace_back(container); + } + + template + void emplace_operator(Args&&... args) + { + operations.emplace_back(std::forward(args)...); + } + + [[nodiscard]] inline tracked_vector& get_operations() + { + return operations; + } + + [[nodiscard]] inline const tracked_vector& get_operations() const + { + return operations; + } + + [[nodiscard]] inline stack_allocator& get_values() + { + return values; + } + + [[nodiscard]] inline const blt::gp::stack_allocator& get_values() const + { + return values; + } + + template || std::is_null_pointer_v), bool> = true> + [[nodiscard]] evaluation_context& evaluate(const T& context) const + { + return (*func)(*this, const_cast(static_cast(&context))); + } + + [[nodiscard]] evaluation_context& evaluate() const + { + return (*func)(*this, nullptr); + } + + blt::size_t get_depth(gp_program& program) const; + + /** + * Helper template for returning the result of the last evaluation + */ + template + T get_evaluation_value(evaluation_context& context) const + { + return context.values.pop(); + } + + /** + * Helper template for returning the result of the last evaluation + */ + template + T& get_evaluation_ref(evaluation_context& context) const + { + return context.values.from(0); + } + + /** + * Helper template for returning the result of evaluation (this calls it) + */ + template + T get_evaluation_value(const Context& context) const + { + return evaluate(context).values.template pop(); + } + + template + T get_evaluation_value() const + { + return evaluate().values.pop(); + } + + void print(gp_program& program, std::ostream& output, bool print_literals = true, bool pretty_indent = false, + bool include_types = false) const; + + bool check(gp_program& program, void* context) const; + + void find_child_extends(gp_program& program, tracked_vector& vec, blt::size_t parent_node, blt::size_t argc) const; + + // places one past the end of the child. so it's [start, end) + blt::ptrdiff_t find_endpoint(blt::gp::gp_program& program, blt::ptrdiff_t start) const; + + blt::ptrdiff_t find_parent(blt::gp::gp_program& program, blt::ptrdiff_t start) const; + + // valid for [begin, end) + static size_t total_value_bytes(const detail::const_op_iter_t begin, const detail::const_op_iter_t end) + { + size_t total = 0; + for (auto it = begin; it != end; ++it) + { + if (it->is_value()) + total += it->type_size(); + } + return total; + } + + [[nodiscard]] size_t total_value_bytes(const size_t begin, const size_t end) const + { + return total_value_bytes(operations.begin() + static_cast(begin), + operations.begin() + static_cast(end)); + } + + [[nodiscard]] size_t total_value_bytes(const size_t begin) const + { + return total_value_bytes(operations.begin() + static_cast(begin), operations.end()); + } + + [[nodiscard]] size_t total_value_bytes() const + { + return total_value_bytes(operations.begin(), operations.end()); + } + + template + static auto make_execution_lambda(size_t call_reserve_size, Operators&... operators) + { + return [call_reserve_size, &operators...](const tree_t& tree, void* context) -> evaluation_context& { + const auto& ops = tree.operations; + const auto& vals = tree.values; + + thread_local evaluation_context results{}; + results.values.reset(); + results.values.reserve(call_reserve_size); + + size_t total_so_far = 0; + + for (const auto& operation : iterate(ops).rev()) + { + if (operation.is_value()) + { + total_so_far += operation.type_size(); + results.values.copy_from(vals.from(total_so_far), operation.type_size()); + continue; + } + call_jmp_table(operation.id(), context, results.values, results.values, operators...); + } + + return results; }; - - [[nodiscard]] inline tracked_vector& get_operations() + } + + private: + template + static void execute(void* context, stack_allocator& write_stack, stack_allocator& read_stack, Operator& operation) + { + if constexpr (std::is_same_v, Context>) { - return operations; + write_stack.push(operation(context, read_stack)); } - - [[nodiscard]] inline const tracked_vector& get_operations() const + else { - return operations; + write_stack.push(operation(read_stack)); } - - [[nodiscard]] inline stack_allocator& get_values() + } + + template + static bool call(const size_t op, void* context, stack_allocator& write_stack, stack_allocator& read_stack, Operator& operation) + { + if (id == op) { - return values; + execute(context, write_stack, read_stack, operation); + return false; } - - [[nodiscard]] inline const blt::gp::stack_allocator& get_values() const + return true; + } + + template + static void call_jmp_table_internal(size_t op, void* context, stack_allocator& write_stack, stack_allocator& read_stack, + std::integer_sequence, Operators&... operators) + { + if (op >= sizeof...(operator_ids)) { - return values; + BLT_UNREACHABLE; } - - template || std::is_null_pointer_v), bool> = true> - [[nodiscard]] evaluation_context& evaluate(const T& context) const - { - return (*func)(*this, const_cast(static_cast(&context))); - } - - [[nodiscard]] evaluation_context& evaluate() const - { - return (*func)(*this, nullptr); - } - - blt::size_t get_depth(gp_program& program) const; - - /** - * Helper template for returning the result of the last evaluation - */ - template - T get_evaluation_value(evaluation_context& context) const - { - return context.values.pop(); - } - - /** - * Helper template for returning the result of the last evaluation - */ - template - T& get_evaluation_ref(evaluation_context& context) const - { - return context.values.from(0); - } - - /** - * Helper template for returning the result of evaluation (this calls it) - */ - template - T get_evaluation_value(const Context& context) const - { - return evaluate(context).values.template pop(); - } - - template - T get_evaluation_value() const - { - return evaluate().values.pop(); - } - - void print(gp_program& program, std::ostream& output, bool print_literals = true, bool pretty_indent = false, - bool include_types = false) const; - - bool check(gp_program& program, void* context) const; - - void find_child_extends(gp_program& program, tracked_vector& vec, blt::size_t parent_node, blt::size_t argc) const; - - // places one past the end of the child. so it's [start, end) - blt::ptrdiff_t find_endpoint(blt::gp::gp_program& program, blt::ptrdiff_t start) const; - - blt::ptrdiff_t find_parent(blt::gp::gp_program& program, blt::ptrdiff_t start) const; - - // valid for [begin, end) - static size_t total_value_bytes(const detail::const_op_iter_t begin, const detail::const_op_iter_t end) - { - size_t total = 0; - for (auto it = begin; it != end; ++it) - { - if (it->is_value()) - total += it->type_size(); - } - return total; - } - - [[nodiscard]] size_t total_value_bytes(const size_t begin, const size_t end) const - { - return total_value_bytes(operations.begin() + static_cast(begin), - operations.begin() + static_cast(end)); - } - - [[nodiscard]] size_t total_value_bytes(const size_t begin) const - { - return total_value_bytes(operations.begin() + static_cast(begin), operations.end()); - } - - [[nodiscard]] size_t total_value_bytes() const - { - return total_value_bytes(operations.begin(), operations.end()); - } - - private: - tracked_vector operations; - stack_allocator values; - detail::eval_func_t* func; + (call(op, context, write_stack, read_stack, operators) && ...); + } + + template + static void call_jmp_table(size_t op, void* context, stack_allocator& write_stack, stack_allocator& read_stack, + Operators&... operators) + { + call_jmp_table_internal(op, context, write_stack, read_stack, std::index_sequence_for(), operators...); + } + + tracked_vector operations; + stack_allocator values; + detail::eval_func_t* func; }; - + struct fitness_t { double raw_fitness = 0; @@ -268,12 +349,12 @@ namespace blt::gp double adjusted_fitness = 0; i64 hits = 0; }; - + struct individual_t { tree_t tree; fitness_t fitness; - + void copy_fast(const tree_t& copy) { // fast copy of the tree @@ -281,131 +362,134 @@ namespace blt::gp // reset fitness fitness = {}; } - + individual_t() = delete; - + explicit individual_t(tree_t&& tree): tree(std::move(tree)) - {} - + { + } + explicit individual_t(const tree_t& tree): tree(tree) - {} - + { + } + individual_t(const individual_t&) = default; - + individual_t(individual_t&&) = default; - + individual_t& operator=(const individual_t&) = delete; - + individual_t& operator=(individual_t&&) = default; }; - + class population_t { + public: + class population_tree_iterator + { public: - class population_tree_iterator + population_tree_iterator(tracked_vector& ind, blt::size_t pos): ind(ind), pos(pos) { - public: - population_tree_iterator(tracked_vector& ind, blt::size_t pos): ind(ind), pos(pos) - {} - - auto begin() - { - return population_tree_iterator(ind, 0); - } - - auto end() - { - return population_tree_iterator(ind, ind.size()); - } - - population_tree_iterator operator++(int) - { - auto prev = pos++; - return {ind, prev}; - } - - population_tree_iterator operator++() - { - return {ind, ++pos}; - } - - tree_t& operator*() - { - return ind[pos].tree; - } - - tree_t& operator->() - { - return ind[pos].tree; - } - - friend bool operator==(population_tree_iterator a, population_tree_iterator b) - { - return a.pos == b.pos; - } - - friend bool operator!=(population_tree_iterator a, population_tree_iterator b) - { - return a.pos != b.pos; - } - - private: - tracked_vector& ind; - blt::size_t pos; - }; - - tracked_vector& get_individuals() - { - return individuals; } - - [[nodiscard]] const tracked_vector& get_individuals() const - { - return individuals; - } - - population_tree_iterator for_each_tree() - { - return population_tree_iterator{individuals, 0}; - } - + auto begin() { - return individuals.begin(); + return population_tree_iterator(ind, 0); } - + auto end() { - return individuals.end(); + return population_tree_iterator(ind, ind.size()); } - - [[nodiscard]] auto begin() const + + population_tree_iterator operator++(int) { - return individuals.begin(); + auto prev = pos++; + return {ind, prev}; } - - [[nodiscard]] auto end() const + + population_tree_iterator operator++() { - return individuals.end(); + return {ind, ++pos}; } - - void clear() + + tree_t& operator*() { - individuals.clear(); + return ind[pos].tree; } - - population_t() = default; - - population_t(const population_t&) = default; - - population_t(population_t&&) = default; - - population_t& operator=(const population_t&) = delete; - - population_t& operator=(population_t&&) = default; - + + tree_t& operator->() + { + return ind[pos].tree; + } + + friend bool operator==(population_tree_iterator a, population_tree_iterator b) + { + return a.pos == b.pos; + } + + friend bool operator!=(population_tree_iterator a, population_tree_iterator b) + { + return a.pos != b.pos; + } + private: - tracked_vector individuals; + tracked_vector& ind; + blt::size_t pos; + }; + + tracked_vector& get_individuals() + { + return individuals; + } + + [[nodiscard]] const tracked_vector& get_individuals() const + { + return individuals; + } + + population_tree_iterator for_each_tree() + { + return population_tree_iterator{individuals, 0}; + } + + auto begin() + { + return individuals.begin(); + } + + auto end() + { + return individuals.end(); + } + + [[nodiscard]] auto begin() const + { + return individuals.begin(); + } + + [[nodiscard]] auto end() const + { + return individuals.end(); + } + + void clear() + { + individuals.clear(); + } + + population_t() = default; + + population_t(const population_t&) = default; + + population_t(population_t&&) = default; + + population_t& operator=(const population_t&) = delete; + + population_t& operator=(population_t&&) = default; + + private: + tracked_vector individuals; }; } diff --git a/include/blt/gp/util/meta.h b/include/blt/gp/util/meta.h new file mode 100644 index 0000000..e9f78df --- /dev/null +++ b/include/blt/gp/util/meta.h @@ -0,0 +1,66 @@ +#pragma once +/* + * 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 . + */ + +#ifndef BLT_GP_UTIL_META_H +#define BLT_GP_UTIL_META_H + +#include + +namespace blt::gp::detail +{ + template + using remove_cv_ref = std::remove_cv_t>; + + + template + struct first_arg; + + template + struct first_arg + { + using type = First; + }; + + template <> + struct first_arg<> + { + using type = void; + }; + + template + struct is_same; + + template + struct is_same : std::false_type + { + }; + + template + struct is_same : std::is_same + { + }; + + template + constexpr bool is_same_v = is_same::value; + + struct empty_t + { + }; +} + +#endif //BLT_GP_UTIL_META_H diff --git a/src/generators.cpp b/src/generators.cpp index 803a24f..0e08605 100644 --- a/src/generators.cpp +++ b/src/generators.cpp @@ -61,7 +61,7 @@ namespace blt::gp auto& info = args.program.get_operator_info(top.id); - tree.get_operations().emplace_back( + tree.emplace_operator( args.program.get_typesystem().get_type(info.return_type).size(), top.id, args.program.is_operator_ephemeral(top.id)); diff --git a/tests/symbolic_regression_test.cpp b/tests/symbolic_regression_test.cpp index 8460410..c1df4ee 100644 --- a/tests/symbolic_regression_test.cpp +++ b/tests/symbolic_regression_test.cpp @@ -189,22 +189,22 @@ inline void there(blt::size_t) int main() { - blt::gp::thread_manager_t threads{ - std::thread::hardware_concurrency(), blt::gp::task_builder_t::make_callable( - blt::gp::task_t{test::hello, hello}, - blt::gp::task_t{test::there, there} - ) - }; + // blt::gp::thread_manager_t threads{ + // std::thread::hardware_concurrency(), blt::gp::task_builder_t::make_callable( + // blt::gp::task_t{test::hello, hello}, + // blt::gp::task_t{test::there, there} + // ) + // }; - threads.add_task(test::hello); - threads.add_task(test::hello); - threads.add_task(test::hello); - threads.add_task(test::there); + // threads.add_task(test::hello); + // threads.add_task(test::hello); + // threads.add_task(test::hello); + // threads.add_task(test::there); - while (threads.has_tasks_left()) - threads.execute(); + // while (threads.has_tasks_left()) + // threads.execute(); - // for (int i = 0; i < 1; i++) - // do_run(); + for (int i = 0; i < 1; i++) + do_run(); return 0; }