diff --git a/CMakeLists.txt b/CMakeLists.txt index 4014091..10e4896 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(blt-gp VERSION 0.1.51) +project(blt-gp VERSION 0.1.52) include(CTest) diff --git a/examples/symbolic_regression.cpp b/examples/symbolic_regression.cpp index b722744..aebb0ba 100644 --- a/examples/symbolic_regression.cpp +++ b/examples/symbolic_regression.cpp @@ -71,7 +71,7 @@ constexpr auto fitness_function = [](blt::gp::tree_t& current_tree, blt::gp::fit constexpr double value_cutoff = 1.e15; for (auto& fitness_case : training_cases) { - auto diff = std::abs(fitness_case.y - current_tree.get_evaluation_value(&fitness_case)); + auto diff = std::abs(fitness_case.y - current_tree.get_evaluation_value(fitness_case)); if (diff < value_cutoff) { fitness.raw_fitness += diff; @@ -92,8 +92,6 @@ float example_function(float x) int main() { - test t; - BLT_INFO("Starting BLT-GP Symbolic Regression Example"); BLT_START_INTERVAL("Symbolic Regression", "Main"); BLT_DEBUG("Setup Fitness cases"); diff --git a/include/blt/gp/allocator.h b/include/blt/gp/allocator.h new file mode 100644 index 0000000..807d45a --- /dev/null +++ b/include/blt/gp/allocator.h @@ -0,0 +1,129 @@ +#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_ALLOCATOR_H +#define BLT_GP_ALLOCATOR_H + +#include +#include +#include + +namespace blt::gp +{ + class aligned_allocator + { + public: + void* allocate(blt::size_t bytes) // NOLINT + { +#ifdef BLT_TRACK_ALLOCATIONS + tracker.allocate(bytes); +// std::cout << "Hey our aligned allocator allocated " << bytes << " bytes!\n"; +#endif + return std::aligned_alloc(8, bytes); + } + + void deallocate(void* ptr, blt::size_t bytes) // NOLINT + { + if (ptr == nullptr) + return; +#ifdef BLT_TRACK_ALLOCATIONS + tracker.deallocate(bytes); +// std::cout << "[Hey our aligned allocator deallocated " << bytes << " bytes!]\n"; +#else + (void) bytes; +#endif + std::free(ptr); + } + }; + + template + class tracked_allocator_t + { + public: + using value_type = T; + using reference = T&; + using const_reference = const T&; + using pointer = T*; + using const_pointer = const T*; + using void_pointer = void*; + using const_void_pointer = const void*; + using difference_type = blt::ptrdiff_t; + using size_type = blt::size_t; + template + struct rebind + { + typedef tracked_allocator_t other; + }; + + pointer allocate(size_type n) + { +#ifdef BLT_TRACK_ALLOCATIONS + tracker.allocate(n * sizeof(T)); +// std::cout << "Hey our tracked allocator allocated " << (n * sizeof(T)) << " bytes!\n"; +#endif + return static_cast(std::malloc(n * sizeof(T))); + } + + pointer allocate(size_type n, const_void_pointer) + { + return allocate(n); + } + + void deallocate(pointer p, size_type n) + { +#ifdef BLT_TRACK_ALLOCATIONS + tracker.deallocate(n * sizeof(T)); +// std::cout << "[Hey our tracked allocator deallocated " << (n * sizeof(T)) << " bytes!]\n"; +#else + (void) n; +#endif + std::free(p); + } + + template + void construct(U* p, Args&& ... args) + { + new(p) T(std::forward(args)...); + } + + template + void destroy(U* p) + { + p->~T(); + } + + [[nodiscard]] size_type max_size() const noexcept + { + return std::numeric_limits::max(); + } + }; + + template + inline static bool operator==(const tracked_allocator_t& lhs, const tracked_allocator_t& rhs) noexcept + { + return &lhs == &rhs; + } + + template + inline static bool operator!=(const tracked_allocator_t& lhs, const tracked_allocator_t& rhs) noexcept + { + return &lhs != &rhs; + } +} + +#endif //BLT_GP_ALLOCATOR_H diff --git a/include/blt/gp/fwdecl.h b/include/blt/gp/fwdecl.h index 1a75b90..3e30e69 100644 --- a/include/blt/gp/fwdecl.h +++ b/include/blt/gp/fwdecl.h @@ -28,6 +28,7 @@ #include #include #include +#include namespace blt::gp { @@ -55,6 +56,8 @@ namespace blt::gp struct type_id; + struct operator_info_t; + class type_provider; struct op_container_t; @@ -90,259 +93,6 @@ namespace blt::gp // using individual_vector_t = tracked_vector>; // using tree_vector_t = tracked_vector; - class aligned_allocator - { - public: - void* allocate(blt::size_t bytes) // NOLINT - { -#ifdef BLT_TRACK_ALLOCATIONS - tracker.allocate(bytes); -// std::cout << "Hey our aligned allocator allocated " << bytes << " bytes!\n"; -#endif - return std::aligned_alloc(8, bytes); - } - - void deallocate(void* ptr, blt::size_t bytes) // NOLINT - { - if (ptr == nullptr) - return; -#ifdef BLT_TRACK_ALLOCATIONS - tracker.deallocate(bytes); -// std::cout << "[Hey our aligned allocator deallocated " << bytes << " bytes!]\n"; -#else - (void) bytes; -#endif - std::free(ptr); - } - }; - - template - class variable_bump_allocator - { - public: - explicit variable_bump_allocator(blt::size_t default_block_size = BLT_2MB_SIZE): default_block_size(default_block_size) - {} - - void* allocate(blt::size_t bytes) - { -#ifdef BLT_TRACK_ALLOCATIONS - tracker.allocate(bytes); -#endif - std::scoped_lock lock(mutex); - if (head == nullptr || head->remaining_bytes_in_block() < static_cast(bytes)) - { - push_block(bytes); - } - auto ptr = head->metadata.offset; - head->metadata.offset += bytes; - ++head->metadata.allocated_objects; - return ptr; - } - - void deallocate(void* ptr, blt::size_t bytes) - { - if (ptr == nullptr) - return; -#ifdef BLT_TRACK_ALLOCATIONS - tracker.deallocate(bytes); -#else - (void) bytes; -#endif - std::scoped_lock lock(mutex); - block_t* blk = to_block(ptr); - --blk->metadata.allocated_objects; - if (blk->metadata.allocated_objects == 0) - { - if (blk->metadata.has_deallocated) - alloc.deallocate(blk, blk->metadata.size); - else - { - if (head == blk) - head = head->metadata.next; - else - { - auto prev = head; - auto next = head->metadata.next; - while (next != blk) - { - prev = next; - next = next->metadata.next; - } - prev->metadata.next = next->metadata.next; - } - deallocated_blocks.push_back(blk); - } - } - } - - ~variable_bump_allocator() - { - std::scoped_lock lock(mutex); - for (auto* blk : deallocated_blocks) - { - alloc.deallocate(blk, blk->metadata.size); - } - auto cur = head; - while (cur != nullptr) - { - auto* ptr = cur; - ptr->metadata.has_deallocated = true; - cur = cur->metadata.next; - } - head = nullptr; - } - - private: - struct block_t - { - struct block_metadata_t - { - blt::size_t size; - blt::size_t allocated_objects : 63; - bool has_deallocated : 1; - block_t* next; - blt::u8* offset; - } metadata; - blt::u8 buffer[8]{}; - - explicit block_t(blt::size_t size): metadata{size, 0, false, nullptr, nullptr} - { - reset(); - } - - void reset() - { - metadata.offset = buffer; - metadata.allocated_objects = 0; - metadata.next = nullptr; - } - - [[nodiscard]] blt::ptrdiff_t storage_size() const noexcept - { - return static_cast(metadata.size - sizeof(typename block_t::block_metadata_t)); - } - - [[nodiscard]] blt::ptrdiff_t used_bytes_in_block() const noexcept - { - return static_cast(metadata.offset - buffer); - } - - [[nodiscard]] blt::ptrdiff_t remaining_bytes_in_block() const noexcept - { - return storage_size() - used_bytes_in_block(); - } - }; - - static inline block_t* to_block(void* p) - { - return reinterpret_cast(reinterpret_cast(p) & static_cast(~(BLT_2MB_SIZE - 1))); - } - - void push_block(blt::size_t bytes) - { - auto blk = allocate_block(bytes); -// BLT_TRACE("Allocated block %p", blk); - blk->metadata.next = head; - head = blk; - } - - inline block_t* allocate_block(blt::size_t bytes) - { - if (!deallocated_blocks.empty()) - { - block_t* blk = deallocated_blocks.back(); - deallocated_blocks.pop_back(); - blk->reset(); - return blk; - } - auto size = align_size_to(bytes + sizeof(typename block_t::block_metadata_t), default_block_size); - auto* ptr = static_cast(alloc.allocate(size)); - new(ptr) block_t{size}; - return ptr; - } - - private: - block_t* head = nullptr; - std::mutex mutex; - std::vector deallocated_blocks; - blt::size_t default_block_size; - Alloc alloc; - }; - - template - class tracked_allocator_t - { - public: - using value_type = T; - using reference = T&; - using const_reference = const T&; - using pointer = T*; - using const_pointer = const T*; - using void_pointer = void*; - using const_void_pointer = const void*; - using difference_type = blt::ptrdiff_t; - using size_type = blt::size_t; - template - struct rebind - { - typedef tracked_allocator_t other; - }; - - pointer allocate(size_type n) - { -#ifdef BLT_TRACK_ALLOCATIONS - tracker.allocate(n * sizeof(T)); -// std::cout << "Hey our tracked allocator allocated " << (n * sizeof(T)) << " bytes!\n"; -#endif - return static_cast(std::malloc(n * sizeof(T))); - } - - pointer allocate(size_type n, const_void_pointer) - { - return allocate(n); - } - - void deallocate(pointer p, size_type n) - { -#ifdef BLT_TRACK_ALLOCATIONS - tracker.deallocate(n * sizeof(T)); -// std::cout << "[Hey our tracked allocator deallocated " << (n * sizeof(T)) << " bytes!]\n"; -#else - (void) n; -#endif - std::free(p); - } - - template - void construct(U* p, Args&& ... args) - { - new(p) T(std::forward(args)...); - } - - template - void destroy(U* p) - { - p->~T(); - } - - [[nodiscard]] size_type max_size() const noexcept - { - return std::numeric_limits::max(); - } - }; - - template - inline static bool operator==(const tracked_allocator_t& lhs, const tracked_allocator_t& rhs) noexcept - { - return &lhs == &rhs; - } - - template - inline static bool operator!=(const tracked_allocator_t& lhs, const tracked_allocator_t& rhs) noexcept - { - return &lhs != &rhs; - } - namespace detail { class operator_storage_test; diff --git a/include/blt/gp/transformers.h b/include/blt/gp/transformers.h index 0879c11..06bb8de 100644 --- a/include/blt/gp/transformers.h +++ b/include/blt/gp/transformers.h @@ -60,6 +60,7 @@ namespace blt::gp struct point_info_t { type_id return_type; + operator_info_t& type_operator_info; }; struct crossover_point_t { @@ -83,7 +84,7 @@ namespace blt::gp std::optional get_crossover_point(gp_program& program, const tree_t& c1, const tree_t& c2) const; - static std::optional find_place_of_type(gp_program& program, const tree_t& t, type_id type); + static std::optional find_place_of_type(gp_program& program, const tree_t& t, type_id type); /** * child1 and child2 are copies of the parents, the result of selecting a crossover point and performing standard subtree crossover. diff --git a/include/blt/gp/tree.h b/include/blt/gp/tree.h index efda04b..dacffe0 100644 --- a/include/blt/gp/tree.h +++ b/include/blt/gp/tree.h @@ -116,9 +116,10 @@ namespace blt::gp return values; } - evaluation_context& evaluate(void* context) const + template, bool> = true> + [[nodiscard]] evaluation_context& evaluate(const T& context) const { - return (*func)(*this, context); + return (*func)(*this, const_cast(static_cast(&context))); } blt::size_t get_depth(gp_program& program); @@ -144,11 +145,10 @@ namespace blt::gp /** * Helper template for returning the result of evaluation (this calls it) */ - template - T get_evaluation_value(void* context) + template + T get_evaluation_value(const Context& context) { - auto& results = evaluate(context); - return results.values.pop(); + return evaluate(context).values.template pop(); } void print(gp_program& program, std::ostream& output, bool print_literals = true, bool pretty_indent = false, diff --git a/src/transformers.cpp b/src/transformers.cpp index fca6946..5016810 100644 --- a/src/transformers.cpp +++ b/src/transformers.cpp @@ -211,12 +211,12 @@ namespace blt::gp return crossover_point_t{static_cast(crossover_point), static_cast(attempted_point)}; } - std::optional crossover_t::find_place_of_type(gp_program& program, const tree_t& t, type_id type) + std::optional crossover_t::find_place_of_type(gp_program& program, const tree_t& t, type_id type) { auto attempted_point = program.get_random().get_size_t(1ul, t.get_operations().size()); auto& attempted_point_type = program.get_operator_info(t.get_operations()[attempted_point].id); if (type == attempted_point_type.return_type) - return {attempted_point}; + return {{attempted_point, attempted_point_type}}; return {}; }