From 3972a70bc52f08d20d1572fb377ac0d5d178b6a5 Mon Sep 17 00:00:00 2001 From: Brett Date: Mon, 5 Aug 2024 02:40:16 -0400 Subject: [PATCH] silly little copy bytes --- CMakeLists.txt | 2 +- include/blt/gp/program.h | 17 ++++- include/blt/gp/stack.h | 62 +++++++++++------ lib/blt | 2 +- src/transformers.cpp | 146 +++++---------------------------------- 5 files changed, 75 insertions(+), 154 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3142371..908a350 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(blt-gp VERSION 0.0.115) +project(blt-gp VERSION 0.0.116) include(CTest) diff --git a/include/blt/gp/program.h b/include/blt/gp/program.h index 7759238..d6fee51 100644 --- a/include/blt/gp/program.h +++ b/include/blt/gp/program.h @@ -103,7 +103,15 @@ namespace blt::gp public: explicit operator_builder(type_provider& system): system(system) - {} + { +// // ensure every storage has a vec defined for every type! +// for (const auto& type : system) +// { +// storage.terminals[type.second.id()] = {}; +// storage.non_terminals[type.second.id()] = {}; +// storage.operators_ordered_terminals[type.second.id()] = {}; +// } + } template operator_builder& add_operator(operation_t& op, bool is_static = false) @@ -424,11 +432,18 @@ namespace blt::gp inline operator_id select_non_terminal(type_id id) { + // non-terminal doesn't exist, return a terminal. This is useful for types that are defined only to have a random value, nothing more. + // was considering an std::optional<> but that would complicate the generator code considerably. I'll mark this as a TODO for v2 + if (storage.non_terminals[id].empty()) + return select_terminal(id); return get_random().select(storage.non_terminals[id]); } inline operator_id select_non_terminal_too_deep(type_id id) { + // this should probably be an error. + if (storage.operators_ordered_terminals[id].empty()) + BLT_ABORT("An impossible state has been reached. Please consult the manual. Error 43"); return get_random().select(storage.operators_ordered_terminals[id]).first; } diff --git a/include/blt/gp/stack.h b/include/blt/gp/stack.h index 4db12c1..ce92a36 100644 --- a/include/blt/gp/stack.h +++ b/include/blt/gp/stack.h @@ -136,6 +136,8 @@ namespace blt::gp */ void copy_from(const stack_allocator& stack, blt::size_t bytes) { + if (bytes == 0) + return; if (stack.empty()) { BLT_WARN("This stack is empty, we will copy no bytes from it!"); @@ -166,16 +168,51 @@ namespace blt::gp break; } } - // we can copy the bytes directly, no special allocation - if (head->remaining_bytes_in_block() >= bytes_left) + if (bytes_left > 0) { + auto insert = head; + while (insert != nullptr) + { + if (insert->remaining_bytes_in_block() >= bytes_left) + break; + insert = insert->metadata.next; + } + // can directly copy into a block. this stack's head is now the insert point + if (insert != nullptr && insert->remaining_bytes_in_block() >= bytes_left) + head = insert; + else + { + // need to push a block to the end. + // make sure head is at the end. + while (head != nullptr && head->metadata.next != nullptr) + head = head->metadata.next; + push_block(bytes_left); + } std::memcpy(head->metadata.offset, start_point, bytes_left); head->metadata.offset += bytes_left; start_block = start_block->metadata.next; } + // we now copy whole blocks at a time. while (start_block != nullptr) { - + auto prev = head; + auto insert = head; + while (insert != nullptr) + { + if (insert->remaining_bytes_in_block() >= start_block->used_bytes_in_block()) + break; + prev = insert; + insert = insert->metadata.next; + } + if (insert == nullptr) + { + head = prev; + push_block(start_block->used_bytes_in_block()); + } else + head = insert; + std::memcpy(head->metadata.offset, start_block->buffer, start_block->used_bytes_in_block()); + head->metadata.offset += start_block->used_bytes_in_block(); + start_block = start_block->metadata.next; } } @@ -257,25 +294,8 @@ namespace blt::gp void pop_bytes(blt::ptrdiff_t bytes) { -#if BLT_DEBUG_LEVEL >= 3 - blt::size_t counter = 0; -#endif while (bytes > 0) { -#if BLT_DEBUG_LEVEL > 0 - if (head == nullptr) - { - BLT_WARN("Head is nullptr, unable to pop bytes!"); - BLT_WARN("This error is normally caused by an invalid tree!"); -#if BLT_DEBUG_LEVEL >= 3 - BLT_WARN("Made it to %ld iterations", counter); -#endif - return; - } -#if BLT_DEBUG_LEVEL >= 3 - counter++; -#endif -#endif auto diff = head->used_bytes_in_block() - bytes; // if there is not enough room left to pop completely off the block, then move to the next previous block // and pop from it, update the amount of bytes to reflect the amount removed from the current block @@ -292,7 +312,7 @@ namespace blt::gp break; } } - while (head->used_bytes_in_block() == 0 && move_back()); + while (head != nullptr && head->used_bytes_in_block() == 0 && move_back()); } /** diff --git a/lib/blt b/lib/blt index 79ad108..8535480 160000 --- a/lib/blt +++ b/lib/blt @@ -1 +1 @@ -Subproject commit 79ad108fab60159461d4c78ffb97b910d446cc11 +Subproject commit 8535480ad57553e592640b98e9e005d7a4d27501 diff --git a/src/transformers.cpp b/src/transformers.cpp index fe03bc4..f3aaddf 100644 --- a/src/transformers.cpp +++ b/src/transformers.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include namespace blt::gp @@ -195,8 +196,15 @@ namespace blt::gp auto& new_vals_r = new_tree.get_values(); stack_allocator stack_after; - //stack_allocator new_vals_flip; // this is annoying. - transfer_backward(vals_r, stack_after, ops_r.end() - 1, end_itr - 1); + blt::size_t total_bytes_after = 0; + for (auto it = end_itr; it != ops_r.end(); it++) + { + if (it->is_value) + total_bytes_after += stack_allocator::aligned_size(it->type_size); + } +// transfer_backward(vals_r, stack_after, ops_r.end() - 1, end_itr - 1); + stack_after.copy_from(vals_r, total_bytes_after); + vals_r.pop_bytes(static_cast(total_bytes_after)); for (auto it = end_itr - 1; it != begin_itr - 1; it--) { if (it->is_value) @@ -204,12 +212,15 @@ namespace blt::gp } vals_r.insert(std::move(new_vals_r)); - transfer_forward(stack_after, vals_r, end_itr, ops_r.end()); + //transfer_forward(stack_after, vals_r, end_itr, ops_r.end()); + vals_r.copy_from(stack_after, total_bytes_after); + stack_after = {}; auto before = begin_itr - 1; ops_r.erase(begin_itr, end_itr); ops_r.insert(++before, new_ops_r.begin(), new_ops_r.end()); - + + // this will check to make sure that the tree is in a correct and executable state. it requires that the evaluation is context free! #if BLT_DEBUG_LEVEL >= 2 BLT_ASSERT(new_vals_r.empty()); BLT_ASSERT(stack_after.empty()); @@ -227,6 +238,7 @@ namespace blt::gp BLT_WARN_STREAM << "Stack state: " << vals_r.size() << "\n"; BLT_WARN("Child tree bytes %ld vs expected %ld, difference: %ld", bytes_size, bytes_expected, static_cast(bytes_expected) - static_cast(bytes_size)); + BLT_TRACE("Total bytes after: %ld", total_bytes_after); BLT_ABORT("Amount of bytes in stack doesn't match the number of bytes expected for the operations"); } auto copy = c; @@ -254,132 +266,6 @@ namespace blt::gp return c; } - -// tree_t mutation_t::apply(gp_program& program, const tree_t& p) -// { -// auto c = p; -// -//#if BLT_DEBUG_LEVEL >= 2 -// blt::size_t parent_bytes = 0; -// blt::size_t parent_size = p.get_values().size().total_used_bytes; -// for (const auto& op : p.get_operations()) -// { -// if (op.is_value) -// parent_bytes += stack_allocator::aligned_size(op.type_size); -// } -// if (parent_bytes != parent_size) -// { -// BLT_WARN("Parent bytes %ld do not match expected %ld", parent_size, parent_bytes); -// BLT_ABORT("You should not ignore the mismatched parent bytes!"); -// } -//#endif -// -// auto& ops = c.get_operations(); -// auto& vals = c.get_values(); -// -// auto point = static_cast(program.get_random().get_size_t(0ul, ops.size())); -// const auto& type_info = program.get_operator_info(ops[point].id); -// -// auto new_tree = config.generator.get().generate({program, type_info.return_type, config.replacement_min_depth, config.replacement_max_depth}); -// -// auto& new_ops = new_tree.get_operations(); -// auto& new_vals = new_tree.get_values(); -// -//#if BLT_DEBUG_LEVEL >= 2 -// blt::size_t new_tree_bytes = 0; -// blt::size_t new_tree_size = new_vals.size().total_used_bytes; -// for (const auto& op : new_ops) -// { -// if (op.is_value) -// new_tree_bytes += stack_allocator::aligned_size(op.type_size); -// } -//#endif -// -// auto begin_p = ops.begin() + point; -// auto end_p = ops.begin() + find_endpoint(program, ops, point); -// -// stack_allocator after_stack; -// -//#if BLT_DEBUG_LEVEL >= 2 -// blt::size_t after_stack_bytes = 0; -// blt::size_t for_bytes = 0; -// for (auto it = ops.end() - 1; it != end_p - 1; it--) -// { -// if (it->is_value) -// { -// after_stack_bytes += stack_allocator::aligned_size(it->type_size); -// } -// } -//#endif -// -// transfer_backward(vals, after_stack, ops.end() - 1, end_p - 1); -// //for (auto it = ops.end() - 1; it != end_p; it++) -// -// for (auto it = end_p - 1; it != begin_p - 1; it--) -// { -// if (it->is_value) -// { -//#if BLT_DEBUG_LEVEL >= 2 -// auto size_b = vals.size().total_used_bytes; -//#endif -// vals.pop_bytes(static_cast(stack_allocator::aligned_size(it->type_size))); -//#if BLT_DEBUG_LEVEL >= 2 -// auto size_a = vals.size().total_used_bytes; -// if (size_a != size_b - stack_allocator::aligned_size(it->type_size)) -// { -// BLT_WARN("After pop size: %ld before pop size: %ld; expected pop amount %ld", size_a, size_b, -// stack_allocator::aligned_size(it->type_size)); -// BLT_ABORT("Popping bytes didn't remove the correct amount!"); -// } -// for_bytes += stack_allocator::aligned_size(it->type_size); -//#endif -// } -// } -// -// transfer_backward(new_vals, vals, new_ops.end() - 1, new_ops.begin() - 1); -// -// transfer_forward(after_stack, vals, end_p, ops.end()); -// -// auto before = begin_p - 1; -// ops.erase(begin_p, end_p); -// ops.insert(++before, new_ops.begin(), new_ops.end()); -// -//#if BLT_DEBUG_LEVEL >= 2 -// blt::size_t bytes_expected = 0; -// auto bytes_size = c.get_values().size().total_used_bytes; -// -// for (const auto& op : c.get_operations()) -// { -// if (op.is_value) -// bytes_expected += stack_allocator::aligned_size(op.type_size); -// } -// -// if (bytes_expected != bytes_size || parent_size != parent_bytes || new_tree_size != new_tree_bytes) -// { -// BLT_WARN("Parent bytes %ld vs expected %ld", parent_size, parent_bytes); -// BLT_WARN("After stack bytes: %ld; popped bytes %ld", after_stack_bytes, for_bytes); -// BLT_WARN("Tree bytes %ld vs expected %ld", new_tree_size, new_tree_bytes); -// BLT_WARN("Child tree bytes %ld vs expected %ld", bytes_size, bytes_expected); -// BLT_ABORT("Amount of bytes in stack doesn't match the number of bytes expected for the operations"); -// } -// auto copy = c; -// try -// { -// auto result = copy.evaluate(nullptr); -// blt::black_box(result); -// } catch(...) { -// std::cout << "Parent:\n"; -// p.print(program, std::cout, false, true); -// std::cout << "Child:\n"; -// c.print(program, std::cout, false, true); -// c.print(program, std::cout, true, true); -// throw std::exception(); -// } -// -//#endif -// -// return c; -// } mutation_t::config_t::config_t(): generator(grow_generator) {}