From 95460e7bf1a9122f8281eac4f967716bfe937bf2 Mon Sep 17 00:00:00 2001 From: Brett Date: Thu, 22 Aug 2024 02:10:55 -0400 Subject: [PATCH 01/19] reduce allocations and make faster --- CMakeLists.txt | 2 +- examples/symbolic_regression.cpp | 8 ++++++++ include/blt/gp/fwdecl.h | 2 +- include/blt/gp/program.h | 8 +++++--- include/blt/gp/stack.h | 34 +++++++++++++++++++++++++++++--- include/blt/gp/tree.h | 4 ++-- 6 files changed, 48 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 00f2f30..d3d5f2c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(blt-gp VERSION 0.1.19) +project(blt-gp VERSION 0.1.20) include(CTest) diff --git a/examples/symbolic_regression.cpp b/examples/symbolic_regression.cpp index d9c0b05..f620edd 100644 --- a/examples/symbolic_regression.cpp +++ b/examples/symbolic_regression.cpp @@ -113,6 +113,7 @@ int main() while (!program.should_terminate()) { BLT_TRACE("------------{Begin Generation %ld}------------", program.get_current_generation()); + BLT_TRACE("Creating next generation"); BLT_START_INTERVAL("Symbolic Regression", "Gen"); program.create_next_generation(); BLT_END_INTERVAL("Symbolic Regression", "Gen"); @@ -148,5 +149,12 @@ int main() BLT_PRINT_PROFILE("Symbolic Regression", blt::PRINT_CYCLES | blt::PRINT_THREAD | blt::PRINT_WALL); +// BLT_TRACE("Allocations:"); +// auto h = static_cast(blt::gp::hello.load()); +// auto u = static_cast(blt::gp::unhello.load()); +// BLT_TRACE("Allocated: %ld", h); +// BLT_TRACE("Deallocated: %ld", u); +// BLT_TRACE("Ratio: %lf Difference: %ld", static_cast(h) / static_cast(u), std::abs(h - u)); + return 0; } \ No newline at end of file diff --git a/include/blt/gp/fwdecl.h b/include/blt/gp/fwdecl.h index c058d5a..2c41f91 100644 --- a/include/blt/gp/fwdecl.h +++ b/include/blt/gp/fwdecl.h @@ -55,7 +55,7 @@ namespace blt::gp class operator_storage_test; // context*, read stack, write stack using operator_func_t = std::function; - using eval_func_t = std::function; + using eval_func_t = std::function; // debug function, using print_func_t = std::function; diff --git a/include/blt/gp/program.h b/include/blt/gp/program.h index 8a87553..69e52cf 100644 --- a/include/blt/gp/program.h +++ b/include/blt/gp/program.h @@ -115,12 +115,14 @@ namespace blt::gp for (auto v : sizes) largest = std::max(v, largest); - storage.eval_func = [&operators..., largest](const tree_t& tree, void* context) { + 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(); - evaluation_context results{}; + static thread_local evaluation_context results{}; + results.values.reset(); results.values.reserve(largest); +// BLT_DEBUG("%ld stored %ld", largest, results.values.internal_storage_size()); blt::size_t total_so_far = 0; @@ -261,7 +263,7 @@ namespace blt::gp storage.names.push_back(op.get_name()); if (op.is_ephemeral()) storage.static_types.insert(operator_id); - return total_size_required; + return total_size_required * 2; } template diff --git a/include/blt/gp/stack.h b/include/blt/gp/stack.h index 21668eb..d2e4fe3 100644 --- a/include/blt/gp/stack.h +++ b/include/blt/gp/stack.h @@ -20,7 +20,7 @@ #define BLT_GP_STACK_H #include -#include +#include #include #include #include @@ -43,16 +43,25 @@ namespace blt::gp BLT_META_MAKE_FUNCTION_CHECK(drop); } +// inline std::atomic_uint64_t hello = 0; +// inline std::atomic_uint64_t unhello = 0; + class aligned_allocator { public: void* allocate(blt::size_t bytes) // NOLINT { +// hello.fetch_add(1, std::memory_order_relaxed); +// BLT_TRACE("Allocating %ld bytes", bytes); return std::aligned_alloc(8, bytes); } void deallocate(void* ptr, blt::size_t) // NOLINT { +// if (ptr == nullptr) +// return; +// unhello.fetch_add(1, std::memory_order_relaxed); +// BLT_TRACE("Deallocating %ld bytes", bytes); std::free(ptr); } }; @@ -122,7 +131,6 @@ namespace blt::gp ~stack_allocator() { - //std::free(data_); get_allocator().deallocate(data_, size_); } @@ -266,13 +274,33 @@ namespace blt::gp void reserve(blt::size_t bytes) { if (bytes > size_) - expand(bytes); + expand_raw(bytes); + } + + [[nodiscard]] blt::size_t stored() const + { + return bytes_stored; + } + + [[nodiscard]] blt::size_t internal_storage_size() const + { + return size_; + } + + void reset() + { + bytes_stored = 0; } private: void expand(blt::size_t bytes) { bytes = to_nearest_page_size(bytes); + expand_raw(bytes); + } + + void expand_raw(blt::size_t bytes) + { auto new_data = static_cast(get_allocator().allocate(bytes)); if (bytes_stored > 0) std::memcpy(new_data, data_, bytes_stored); diff --git a/include/blt/gp/tree.h b/include/blt/gp/tree.h index 96dedfa..8045428 100644 --- a/include/blt/gp/tree.h +++ b/include/blt/gp/tree.h @@ -84,7 +84,7 @@ namespace blt::gp return values; } - evaluation_context evaluate(void* context) const + evaluation_context& evaluate(void* context) const { return (*func)(*this, context); } @@ -115,7 +115,7 @@ namespace blt::gp template T get_evaluation_value(void* context) { - auto results = evaluate(context); + auto& results = evaluate(context); return results.values.pop(); } From ec8153bef074e740e8b6b0601c79b3906e50e27d Mon Sep 17 00:00:00 2001 From: Brett Date: Thu, 22 Aug 2024 02:45:10 -0400 Subject: [PATCH 02/19] cleanup --- CMakeLists.txt | 2 +- examples/symbolic_regression.cpp | 1 + include/blt/gp/stack.h | 7 ++++++- src/tree.cpp | 30 ++++++++++++++---------------- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d3d5f2c..6e0a494 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(blt-gp VERSION 0.1.20) +project(blt-gp VERSION 0.1.21) include(CTest) diff --git a/examples/symbolic_regression.cpp b/examples/symbolic_regression.cpp index f620edd..4db050b 100644 --- a/examples/symbolic_regression.cpp +++ b/examples/symbolic_regression.cpp @@ -155,6 +155,7 @@ int main() // BLT_TRACE("Allocated: %ld", h); // BLT_TRACE("Deallocated: %ld", u); // BLT_TRACE("Ratio: %lf Difference: %ld", static_cast(h) / static_cast(u), std::abs(h - u)); +// BLT_TRACE("Total Allocated Bytes: %ld", blt::gp::hello_bytes.load()); return 0; } \ No newline at end of file diff --git a/include/blt/gp/stack.h b/include/blt/gp/stack.h index d2e4fe3..71a10a7 100644 --- a/include/blt/gp/stack.h +++ b/include/blt/gp/stack.h @@ -44,6 +44,7 @@ namespace blt::gp } // inline std::atomic_uint64_t hello = 0; +// inline std::atomic_uint64_t hello_bytes = 0; // inline std::atomic_uint64_t unhello = 0; class aligned_allocator @@ -52,12 +53,16 @@ namespace blt::gp void* allocate(blt::size_t bytes) // NOLINT { // hello.fetch_add(1, std::memory_order_relaxed); +// hello_bytes += bytes; // BLT_TRACE("Allocating %ld bytes", bytes); return std::aligned_alloc(8, bytes); } void deallocate(void* ptr, blt::size_t) // NOLINT { +// if (ptr == nullptr && bytes != 0) { +// BLT_ABORT(("Nullptr called with non zero bytes! " + std::to_string(bytes)).c_str()); +// } // if (ptr == nullptr) // return; // unhello.fetch_add(1, std::memory_order_relaxed); @@ -116,7 +121,7 @@ namespace blt::gp } stack_allocator(stack_allocator&& move) noexcept: - data_(std::exchange(move.data_, nullptr)), bytes_stored(move.bytes_stored), size_(move.size_) + data_(std::exchange(move.data_, nullptr)), bytes_stored(std::exchange(move.bytes_stored, 0)), size_(std::exchange(move.size_, 0)) {} stack_allocator& operator=(const stack_allocator& copy) = delete; diff --git a/src/tree.cpp b/src/tree.cpp index 5d6ac32..bf80000 100644 --- a/src/tree.cpp +++ b/src/tree.cpp @@ -247,22 +247,20 @@ namespace blt::gp blt::size_t total_produced = 0; blt::size_t total_consumed = 0; -// for (const auto& operation : blt::reverse_iterate(operations.begin(), operations.end())) -// { -// if (operation.is_value) -// { -// value_stack.transfer_bytes(values_process, operation.type_size); -// total_produced += stack_allocator::aligned_size(operation.type_size); -// bitfield.push_back(false); -// continue; -// } -// auto& info = program.get_operator_info(operation.id); -// for (auto& arg : info.argument_types) -// total_consumed += stack_allocator::aligned_size(program.get_typesystem().get_type(arg).size()); -// operation.func(context, values_process, values_process, &bitfield); -// bitfield.push_back(true); -// total_produced += stack_allocator::aligned_size(program.get_typesystem().get_type(info.return_type).size()); -// } + for (const auto& operation : blt::reverse_iterate(operations.begin(), operations.end())) + { + if (operation.is_value) + { + value_stack.transfer_bytes(values_process, operation.type_size); + total_produced += stack_allocator::aligned_size(operation.type_size); + continue; + } + auto& info = program.get_operator_info(operation.id); + for (auto& arg : info.argument_types) + total_consumed += stack_allocator::aligned_size(program.get_typesystem().get_type(arg).size()); + program.get_operator_info(operation.id).func(context, values_process, values_process); + total_produced += stack_allocator::aligned_size(program.get_typesystem().get_type(info.return_type).size()); + } auto v1 = results.values.bytes_in_head(); auto v2 = static_cast(stack_allocator::aligned_size(operations.front().type_size)); From d28577def24f03015db7048b21e1ab70dfdca388 Mon Sep 17 00:00:00 2001 From: Brett Laptop Date: Fri, 23 Aug 2024 21:13:33 -0400 Subject: [PATCH 03/19] stats, optional --- CMakeLists.txt | 11 ++- examples/symbolic_regression.cpp | 23 +++++- include/blt/gp/stack.h | 129 +++++++++++++++++++++++++++---- lib/blt | 2 +- 4 files changed, 148 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e0a494..3843b6f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(blt-gp VERSION 0.1.21) +project(blt-gp VERSION 0.1.22) include(CTest) @@ -9,6 +9,7 @@ option(ENABLE_TSAN "Enable the thread data race sanitizer" OFF) option(BUILD_EXAMPLES "Build example programs. This will build with CTest" OFF) option(BUILD_GP_TESTS "Build test programs." OFF) option(DEBUG_LEVEL "Enable debug features which prints extra information to the console, might slow processing down. [0, 3)" 0) +option(TRACK_ALLOCATIONS "Track total allocations. Can be accessed with blt::gp::tracker" ON) set(CMAKE_CXX_STANDARD 17) @@ -38,6 +39,10 @@ target_include_directories(blt-gp PUBLIC include/) target_link_libraries(blt-gp PRIVATE BLT Threads::Threads) target_compile_definitions(blt-gp PRIVATE BLT_DEBUG_LEVEL=${DEBUG_LEVEL}) +if (${TRACK_ALLOCATIONS}) + target_compile_definitions(blt-gp PRIVATE BLT_TRACK_ALLOCATIONS=1) +endif () + if (${ENABLE_ADDRSAN} MATCHES ON) target_compile_options(blt-gp PRIVATE -fsanitize=address) target_link_options(blt-gp PRIVATE -fsanitize=address) @@ -67,6 +72,10 @@ macro(blt_add_project name source type) target_link_options(${name}-${type} PRIVATE -Wall -Wextra -Wpedantic -Wno-comment) target_compile_definitions(${name}-${type} PRIVATE BLT_DEBUG_LEVEL=${DEBUG_LEVEL}) + if (${TRACK_ALLOCATIONS}) + target_compile_definitions(${name}-${type} PRIVATE BLT_TRACK_ALLOCATIONS=1) + endif () + if (${ENABLE_ADDRSAN} MATCHES ON) target_compile_options(${name}-${type} PRIVATE -fsanitize=address) target_link_options(${name}-${type} PRIVATE -fsanitize=address) diff --git a/examples/symbolic_regression.cpp b/examples/symbolic_regression.cpp index 4db050b..3f2d7c3 100644 --- a/examples/symbolic_regression.cpp +++ b/examples/symbolic_regression.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include //static constexpr long SEED = 41912; @@ -114,15 +115,35 @@ int main() { BLT_TRACE("------------{Begin Generation %ld}------------", program.get_current_generation()); BLT_TRACE("Creating next generation"); + +#ifdef BLT_TRACK_ALLOCATIONS + auto gen_alloc = blt::gp::tracker.start_measurement(); +#endif + BLT_START_INTERVAL("Symbolic Regression", "Gen"); program.create_next_generation(); BLT_END_INTERVAL("Symbolic Regression", "Gen"); + +#ifdef BLT_TRACK_ALLOCATIONS + blt::gp::tracker.stop_measurement(gen_alloc); + BLT_TRACE("Generation Allocated %ld times with a total of %s", gen_alloc.getAllocationDifference(), + blt::byte_convert_t(gen_alloc.getAllocatedByteDifference()).convert_to_nearest_type().to_pretty_string().c_str()); + auto fitness_alloc = blt::gp::tracker.start_measurement(); +#endif + BLT_TRACE("Move to next generation"); BLT_START_INTERVAL("Symbolic Regression", "Fitness"); program.next_generation(); BLT_TRACE("Evaluate Fitness"); program.evaluate_fitness(); BLT_END_INTERVAL("Symbolic Regression", "Fitness"); + +#ifdef BLT_TRACK_ALLOCATIONS + blt::gp::tracker.stop_measurement(fitness_alloc); + BLT_TRACE("Fitness Allocated %ld times with a total of %s", fitness_alloc.getAllocationDifference(), + blt::byte_convert_t(fitness_alloc.getAllocatedByteDifference()).convert_to_nearest_type().to_pretty_string().c_str()); +#endif + BLT_TRACE("----------------------------------------------"); std::cout << std::endl; } @@ -148,7 +169,7 @@ int main() // TODO: make stats helper BLT_PRINT_PROFILE("Symbolic Regression", blt::PRINT_CYCLES | blt::PRINT_THREAD | blt::PRINT_WALL); - + // BLT_TRACE("Allocations:"); // auto h = static_cast(blt::gp::hello.load()); // auto u = static_cast(blt::gp::unhello.load()); diff --git a/include/blt/gp/stack.h b/include/blt/gp/stack.h index 71a10a7..f16590d 100644 --- a/include/blt/gp/stack.h +++ b/include/blt/gp/stack.h @@ -43,30 +43,131 @@ namespace blt::gp BLT_META_MAKE_FUNCTION_CHECK(drop); } -// inline std::atomic_uint64_t hello = 0; -// inline std::atomic_uint64_t hello_bytes = 0; -// inline std::atomic_uint64_t unhello = 0; + class allocation_tracker_t + { + public: + struct allocation_data_t + { + blt::u64 start_allocations = 0; + blt::u64 start_deallocations = 0; + blt::u64 start_allocated_bytes = 0; + blt::u64 start_deallocated_bytes = 0; + + blt::u64 end_allocations = 0; + blt::u64 end_deallocations = 0; + blt::u64 end_allocated_bytes = 0; + blt::u64 end_deallocated_bytes = 0; + + [[nodiscard]] blt::u64 getAllocationDifference() const + { + return end_allocations - start_allocations; + } + + [[nodiscard]] blt::u64 getDeallocationDifference() const + { + return end_deallocations - start_deallocations; + } + + [[nodiscard]] blt::u64 getAllocatedByteDifference() const + { + return end_allocated_bytes - start_allocated_bytes; + } + + [[nodiscard]] blt::u64 getDeallocatedByteDifference() const + { + return end_deallocated_bytes - start_deallocated_bytes; + } + }; + + void allocate(blt::size_t bytes) + { + allocations++; + allocated_bytes += bytes; + } + + void deallocate(blt::size_t bytes) + { + deallocations++; + deallocated_bytes += bytes; + } + + [[nodiscard]] blt::u64 getAllocations() const + { + return allocations; + } + + [[nodiscard]] blt::u64 getDeallocations() const + { + return deallocations; + } + + [[nodiscard]] blt::u64 getAllocatedBytes() const + { + return allocated_bytes; + } + + [[nodiscard]] blt::u64 getDeallocatedBytes() const + { + return deallocated_bytes; + } + + [[nodiscard]] blt::u64 getAllocationDifference() const + { + return std::abs(static_cast(getAllocations()) - static_cast(getDeallocations())); + } + + [[nodiscard]] blt::u64 getCurrentlyAllocatedBytes() const + { + return getAllocatedBytes() - getDeallocatedBytes(); + } + + allocation_data_t start_measurement() + { + allocation_data_t data{}; + data.start_allocations = allocations; + data.start_deallocations = deallocations; + data.start_allocated_bytes = allocated_bytes; + data.start_deallocated_bytes = deallocated_bytes; + return data; + } + + void stop_measurement(allocation_data_t& data) + { + data.end_allocations = allocations; + data.end_deallocations = deallocations; + data.end_allocated_bytes = allocated_bytes; + data.end_deallocated_bytes = deallocated_bytes; + } + + private: + std::atomic_uint64_t allocations = 0; + std::atomic_uint64_t deallocations = 0; + std::atomic_uint64_t allocated_bytes = 0; + std::atomic_uint64_t deallocated_bytes = 0; + }; + + inline allocation_tracker_t tracker; class aligned_allocator { public: void* allocate(blt::size_t bytes) // NOLINT { -// hello.fetch_add(1, std::memory_order_relaxed); -// hello_bytes += bytes; -// BLT_TRACE("Allocating %ld bytes", bytes); +#ifdef BLT_TRACK_ALLOCATIONS + tracker.allocate(bytes); +#endif return std::aligned_alloc(8, bytes); } - void deallocate(void* ptr, blt::size_t) // NOLINT + void deallocate(void* ptr, blt::size_t bytes) // NOLINT { -// if (ptr == nullptr && bytes != 0) { -// BLT_ABORT(("Nullptr called with non zero bytes! " + std::to_string(bytes)).c_str()); -// } -// if (ptr == nullptr) -// return; -// unhello.fetch_add(1, std::memory_order_relaxed); -// BLT_TRACE("Deallocating %ld bytes", bytes); + if (ptr == nullptr) + return; +#ifdef BLT_TRACK_ALLOCATIONS + tracker.deallocate(bytes); +#else + (void) bytes; +#endif std::free(ptr); } }; diff --git a/lib/blt b/lib/blt index 9ce6c89..1b09483 160000 --- a/lib/blt +++ b/lib/blt @@ -1 +1 @@ -Subproject commit 9ce6c89ce0145902d31515194a707a9aca948121 +Subproject commit 1b09483af06bab0e73fa6f0c1ab2a4ed8134ca9d From 6ded7258029995ce34b7ba8d6c7b90dc793fb444 Mon Sep 17 00:00:00 2001 From: Brett Laptop Date: Fri, 23 Aug 2024 22:40:13 -0400 Subject: [PATCH 04/19] silly --- CMakeLists.txt | 4 ++-- include/blt/gp/program.h | 5 +++++ include/blt/gp/stack.h | 4 ++-- lib/blt | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3843b6f..a7055ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(blt-gp VERSION 0.1.22) +project(blt-gp VERSION 0.1.23) include(CTest) @@ -9,7 +9,7 @@ option(ENABLE_TSAN "Enable the thread data race sanitizer" OFF) option(BUILD_EXAMPLES "Build example programs. This will build with CTest" OFF) option(BUILD_GP_TESTS "Build test programs." OFF) option(DEBUG_LEVEL "Enable debug features which prints extra information to the console, might slow processing down. [0, 3)" 0) -option(TRACK_ALLOCATIONS "Track total allocations. Can be accessed with blt::gp::tracker" ON) +option(TRACK_ALLOCATIONS "Track total allocations. Can be accessed with blt::gp::tracker" OFF) set(CMAKE_CXX_STANDARD 17) diff --git a/include/blt/gp/program.h b/include/blt/gp/program.h index 69e52cf..f4e8685 100644 --- a/include/blt/gp/program.h +++ b/include/blt/gp/program.h @@ -52,6 +52,7 @@ #include #include #include +#include "blt/std/format.h" namespace blt::gp { @@ -511,8 +512,12 @@ namespace blt::gp std::memory_order::memory_order_relaxed, std::memory_order::memory_order_relaxed)); + //auto measure = tracker.start_measurement(); for (blt::size_t i = begin; i < end; i++) func(args, crossover_selection, mutation_selection, reproduction_selection); + //tracker.stop_measurement(measure); + //BLT_TRACE("Allocated %ld times with a total of %s", measure.getAllocationDifference(), + // blt::byte_convert_t(measure.getAllocatedByteDifference()).convert_to_nearest_type().to_pretty_string().c_str()); { std::scoped_lock lock(thread_helper.thread_generation_lock); diff --git a/include/blt/gp/stack.h b/include/blt/gp/stack.h index f16590d..4e6b62c 100644 --- a/include/blt/gp/stack.h +++ b/include/blt/gp/stack.h @@ -174,7 +174,7 @@ namespace blt::gp class stack_allocator { - constexpr static blt::size_t PAGE_SIZE = 0x1000; + constexpr static blt::size_t PAGE_SIZE = 0x100; constexpr static blt::size_t MAX_ALIGNMENT = 8; template using NO_REF_T = std::remove_cv_t>; @@ -401,7 +401,7 @@ namespace blt::gp private: void expand(blt::size_t bytes) { - bytes = to_nearest_page_size(bytes); + //bytes = to_nearest_page_size(bytes); expand_raw(bytes); } diff --git a/lib/blt b/lib/blt index 1b09483..6632d04 160000 --- a/lib/blt +++ b/lib/blt @@ -1 +1 @@ -Subproject commit 1b09483af06bab0e73fa6f0c1ab2a4ed8134ca9d +Subproject commit 6632d045286b42d257eb3783e96256c13b588186 From 95b88aeebfe3adca38614c4309315adac8e1326b Mon Sep 17 00:00:00 2001 From: Brett Laptop Date: Sun, 25 Aug 2024 17:01:06 -0400 Subject: [PATCH 05/19] shared memory? --- CMakeLists.txt | 2 +- examples/symbolic_regression.cpp | 5 ++++ include/blt/gp/generators.h | 6 ++--- include/blt/gp/program.h | 7 ++++- include/blt/gp/tree.h | 2 ++ src/generators.cpp | 45 ++++++++++++++++++++------------ src/transformers.cpp | 26 +++++++++++++----- src/tree.cpp | 9 +++++++ 8 files changed, 73 insertions(+), 29 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a7055ed..a89ee8b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(blt-gp VERSION 0.1.23) +project(blt-gp VERSION 0.1.25) include(CTest) diff --git a/examples/symbolic_regression.cpp b/examples/symbolic_regression.cpp index 3f2d7c3..7bb1160 100644 --- a/examples/symbolic_regression.cpp +++ b/examples/symbolic_regression.cpp @@ -170,6 +170,11 @@ int main() BLT_PRINT_PROFILE("Symbolic Regression", blt::PRINT_CYCLES | blt::PRINT_THREAD | blt::PRINT_WALL); +#ifdef BLT_TRACK_ALLOCATIONS + BLT_TRACE("Total Allocations: %ld times with a total of %s", blt::gp::tracker.getAllocations(), + blt::byte_convert_t(blt::gp::tracker.getAllocatedBytes()).convert_to_nearest_type().to_pretty_string().c_str()); +#endif + // BLT_TRACE("Allocations:"); // auto h = static_cast(blt::gp::hello.load()); // auto u = static_cast(blt::gp::unhello.load()); diff --git a/include/blt/gp/generators.h b/include/blt/gp/generators.h index fac1ec7..99127d0 100644 --- a/include/blt/gp/generators.h +++ b/include/blt/gp/generators.h @@ -51,7 +51,7 @@ namespace blt::gp class tree_generator_t { public: - virtual tree_t generate(const generator_arguments& args) = 0; + virtual void generate(tree_t& out, const generator_arguments& args) = 0; virtual ~tree_generator_t() = default; }; @@ -59,13 +59,13 @@ namespace blt::gp class grow_generator_t : public tree_generator_t { public: - tree_t generate(const generator_arguments& args) final; + void generate(tree_t& out, const generator_arguments& args) final; }; class full_generator_t : public tree_generator_t { public: - tree_t generate(const generator_arguments& args) final; + void generate(tree_t& out, const generator_arguments& args) final; }; class population_initializer_t diff --git a/include/blt/gp/program.h b/include/blt/gp/program.h index f4e8685..5d7bfec 100644 --- a/include/blt/gp/program.h +++ b/include/blt/gp/program.h @@ -136,6 +136,11 @@ namespace blt::gp continue; } call_jmp_table(operation.id, context, results.values, results.values, operators...); +// if (results.values.internal_storage_size() != l) +// { +// BLT_DEBUG("Size %ld is now %ld", l, results.values.internal_storage_size()); +// l = results.values.internal_storage_size(); +// } } return results; @@ -301,7 +306,7 @@ namespace blt::gp template static inline void call_jmp_table_internal(size_t op, void* context, stack_allocator& write_stack, stack_allocator& read_stack, - std::integer_sequence, Operators&... operators) + std::integer_sequence, Operators& ... operators) { if (op >= sizeof...(operator_ids)) { diff --git a/include/blt/gp/tree.h b/include/blt/gp/tree.h index 8045428..2fc985e 100644 --- a/include/blt/gp/tree.h +++ b/include/blt/gp/tree.h @@ -57,6 +57,8 @@ namespace blt::gp public: explicit tree_t(gp_program& program); + void clear(gp_program& program); + struct child_t { blt::ptrdiff_t start; diff --git a/src/generators.cpp b/src/generators.cpp index 1ca796e..0b9185a 100644 --- a/src/generators.cpp +++ b/src/generators.cpp @@ -49,11 +49,10 @@ namespace blt::gp } template - inline tree_t create_tree(Func&& perChild, const generator_arguments& args) + inline void create_tree(tree_t& tree, Func&& perChild, const generator_arguments& args) { std::stack tree_generator = get_initial_stack(args.program, args.root_type); blt::size_t max_depth = 0; - tree_t tree{args.program}; while (!tree_generator.empty()) { @@ -77,13 +76,11 @@ namespace blt::gp for (const auto& child : info.argument_types) std::forward(perChild)(args.program, tree_generator, child, top.depth + 1); } - - return tree; } - tree_t grow_generator_t::generate(const generator_arguments& args) + void grow_generator_t::generate(tree_t& tree, const generator_arguments& args) { - return create_tree([args](gp_program& program, std::stack& tree_generator, type_id type, blt::size_t new_depth) { + return create_tree(tree, [args](gp_program& program, std::stack& tree_generator, type_id type, blt::size_t new_depth) { if (new_depth >= args.max_depth) { if (program.get_type_terminals(type).empty()) @@ -99,9 +96,9 @@ namespace blt::gp }, args); } - tree_t full_generator_t::generate(const generator_arguments& args) + void full_generator_t::generate(tree_t& tree, const generator_arguments& args) { - return create_tree([args](gp_program& program, std::stack& tree_generator, type_id type, blt::size_t new_depth) { + return create_tree(tree, [args](gp_program& program, std::stack& tree_generator, type_id type, blt::size_t new_depth) { if (new_depth >= args.max_depth) { if (program.get_type_terminals(type).empty()) @@ -119,7 +116,11 @@ namespace blt::gp population_t pop; for (auto i = 0ul; i < args.size; i++) - pop.get_individuals().emplace_back(grow.generate(args.to_gen_args())); + { + tree_t tree{args.program}; + grow.generate(tree, args.to_gen_args()); + pop.get_individuals().emplace_back(tree); + } return pop; } @@ -129,7 +130,11 @@ namespace blt::gp population_t pop; for (auto i = 0ul; i < args.size; i++) - pop.get_individuals().emplace_back(full.generate(args.to_gen_args())); + { + tree_t tree{args.program}; + full.generate(tree, args.to_gen_args()); + pop.get_individuals().emplace_back(tree); + } return pop; } @@ -140,10 +145,12 @@ namespace blt::gp for (auto i = 0ul; i < args.size; i++) { + tree_t tree{args.program}; if (args.program.get_random().choice()) - pop.get_individuals().emplace_back(full.generate(args.to_gen_args())); + full.generate(tree, args.to_gen_args()); else - pop.get_individuals().emplace_back(grow.generate(args.to_gen_args())); + grow.generate(tree, args.to_gen_args()); + pop.get_individuals().emplace_back(tree); } return pop; @@ -160,22 +167,26 @@ namespace blt::gp { for (auto i = 0ul; i < per_step; i++) { + tree_t tree{args.program}; if (args.program.get_random().choice()) - pop.get_individuals().emplace_back(full.generate({args.program, args.root_type, args.min_depth, depth})); + full.generate(tree, {args.program, args.root_type, args.min_depth, depth}); else - pop.get_individuals().emplace_back(grow.generate({args.program, args.root_type, args.min_depth, depth})); + grow.generate(tree, {args.program, args.root_type, args.min_depth, depth}); + pop.get_individuals().emplace_back(tree); } } for (auto i = 0ul; i < remainder; i++) { + tree_t tree{args.program}; if (args.program.get_random().choice()) - pop.get_individuals().emplace_back(full.generate(args.to_gen_args())); + full.generate(tree, args.to_gen_args()); else - pop.get_individuals().emplace_back(grow.generate(args.to_gen_args())); + grow.generate(tree, args.to_gen_args()); + pop.get_individuals().emplace_back(tree); } - blt_assert(pop.get_individuals().size() == args.size); + BLT_ASSERT(pop.get_individuals().size() == args.size); return pop; } diff --git a/src/transformers.cpp b/src/transformers.cpp index a64faa5..149a5d4 100644 --- a/src/transformers.cpp +++ b/src/transformers.cpp @@ -27,6 +27,15 @@ namespace blt::gp { + grow_generator_t grow_generator; + + inline tree_t& get_static_tree_tl(gp_program& program) + { + static thread_local tree_t new_tree{program}; + new_tree.clear(program); + return new_tree; + } + inline blt::size_t accumulate_type_sizes(detail::op_iter begin, detail::op_iter end) { blt::size_t total = 0; @@ -47,8 +56,6 @@ namespace blt::gp return buffer.data(); } - grow_generator_t grow_generator; - mutation_t::config_t::config_t(): generator(grow_generator) {} @@ -228,7 +235,8 @@ namespace blt::gp auto begin_itr = ops_r.begin() + begin_point; auto end_itr = ops_r.begin() + end_point; - auto new_tree = config.generator.get().generate({program, type_info.return_type, config.replacement_min_depth, config.replacement_max_depth}); + auto& new_tree = get_static_tree_tl(program); + config.generator.get().generate(new_tree, {program, type_info.return_type, config.replacement_min_depth, config.replacement_max_depth}); auto& new_ops_r = new_tree.get_operations(); auto& new_vals_r = new_tree.get_values(); @@ -364,7 +372,8 @@ namespace blt::gp if (index < current_func_info.argument_types.size() && val.id != current_func_info.argument_types[index].id) { // TODO: new config? - auto tree = config.generator.get().generate( + auto& tree = get_static_tree_tl(program); + config.generator.get().generate(tree, {program, val.id, config.replacement_min_depth, config.replacement_max_depth}); auto& child = children_data[children_data.size() - 1 - index]; @@ -445,7 +454,8 @@ namespace blt::gp for (blt::ptrdiff_t i = static_cast(replacement_func_info.argc.argc) - 1; i >= current_func_info.argc.argc; i--) { - auto tree = config.generator.get().generate( + auto& tree = get_static_tree_tl(program); + config.generator.get().generate(tree, {program, replacement_func_info.argument_types[i].id, config.replacement_min_depth, config.replacement_max_depth}); blt::size_t total_bytes_for = tree.total_value_bytes(); @@ -518,7 +528,8 @@ namespace blt::gp blt::size_t start_index = c_node; for (blt::ptrdiff_t i = new_argc - 1; i > static_cast(arg_position); i--) { - auto tree = config.generator.get().generate( + auto& tree = get_static_tree_tl(program); + config.generator.get().generate(tree, {program, replacement_func_info.argument_types[i].id, config.replacement_min_depth, config.replacement_max_depth}); blt::size_t total_bytes_for = tree.total_value_bytes(); @@ -531,7 +542,8 @@ namespace blt::gp vals.copy_from(combined_ptr, for_bytes); for (blt::ptrdiff_t i = static_cast(arg_position) - 1; i >= 0; i--) { - auto tree = config.generator.get().generate( + auto& tree = get_static_tree_tl(program); + config.generator.get().generate(tree, {program, replacement_func_info.argument_types[i].id, config.replacement_min_depth, config.replacement_max_depth}); blt::size_t total_bytes_for = tree.total_value_bytes(); diff --git a/src/tree.cpp b/src/tree.cpp index bf80000..8ec56d3 100644 --- a/src/tree.cpp +++ b/src/tree.cpp @@ -299,4 +299,13 @@ namespace blt::gp { } + + void tree_t::clear(gp_program& program) + { + auto* f = &program.get_eval_func(); + if (f != func) + func = f; + operations.clear(); + values.reset(); + } } \ No newline at end of file From e5966789be45685ed73d0a152319c8da1793e53f Mon Sep 17 00:00:00 2001 From: Brett Laptop Date: Sun, 25 Aug 2024 18:08:23 -0400 Subject: [PATCH 06/19] annoying --- CMakeLists.txt | 2 +- include/blt/gp/program.h | 15 ++------------- include/blt/gp/selection.h | 16 +++++++++++++++- include/blt/gp/stack.h | 4 ++-- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a89ee8b..f41377d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(blt-gp VERSION 0.1.25) +project(blt-gp VERSION 0.1.26) include(CTest) diff --git a/include/blt/gp/program.h b/include/blt/gp/program.h index 5d7bfec..b86080f 100644 --- a/include/blt/gp/program.h +++ b/include/blt/gp/program.h @@ -123,7 +123,6 @@ namespace blt::gp static thread_local evaluation_context results{}; results.values.reset(); results.values.reserve(largest); -// BLT_DEBUG("%ld stored %ld", largest, results.values.internal_storage_size()); blt::size_t total_so_far = 0; @@ -136,11 +135,6 @@ namespace blt::gp continue; } call_jmp_table(operation.id, context, results.values, results.values, operators...); -// if (results.values.internal_storage_size() != l) -// { -// BLT_DEBUG("Size %ld is now %ld", l, results.values.internal_storage_size()); -// l = results.values.internal_storage_size(); -// } } return results; @@ -507,22 +501,16 @@ namespace blt::gp while (thread_helper.next_gen_left > 0) { blt::size_t size = 0; - blt::size_t begin = 0; blt::size_t end = thread_helper.next_gen_left.load(std::memory_order_relaxed); do { size = std::min(end, config.evaluation_size); - begin = end - size; } while (!thread_helper.next_gen_left.compare_exchange_weak(end, end - size, std::memory_order::memory_order_relaxed, std::memory_order::memory_order_relaxed)); - //auto measure = tracker.start_measurement(); - for (blt::size_t i = begin; i < end; i++) + while (new_children.size() < size) func(args, crossover_selection, mutation_selection, reproduction_selection); - //tracker.stop_measurement(measure); - //BLT_TRACE("Allocated %ld times with a total of %s", measure.getAllocationDifference(), - // blt::byte_convert_t(measure.getAllocatedByteDifference()).convert_to_nearest_type().to_pretty_string().c_str()); { std::scoped_lock lock(thread_helper.thread_generation_lock); @@ -553,6 +541,7 @@ namespace blt::gp void next_generation() { + BLT_ASSERT_MSG(next_pop.get_individuals().size() == config.population_size, ("pop size: " + std::to_string(next_pop.get_individuals().size())).c_str()); current_pop = std::move(next_pop); current_generation++; } diff --git a/include/blt/gp/selection.h b/include/blt/gp/selection.h index 5c99354..baf4842 100644 --- a/include/blt/gp/selection.h +++ b/include/blt/gp/selection.h @@ -24,6 +24,7 @@ #include #include #include +#include "blt/std/format.h" namespace blt::gp { @@ -84,6 +85,7 @@ namespace blt::gp // everyone gets a chance once per loop. if (random.choice(config.crossover_chance)) { +// auto state = tracker.start_measurement(); // crossover auto& p1 = crossover_selection.select(program, current_pop, current_stats); auto& p2 = crossover_selection.select(program, current_pop, current_stats); @@ -94,24 +96,36 @@ namespace blt::gp if (results) { next_pop.push_back(std::move(results->child1)); - next_pop.push_back(std::move(results->child2)); + if (next_pop.size() != config.population_size) + next_pop.push_back(std::move(results->child2)); } +// tracker.stop_measurement(state); +// BLT_TRACE("Crossover Allocated %ld times with a total of %s", state.getAllocationDifference(), +// blt::byte_convert_t(state.getAllocatedByteDifference()).convert_to_nearest_type().to_pretty_string().c_str()); } break; case 1: if (random.choice(config.mutation_chance)) { +// auto state = tracker.start_measurement(); // mutation auto& p = mutation_selection.select(program, current_pop, current_stats); next_pop.push_back(std::move(config.mutator.get().apply(program, p))); +// tracker.stop_measurement(state); +// BLT_TRACE("Mutation Allocated %ld times with a total of %s", state.getAllocationDifference(), +// blt::byte_convert_t(state.getAllocatedByteDifference()).convert_to_nearest_type().to_pretty_string().c_str()); } break; case 2: if (config.reproduction_chance > 0 && random.choice(config.reproduction_chance)) { +// auto state = tracker.start_measurement(); // reproduction auto& p = reproduction_selection.select(program, current_pop, current_stats); next_pop.push_back(p); +// tracker.stop_measurement(state); +// BLT_TRACE("Reproduction Allocated %ld times with a total of %s", state.getAllocationDifference(), +// blt::byte_convert_t(state.getAllocatedByteDifference()).convert_to_nearest_type().to_pretty_string().c_str()); } break; default: diff --git a/include/blt/gp/stack.h b/include/blt/gp/stack.h index 4e6b62c..2ecb12c 100644 --- a/include/blt/gp/stack.h +++ b/include/blt/gp/stack.h @@ -121,7 +121,7 @@ namespace blt::gp return getAllocatedBytes() - getDeallocatedBytes(); } - allocation_data_t start_measurement() + [[nodiscard]] allocation_data_t start_measurement() const { allocation_data_t data{}; data.start_allocations = allocations; @@ -131,7 +131,7 @@ namespace blt::gp return data; } - void stop_measurement(allocation_data_t& data) + void stop_measurement(allocation_data_t& data) const { data.end_allocations = allocations; data.end_deallocations = deallocations; From 68dc109dadc7c9955aa79ed7addbc178f7f9d805 Mon Sep 17 00:00:00 2001 From: Brett Laptop Date: Tue, 27 Aug 2024 16:31:34 -0400 Subject: [PATCH 07/19] adding rice because symbolic regression is too unstable --- .gitignore | 4 +- CMakeLists.txt | 3 +- examples/operations_common.h | 33 +++++ examples/rice_classification.cpp | 245 +++++++++++++++++++++++++++++++ examples/symbolic_regression.cpp | 22 +-- include/blt/gp/random.h | 88 +---------- lib/blt | 2 +- 7 files changed, 286 insertions(+), 111 deletions(-) create mode 100644 examples/operations_common.h create mode 100644 examples/rice_classification.cpp diff --git a/.gitignore b/.gitignore index c1d1eb6..c0f2ba8 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,5 @@ out/ massif.* callgrind.* *.out.* -<<<<<<< HEAD heaptrack.* -======= ->>>>>>> refs/remotes/origin/main +Rice_Cammeo_Osmancik.arff diff --git a/CMakeLists.txt b/CMakeLists.txt index f41377d..6ac753d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(blt-gp VERSION 0.1.26) +project(blt-gp VERSION 0.1.27) include(CTest) @@ -106,6 +106,7 @@ endmacro() if (${BUILD_EXAMPLES}) blt_add_project(blt-symbolic-regression examples/symbolic_regression.cpp example) + blt_add_project(blt-rice-classification examples/rice_classification.cpp example) endif () diff --git a/examples/operations_common.h b/examples/operations_common.h new file mode 100644 index 0000000..5140595 --- /dev/null +++ b/examples/operations_common.h @@ -0,0 +1,33 @@ +#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_OPERATIONS_COMMON_H +#define BLT_GP_OPERATIONS_COMMON_H + +#include + +blt::gp::operation_t add([](float a, float b) { return a + b; }, "add"); +blt::gp::operation_t sub([](float a, float b) { return a - b; }, "sub"); +blt::gp::operation_t mul([](float a, float b) { return a * b; }, "mul"); +blt::gp::operation_t pro_div([](float a, float b) { return b == 0.0f ? 1.0f : a / b; }, "div"); +blt::gp::operation_t op_sin([](float a) { return std::sin(a); }, "sin"); +blt::gp::operation_t op_cos([](float a) { return std::cos(a); }, "cos"); +blt::gp::operation_t op_exp([](float a) { return std::exp(a); }, "exp"); +blt::gp::operation_t op_log([](float a) { return a == 0.0f ? 0.0f : std::log(a); }, "log"); + +#endif //BLT_GP_OPERATIONS_COMMON_H diff --git a/examples/rice_classification.cpp b/examples/rice_classification.cpp new file mode 100644 index 0000000..1a0a0f0 --- /dev/null +++ b/examples/rice_classification.cpp @@ -0,0 +1,245 @@ +/* + * + * 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 +#include +#include +#include +#include +#include +#include +#include "operations_common.h" +#include "blt/fs/loader.h" + + +//static constexpr long SEED = 41912; +static const unsigned long SEED = std::random_device()(); + +enum class rice_type_t +{ + Cammeo, + Osmancik +}; + +struct rice_record +{ + float area; + float perimeter; + float major_axis_length; + float minor_axis_length; + float eccentricity; + float convex_area; + float extent; + rice_type_t type; +}; + +std::vector fitness_cases; +std::vector testing_cases; + +blt::gp::prog_config_t config = blt::gp::prog_config_t() + .set_initial_min_tree_size(2) + .set_initial_max_tree_size(6) + .set_elite_count(2) + .set_crossover_chance(0.9) + .set_mutation_chance(0.1) + .set_reproduction_chance(0) + .set_max_generations(50) + .set_pop_size(500) + .set_thread_count(0); + +blt::gp::type_provider type_system; +blt::gp::gp_program program{type_system, SEED, config}; + +auto lit = blt::gp::operation_t([]() { + return program.get_random().get_float(-32000.0f, 32000.0f); +}, "lit").set_ephemeral(); + +blt::gp::operation_t op_area([](const rice_record& rice_data) { + return rice_data.area; +}, "area"); + +blt::gp::operation_t op_perimeter([](const rice_record& rice_data) { + return rice_data.perimeter; +}, "perimeter"); + +blt::gp::operation_t op_major_axis_length([](const rice_record& rice_data) { + return rice_data.major_axis_length; +}, "major_axis_length"); + +blt::gp::operation_t op_minor_axis_length([](const rice_record& rice_data) { + return rice_data.minor_axis_length; +}, "minor_axis_length"); + +blt::gp::operation_t op_eccentricity([](const rice_record& rice_data) { + return rice_data.eccentricity; +}, "eccentricity"); + +blt::gp::operation_t op_convex_area([](const rice_record& rice_data) { + return rice_data.convex_area; +}, "convex_area"); + +blt::gp::operation_t op_extent([](const rice_record& rice_data) { + return rice_data.extent; +}, "extent"); + +constexpr auto fitness_function = [](blt::gp::tree_t& current_tree, blt::gp::fitness_t& fitness, blt::size_t) { + constexpr double value_cutoff = 1.e15; + for (auto& fitness_case : fitness_cases) + { + auto diff = std::abs(fitness_case.y - current_tree.get_evaluation_value(&fitness_case)); + if (diff < value_cutoff) + { + fitness.raw_fitness += diff; + if (diff < 0.01) + fitness.hits++; + } else + fitness.raw_fitness += value_cutoff; + } + fitness.standardized_fitness = fitness.raw_fitness; + fitness.adjusted_fitness = (1.0 / (1.0 + fitness.standardized_fitness)); + return static_cast(fitness.hits) == fitness_cases.size(); +}; + +void load_rice_data(std::string_view rice_file_path) +{ + auto rice_file_data = blt::fs::getLinesFromFile(rice_file_path); + size_t index = 0; + while (!blt::string::contains(rice_file_data[index++], "@DATA")) + {} + std::vector c; + std::vector o; + for (std::string_view v : blt::itr_offset(rice_file_data, index)) + { + auto data = blt::string::split(v, ','); + rice_record r{std::stof(data[0]), std::stof(data[1]), std::stof(data[2]), std::stof(data[3]), std::stof(data[4]), std::stof(data[5]), + std::stof(data[6])}; + if (blt::string::contains(data[7], "Cammeo")) + { + r.type = rice_type_t::Cammeo; + c.push_back(r); + } else + { + r.type = rice_type_t::Osmancik; + o.push_back(r); + } + } + blt::size_t total_records = c.size() + o.size(); + blt::size_t training_size = total_records / 3; + for (blt::size_t i = 0; i < training_size; i++) + { + auto& random = program.get_random(); + auto& vec = random.choice() ? c : o; + auto pos = random.get_i64(0, static_cast(vec.size())); + fitness_cases.push_back(vec[pos]); + vec.erase(vec.begin() + pos); + } + testing_cases.insert(testing_cases.end(), c.begin(), c.end()); + testing_cases.insert(testing_cases.end(), o.begin(), o.end()); + std::shuffle(testing_cases.begin(), testing_cases.end(), program.get_random()); +} + +int main(int argc, const char** argv) +{ + blt::arg_parse parser; + parser.addArgument(blt::arg_builder{"-f", "--file"}.setHelp("File for rice data. Should be in .arff format.").setRequired().build()); + + auto args = parser.parse_args(argc, argv); + + auto rice_file_path = args.get("-f"); + + BLT_INFO("Starting BLT-GP Rice Classification Example"); + BLT_START_INTERVAL("Rice Classification", "Main"); + BLT_DEBUG("Setup Fitness cases"); + load_rice_data(rice_file_path); + + BLT_DEBUG("Setup Types and Operators"); + type_system.register_type(); + + blt::gp::operator_builder builder{type_system}; + program.set_operations(builder.build(add, sub, mul, pro_div, op_sin, op_cos, op_exp, op_log, lit, op_x)); + + BLT_DEBUG("Generate Initial Population"); + auto sel = blt::gp::select_tournament_t{}; + program.generate_population(type_system.get_type().id(), fitness_function, sel, sel, sel); + + BLT_DEBUG("Begin Generation Loop"); + while (!program.should_terminate()) + { + BLT_TRACE("------------{Begin Generation %ld}------------", program.get_current_generation()); + BLT_TRACE("Creating next generation"); + +#ifdef BLT_TRACK_ALLOCATIONS + auto gen_alloc = blt::gp::tracker.start_measurement(); +#endif + + BLT_START_INTERVAL("Rice Classification", "Gen"); + program.create_next_generation(); + BLT_END_INTERVAL("Rice Classification", "Gen"); + +#ifdef BLT_TRACK_ALLOCATIONS + blt::gp::tracker.stop_measurement(gen_alloc); + BLT_TRACE("Generation Allocated %ld times with a total of %s", gen_alloc.getAllocationDifference(), + blt::byte_convert_t(gen_alloc.getAllocatedByteDifference()).convert_to_nearest_type().to_pretty_string().c_str()); + auto fitness_alloc = blt::gp::tracker.start_measurement(); +#endif + + BLT_TRACE("Move to next generation"); + BLT_START_INTERVAL("Rice Classification", "Fitness"); + program.next_generation(); + BLT_TRACE("Evaluate Fitness"); + program.evaluate_fitness(); + BLT_END_INTERVAL("Rice Classification", "Fitness"); + +#ifdef BLT_TRACK_ALLOCATIONS + blt::gp::tracker.stop_measurement(fitness_alloc); + BLT_TRACE("Fitness Allocated %ld times with a total of %s", fitness_alloc.getAllocationDifference(), + blt::byte_convert_t(fitness_alloc.getAllocatedByteDifference()).convert_to_nearest_type().to_pretty_string().c_str()); +#endif + + BLT_TRACE("----------------------------------------------"); + std::cout << std::endl; + } + + BLT_END_INTERVAL("Rice Classification", "Main"); + + auto best = program.get_best_individuals<3>(); + + BLT_INFO("Best approximations:"); + for (auto& i_ref : best) + { + auto& i = i_ref.get(); + BLT_DEBUG("Fitness: %lf, stand: %lf, raw: %lf", i.fitness.adjusted_fitness, i.fitness.standardized_fitness, i.fitness.raw_fitness); + i.tree.print(program, std::cout); + std::cout << "\n"; + } + auto& stats = program.get_population_stats(); + BLT_INFO("Stats:"); + BLT_INFO("Average fitness: %lf", stats.average_fitness.load()); + BLT_INFO("Best fitness: %lf", stats.best_fitness.load()); + BLT_INFO("Worst fitness: %lf", stats.worst_fitness.load()); + BLT_INFO("Overall fitness: %lf", stats.overall_fitness.load()); + // TODO: make stats helper + + BLT_PRINT_PROFILE("Rice Classification", blt::PRINT_CYCLES | blt::PRINT_THREAD | blt::PRINT_WALL); + +#ifdef BLT_TRACK_ALLOCATIONS + BLT_TRACE("Total Allocations: %ld times with a total of %s", blt::gp::tracker.getAllocations(), + blt::byte_convert_t(blt::gp::tracker.getAllocatedBytes()).convert_to_nearest_type().to_pretty_string().c_str()); +#endif + + return 0; +} \ No newline at end of file diff --git a/examples/symbolic_regression.cpp b/examples/symbolic_regression.cpp index 7bb1160..3e2d24c 100644 --- a/examples/symbolic_regression.cpp +++ b/examples/symbolic_regression.cpp @@ -21,6 +21,7 @@ #include #include #include +#include "operations_common.h" //static constexpr long SEED = 41912; static const unsigned long SEED = std::random_device()(); @@ -40,21 +41,12 @@ blt::gp::prog_config_t config = blt::gp::prog_config_t() .set_mutation_chance(0.1) .set_reproduction_chance(0) .set_max_generations(50) - .set_pop_size(5000) + .set_pop_size(500) .set_thread_count(0); blt::gp::type_provider type_system; blt::gp::gp_program program{type_system, SEED, config}; -blt::gp::operation_t add([](float a, float b) { return a + b; }, "add"); -blt::gp::operation_t sub([](float a, float b) { return a - b; }, "sub"); -blt::gp::operation_t mul([](float a, float b) { return a * b; }, "mul"); -blt::gp::operation_t pro_div([](float a, float b) { return b == 0.0f ? 1.0f : a / b; }, "div"); -blt::gp::operation_t op_sin([](float a) { return std::sin(a); }, "sin"); -blt::gp::operation_t op_cos([](float a) { return std::cos(a); }, "cos"); -blt::gp::operation_t op_exp([](float a) { return std::exp(a); }, "exp"); -blt::gp::operation_t op_log([](float a) { return a == 0.0f ? 0.0f : std::log(a); }, "log"); - auto lit = blt::gp::operation_t([]() { return program.get_random().get_float(-320.0f, 320.0f); }, "lit").set_ephemeral(); @@ -107,7 +99,7 @@ int main() program.set_operations(builder.build(add, sub, mul, pro_div, op_sin, op_cos, op_exp, op_log, lit, op_x)); BLT_DEBUG("Generate Initial Population"); - auto sel = blt::gp::select_fitness_proportionate_t{}; + auto sel = blt::gp::select_tournament_t{}; program.generate_population(type_system.get_type().id(), fitness_function, sel, sel, sel); BLT_DEBUG("Begin Generation Loop"); @@ -174,14 +166,6 @@ int main() BLT_TRACE("Total Allocations: %ld times with a total of %s", blt::gp::tracker.getAllocations(), blt::byte_convert_t(blt::gp::tracker.getAllocatedBytes()).convert_to_nearest_type().to_pretty_string().c_str()); #endif - -// BLT_TRACE("Allocations:"); -// auto h = static_cast(blt::gp::hello.load()); -// auto u = static_cast(blt::gp::unhello.load()); -// BLT_TRACE("Allocated: %ld", h); -// BLT_TRACE("Deallocated: %ld", u); -// BLT_TRACE("Ratio: %lf Difference: %ld", static_cast(h) / static_cast(u), std::abs(h - u)); -// BLT_TRACE("Total Allocated Bytes: %ld", blt::gp::hello_bytes.load()); return 0; } \ No newline at end of file diff --git a/include/blt/gp/random.h b/include/blt/gp/random.h index eecc3bd..4875a57 100644 --- a/include/blt/gp/random.h +++ b/include/blt/gp/random.h @@ -25,93 +25,7 @@ namespace blt::gp { -#define BLT_RANDOM_FUNCTION blt::random::murmur_random64 -#define BLT_RANDOM_FLOAT blt::random::murmur_float64 -#define BLT_RANDOM_DOUBLE blt::random::murmur_double64 - - class random_t - { - public: - explicit random_t(blt::u64 seed): seed(seed) - {} - - void set_seed(blt::u64 s) - { - seed = s; - } - - float get_float() - { - return BLT_RANDOM_FLOAT(seed); - } - - double get_double() - { - return BLT_RANDOM_DOUBLE(seed); - } - - // [min, max) - double get_double(double min, double max) - { - return BLT_RANDOM_FUNCTION(seed, min, max); - } - - // [min, max) - float get_float(float min, float max) - { - return BLT_RANDOM_FUNCTION(seed, min, max); - } - - i32 get_i32(i32 min, i32 max) - { - return BLT_RANDOM_FUNCTION(seed, min, max); - } - - u32 get_u32(u32 min, u32 max) - { - return BLT_RANDOM_FUNCTION(seed, min, max); - } - - i64 get_i64(i64 min, i64 max) - { - return BLT_RANDOM_FUNCTION(seed, min, max); - } - - u64 get_u64(u64 min, u64 max) - { - return BLT_RANDOM_FUNCTION(seed, min, max); - } - - blt::size_t get_size_t(blt::size_t min, blt::size_t max) - { - return BLT_RANDOM_FUNCTION(seed, min, max); - } - - bool choice() - { - return BLT_RANDOM_DOUBLE(seed) < 0.5; - } - - bool choice(double cutoff) - { - return BLT_RANDOM_DOUBLE(seed) <= cutoff; - } - - template - auto& select(Container& container) - { - return container[get_u64(0, container.size())]; - } - - template - const auto& select(const Container& container) - { - return container[get_u64(0, container.size())]; - } - - private: - blt::u64 seed; - }; + using random_t = blt::random::random_t; } diff --git a/lib/blt b/lib/blt index 6632d04..b6354be 160000 --- a/lib/blt +++ b/lib/blt @@ -1 +1 @@ -Subproject commit 6632d045286b42d257eb3783e96256c13b588186 +Subproject commit b6354bed7846078e863767ce5afc7daa53b93988 From ac76b3c5dfbcb4a01f2b82f183b6acbf58a9c07e Mon Sep 17 00:00:00 2001 From: Brett Laptop Date: Tue, 27 Aug 2024 18:07:22 -0400 Subject: [PATCH 08/19] hmm works? --- CMakeLists.txt | 2 +- examples/rice_classification.cpp | 109 +++++++++++++++++++++++-------- examples/symbolic_regression.cpp | 8 +-- lib/blt | 2 +- tests/destructor_test.cpp | 8 +-- 5 files changed, 91 insertions(+), 38 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ac753d..66e8ded 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(blt-gp VERSION 0.1.27) +project(blt-gp VERSION 0.1.28) include(CTest) diff --git a/examples/rice_classification.cpp b/examples/rice_classification.cpp index 1a0a0f0..fcd9c08 100644 --- a/examples/rice_classification.cpp +++ b/examples/rice_classification.cpp @@ -1,5 +1,8 @@ /* - * + * This rice classification example uses data from the UC Irvine Machine Learning repository. + * The data for this example can be found at: + * https://archive.ics.uci.edu/dataset/545/rice+cammeo+and+osmancik + * * Copyright (C) 2024 Brett Terpstra * * This program is free software: you can redistribute it and/or modify @@ -25,8 +28,6 @@ #include "operations_common.h" #include "blt/fs/loader.h" - -//static constexpr long SEED = 41912; static const unsigned long SEED = std::random_device()(); enum class rice_type_t @@ -47,7 +48,7 @@ struct rice_record rice_type_t type; }; -std::vector fitness_cases; +std::vector training_cases; std::vector testing_cases; blt::gp::prog_config_t config = blt::gp::prog_config_t() @@ -58,7 +59,7 @@ blt::gp::prog_config_t config = blt::gp::prog_config_t() .set_mutation_chance(0.1) .set_reproduction_chance(0) .set_max_generations(50) - .set_pop_size(500) + .set_pop_size(5000) .set_thread_count(0); blt::gp::type_provider type_system; @@ -97,21 +98,25 @@ blt::gp::operation_t op_extent([](const rice_record& rice_data) { }, "extent"); constexpr auto fitness_function = [](blt::gp::tree_t& current_tree, blt::gp::fitness_t& fitness, blt::size_t) { - constexpr double value_cutoff = 1.e15; - for (auto& fitness_case : fitness_cases) + for (auto& training_case : training_cases) { - auto diff = std::abs(fitness_case.y - current_tree.get_evaluation_value(&fitness_case)); - if (diff < value_cutoff) + auto v = current_tree.get_evaluation_value(&training_case); + switch (training_case.type) { - fitness.raw_fitness += diff; - if (diff < 0.01) - fitness.hits++; - } else - fitness.raw_fitness += value_cutoff; + case rice_type_t::Cammeo: + if (v >= 0) + fitness.hits++; + break; + case rice_type_t::Osmancik: + if (v < 0) + fitness.hits++; + break; + } } + fitness.raw_fitness = static_cast(fitness.hits) / static_cast(training_cases.size()); fitness.standardized_fitness = fitness.raw_fitness; - fitness.adjusted_fitness = (1.0 / (1.0 + fitness.standardized_fitness)); - return static_cast(fitness.hits) == fitness_cases.size(); + fitness.adjusted_fitness = 1.0 - (1.0 / (1.0 + fitness.standardized_fitness)); + return static_cast(fitness.hits) == training_cases.size(); }; void load_rice_data(std::string_view rice_file_path) @@ -126,30 +131,32 @@ void load_rice_data(std::string_view rice_file_path) { auto data = blt::string::split(v, ','); rice_record r{std::stof(data[0]), std::stof(data[1]), std::stof(data[2]), std::stof(data[3]), std::stof(data[4]), std::stof(data[5]), - std::stof(data[6])}; - if (blt::string::contains(data[7], "Cammeo")) + std::stof(data[6]), blt::string::contains(data[7], "Cammeo") ? rice_type_t::Cammeo : rice_type_t::Osmancik}; + switch (r.type) { - r.type = rice_type_t::Cammeo; - c.push_back(r); - } else - { - r.type = rice_type_t::Osmancik; - o.push_back(r); + case rice_type_t::Cammeo: + c.push_back(r); + break; + case rice_type_t::Osmancik: + o.push_back(r); + break; } } + blt::size_t total_records = c.size() + o.size(); - blt::size_t training_size = total_records / 3; + blt::size_t training_size = std::min(total_records / 3, 1000ul); for (blt::size_t i = 0; i < training_size; i++) { auto& random = program.get_random(); auto& vec = random.choice() ? c : o; auto pos = random.get_i64(0, static_cast(vec.size())); - fitness_cases.push_back(vec[pos]); + training_cases.push_back(vec[pos]); vec.erase(vec.begin() + pos); } testing_cases.insert(testing_cases.end(), c.begin(), c.end()); testing_cases.insert(testing_cases.end(), o.begin(), o.end()); std::shuffle(testing_cases.begin(), testing_cases.end(), program.get_random()); + BLT_INFO("Created training set of size %ld, testing set is of size %ld", training_size, testing_cases.size()); } int main(int argc, const char** argv) @@ -159,7 +166,13 @@ int main(int argc, const char** argv) auto args = parser.parse_args(argc, argv); - auto rice_file_path = args.get("-f"); + if (!args.contains("file")) + { + BLT_WARN("Please provide path to file with -f or --file"); + return 1; + } + + auto rice_file_path = args.get("file"); BLT_INFO("Starting BLT-GP Rice Classification Example"); BLT_START_INTERVAL("Rice Classification", "Main"); @@ -170,7 +183,8 @@ int main(int argc, const char** argv) type_system.register_type(); blt::gp::operator_builder builder{type_system}; - program.set_operations(builder.build(add, sub, mul, pro_div, op_sin, op_cos, op_exp, op_log, lit, op_x)); + program.set_operations(builder.build(add, sub, mul, pro_div, op_exp, op_log, lit, op_area, op_perimeter, op_major_axis_length, + op_minor_axis_length, op_eccentricity, op_convex_area, op_extent)); BLT_DEBUG("Generate Initial Population"); auto sel = blt::gp::select_tournament_t{}; @@ -222,8 +236,47 @@ int main(int argc, const char** argv) for (auto& i_ref : best) { auto& i = i_ref.get(); + struct match_t + { + blt::size_t cc = 0; + blt::size_t co = 0; + blt::size_t oo = 0; + blt::size_t oc = 0; + }; + + match_t match; + + for (auto& testing_case : testing_cases) + { + auto result = i.tree.get_evaluation_value(&testing_case); + switch (testing_case.type) + { + case rice_type_t::Cammeo: + if (result >= 0) + match.cc++; // cammeo cammeo + else if (result < 0) + match.co++; // cammeo osmancik + break; + case rice_type_t::Osmancik: + if (result < 0) + match.oo++; // osmancik osmancik + else if (result >= 0) + match.oc++; // osmancik cammeo + break; + } + } + + auto hits = match.cc + match.oo; + auto size = testing_cases.size(); + + BLT_INFO("Hits %ld, Total Cases %ld, Percent Hit: %lf", hits, size, static_cast(hits) / static_cast(size) * 100); + BLT_DEBUG("Cammeo Cammeo: %ld", match.cc); + BLT_DEBUG("Cammeo Osmancik: %ld", match.co); + BLT_DEBUG("Osmancik Osmancik: %ld", match.oo); + BLT_DEBUG("Osmancik Cammeo: %ld", match.oc); BLT_DEBUG("Fitness: %lf, stand: %lf, raw: %lf", i.fitness.adjusted_fitness, i.fitness.standardized_fitness, i.fitness.raw_fitness); i.tree.print(program, std::cout); + std::cout << "\n"; } auto& stats = program.get_population_stats(); diff --git a/examples/symbolic_regression.cpp b/examples/symbolic_regression.cpp index 3e2d24c..adb912f 100644 --- a/examples/symbolic_regression.cpp +++ b/examples/symbolic_regression.cpp @@ -31,7 +31,7 @@ struct context float x, y; }; -std::array fitness_cases; +std::array training_cases; blt::gp::prog_config_t config = blt::gp::prog_config_t() .set_initial_min_tree_size(2) @@ -57,7 +57,7 @@ blt::gp::operation_t op_x([](const context& context) { constexpr auto fitness_function = [](blt::gp::tree_t& current_tree, blt::gp::fitness_t& fitness, blt::size_t) { constexpr double value_cutoff = 1.e15; - for (auto& fitness_case : fitness_cases) + for (auto& fitness_case : training_cases) { auto diff = std::abs(fitness_case.y - current_tree.get_evaluation_value(&fitness_case)); if (diff < value_cutoff) @@ -70,7 +70,7 @@ constexpr auto fitness_function = [](blt::gp::tree_t& current_tree, blt::gp::fit } fitness.standardized_fitness = fitness.raw_fitness; fitness.adjusted_fitness = (1.0 / (1.0 + fitness.standardized_fitness)); - return static_cast(fitness.hits) == fitness_cases.size(); + return static_cast(fitness.hits) == training_cases.size(); }; float example_function(float x) @@ -83,7 +83,7 @@ int main() BLT_INFO("Starting BLT-GP Symbolic Regression Example"); BLT_START_INTERVAL("Symbolic Regression", "Main"); BLT_DEBUG("Setup Fitness cases"); - for (auto& fitness_case : fitness_cases) + for (auto& fitness_case : training_cases) { constexpr float range = 10; constexpr float half_range = range / 2.0; diff --git a/lib/blt b/lib/blt index b6354be..79e080c 160000 --- a/lib/blt +++ b/lib/blt @@ -1 +1 @@ -Subproject commit b6354bed7846078e863767ce5afc7daa53b93988 +Subproject commit 79e080cfd34fb47342f67f19b95ffa27efb0f715 diff --git a/tests/destructor_test.cpp b/tests/destructor_test.cpp index bf898aa..1da4d5e 100644 --- a/tests/destructor_test.cpp +++ b/tests/destructor_test.cpp @@ -107,7 +107,7 @@ struct context float x, y; }; -std::array fitness_cases; +std::array training_cases; blt::gp::prog_config_t config = blt::gp::prog_config_t() .set_initial_min_tree_size(2) @@ -141,7 +141,7 @@ blt::gp::operation_t op_x([](const context& context) { constexpr auto fitness_function = [](blt::gp::tree_t& current_tree, blt::gp::fitness_t& fitness, blt::size_t) { constexpr double value_cutoff = 1.e15; - for (auto& fitness_case : fitness_cases) + for (auto& fitness_case : training_cases) { auto ctx = current_tree.evaluate(&fitness_case); auto diff = std::abs(fitness_case.y - *current_tree.get_evaluation_ref(ctx)); @@ -157,7 +157,7 @@ constexpr auto fitness_function = [](blt::gp::tree_t& current_tree, blt::gp::fit } fitness.standardized_fitness = fitness.raw_fitness; fitness.adjusted_fitness = (1.0 / (1.0 + fitness.standardized_fitness)); - return static_cast(fitness.hits) == fitness_cases.size(); + return static_cast(fitness.hits) == training_cases.size(); }; float example_function(float x) @@ -170,7 +170,7 @@ int main() BLT_INFO("Starting BLT-GP Symbolic Regression Example"); BLT_START_INTERVAL("Symbolic Regression", "Main"); BLT_DEBUG("Setup Fitness cases"); - for (auto& fitness_case : fitness_cases) + for (auto& fitness_case : training_cases) { constexpr float range = 10; constexpr float half_range = range / 2.0; From d30ba51820ba8196f1a3a64d6c632d35e8c07713 Mon Sep 17 00:00:00 2001 From: Brett Laptop Date: Tue, 27 Aug 2024 19:57:37 -0400 Subject: [PATCH 09/19] pi --- CMakeLists.txt | 2 +- examples/rice_classification.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 66e8ded..524a9df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(blt-gp VERSION 0.1.28) +project(blt-gp VERSION 0.1.29) include(CTest) diff --git a/examples/rice_classification.cpp b/examples/rice_classification.cpp index fcd9c08..57723ee 100644 --- a/examples/rice_classification.cpp +++ b/examples/rice_classification.cpp @@ -187,7 +187,7 @@ int main(int argc, const char** argv) op_minor_axis_length, op_eccentricity, op_convex_area, op_extent)); BLT_DEBUG("Generate Initial Population"); - auto sel = blt::gp::select_tournament_t{}; + auto sel = blt::gp::select_fitness_proportionate_t{}; program.generate_population(type_system.get_type().id(), fitness_function, sel, sel, sel); BLT_DEBUG("Begin Generation Loop"); From 144b3a4cebcda72070357296b7fe46a52ed4c1d8 Mon Sep 17 00:00:00 2001 From: Brett Laptop Date: Tue, 27 Aug 2024 21:34:31 -0400 Subject: [PATCH 10/19] hm --- CMakeLists.txt | 2 +- examples/rice_classification.cpp | 10 ++++++++-- src/selection.cpp | 1 + 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 524a9df..ba3639b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(blt-gp VERSION 0.1.29) +project(blt-gp VERSION 0.1.30) include(CTest) diff --git a/examples/rice_classification.cpp b/examples/rice_classification.cpp index 57723ee..47100d8 100644 --- a/examples/rice_classification.cpp +++ b/examples/rice_classification.cpp @@ -113,7 +113,7 @@ constexpr auto fitness_function = [](blt::gp::tree_t& current_tree, blt::gp::fit break; } } - fitness.raw_fitness = static_cast(fitness.hits) / static_cast(training_cases.size()); + fitness.raw_fitness = static_cast(fitness.hits); fitness.standardized_fitness = fitness.raw_fitness; fitness.adjusted_fitness = 1.0 - (1.0 / (1.0 + fitness.standardized_fitness)); return static_cast(fitness.hits) == training_cases.size(); @@ -187,7 +187,7 @@ int main(int argc, const char** argv) op_minor_axis_length, op_eccentricity, op_convex_area, op_extent)); BLT_DEBUG("Generate Initial Population"); - auto sel = blt::gp::select_fitness_proportionate_t{}; + auto sel = blt::gp::select_tournament_t{}; program.generate_population(type_system.get_type().id(), fitness_function, sel, sel, sel); BLT_DEBUG("Begin Generation Loop"); @@ -217,6 +217,12 @@ int main(int argc, const char** argv) BLT_TRACE("Evaluate Fitness"); program.evaluate_fitness(); BLT_END_INTERVAL("Rice Classification", "Fitness"); + auto& stats = program.get_population_stats(); + BLT_TRACE("Stats:"); + BLT_TRACE("Average fitness: %lf", stats.average_fitness.load()); + BLT_TRACE("Best fitness: %lf", stats.best_fitness.load()); + BLT_TRACE("Worst fitness: %lf", stats.worst_fitness.load()); + BLT_TRACE("Overall fitness: %lf", stats.overall_fitness.load()); #ifdef BLT_TRACK_ALLOCATIONS blt::gp::tracker.stop_measurement(fitness_alloc); diff --git a/src/selection.cpp b/src/selection.cpp index 34343ed..d54c824 100644 --- a/src/selection.cpp +++ b/src/selection.cpp @@ -67,6 +67,7 @@ namespace blt::gp for (blt::size_t i = 0; i < selection_size - 1; i++) { auto& sel = pop.get_individuals()[program.get_random().get_size_t(0ul, pop.get_individuals().size())]; + BLT_TRACE("Selection %ld (of %ld) = %lf, ind %p, first: %p", i, selection_size, sel.fitness.adjusted_fitness, &sel, &first); if (sel.fitness.adjusted_fitness > best_guy) { best_guy = sel.fitness.adjusted_fitness; From bc903e0251240ca55811e5319854981094c989c7 Mon Sep 17 00:00:00 2001 From: Brett Laptop Date: Wed, 28 Aug 2024 17:08:59 -0400 Subject: [PATCH 11/19] working on new stuff? --- CMakeLists.txt | 2 +- examples/rice_classification.cpp | 48 ++++++++++++++++++++++++++++++-- include/blt/gp/program.h | 14 ++++++++-- include/blt/gp/selection.h | 2 +- src/program.cpp | 2 +- src/selection.cpp | 21 +++++--------- 6 files changed, 66 insertions(+), 23 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ba3639b..dacfcbe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(blt-gp VERSION 0.1.30) +project(blt-gp VERSION 0.1.31) include(CTest) diff --git a/examples/rice_classification.cpp b/examples/rice_classification.cpp index 47100d8..9a7c48c 100644 --- a/examples/rice_classification.cpp +++ b/examples/rice_classification.cpp @@ -28,7 +28,7 @@ #include "operations_common.h" #include "blt/fs/loader.h" -static const unsigned long SEED = std::random_device()(); +static const auto SEED_FUNC = [] { return std::random_device()(); }; enum class rice_type_t { @@ -59,11 +59,11 @@ blt::gp::prog_config_t config = blt::gp::prog_config_t() .set_mutation_chance(0.1) .set_reproduction_chance(0) .set_max_generations(50) - .set_pop_size(5000) + .set_pop_size(500) .set_thread_count(0); blt::gp::type_provider type_system; -blt::gp::gp_program program{type_system, SEED, config}; +blt::gp::gp_program program{type_system, SEED_FUNC, config}; auto lit = blt::gp::operation_t([]() { return program.get_random().get_float(-32000.0f, 32000.0f); @@ -159,6 +159,48 @@ void load_rice_data(std::string_view rice_file_path) BLT_INFO("Created training set of size %ld, testing set is of size %ld", training_size, testing_cases.size()); } +struct test_results_t +{ + blt::size_t cc = 0; + blt::size_t co = 0; + blt::size_t oo = 0; + blt::size_t oc = 0; + blt::size_t hits = 0; + blt::size_t size = 0; + double percent_hit = 0; +}; + +test_results_t test_individual(blt::gp::individual& i) +{ + test_results_t results; + + for (auto& testing_case : testing_cases) + { + auto result = i.tree.get_evaluation_value(&testing_case); + switch (testing_case.type) + { + case rice_type_t::Cammeo: + if (result >= 0) + results.cc++; // cammeo cammeo + else if (result < 0) + results.co++; // cammeo osmancik + break; + case rice_type_t::Osmancik: + if (result < 0) + results.oo++; // osmancik osmancik + else if (result >= 0) + results.oc++; // osmancik cammeo + break; + } + } + + results.hits = results.cc + results.oo; + results.size = testing_cases.size(); + results.percent_hit = static_cast(results.hits) / static_cast(results.size) * 100; + + return results; +} + int main(int argc, const char** argv) { blt::arg_parse parser; diff --git a/include/blt/gp/program.h b/include/blt/gp/program.h index b86080f..c15cc64 100644 --- a/include/blt/gp/program.h +++ b/include/blt/gp/program.h @@ -332,11 +332,19 @@ namespace blt::gp * @param context_size number of arguments which are always present as "context" to the GP system / operators */ explicit gp_program(type_provider& system, blt::u64 seed): - system(system), seed(seed) + system(system), seed_func([seed]{return seed;}) { create_threads(); } explicit gp_program(type_provider& system, blt::u64 seed, prog_config_t config): - system(system), seed(seed), config(config) + system(system), seed_func([seed]{return seed;}), config(config) + { create_threads(); } + + explicit gp_program(type_provider& system, std::function seed_func): + system(system), seed_func(std::move(seed_func)) + { create_threads(); } + + explicit gp_program(type_provider& system, std::function seed_func, prog_config_t config): + system(system), seed_func(std::move(seed_func)), config(config) { create_threads(); } void create_next_generation() @@ -719,7 +727,7 @@ namespace blt::gp std::atomic_uint64_t current_generation = 0; std::atomic_bool fitness_should_exit = false; - blt::u64 seed; + std::function seed_func; prog_config_t config{}; struct concurrency_storage diff --git a/include/blt/gp/selection.h b/include/blt/gp/selection.h index baf4842..e23cc6e 100644 --- a/include/blt/gp/selection.h +++ b/include/blt/gp/selection.h @@ -177,7 +177,7 @@ namespace blt::gp public: explicit select_tournament_t(blt::size_t selection_size = 3): selection_size(selection_size) { - if (selection_size < 1) + if (selection_size == 0) BLT_ABORT("Unable to select with this size. Must select at least 1 individual!"); } diff --git a/src/program.cpp b/src/program.cpp index b5e01be..413c932 100644 --- a/src/program.cpp +++ b/src/program.cpp @@ -46,7 +46,7 @@ namespace blt::gp random_t& gp_program::get_random() const { - thread_local static blt::gp::random_t random_engine{seed}; + thread_local static blt::gp::random_t random_engine{seed_func()}; return random_engine; } diff --git a/src/selection.cpp b/src/selection.cpp index d54c824..e94a7f2 100644 --- a/src/selection.cpp +++ b/src/selection.cpp @@ -60,22 +60,15 @@ namespace blt::gp tree_t& select_tournament_t::select(gp_program& program, population_t& pop, population_stats&) { - - auto& first = pop.get_individuals()[program.get_random().get_size_t(0ul, pop.get_individuals().size())]; - individual* ind = &first; - double best_guy = first.fitness.adjusted_fitness; - for (blt::size_t i = 0; i < selection_size - 1; i++) + blt::u64 best = program.get_random().get_u64(0, pop.get_individuals().size()); + auto& i_ref = pop.get_individuals(); + for (blt::size_t i = 0; i < selection_size; i++) { - auto& sel = pop.get_individuals()[program.get_random().get_size_t(0ul, pop.get_individuals().size())]; - BLT_TRACE("Selection %ld (of %ld) = %lf, ind %p, first: %p", i, selection_size, sel.fitness.adjusted_fitness, &sel, &first); - if (sel.fitness.adjusted_fitness > best_guy) - { - best_guy = sel.fitness.adjusted_fitness; - ind = &sel; - } + auto sel_point = program.get_random().get_u64(0ul, pop.get_individuals().size()); + if (i_ref[sel_point].fitness.adjusted_fitness > i_ref[best].fitness.adjusted_fitness) + best = sel_point; } - - return ind->tree; + return i_ref[best].tree; } tree_t& select_fitness_proportionate_t::select(gp_program& program, population_t& pop, population_stats& stats) From 3cf2d319b6ff164c39edcb22f7ac21b20e25998a Mon Sep 17 00:00:00 2001 From: Brett Laptop Date: Wed, 28 Aug 2024 19:23:42 -0400 Subject: [PATCH 12/19] think it works now --- CMakeLists.txt | 2 +- examples/rice_classification.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dacfcbe..37be487 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(blt-gp VERSION 0.1.31) +project(blt-gp VERSION 0.1.32) include(CTest) diff --git a/examples/rice_classification.cpp b/examples/rice_classification.cpp index 9a7c48c..b15c07e 100644 --- a/examples/rice_classification.cpp +++ b/examples/rice_classification.cpp @@ -59,7 +59,7 @@ blt::gp::prog_config_t config = blt::gp::prog_config_t() .set_mutation_chance(0.1) .set_reproduction_chance(0) .set_max_generations(50) - .set_pop_size(500) + .set_pop_size(5000) .set_thread_count(0); blt::gp::type_provider type_system; From 5f1a80c06228b89d527f0fb64a7233d588fc8d60 Mon Sep 17 00:00:00 2001 From: Brett Laptop Date: Wed, 28 Aug 2024 19:55:29 -0400 Subject: [PATCH 13/19] silly --- CMakeLists.txt | 2 +- examples/rice_classification.cpp | 120 ++++++++++++++++++++----------- 2 files changed, 78 insertions(+), 44 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 37be487..8ea7b1a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(blt-gp VERSION 0.1.32) +project(blt-gp VERSION 0.1.33) include(CTest) diff --git a/examples/rice_classification.cpp b/examples/rice_classification.cpp index b15c07e..2631a79 100644 --- a/examples/rice_classification.cpp +++ b/examples/rice_classification.cpp @@ -168,6 +168,35 @@ struct test_results_t blt::size_t hits = 0; blt::size_t size = 0; double percent_hit = 0; + + test_results_t& operator+=(const test_results_t& a) + { + cc += a.cc; + co += a.co; + oo += a.oo; + oc += a.oc; + hits += a.hits; + size += a.size; + percent_hit += a.percent_hit; + return *this; + } + + test_results_t& operator/=(blt::size_t s) + { + cc /= s; + co /= s; + oo /= s; + oc /= s; + hits /= s; + size /= s; + percent_hit /= static_cast(s); + return *this; + } + + friend bool operator<(const test_results_t& a, const test_results_t& b) + { + return a.hits < b.hits; + } }; test_results_t test_individual(blt::gp::individual& i) @@ -182,13 +211,13 @@ test_results_t test_individual(blt::gp::individual& i) case rice_type_t::Cammeo: if (result >= 0) results.cc++; // cammeo cammeo - else if (result < 0) + else results.co++; // cammeo osmancik break; case rice_type_t::Osmancik: if (result < 0) results.oo++; // osmancik osmancik - else if (result >= 0) + else results.oc++; // osmancik cammeo break; } @@ -278,55 +307,60 @@ int main(int argc, const char** argv) BLT_END_INTERVAL("Rice Classification", "Main"); - auto best = program.get_best_individuals<3>(); + std::vector> results; + for (auto& i : program.get_current_pop().get_individuals()) + results.emplace_back(test_individual(i), &i); + std::sort(results.begin(), results.end(), [](const auto& a, const auto& b) { + return !(a.first < b.first); + }); - BLT_INFO("Best approximations:"); - for (auto& i_ref : best) + BLT_INFO("Best results:"); + for (blt::size_t index = 0; index < 3; index++) { - auto& i = i_ref.get(); - struct match_t - { - blt::size_t cc = 0; - blt::size_t co = 0; - blt::size_t oo = 0; - blt::size_t oc = 0; - }; + const auto& record = results[index].first; + const auto& i = *results[index].second; - match_t match; - - for (auto& testing_case : testing_cases) - { - auto result = i.tree.get_evaluation_value(&testing_case); - switch (testing_case.type) - { - case rice_type_t::Cammeo: - if (result >= 0) - match.cc++; // cammeo cammeo - else if (result < 0) - match.co++; // cammeo osmancik - break; - case rice_type_t::Osmancik: - if (result < 0) - match.oo++; // osmancik osmancik - else if (result >= 0) - match.oc++; // osmancik cammeo - break; - } - } - - auto hits = match.cc + match.oo; - auto size = testing_cases.size(); - - BLT_INFO("Hits %ld, Total Cases %ld, Percent Hit: %lf", hits, size, static_cast(hits) / static_cast(size) * 100); - BLT_DEBUG("Cammeo Cammeo: %ld", match.cc); - BLT_DEBUG("Cammeo Osmancik: %ld", match.co); - BLT_DEBUG("Osmancik Osmancik: %ld", match.oo); - BLT_DEBUG("Osmancik Cammeo: %ld", match.oc); + BLT_INFO("Hits %ld, Total Cases %ld, Percent Hit: %lf", record.hits, record.size, record.percent_hit); + BLT_DEBUG("Cammeo Cammeo: %ld", record.cc); + BLT_DEBUG("Cammeo Osmancik: %ld", record.co); + BLT_DEBUG("Osmancik Osmancik: %ld", record.oo); + BLT_DEBUG("Osmancik Cammeo: %ld", record.oc); BLT_DEBUG("Fitness: %lf, stand: %lf, raw: %lf", i.fitness.adjusted_fitness, i.fitness.standardized_fitness, i.fitness.raw_fitness); i.tree.print(program, std::cout); std::cout << "\n"; } + + BLT_INFO("Worst Results:"); + for (blt::size_t index = 0; index < 3; index++) + { + const auto& record = results[results.size() - 1 - index].first; + const auto& i = *results[results.size() - 1 - index].second; + + BLT_INFO("Hits %ld, Total Cases %ld, Percent Hit: %lf", record.hits, record.size, record.percent_hit); + BLT_DEBUG("Cammeo Cammeo: %ld", record.cc); + BLT_DEBUG("Cammeo Osmancik: %ld", record.co); + BLT_DEBUG("Osmancik Osmancik: %ld", record.oo); + BLT_DEBUG("Osmancik Cammeo: %ld", record.oc); + BLT_DEBUG("Fitness: %lf, stand: %lf, raw: %lf", i.fitness.adjusted_fitness, i.fitness.standardized_fitness, i.fitness.raw_fitness); + i.tree.print(program, std::cout); + + std::cout << "\n"; + } + + BLT_INFO("Average Results"); + test_results_t avg{}; + for (const auto& v : results) + avg += v.first; + avg /= results.size(); + BLT_INFO("Hits %ld, Total Cases %ld, Percent Hit: %lf", avg.hits, avg.size, avg.percent_hit); + BLT_DEBUG("Cammeo Cammeo: %ld", avg.cc); + BLT_DEBUG("Cammeo Osmancik: %ld", avg.co); + BLT_DEBUG("Osmancik Osmancik: %ld", avg.oo); + BLT_DEBUG("Osmancik Cammeo: %ld", avg.oc); + std::cout << "\n"; + + auto& stats = program.get_population_stats(); BLT_INFO("Stats:"); BLT_INFO("Average fitness: %lf", stats.average_fitness.load()); From cc76f2791acc800e7c32aa400fbb8172e66cdcfc Mon Sep 17 00:00:00 2001 From: Brett Laptop Date: Thu, 29 Aug 2024 19:47:35 -0400 Subject: [PATCH 14/19] getting a random segfault. was present before this commit. adding tracked_alloactor to vectors --- CMakeLists.txt | 2 +- dhat.out.293761 | 6 +- examples/rice_classification.cpp | 4 +- include/blt/gp/fwdecl.h | 119 ++++++++++++++++++++++++++- include/blt/gp/program.h | 12 +-- include/blt/gp/selection.h | 7 +- include/blt/gp/stack.h | 131 +----------------------------- include/blt/gp/stats.h | 134 +++++++++++++++++++++++++++++++ include/blt/gp/transformers.h | 2 - include/blt/gp/tree.h | 33 ++++---- src/selection.cpp | 2 +- src/stats.cpp | 24 ++++++ src/transformers.cpp | 2 +- 13 files changed, 311 insertions(+), 167 deletions(-) create mode 100644 include/blt/gp/stats.h create mode 100644 src/stats.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ea7b1a..14b4945 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(blt-gp VERSION 0.1.33) +project(blt-gp VERSION 0.1.34) include(CTest) diff --git a/dhat.out.293761 b/dhat.out.293761 index 51e9381..21dd56d 100644 --- a/dhat.out.293761 +++ b/dhat.out.293761 @@ -1302,7 +1302,7 @@ ,"0x4845DA0: memalign (in /usr/libexec/valgrind/vgpreload_dhat-amd64-linux.so)" ,"0x129674: std::_Function_handler::make_callable() const::{lambda(void*, blt::gp::stack_allocator&, blt::gp::stack_allocator&)#1}>::_M_invoke(std::_Any_data const&, void*&&, blt::gp::stack_allocator&, blt::gp::stack_allocator&) (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" ,"0x158664: blt::gp::tree_t blt::gp::create_tree > >&, blt::gp::type_id, unsigned long)#1}>(blt::gp::grow_generator_t::generate(blt::gp::generator_arguments const&)::{lambda(blt::gp::gp_program&, std::stack > >&, blt::gp::type_id, unsigned long)#1}&&, blt::gp::generator_arguments const&) (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" - ,"0x15D0C0: void std::vector >::_M_realloc_insert(__gnu_cxx::__normal_iterator > >, blt::gp::tree_t&&) (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" + ,"0x15D0C0: void std::vector >::_M_realloc_insert(__gnu_cxx::__normal_iterator > >, blt::gp::tree_t&&) (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" ,"0x15CCFA: blt::gp::ramped_half_initializer_t::generate(blt::gp::initializer_arguments const&) (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" ,"0x15A9F0: blt::gp::tree_t blt::gp::create_tree > >&, blt::gp::type_id, unsigned long)#1}>(blt::gp::full_generator_t::generate(blt::gp::generator_arguments const&)::{lambda(blt::gp::gp_program&, std::stack > >&, blt::gp::type_id, unsigned long)#1}&&, blt::gp::generator_arguments const&) (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" ,"0x15C977: blt::gp::ramped_half_initializer_t::generate(blt::gp::initializer_arguments const&) (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" @@ -1330,7 +1330,7 @@ ,"0x1548AB: blt::gp::crossover_t::apply(blt::gp::gp_program&, blt::gp::tree_t const&, blt::gp::tree_t const&) (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" ,"0x1549A7: blt::gp::crossover_t::apply(blt::gp::gp_program&, blt::gp::tree_t const&, blt::gp::tree_t const&) (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" ,"0x127C77: blt::gp::default_next_pop_creator::{lambda(blt::gp::selector_args&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&)#1}::operator()(blt::gp::selector_args&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&) const [clone .isra.0] (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" - ,"0x1268EF: blt::gp::individual& std::vector >::emplace_back(blt::gp::individual&&) [clone .isra.0] (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" + ,"0x1268EF: blt::gp::individual_t& std::vector >::emplace_back(blt::gp::individual_t&&) [clone .isra.0] (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" ,"0x127DDE: blt::gp::default_next_pop_creator::{lambda(blt::gp::selector_args&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&)#1}::operator()(blt::gp::selector_args&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&) const [clone .isra.0] (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" ,"0x127F26: blt::gp::default_next_pop_creator::{lambda(blt::gp::selector_args&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&)#1}::operator()(blt::gp::selector_args&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&) const [clone .isra.0] (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" ,"0x127FE6: blt::gp::default_next_pop_creator::{lambda(blt::gp::selector_args&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&)#1}::operator()(blt::gp::selector_args&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&) const [clone .isra.0] (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" @@ -1366,7 +1366,7 @@ ,"0x1247AC: main (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" ,"0x12497D: main (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" ,"0x124A4A: main (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" - ,"0x126AFC: blt::gp::individual& std::vector >::emplace_back(blt::gp::tree_t&&) [clone .isra.0] (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" + ,"0x126AFC: blt::gp::individual_t& std::vector >::emplace_back(blt::gp::tree_t&&) [clone .isra.0] (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" ,"0x127AE9: blt::gp::default_next_pop_creator::{lambda(blt::gp::selector_args&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&)#1}::operator()(blt::gp::selector_args&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&) const [clone .isra.0] (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" ,"0x127E76: blt::gp::default_next_pop_creator::{lambda(blt::gp::selector_args&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&)#1}::operator()(blt::gp::selector_args&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&) const [clone .isra.0] (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" ,"0x127EAF: blt::gp::default_next_pop_creator::{lambda(blt::gp::selector_args&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&)#1}::operator()(blt::gp::selector_args&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&, blt::gp::select_tournament_t&&) const [clone .isra.0] (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" diff --git a/examples/rice_classification.cpp b/examples/rice_classification.cpp index 2631a79..e373812 100644 --- a/examples/rice_classification.cpp +++ b/examples/rice_classification.cpp @@ -199,7 +199,7 @@ struct test_results_t } }; -test_results_t test_individual(blt::gp::individual& i) +test_results_t test_individual(blt::gp::individual_t& i) { test_results_t results; @@ -307,7 +307,7 @@ int main(int argc, const char** argv) BLT_END_INTERVAL("Rice Classification", "Main"); - std::vector> results; + std::vector> results; for (auto& i : program.get_current_pop().get_individuals()) results.emplace_back(test_individual(i), &i); std::sort(results.begin(), results.end(), [](const auto& a, const auto& b) { diff --git a/include/blt/gp/fwdecl.h b/include/blt/gp/fwdecl.h index 2c41f91..a00407f 100644 --- a/include/blt/gp/fwdecl.h +++ b/include/blt/gp/fwdecl.h @@ -22,16 +22,22 @@ #include #include #include +#include #include -#include +#include namespace blt::gp { + inline allocation_tracker_t tracker; class gp_program; class type; + struct operator_id; + + struct type_id; + class type_provider; struct op_container_t; @@ -40,6 +46,8 @@ namespace blt::gp class tree_t; + struct individual_t; + class population_t; class tree_generator_t; @@ -50,6 +58,112 @@ namespace blt::gp class stack_allocator; + template + class tracked_allocator_t; + + template + using tracked_vector = std::vector>; + +// using operation_vector_t = tracked_vector; +// 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); +#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); +#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)); +#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)); +#else + (void) n; +#endif + std::free(p); + } + + template + void construct(U* p, Args&& ... args) + { + new(p) T(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; @@ -66,6 +180,9 @@ namespace blt::gp }; using destroy_func_t = std::function; + + using const_op_iter_t = tracked_vector::const_iterator; + using op_iter_t = tracked_vector::iterator; } } diff --git a/include/blt/gp/program.h b/include/blt/gp/program.h index c15cc64..b95c341 100644 --- a/include/blt/gp/program.h +++ b/include/blt/gp/program.h @@ -409,7 +409,7 @@ namespace blt::gp } if (thread_helper.next_gen_left > 0) { - static thread_local std::vector new_children; + static thread_local tracked_vector new_children; new_children.clear(); auto args = get_selector_args(new_children); @@ -486,7 +486,7 @@ namespace blt::gp } if (thread_helper.next_gen_left > 0) { - static thread_local std::vector new_children; + static thread_local tracked_vector new_children; new_children.clear(); auto args = get_selector_args(new_children); if (id == 0) @@ -593,11 +593,11 @@ namespace blt::gp template auto get_best_individuals() { - return convert_array, size>>(get_best_indexes(), - [this](auto&& arr, blt::size_t index) -> individual& { + return convert_array, size>>(get_best_indexes(), + [this](auto&& arr, blt::size_t index) -> individual_t& { return current_pop.get_individuals()[arr[index]]; }, - std::make_integer_sequence()); + std::make_integer_sequence()); } [[nodiscard]] bool should_terminate() const @@ -751,7 +751,7 @@ namespace blt::gp // for convenience, shouldn't decrease performance too much std::atomic*> thread_execution_service = nullptr; - inline selector_args get_selector_args(std::vector& next_pop_trees) + inline selector_args get_selector_args(tracked_vector& next_pop_trees) { return {*this, next_pop_trees, current_pop, current_stats, config, get_random()}; } diff --git a/include/blt/gp/selection.h b/include/blt/gp/selection.h index e23cc6e..2b6b64c 100644 --- a/include/blt/gp/selection.h +++ b/include/blt/gp/selection.h @@ -32,7 +32,7 @@ namespace blt::gp struct selector_args { gp_program& program; - std::vector& next_pop; + tracked_vector& next_pop; population_t& current_pop; population_stats& current_stats; prog_config_t& config; @@ -44,7 +44,8 @@ namespace blt::gp if (config.elites > 0) { - std::vector> values; + static thread_local tracked_vector> values; + values.clear(); for (blt::size_t i = 0; i < config.elites; i++) values.emplace_back(i, current_pop.get_individuals()[i].fitness.adjusted_fitness); @@ -178,7 +179,7 @@ namespace blt::gp explicit select_tournament_t(blt::size_t selection_size = 3): selection_size(selection_size) { if (selection_size == 0) - BLT_ABORT("Unable to select with this size. Must select at least 1 individual!"); + BLT_ABORT("Unable to select with this size. Must select at least 1 individual_t!"); } tree_t& select(gp_program& program, population_t& pop, population_stats& stats) final; diff --git a/include/blt/gp/stack.h b/include/blt/gp/stack.h index 2ecb12c..6ee7e0a 100644 --- a/include/blt/gp/stack.h +++ b/include/blt/gp/stack.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -37,141 +38,11 @@ namespace blt::gp { - namespace detail { BLT_META_MAKE_FUNCTION_CHECK(drop); } - class allocation_tracker_t - { - public: - struct allocation_data_t - { - blt::u64 start_allocations = 0; - blt::u64 start_deallocations = 0; - blt::u64 start_allocated_bytes = 0; - blt::u64 start_deallocated_bytes = 0; - - blt::u64 end_allocations = 0; - blt::u64 end_deallocations = 0; - blt::u64 end_allocated_bytes = 0; - blt::u64 end_deallocated_bytes = 0; - - [[nodiscard]] blt::u64 getAllocationDifference() const - { - return end_allocations - start_allocations; - } - - [[nodiscard]] blt::u64 getDeallocationDifference() const - { - return end_deallocations - start_deallocations; - } - - [[nodiscard]] blt::u64 getAllocatedByteDifference() const - { - return end_allocated_bytes - start_allocated_bytes; - } - - [[nodiscard]] blt::u64 getDeallocatedByteDifference() const - { - return end_deallocated_bytes - start_deallocated_bytes; - } - }; - - void allocate(blt::size_t bytes) - { - allocations++; - allocated_bytes += bytes; - } - - void deallocate(blt::size_t bytes) - { - deallocations++; - deallocated_bytes += bytes; - } - - [[nodiscard]] blt::u64 getAllocations() const - { - return allocations; - } - - [[nodiscard]] blt::u64 getDeallocations() const - { - return deallocations; - } - - [[nodiscard]] blt::u64 getAllocatedBytes() const - { - return allocated_bytes; - } - - [[nodiscard]] blt::u64 getDeallocatedBytes() const - { - return deallocated_bytes; - } - - [[nodiscard]] blt::u64 getAllocationDifference() const - { - return std::abs(static_cast(getAllocations()) - static_cast(getDeallocations())); - } - - [[nodiscard]] blt::u64 getCurrentlyAllocatedBytes() const - { - return getAllocatedBytes() - getDeallocatedBytes(); - } - - [[nodiscard]] allocation_data_t start_measurement() const - { - allocation_data_t data{}; - data.start_allocations = allocations; - data.start_deallocations = deallocations; - data.start_allocated_bytes = allocated_bytes; - data.start_deallocated_bytes = deallocated_bytes; - return data; - } - - void stop_measurement(allocation_data_t& data) const - { - data.end_allocations = allocations; - data.end_deallocations = deallocations; - data.end_allocated_bytes = allocated_bytes; - data.end_deallocated_bytes = deallocated_bytes; - } - - private: - std::atomic_uint64_t allocations = 0; - std::atomic_uint64_t deallocations = 0; - std::atomic_uint64_t allocated_bytes = 0; - std::atomic_uint64_t deallocated_bytes = 0; - }; - - inline allocation_tracker_t tracker; - - class aligned_allocator - { - public: - void* allocate(blt::size_t bytes) // NOLINT - { -#ifdef BLT_TRACK_ALLOCATIONS - tracker.allocate(bytes); -#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); -#else - (void) bytes; -#endif - std::free(ptr); - } - }; - class stack_allocator { constexpr static blt::size_t PAGE_SIZE = 0x100; diff --git a/include/blt/gp/stats.h b/include/blt/gp/stats.h new file mode 100644 index 0000000..64b3f13 --- /dev/null +++ b/include/blt/gp/stats.h @@ -0,0 +1,134 @@ +#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_STATS_H +#define BLT_GP_STATS_H + +#include +#include +#include + +namespace blt::gp +{ + + class allocation_tracker_t + { + public: + struct allocation_data_t + { + blt::u64 start_allocations = 0; + blt::u64 start_deallocations = 0; + blt::u64 start_allocated_bytes = 0; + blt::u64 start_deallocated_bytes = 0; + + blt::u64 end_allocations = 0; + blt::u64 end_deallocations = 0; + blt::u64 end_allocated_bytes = 0; + blt::u64 end_deallocated_bytes = 0; + + [[nodiscard]] blt::u64 getAllocationDifference() const + { + return end_allocations - start_allocations; + } + + [[nodiscard]] blt::u64 getDeallocationDifference() const + { + return end_deallocations - start_deallocations; + } + + [[nodiscard]] blt::u64 getAllocatedByteDifference() const + { + return end_allocated_bytes - start_allocated_bytes; + } + + [[nodiscard]] blt::u64 getDeallocatedByteDifference() const + { + return end_deallocated_bytes - start_deallocated_bytes; + } + }; + + void allocate(blt::size_t bytes) + { + allocations++; + allocated_bytes += bytes; + } + + void deallocate(blt::size_t bytes) + { + deallocations++; + deallocated_bytes += bytes; + } + + [[nodiscard]] blt::u64 getAllocations() const + { + return allocations; + } + + [[nodiscard]] blt::u64 getDeallocations() const + { + return deallocations; + } + + [[nodiscard]] blt::u64 getAllocatedBytes() const + { + return allocated_bytes; + } + + [[nodiscard]] blt::u64 getDeallocatedBytes() const + { + return deallocated_bytes; + } + + [[nodiscard]] blt::u64 getAllocationDifference() const + { + return std::abs(static_cast(getAllocations()) - static_cast(getDeallocations())); + } + + [[nodiscard]] blt::u64 getCurrentlyAllocatedBytes() const + { + return getAllocatedBytes() - getDeallocatedBytes(); + } + + [[nodiscard]] allocation_data_t start_measurement() const + { + allocation_data_t data{}; + data.start_allocations = allocations; + data.start_deallocations = deallocations; + data.start_allocated_bytes = allocated_bytes; + data.start_deallocated_bytes = deallocated_bytes; + return data; + } + + void stop_measurement(allocation_data_t& data) const + { + data.end_allocations = allocations; + data.end_deallocations = deallocations; + data.end_allocated_bytes = allocated_bytes; + data.end_deallocated_bytes = deallocated_bytes; + } + + private: + std::atomic_uint64_t allocations = 0; + std::atomic_uint64_t deallocations = 0; + std::atomic_uint64_t allocated_bytes = 0; + std::atomic_uint64_t deallocated_bytes = 0; + }; + +} + +#endif //BLT_GP_STATS_H diff --git a/include/blt/gp/transformers.h b/include/blt/gp/transformers.h index a2524f1..e004afa 100644 --- a/include/blt/gp/transformers.h +++ b/include/blt/gp/transformers.h @@ -29,8 +29,6 @@ namespace blt::gp { namespace detail { - using op_iter = std::vector::iterator; - template inline static constexpr double sum(const T& array) { diff --git a/include/blt/gp/tree.h b/include/blt/gp/tree.h index 2fc985e..2ed4e3f 100644 --- a/include/blt/gp/tree.h +++ b/include/blt/gp/tree.h @@ -53,7 +53,6 @@ namespace blt::gp class tree_t { - using iter_type = std::vector::const_iterator; public: explicit tree_t(gp_program& program); @@ -66,12 +65,12 @@ namespace blt::gp blt::ptrdiff_t end; }; - [[nodiscard]] inline std::vector& get_operations() + [[nodiscard]] inline tracked_vector& get_operations() { return operations; } - [[nodiscard]] inline const std::vector& get_operations() const + [[nodiscard]] inline const tracked_vector& get_operations() const { return operations; } @@ -133,7 +132,7 @@ namespace blt::gp blt::ptrdiff_t find_parent(blt::gp::gp_program& program, blt::ptrdiff_t start) const; // valid for [begin, end) - static blt::size_t total_value_bytes(iter_type begin, iter_type end) + static blt::size_t total_value_bytes(detail::const_op_iter_t begin, detail::const_op_iter_t end) { blt::size_t total = 0; for (auto it = begin; it != end; it++) @@ -161,7 +160,7 @@ namespace blt::gp } private: - std::vector operations; + tracked_vector operations; blt::gp::stack_allocator values; detail::eval_func_t* func; }; @@ -174,26 +173,26 @@ namespace blt::gp blt::i64 hits = 0; }; - struct individual + struct individual_t { tree_t tree; fitness_t fitness; - individual() = default; + individual_t() = delete; - explicit individual(tree_t&& tree): tree(std::move(tree)) + explicit individual_t(tree_t&& tree): tree(std::move(tree)) {} - explicit individual(const tree_t& tree): tree(tree) + explicit individual_t(const tree_t& tree): tree(tree) {} - individual(const individual&) = default; + individual_t(const individual_t&) = default; - individual(individual&&) = default; + individual_t(individual_t&&) = default; - individual& operator=(const individual&) = delete; + individual_t& operator=(const individual_t&) = delete; - individual& operator=(individual&&) = default; + individual_t& operator=(individual_t&&) = default; }; struct population_stats @@ -220,7 +219,7 @@ namespace blt::gp class population_tree_iterator { public: - population_tree_iterator(std::vector& ind, blt::size_t pos): ind(ind), pos(pos) + population_tree_iterator(tracked_vector& ind, blt::size_t pos): ind(ind), pos(pos) {} auto begin() @@ -265,11 +264,11 @@ namespace blt::gp } private: - std::vector& ind; + tracked_vector& ind; blt::size_t pos; }; - std::vector& get_individuals() + tracked_vector& get_individuals() { return individuals; } @@ -315,7 +314,7 @@ namespace blt::gp population_t& operator=(population_t&&) = default; private: - std::vector individuals; + tracked_vector individuals; }; } diff --git a/src/selection.cpp b/src/selection.cpp index e94a7f2..5621dc6 100644 --- a/src/selection.cpp +++ b/src/selection.cpp @@ -86,7 +86,7 @@ namespace blt::gp return ref.tree; } } - BLT_WARN("Unable to find individual with fitness proportionate. This should not be a possible code path! (%lf)", choice); + BLT_WARN("Unable to find individual_t with fitness proportionate. This should not be a possible code path! (%lf)", choice); return pop.get_individuals()[0].tree; //BLT_ABORT("Unable to find individual"); } diff --git a/src/stats.cpp b/src/stats.cpp new file mode 100644 index 0000000..d5be6a7 --- /dev/null +++ b/src/stats.cpp @@ -0,0 +1,24 @@ +/* + * + * 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 +#include + +namespace blt::gp +{ + +} \ No newline at end of file diff --git a/src/transformers.cpp b/src/transformers.cpp index 149a5d4..4994fb3 100644 --- a/src/transformers.cpp +++ b/src/transformers.cpp @@ -36,7 +36,7 @@ namespace blt::gp return new_tree; } - inline blt::size_t accumulate_type_sizes(detail::op_iter begin, detail::op_iter end) + inline blt::size_t accumulate_type_sizes(detail::op_iter_t begin, detail::op_iter_t end) { blt::size_t total = 0; for (auto it = begin; it != end; ++it) From d4e6c40fe1d28ab9ed02dcc3d7484aa49ae743ec Mon Sep 17 00:00:00 2001 From: Brett Date: Fri, 30 Aug 2024 23:27:25 -0400 Subject: [PATCH 15/19] working on swaping new populations --- CMakeLists.txt | 4 +- examples/rice_classification.cpp | 45 +--- examples/symbolic_regression.cpp | 9 +- include/blt/gp/program.h | 382 +++++++++++++++++-------------- include/blt/gp/selection.h | 24 +- include/blt/gp/tree.h | 11 + include/blt/gp/typesystem.h | 10 +- src/generators.cpp | 4 +- src/program.cpp | 2 +- src/selection.cpp | 23 +- src/transformers.cpp | 4 +- src/tree.cpp | 2 +- 12 files changed, 264 insertions(+), 256 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 14b4945..fa56855 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(blt-gp VERSION 0.1.34) +project(blt-gp VERSION 0.1.35) include(CTest) @@ -16,6 +16,8 @@ set(CMAKE_CXX_STANDARD 17) set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) +SET(CMAKE_CXX_FLAGS_RELEASE "-O3 -g") + if (NOT TARGET BLT) add_subdirectory(lib/blt) endif () diff --git a/examples/rice_classification.cpp b/examples/rice_classification.cpp index e373812..181aa02 100644 --- a/examples/rice_classification.cpp +++ b/examples/rice_classification.cpp @@ -62,8 +62,7 @@ blt::gp::prog_config_t config = blt::gp::prog_config_t() .set_pop_size(5000) .set_thread_count(0); -blt::gp::type_provider type_system; -blt::gp::gp_program program{type_system, SEED_FUNC, config}; +blt::gp::gp_program program{SEED_FUNC, config}; auto lit = blt::gp::operation_t([]() { return program.get_random().get_float(-32000.0f, 32000.0f); @@ -197,6 +196,11 @@ struct test_results_t { return a.hits < b.hits; } + + friend bool operator>(const test_results_t& a, const test_results_t& b) + { + return a.hits > b.hits; + } }; test_results_t test_individual(blt::gp::individual_t& i) @@ -251,37 +255,23 @@ int main(int argc, const char** argv) load_rice_data(rice_file_path); BLT_DEBUG("Setup Types and Operators"); - type_system.register_type(); - blt::gp::operator_builder builder{type_system}; + blt::gp::operator_builder builder{}; program.set_operations(builder.build(add, sub, mul, pro_div, op_exp, op_log, lit, op_area, op_perimeter, op_major_axis_length, op_minor_axis_length, op_eccentricity, op_convex_area, op_extent)); BLT_DEBUG("Generate Initial Population"); auto sel = blt::gp::select_tournament_t{}; - program.generate_population(type_system.get_type().id(), fitness_function, sel, sel, sel); + program.generate_population(program.get_typesystem().get_type().id(), fitness_function, sel, sel, sel); BLT_DEBUG("Begin Generation Loop"); while (!program.should_terminate()) { BLT_TRACE("------------{Begin Generation %ld}------------", program.get_current_generation()); BLT_TRACE("Creating next generation"); - -#ifdef BLT_TRACK_ALLOCATIONS - auto gen_alloc = blt::gp::tracker.start_measurement(); -#endif - BLT_START_INTERVAL("Rice Classification", "Gen"); program.create_next_generation(); BLT_END_INTERVAL("Rice Classification", "Gen"); - -#ifdef BLT_TRACK_ALLOCATIONS - blt::gp::tracker.stop_measurement(gen_alloc); - BLT_TRACE("Generation Allocated %ld times with a total of %s", gen_alloc.getAllocationDifference(), - blt::byte_convert_t(gen_alloc.getAllocatedByteDifference()).convert_to_nearest_type().to_pretty_string().c_str()); - auto fitness_alloc = blt::gp::tracker.start_measurement(); -#endif - BLT_TRACE("Move to next generation"); BLT_START_INTERVAL("Rice Classification", "Fitness"); program.next_generation(); @@ -294,13 +284,6 @@ int main(int argc, const char** argv) BLT_TRACE("Best fitness: %lf", stats.best_fitness.load()); BLT_TRACE("Worst fitness: %lf", stats.worst_fitness.load()); BLT_TRACE("Overall fitness: %lf", stats.overall_fitness.load()); - -#ifdef BLT_TRACK_ALLOCATIONS - blt::gp::tracker.stop_measurement(fitness_alloc); - BLT_TRACE("Fitness Allocated %ld times with a total of %s", fitness_alloc.getAllocationDifference(), - blt::byte_convert_t(fitness_alloc.getAllocatedByteDifference()).convert_to_nearest_type().to_pretty_string().c_str()); -#endif - BLT_TRACE("----------------------------------------------"); std::cout << std::endl; } @@ -311,7 +294,7 @@ int main(int argc, const char** argv) for (auto& i : program.get_current_pop().get_individuals()) results.emplace_back(test_individual(i), &i); std::sort(results.begin(), results.end(), [](const auto& a, const auto& b) { - return !(a.first < b.first); + return a.first > b.first; }); BLT_INFO("Best results:"); @@ -343,7 +326,6 @@ int main(int argc, const char** argv) BLT_DEBUG("Osmancik Osmancik: %ld", record.oo); BLT_DEBUG("Osmancik Cammeo: %ld", record.oc); BLT_DEBUG("Fitness: %lf, stand: %lf, raw: %lf", i.fitness.adjusted_fitness, i.fitness.standardized_fitness, i.fitness.raw_fitness); - i.tree.print(program, std::cout); std::cout << "\n"; } @@ -360,15 +342,6 @@ int main(int argc, const char** argv) BLT_DEBUG("Osmancik Cammeo: %ld", avg.oc); std::cout << "\n"; - - auto& stats = program.get_population_stats(); - BLT_INFO("Stats:"); - BLT_INFO("Average fitness: %lf", stats.average_fitness.load()); - BLT_INFO("Best fitness: %lf", stats.best_fitness.load()); - BLT_INFO("Worst fitness: %lf", stats.worst_fitness.load()); - BLT_INFO("Overall fitness: %lf", stats.overall_fitness.load()); - // TODO: make stats helper - BLT_PRINT_PROFILE("Rice Classification", blt::PRINT_CYCLES | blt::PRINT_THREAD | blt::PRINT_WALL); #ifdef BLT_TRACK_ALLOCATIONS diff --git a/examples/symbolic_regression.cpp b/examples/symbolic_regression.cpp index adb912f..e1ce917 100644 --- a/examples/symbolic_regression.cpp +++ b/examples/symbolic_regression.cpp @@ -44,8 +44,7 @@ blt::gp::prog_config_t config = blt::gp::prog_config_t() .set_pop_size(500) .set_thread_count(0); -blt::gp::type_provider type_system; -blt::gp::gp_program program{type_system, SEED, config}; +blt::gp::gp_program program{SEED, config}; auto lit = blt::gp::operation_t([]() { return program.get_random().get_float(-320.0f, 320.0f); @@ -93,14 +92,12 @@ int main() } BLT_DEBUG("Setup Types and Operators"); - type_system.register_type(); - - blt::gp::operator_builder builder{type_system}; + blt::gp::operator_builder builder{}; program.set_operations(builder.build(add, sub, mul, pro_div, op_sin, op_cos, op_exp, op_log, lit, op_x)); BLT_DEBUG("Generate Initial Population"); auto sel = blt::gp::select_tournament_t{}; - program.generate_population(type_system.get_type().id(), fitness_function, sel, sel, sel); + program.generate_population(program.get_typesystem().get_type().id(), fitness_function, sel, sel, sel); BLT_DEBUG("Begin Generation Loop"); while (!program.should_terminate()) diff --git a/include/blt/gp/program.h b/include/blt/gp/program.h index b95c341..fc4b134 100644 --- a/include/blt/gp/program.h +++ b/include/blt/gp/program.h @@ -56,7 +56,6 @@ namespace blt::gp { - struct argc_t { blt::u32 argc = 0; @@ -80,20 +79,22 @@ namespace blt::gp detail::operator_func_t func; }; - struct operator_storage + struct program_operator_storage_t { // indexed from return TYPE ID, returns index of operator blt::expanding_buffer> terminals; blt::expanding_buffer> non_terminals; blt::expanding_buffer>> operators_ordered_terminals; // indexed from OPERATOR ID (operator number) - blt::hashset_t static_types; + blt::hashset_t ephemeral_leaf_operators; std::vector operators; std::vector print_funcs; std::vector destroy_funcs; std::vector> names; detail::eval_func_t eval_func; + + type_provider system; }; template @@ -104,11 +105,10 @@ namespace blt::gp friend class blt::gp::detail::operator_storage_test; public: - explicit operator_builder(type_provider& system): system(system) - {} + explicit operator_builder() = default; template - operator_storage& build(Operators& ... operators) + program_operator_storage_t& build(Operators& ... operators) { std::vector sizes; (sizes.push_back(add_operator(operators)), ...); @@ -201,7 +201,7 @@ namespace blt::gp return storage; } - operator_storage&& grab() + program_operator_storage_t&& grab() { return std::move(storage); } @@ -210,10 +210,14 @@ namespace blt::gp template auto add_operator(operation_t& op) { + // check for types we can register + (storage.system.register_type(), ...); + storage.system.register_type(); + auto total_size_required = stack_allocator::aligned_size(sizeof(Return)); ((total_size_required += stack_allocator::aligned_size(sizeof(Args))), ...); - auto return_type_id = system.get_type().id(); + auto return_type_id = storage.system.get_type().id(); auto operator_id = blt::gp::operator_id(storage.operators.size()); op.id = operator_id; @@ -262,7 +266,7 @@ namespace blt::gp }); storage.names.push_back(op.get_name()); if (op.is_ephemeral()) - storage.static_types.insert(operator_id); + storage.ephemeral_leaf_operators.insert(operator_id); return total_size_required * 2; } @@ -271,7 +275,7 @@ namespace blt::gp { if constexpr (!std::is_same_v>) { - types.push_back(system.get_type().id()); + types.push_back(storage.system.get_type().id()); } } @@ -316,8 +320,7 @@ namespace blt::gp call_jmp_table_internal(op, context, write_stack, read_stack, std::index_sequence_for(), operators...); } - type_provider& system; - operator_storage storage; + program_operator_storage_t storage; }; class gp_program @@ -327,37 +330,83 @@ namespace blt::gp * Note about context size: This is required as context is passed to every operator in the GP tree, this context will be provided by your * call to one of the evaluator functions. This was the nicest way to provide this as C++ lacks reflection * - * @param system type system to use in tree generation * @param engine random engine to use throughout the program. * @param context_size number of arguments which are always present as "context" to the GP system / operators */ - explicit gp_program(type_provider& system, blt::u64 seed): - system(system), seed_func([seed]{return seed;}) + explicit gp_program(blt::u64 seed): seed_func([seed] { return seed; }) { create_threads(); } - explicit gp_program(type_provider& system, blt::u64 seed, prog_config_t config): - system(system), seed_func([seed]{return seed;}), config(config) + explicit gp_program(blt::u64 seed, prog_config_t config): seed_func([seed] { return seed; }), config(config) { create_threads(); } - explicit gp_program(type_provider& system, std::function seed_func): - system(system), seed_func(std::move(seed_func)) + explicit gp_program(std::function seed_func): seed_func(std::move(seed_func)) { create_threads(); } - explicit gp_program(type_provider& system, std::function seed_func, prog_config_t config): - system(system), seed_func(std::move(seed_func)), config(config) + explicit gp_program(std::function seed_func, prog_config_t config): seed_func(std::move(seed_func)), config(config) { create_threads(); } + ~gp_program() + { + thread_helper.lifetime_over = true; + thread_helper.barrier.notify_all(); + thread_helper.thread_function_condition.notify_all(); + for (auto& thread : thread_helper.threads) + { + if (thread->joinable()) + thread->join(); + } + } + void create_next_generation() { +#ifdef BLT_TRACK_ALLOCATIONS + auto gen_alloc = blt::gp::tracker.start_measurement(); +#endif // should already be empty next_pop.clear(); thread_helper.next_gen_left.store(config.population_size, std::memory_order_release); (*thread_execution_service)(0); +#ifdef BLT_TRACK_ALLOCATIONS + blt::gp::tracker.stop_measurement(gen_alloc); + BLT_TRACE("Generation Allocated %ld times with a total of %s", gen_alloc.getAllocationDifference(), + blt::byte_convert_t(gen_alloc.getAllocatedByteDifference()).convert_to_nearest_type().to_pretty_string().c_str()); +#endif + } + + void next_generation() + { + BLT_ASSERT_MSG(next_pop.get_individuals().size() == config.population_size, + ("pop size: " + std::to_string(next_pop.get_individuals().size())).c_str()); + std::swap(current_pop, next_pop); + current_generation++; } void evaluate_fitness() { +#ifdef BLT_TRACK_ALLOCATIONS + auto fitness_alloc = blt::gp::tracker.start_measurement(); +#endif evaluate_fitness_internal(); +#ifdef BLT_TRACK_ALLOCATIONS + blt::gp::tracker.stop_measurement(fitness_alloc); + BLT_TRACE("Fitness Allocated %ld times with a total of %s", fitness_alloc.getAllocationDifference(), + blt::byte_convert_t(fitness_alloc.getAllocatedByteDifference()).convert_to_nearest_type().to_pretty_string().c_str()); +#endif + + } + + void reset_program(type_id root_type, bool eval_fitness_now = true) + { + current_generation = 0; + current_pop = config.pop_initializer.get().generate( + {*this, root_type, config.population_size, config.initial_min_tree_size, config.initial_max_tree_size}); + if (eval_fitness_now) + evaluate_fitness_internal(); + } + + void kill() + { + thread_helper.lifetime_over = true; } /** @@ -378,32 +427,39 @@ namespace blt::gp using LambdaReturn = typename decltype(blt::meta::lambda_helper(fitness_function))::Return; current_pop = config.pop_initializer.get().generate( {*this, root_type, config.population_size, config.initial_min_tree_size, config.initial_max_tree_size}); + next_pop = population_t(current_pop); if (config.threads == 1) { BLT_INFO("Starting with single thread variant!"); - thread_execution_service = new std::function( + thread_execution_service = std::unique_ptr>(new std::function( [this, &fitness_function, &crossover_selection, &mutation_selection, &reproduction_selection, &func](blt::size_t) { if (thread_helper.evaluation_left > 0) { - for (const auto& ind : blt::enumerate(current_pop.get_individuals())) + current_stats.normalized_fitness.clear(); + double sum_of_prob = 0; + for (const auto& [index, ind] : blt::enumerate(current_pop.get_individuals())) { if constexpr (std::is_same_v || std::is_convertible_v) { - auto result = fitness_function(ind.second.tree, ind.second.fitness, ind.first); + auto result = fitness_function(ind.tree, ind.fitness, index); if (result) fitness_should_exit = true; } else - { - fitness_function(ind.second.tree, ind.second.fitness, ind.first); - } + fitness_function(ind.tree, ind.fitness, index); - if (ind.second.fitness.adjusted_fitness > current_stats.best_fitness) - current_stats.best_fitness = ind.second.fitness.adjusted_fitness; + if (ind.fitness.adjusted_fitness > current_stats.best_fitness) + current_stats.best_fitness = ind.fitness.adjusted_fitness; - if (ind.second.fitness.adjusted_fitness < current_stats.worst_fitness) - current_stats.worst_fitness = ind.second.fitness.adjusted_fitness; + if (ind.fitness.adjusted_fitness < current_stats.worst_fitness) + current_stats.worst_fitness = ind.fitness.adjusted_fitness; - current_stats.overall_fitness = current_stats.overall_fitness + ind.second.fitness.adjusted_fitness; + current_stats.overall_fitness = current_stats.overall_fitness + ind.fitness.adjusted_fitness; + } + for (auto& ind : current_pop) + { + auto prob = (ind.fitness.adjusted_fitness / current_stats.overall_fitness); + current_stats.normalized_fitness.push_back(sum_of_prob + prob); + sum_of_prob += prob; } thread_helper.evaluation_left = 0; } @@ -413,9 +469,9 @@ namespace blt::gp new_children.clear(); auto args = get_selector_args(new_children); - crossover_selection.pre_process(*this, current_pop, current_stats); - mutation_selection.pre_process(*this, current_pop, current_stats); - reproduction_selection.pre_process(*this, current_pop, current_stats); + crossover_selection.pre_process(*this, current_pop); + mutation_selection.pre_process(*this, current_pop); + reproduction_selection.pre_process(*this, current_pop); perform_elitism(args); @@ -427,12 +483,12 @@ namespace blt::gp thread_helper.next_gen_left = 0; } - }); + })); } else { BLT_INFO("Starting thread execution service!"); std::scoped_lock lock(thread_helper.thread_function_control); - thread_execution_service = new std::function( + thread_execution_service = std::unique_ptr>(new std::function( [this, &fitness_function, &crossover_selection, &mutation_selection, &reproduction_selection, &func](blt::size_t id) { thread_helper.barrier.wait(); if (thread_helper.evaluation_left > 0) @@ -491,11 +547,20 @@ namespace blt::gp auto args = get_selector_args(new_children); if (id == 0) { - crossover_selection.pre_process(*this, current_pop, current_stats); + current_stats.normalized_fitness.clear(); + double sum_of_prob = 0; + for (auto& ind : current_pop) + { + auto prob = (ind.fitness.adjusted_fitness / current_stats.overall_fitness); + current_stats.normalized_fitness.push_back(sum_of_prob + prob); + sum_of_prob += prob; + } + + crossover_selection.pre_process(*this, current_pop); if (&crossover_selection != &mutation_selection) - mutation_selection.pre_process(*this, current_pop, current_stats); + mutation_selection.pre_process(*this, current_pop); if (&crossover_selection != &reproduction_selection) - reproduction_selection.pre_process(*this, current_pop, current_stats); + reproduction_selection.pre_process(*this, current_pop); perform_elitism(args); @@ -531,75 +596,13 @@ namespace blt::gp } } thread_helper.barrier.wait(); - }); + })); thread_helper.thread_function_condition.notify_all(); } if (eval_fitness_now) evaluate_fitness_internal(); } - void reset_program(type_id root_type, bool eval_fitness_now = true) - { - current_generation = 0; - current_pop = config.pop_initializer.get().generate( - {*this, root_type, config.population_size, config.initial_min_tree_size, config.initial_max_tree_size}); - if (eval_fitness_now) - evaluate_fitness_internal(); - } - - void next_generation() - { - BLT_ASSERT_MSG(next_pop.get_individuals().size() == config.population_size, ("pop size: " + std::to_string(next_pop.get_individuals().size())).c_str()); - current_pop = std::move(next_pop); - current_generation++; - } - - inline auto& get_current_pop() - { - return current_pop; - } - - template - std::array get_best_indexes() - { - std::array arr; - - std::vector> values; - values.reserve(current_pop.get_individuals().size()); - - for (const auto& ind : blt::enumerate(current_pop.get_individuals())) - values.emplace_back(ind.first, ind.second.fitness.adjusted_fitness); - - std::sort(values.begin(), values.end(), [](const auto& a, const auto& b) { - return a.second > b.second; - }); - - for (blt::size_t i = 0; i < size; i++) - arr[i] = values[i].first; - - return arr; - } - - template - auto get_best_trees() - { - return convert_array, size>>(get_best_indexes(), - [this](auto&& arr, blt::size_t index) -> tree_t& { - return current_pop.get_individuals()[arr[index]].tree; - }, - std::make_integer_sequence()); - } - - template - auto get_best_individuals() - { - return convert_array, size>>(get_best_indexes(), - [this](auto&& arr, blt::size_t index) -> individual_t& { - return current_pop.get_individuals()[arr[index]]; - }, - std::make_integer_sequence()); - } - [[nodiscard]] bool should_terminate() const { return current_generation >= config.max_generations || fitness_should_exit; @@ -610,13 +613,6 @@ namespace blt::gp return thread_helper.lifetime_over; } - [[nodiscard]] random_t& get_random() const; - - [[nodiscard]] inline type_provider& get_typesystem() - { - return system; - } - inline operator_id select_terminal(type_id id) { // we wanted a terminal, but could not find one, so we will select from a function that has a terminal @@ -642,47 +638,49 @@ namespace blt::gp return get_random().select(storage.operators_ordered_terminals[id]).first; } - inline operator_info& get_operator_info(operator_id id) + inline auto& get_current_pop() + { + return current_pop; + } + + [[nodiscard]] random_t& get_random() const; + + [[nodiscard]] inline type_provider& get_typesystem() + { + return storage.system; + } + + [[nodiscard]] inline operator_info& get_operator_info(operator_id id) { return storage.operators[id]; } - inline detail::print_func_t& get_print_func(operator_id id) + [[nodiscard]] inline detail::print_func_t& get_print_func(operator_id id) { return storage.print_funcs[id]; } - inline detail::destroy_func_t& get_destroy_func(operator_id id) + [[nodiscard]] inline detail::destroy_func_t& get_destroy_func(operator_id id) { return storage.destroy_funcs[id]; } - inline std::optional get_name(operator_id id) + [[nodiscard]] inline std::optional get_name(operator_id id) { return storage.names[id]; } - inline std::vector& get_type_terminals(type_id id) + [[nodiscard]] inline std::vector& get_type_terminals(type_id id) { return storage.terminals[id]; } - inline std::vector& get_type_non_terminals(type_id id) + [[nodiscard]] inline std::vector& get_type_non_terminals(type_id id) { return storage.non_terminals[id]; } - inline bool is_static(operator_id id) - { - return storage.static_types.contains(static_cast(id)); - } - - inline void set_operations(operator_storage op) - { - storage = std::move(op); - } - - inline detail::eval_func_t& get_eval_func() + [[nodiscard]] inline detail::eval_func_t& get_eval_func() { return storage.eval_func; } @@ -692,65 +690,63 @@ namespace blt::gp return current_generation.load(); } - [[nodiscard]] inline auto& get_population_stats() + [[nodiscard]] inline const auto& get_population_stats() const { return current_stats; } - ~gp_program() + [[nodiscard]] inline bool is_operator_ephemeral(operator_id id) { - thread_helper.lifetime_over = true; - thread_helper.barrier.notify_all(); - thread_helper.thread_function_condition.notify_all(); - for (auto& thread : thread_helper.threads) - { - if (thread->joinable()) - thread->join(); - } - auto* cpy = thread_execution_service.load(std::memory_order_acquire); - thread_execution_service = nullptr; - delete cpy; + return storage.ephemeral_leaf_operators.contains(static_cast(id)); } - void kill() + inline void set_operations(program_operator_storage_t op) { - thread_helper.lifetime_over = true; + storage = std::move(op); + } + + template + std::array get_best_indexes() + { + std::array arr; + + std::vector> values; + values.reserve(current_pop.get_individuals().size()); + + for (const auto& ind : blt::enumerate(current_pop.get_individuals())) + values.emplace_back(ind.first, ind.second.fitness.adjusted_fitness); + + std::sort(values.begin(), values.end(), [](const auto& a, const auto& b) { + return a.second > b.second; + }); + + for (blt::size_t i = 0; i < size; i++) + arr[i] = values[i].first; + + return arr; + } + + template + auto get_best_trees() + { + return convert_array, size>>(get_best_indexes(), + [this](auto&& arr, blt::size_t index) -> tree_t& { + return current_pop.get_individuals()[arr[index]].tree; + }, + std::make_integer_sequence()); + } + + template + auto get_best_individuals() + { + return convert_array, size>>(get_best_indexes(), + [this](auto&& arr, blt::size_t index) -> individual_t& { + return current_pop.get_individuals()[arr[index]]; + }, + std::make_integer_sequence()); } private: - type_provider& system; - - operator_storage storage; - population_t current_pop; - population_stats current_stats{}; - population_t next_pop; - std::atomic_uint64_t current_generation = 0; - std::atomic_bool fitness_should_exit = false; - - std::function seed_func; - prog_config_t config{}; - - struct concurrency_storage - { - std::vector> threads; - - std::mutex thread_function_control; - std::mutex thread_generation_lock; - std::condition_variable thread_function_condition{}; - - std::atomic_uint64_t evaluation_left = 0; - std::atomic_uint64_t next_gen_left = 0; - - std::atomic_bool lifetime_over = false; - blt::barrier barrier; - - explicit concurrency_storage(blt::size_t threads): barrier(threads, lifetime_over) - {} - } thread_helper{config.threads == 0 ? std::thread::hardware_concurrency() : config.threads}; - - // for convenience, shouldn't decrease performance too much - std::atomic*> thread_execution_service = nullptr; - inline selector_args get_selector_args(tracked_vector& next_pop_trees) { return {*this, next_pop_trees, current_pop, current_stats, config, get_random()}; @@ -767,12 +763,48 @@ namespace blt::gp void evaluate_fitness_internal() { + statistic_history.push_back(current_stats); current_stats.clear(); thread_helper.evaluation_left.store(current_pop.get_individuals().size(), std::memory_order_release); (*thread_execution_service)(0); current_stats.average_fitness = current_stats.overall_fitness / static_cast(config.population_size); } + + private: + program_operator_storage_t storage; + std::function seed_func; + prog_config_t config{}; + + population_t current_pop; + population_t next_pop; + + std::atomic_uint64_t current_generation = 0; + + std::atomic_bool fitness_should_exit = false; + + population_stats current_stats{}; + std::vector statistic_history; + + struct concurrency_storage + { + std::vector> threads; + + std::mutex thread_function_control{}; + std::mutex thread_generation_lock{}; + std::condition_variable thread_function_condition{}; + + std::atomic_uint64_t evaluation_left = 0; + std::atomic_uint64_t next_gen_left = 0; + + std::atomic_bool lifetime_over = false; + blt::barrier barrier; + + explicit concurrency_storage(blt::size_t threads): barrier(threads, lifetime_over) + {} + } thread_helper{config.threads == 0 ? std::thread::hardware_concurrency() : config.threads}; + + std::unique_ptr> thread_execution_service = nullptr; }; } diff --git a/include/blt/gp/selection.h b/include/blt/gp/selection.h index 2b6b64c..c2b83b8 100644 --- a/include/blt/gp/selection.h +++ b/include/blt/gp/selection.h @@ -88,8 +88,8 @@ namespace blt::gp { // auto state = tracker.start_measurement(); // crossover - auto& p1 = crossover_selection.select(program, current_pop, current_stats); - auto& p2 = crossover_selection.select(program, current_pop, current_stats); + auto& p1 = crossover_selection.select(program, current_pop); + auto& p2 = crossover_selection.select(program, current_pop); auto results = config.crossover.get().apply(program, p1, p2); @@ -110,7 +110,7 @@ namespace blt::gp { // auto state = tracker.start_measurement(); // mutation - auto& p = mutation_selection.select(program, current_pop, current_stats); + auto& p = mutation_selection.select(program, current_pop); next_pop.push_back(std::move(config.mutator.get().apply(program, p))); // tracker.stop_measurement(state); // BLT_TRACE("Mutation Allocated %ld times with a total of %s", state.getAllocationDifference(), @@ -122,7 +122,7 @@ namespace blt::gp { // auto state = tracker.start_measurement(); // reproduction - auto& p = reproduction_selection.select(program, current_pop, current_stats); + auto& p = reproduction_selection.select(program, current_pop); next_pop.push_back(p); // tracker.stop_measurement(state); // BLT_TRACE("Reproduction Allocated %ld times with a total of %s", state.getAllocationDifference(), @@ -147,9 +147,9 @@ namespace blt::gp * @param stats the populations statistics * @return */ - virtual tree_t& select(gp_program& program, population_t& pop, population_stats& stats) = 0; + virtual tree_t& select(gp_program& program, population_t& pop) = 0; - virtual void pre_process(gp_program&, population_t&, population_stats&) + virtual void pre_process(gp_program&, population_t&) {} virtual ~selection_t() = default; @@ -158,19 +158,19 @@ namespace blt::gp class select_best_t : public selection_t { public: - tree_t& select(gp_program& program, population_t& pop, population_stats& stats) final; + tree_t& select(gp_program& program, population_t& pop) final; }; class select_worst_t : public selection_t { public: - tree_t& select(gp_program& program, population_t& pop, population_stats& stats) final; + tree_t& select(gp_program& program, population_t& pop) final; }; class select_random_t : public selection_t { public: - tree_t& select(gp_program& program, population_t& pop, population_stats& stats) final; + tree_t& select(gp_program& program, population_t& pop) final; }; class select_tournament_t : public selection_t @@ -182,7 +182,7 @@ namespace blt::gp BLT_ABORT("Unable to select with this size. Must select at least 1 individual_t!"); } - tree_t& select(gp_program& program, population_t& pop, population_stats& stats) final; + tree_t& select(gp_program& program, population_t& pop) final; private: const blt::size_t selection_size; @@ -191,9 +191,7 @@ namespace blt::gp class select_fitness_proportionate_t : public selection_t { public: - void pre_process(gp_program& program, population_t& pop, population_stats& stats) final; - - tree_t& select(gp_program& program, population_t& pop, population_stats& stats) final; + tree_t& select(gp_program& program, population_t& pop) final; }; } diff --git a/include/blt/gp/tree.h b/include/blt/gp/tree.h index 2ed4e3f..7663957 100644 --- a/include/blt/gp/tree.h +++ b/include/blt/gp/tree.h @@ -197,6 +197,17 @@ namespace blt::gp struct population_stats { + population_stats() = default; + + population_stats(const population_stats& copy): + overall_fitness(copy.overall_fitness.load()), average_fitness(copy.average_fitness.load()), best_fitness(copy.best_fitness.load()), + worst_fitness(copy.worst_fitness.load()) + { + normalized_fitness.reserve(copy.normalized_fitness.size()); + for (auto v : copy.normalized_fitness) + normalized_fitness.push_back(v); + } + std::atomic overall_fitness = 0; std::atomic average_fitness = 0; std::atomic best_fitness = 0; diff --git a/include/blt/gp/typesystem.h b/include/blt/gp/typesystem.h index 5a7f974..7c25ac1 100644 --- a/include/blt/gp/typesystem.h +++ b/include/blt/gp/typesystem.h @@ -85,12 +85,13 @@ namespace blt::gp type_provider() = default; template - inline type register_type() + inline void register_type() { + if (has_type()) + return; auto t = type::make_type(types.size()); types.insert({blt::type_string_raw(), t}); types_from_id[t.id()] = t; - return t; } template @@ -99,6 +100,11 @@ namespace blt::gp return types[blt::type_string_raw()]; } + template + inline bool has_type(){ + return types.find(blt::type_string_raw()) != types.end(); + } + inline type get_type(type_id id) { return types_from_id[id]; diff --git a/src/generators.cpp b/src/generators.cpp index 0b9185a..1c59a87 100644 --- a/src/generators.cpp +++ b/src/generators.cpp @@ -64,10 +64,10 @@ namespace blt::gp tree.get_operations().emplace_back( args.program.get_typesystem().get_type(info.return_type).size(), top.id, - args.program.is_static(top.id)); + args.program.is_operator_ephemeral(top.id)); max_depth = std::max(max_depth, top.depth); - if (args.program.is_static(top.id)) + if (args.program.is_operator_ephemeral(top.id)) { info.func(nullptr, tree.get_values(), tree.get_values()); continue; diff --git a/src/program.cpp b/src/program.cpp index 413c932..1f310d3 100644 --- a/src/program.cpp +++ b/src/program.cpp @@ -76,7 +76,7 @@ namespace blt::gp if (should_thread_terminate()) return; } - execution_function = thread_execution_service.load(std::memory_order_acquire); + execution_function = thread_execution_service.get(); } if (execution_function != nullptr) (*execution_function)(i); diff --git a/src/selection.cpp b/src/selection.cpp index 5621dc6..0bdb7f5 100644 --- a/src/selection.cpp +++ b/src/selection.cpp @@ -21,7 +21,7 @@ namespace blt::gp { - tree_t& select_best_t::select(gp_program&, population_t& pop, population_stats&) + tree_t& select_best_t::select(gp_program&, population_t& pop) { auto& first = pop.get_individuals()[0]; double best_fitness = first.fitness.adjusted_fitness; @@ -37,7 +37,7 @@ namespace blt::gp return *tree; } - tree_t& select_worst_t::select(gp_program&, population_t& pop, population_stats&) + tree_t& select_worst_t::select(gp_program&, population_t& pop) { auto& first = pop.get_individuals()[0]; double worst_fitness = first.fitness.adjusted_fitness; @@ -53,12 +53,12 @@ namespace blt::gp return *tree; } - tree_t& select_random_t::select(gp_program& program, population_t& pop, population_stats&) + tree_t& select_random_t::select(gp_program& program, population_t& pop) { return pop.get_individuals()[program.get_random().get_size_t(0ul, pop.get_individuals().size())].tree; } - tree_t& select_tournament_t::select(gp_program& program, population_t& pop, population_stats&) + tree_t& select_tournament_t::select(gp_program& program, population_t& pop) { blt::u64 best = program.get_random().get_u64(0, pop.get_individuals().size()); auto& i_ref = pop.get_individuals(); @@ -71,8 +71,9 @@ namespace blt::gp return i_ref[best].tree; } - tree_t& select_fitness_proportionate_t::select(gp_program& program, population_t& pop, population_stats& stats) + tree_t& select_fitness_proportionate_t::select(gp_program& program, population_t& pop) { + auto& stats = program.get_population_stats(); auto choice = program.get_random().get_double(); for (const auto& [index, ref] : blt::enumerate(pop)) { @@ -90,16 +91,4 @@ namespace blt::gp return pop.get_individuals()[0].tree; //BLT_ABORT("Unable to find individual"); } - - void select_fitness_proportionate_t::pre_process(gp_program&, population_t& pop, population_stats& stats) - { - stats.normalized_fitness.clear(); - double sum_of_prob = 0; - for (auto& ind : pop) - { - auto prob = (ind.fitness.adjusted_fitness / stats.overall_fitness); - stats.normalized_fitness.push_back(sum_of_prob + prob); - sum_of_prob += prob; - } - } } \ No newline at end of file diff --git a/src/transformers.cpp b/src/transformers.cpp index 4994fb3..154e2cf 100644 --- a/src/transformers.cpp +++ b/src/transformers.cpp @@ -468,7 +468,7 @@ namespace blt::gp } // now finally update the type. ops[c_node] = {program.get_typesystem().get_type(replacement_func_info.return_type).size(), random_replacement, - program.is_static(random_replacement)}; + program.is_operator_ephemeral(random_replacement)}; } #if BLT_DEBUG_LEVEL >= 2 if (!c.check(program, nullptr)) @@ -556,7 +556,7 @@ namespace blt::gp ops.insert(ops.begin() + static_cast(c_node), {program.get_typesystem().get_type(replacement_func_info.return_type).size(), - random_replacement, program.is_static(random_replacement)}); + random_replacement, program.is_operator_ephemeral(random_replacement)}); #if BLT_DEBUG_LEVEL >= 2 if (!c.check(program, nullptr)) diff --git a/src/tree.cpp b/src/tree.cpp index 8ec56d3..ed41957 100644 --- a/src/tree.cpp +++ b/src/tree.cpp @@ -92,7 +92,7 @@ namespace blt::gp if (print_literals) { create_indent(out, indent, pretty_print); - if (program.is_static(v.id)) + if (program.is_operator_ephemeral(v.id)) { program.get_print_func(v.id)(out, reversed); reversed.pop_bytes(stack_allocator::aligned_size(v.type_size)); From 91bda10b0a5269bc8380177f94f9c27dd9a13250 Mon Sep 17 00:00:00 2001 From: Brett Date: Fri, 30 Aug 2024 23:33:52 -0400 Subject: [PATCH 16/19] meow --- examples/symbolic_regression.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/symbolic_regression.cpp b/examples/symbolic_regression.cpp index e1ce917..b698963 100644 --- a/examples/symbolic_regression.cpp +++ b/examples/symbolic_regression.cpp @@ -41,7 +41,7 @@ blt::gp::prog_config_t config = blt::gp::prog_config_t() .set_mutation_chance(0.1) .set_reproduction_chance(0) .set_max_generations(50) - .set_pop_size(500) + .set_pop_size(5000) .set_thread_count(0); blt::gp::gp_program program{SEED, config}; From 2f3ea1b3bfd185597ff9a71b058ea5779f9b61ae Mon Sep 17 00:00:00 2001 From: Brett Date: Sat, 31 Aug 2024 00:05:04 -0400 Subject: [PATCH 17/19] silly --- examples/symbolic_regression.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/symbolic_regression.cpp b/examples/symbolic_regression.cpp index b698963..078316e 100644 --- a/examples/symbolic_regression.cpp +++ b/examples/symbolic_regression.cpp @@ -36,18 +36,18 @@ std::array training_cases; blt::gp::prog_config_t config = blt::gp::prog_config_t() .set_initial_min_tree_size(2) .set_initial_max_tree_size(6) - .set_elite_count(2) + .set_elite_count(200) .set_crossover_chance(0.9) .set_mutation_chance(0.1) .set_reproduction_chance(0) .set_max_generations(50) - .set_pop_size(5000) + .set_pop_size(20000) .set_thread_count(0); blt::gp::gp_program program{SEED, config}; auto lit = blt::gp::operation_t([]() { - return program.get_random().get_float(-320.0f, 320.0f); + return program.get_random().get_float(-1.0f, 1.0f); }, "lit").set_ephemeral(); blt::gp::operation_t op_x([](const context& context) { From 3572539b743161067ffc6fa6e7466f4d2fd4f486 Mon Sep 17 00:00:00 2001 From: Brett Date: Sat, 31 Aug 2024 00:10:25 -0400 Subject: [PATCH 18/19] silly2 --- include/blt/gp/transformers.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/blt/gp/transformers.h b/include/blt/gp/transformers.h index e004afa..e1a229d 100644 --- a/include/blt/gp/transformers.h +++ b/include/blt/gp/transformers.h @@ -170,11 +170,11 @@ namespace blt::gp double per_node_mutation_chance = 5.0; static constexpr std::array mutation_operator_chances = detail::aggregate_array( - 0.1, // EXPRESSION - 0.25, // ADJUST + 0.25, // EXPRESSION + 0.15, // ADJUST 0.01, // SUB_FUNC - 0.25, // JUMP_FUNC - 0.12 // COPY + 0.01, // JUMP_FUNC + 0.05 // COPY ); }; From 1789a9e4746beb86a3f6de74ea096bc7c4ca7768 Mon Sep 17 00:00:00 2001 From: Brett Date: Sat, 31 Aug 2024 22:03:22 -0400 Subject: [PATCH 19/19] i think it's shared now? --- CMakeLists.txt | 4 +-- include/blt/gp/program.h | 66 +++++++++++++++++++---------------- include/blt/gp/selection.h | 59 ++++++++++++++++--------------- include/blt/gp/transformers.h | 18 +++------- include/blt/gp/tree.h | 44 +++++++++++++++++++++++ src/selection.cpp | 14 ++++---- src/transformers.cpp | 42 +++++++++++----------- 7 files changed, 143 insertions(+), 104 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fa56855..65a80e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(blt-gp VERSION 0.1.35) +project(blt-gp VERSION 0.1.36) include(CTest) @@ -16,7 +16,7 @@ set(CMAKE_CXX_STANDARD 17) set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) -SET(CMAKE_CXX_FLAGS_RELEASE "-O3 -g") +#SET(CMAKE_CXX_FLAGS_RELEASE "-O3 -g") if (NOT TARGET BLT) add_subdirectory(lib/blt) diff --git a/include/blt/gp/program.h b/include/blt/gp/program.h index fc4b134..ac0be48 100644 --- a/include/blt/gp/program.h +++ b/include/blt/gp/program.h @@ -362,8 +362,11 @@ namespace blt::gp #ifdef BLT_TRACK_ALLOCATIONS auto gen_alloc = blt::gp::tracker.start_measurement(); #endif + BLT_ASSERT_MSG(current_pop.get_individuals().size() == config.population_size, + ("cur pop size: " + std::to_string(current_pop.get_individuals().size())).c_str()); + BLT_ASSERT_MSG(next_pop.get_individuals().size() == config.population_size, + ("next pop size: " + std::to_string(next_pop.get_individuals().size())).c_str()); // should already be empty - next_pop.clear(); thread_helper.next_gen_left.store(config.population_size, std::memory_order_release); (*thread_execution_service)(0); #ifdef BLT_TRACK_ALLOCATIONS @@ -375,8 +378,10 @@ namespace blt::gp void next_generation() { + BLT_ASSERT_MSG(current_pop.get_individuals().size() == config.population_size, + ("cur pop size: " + std::to_string(current_pop.get_individuals().size())).c_str()); BLT_ASSERT_MSG(next_pop.get_individuals().size() == config.population_size, - ("pop size: " + std::to_string(next_pop.get_individuals().size())).c_str()); + ("next pop size: " + std::to_string(next_pop.get_individuals().size())).c_str()); std::swap(current_pop, next_pop); current_generation++; } @@ -400,6 +405,7 @@ namespace blt::gp current_generation = 0; current_pop = config.pop_initializer.get().generate( {*this, root_type, config.population_size, config.initial_min_tree_size, config.initial_max_tree_size}); + next_pop = population_t(current_pop); if (eval_fitness_now) evaluate_fitness_internal(); } @@ -465,21 +471,24 @@ namespace blt::gp } if (thread_helper.next_gen_left > 0) { - static thread_local tracked_vector new_children; - new_children.clear(); - auto args = get_selector_args(new_children); + auto args = get_selector_args(); crossover_selection.pre_process(*this, current_pop); mutation_selection.pre_process(*this, current_pop); reproduction_selection.pre_process(*this, current_pop); - perform_elitism(args); + perform_elitism(args, next_pop); - while (new_children.size() < config.population_size) - func(args, crossover_selection, mutation_selection, reproduction_selection); + blt::size_t start = config.elites; - for (auto& i : new_children) - next_pop.get_individuals().emplace_back(std::move(i)); + while (start < config.population_size) + { + tree_t& c1 = next_pop.get_individuals()[start].tree; + tree_t* c2 = nullptr; + if (start + 1 < config.population_size) + c2 = &next_pop.get_individuals()[start + 1].tree; + start += func(args, crossover_selection, mutation_selection, reproduction_selection, c1, c2); + } thread_helper.next_gen_left = 0; } @@ -542,9 +551,7 @@ namespace blt::gp } if (thread_helper.next_gen_left > 0) { - static thread_local tracked_vector new_children; - new_children.clear(); - auto args = get_selector_args(new_children); + auto args = get_selector_args(); if (id == 0) { current_stats.normalized_fitness.clear(); @@ -562,37 +569,35 @@ namespace blt::gp if (&crossover_selection != &reproduction_selection) reproduction_selection.pre_process(*this, current_pop); - perform_elitism(args); + perform_elitism(args, next_pop); - for (auto& i : new_children) - next_pop.get_individuals().emplace_back(std::move(i)); - thread_helper.next_gen_left -= new_children.size(); - new_children.clear(); + thread_helper.next_gen_left -= config.elites; } thread_helper.barrier.wait(); while (thread_helper.next_gen_left > 0) { blt::size_t size = 0; + blt::size_t begin = 0; blt::size_t end = thread_helper.next_gen_left.load(std::memory_order_relaxed); do { size = std::min(end, config.evaluation_size); + begin = end - size; } while (!thread_helper.next_gen_left.compare_exchange_weak(end, end - size, std::memory_order::memory_order_relaxed, std::memory_order::memory_order_relaxed)); - while (new_children.size() < size) - func(args, crossover_selection, mutation_selection, reproduction_selection); - + while (begin != end) { - std::scoped_lock lock(thread_helper.thread_generation_lock); - for (auto& i : new_children) - { - if (next_pop.get_individuals().size() < config.population_size) - next_pop.get_individuals().emplace_back(i); - } + auto index = config.elites + begin; + tree_t& c1 = next_pop.get_individuals()[index].tree; + tree_t* c2 = nullptr; + if (index + 1 < end) + c2 = &next_pop.get_individuals()[index + 1].tree; + begin += func(args, crossover_selection, mutation_selection, reproduction_selection, c1, c2); } + } } thread_helper.barrier.wait(); @@ -747,9 +752,9 @@ namespace blt::gp } private: - inline selector_args get_selector_args(tracked_vector& next_pop_trees) + inline selector_args get_selector_args() { - return {*this, next_pop_trees, current_pop, current_stats, config, get_random()}; + return {*this, current_pop, current_stats, config, get_random()}; } template @@ -765,7 +770,7 @@ namespace blt::gp { statistic_history.push_back(current_stats); current_stats.clear(); - thread_helper.evaluation_left.store(current_pop.get_individuals().size(), std::memory_order_release); + thread_helper.evaluation_left.store(config.population_size, std::memory_order_release); (*thread_execution_service)(0); current_stats.average_fitness = current_stats.overall_fitness / static_cast(config.population_size); @@ -791,7 +796,6 @@ namespace blt::gp std::vector> threads; std::mutex thread_function_control{}; - std::mutex thread_generation_lock{}; std::condition_variable thread_function_condition{}; std::atomic_uint64_t evaluation_left = 0; diff --git a/include/blt/gp/selection.h b/include/blt/gp/selection.h index c2b83b8..a3c3856 100644 --- a/include/blt/gp/selection.h +++ b/include/blt/gp/selection.h @@ -32,15 +32,14 @@ namespace blt::gp struct selector_args { gp_program& program; - tracked_vector& next_pop; - population_t& current_pop; + const population_t& current_pop; population_stats& current_stats; prog_config_t& config; random_t& random; }; - constexpr inline auto perform_elitism = [](const selector_args& args) { - auto& [program, next_pop, current_pop, current_stats, config, random] = args; + constexpr inline auto perform_elitism = [](const selector_args& args, population_t& next_pop) { + auto& [program, current_pop, current_stats, config, random] = args; if (config.elites > 0) { @@ -70,39 +69,38 @@ namespace blt::gp } for (blt::size_t i = 0; i < config.elites; i++) - next_pop.push_back(current_pop.get_individuals()[values[i].first].tree); + next_pop.get_individuals()[i].copy_fast(current_pop.get_individuals()[values[i].first].tree); } }; template constexpr inline auto default_next_pop_creator = []( - blt::gp::selector_args& args, Crossover& crossover_selection, Mutation& mutation_selection, Reproduction& reproduction_selection) { - auto& [program, next_pop, current_pop, current_stats, config, random] = args; + blt::gp::selector_args& args, Crossover& crossover_selection, Mutation& mutation_selection, Reproduction& reproduction_selection, + tree_t& c1, tree_t* c2) { + auto& [program, current_pop, current_stats, config, random] = args; int sel = random.get_i32(0, 3); switch (sel) { case 0: + if (c2 == nullptr) + return 0; // everyone gets a chance once per loop. if (random.choice(config.crossover_chance)) { // auto state = tracker.start_measurement(); // crossover - auto& p1 = crossover_selection.select(program, current_pop); - auto& p2 = crossover_selection.select(program, current_pop); - - auto results = config.crossover.get().apply(program, p1, p2); - - // if crossover fails, we can check for mutation on these guys. otherwise straight copy them into the next pop - if (results) + const tree_t* p1; + const tree_t* p2; + do { - next_pop.push_back(std::move(results->child1)); - if (next_pop.size() != config.population_size) - next_pop.push_back(std::move(results->child2)); - } + p1 = &crossover_selection.select(program, current_pop); + p2 = &crossover_selection.select(program, current_pop); + } while (!config.crossover.get().apply(program, *p1, *p2, c1, *c2)); // tracker.stop_measurement(state); // BLT_TRACE("Crossover Allocated %ld times with a total of %s", state.getAllocationDifference(), // blt::byte_convert_t(state.getAllocatedByteDifference()).convert_to_nearest_type().to_pretty_string().c_str()); + return 2; } break; case 1: @@ -110,11 +108,15 @@ namespace blt::gp { // auto state = tracker.start_measurement(); // mutation - auto& p = mutation_selection.select(program, current_pop); - next_pop.push_back(std::move(config.mutator.get().apply(program, p))); + const tree_t* p; + do + { + p = &mutation_selection.select(program, current_pop); + } while (!config.mutator.get().apply(program, *p, c1)); // tracker.stop_measurement(state); // BLT_TRACE("Mutation Allocated %ld times with a total of %s", state.getAllocationDifference(), // blt::byte_convert_t(state.getAllocatedByteDifference()).convert_to_nearest_type().to_pretty_string().c_str()); + return 1; } break; case 2: @@ -122,11 +124,11 @@ namespace blt::gp { // auto state = tracker.start_measurement(); // reproduction - auto& p = reproduction_selection.select(program, current_pop); - next_pop.push_back(p); + c1 = reproduction_selection.select(program, current_pop); // tracker.stop_measurement(state); // BLT_TRACE("Reproduction Allocated %ld times with a total of %s", state.getAllocationDifference(), // blt::byte_convert_t(state.getAllocatedByteDifference()).convert_to_nearest_type().to_pretty_string().c_str()); + return 1; } break; default: @@ -136,6 +138,7 @@ namespace blt::gp BLT_UNREACHABLE; #endif } + return 0; }; class selection_t @@ -147,7 +150,7 @@ namespace blt::gp * @param stats the populations statistics * @return */ - virtual tree_t& select(gp_program& program, population_t& pop) = 0; + virtual const tree_t& select(gp_program& program, const population_t& pop) = 0; virtual void pre_process(gp_program&, population_t&) {} @@ -158,19 +161,19 @@ namespace blt::gp class select_best_t : public selection_t { public: - tree_t& select(gp_program& program, population_t& pop) final; + const tree_t& select(gp_program& program, const population_t& pop) final; }; class select_worst_t : public selection_t { public: - tree_t& select(gp_program& program, population_t& pop) final; + const tree_t& select(gp_program& program, const population_t& pop) final; }; class select_random_t : public selection_t { public: - tree_t& select(gp_program& program, population_t& pop) final; + const tree_t& select(gp_program& program, const population_t& pop) final; }; class select_tournament_t : public selection_t @@ -182,7 +185,7 @@ namespace blt::gp BLT_ABORT("Unable to select with this size. Must select at least 1 individual_t!"); } - tree_t& select(gp_program& program, population_t& pop) final; + const tree_t& select(gp_program& program, const population_t& pop) final; private: const blt::size_t selection_size; @@ -191,7 +194,7 @@ namespace blt::gp class select_fitness_proportionate_t : public selection_t { public: - tree_t& select(gp_program& program, population_t& pop) final; + const tree_t& select(gp_program& program, const population_t& pop) final; }; } diff --git a/include/blt/gp/transformers.h b/include/blt/gp/transformers.h index e1a229d..15c5765 100644 --- a/include/blt/gp/transformers.h +++ b/include/blt/gp/transformers.h @@ -57,16 +57,6 @@ namespace blt::gp class crossover_t { public: - enum class error_t - { - NO_VALID_TYPE, - TREE_TOO_SMALL - }; - struct result_t - { - tree_t child1; - tree_t child2; - }; struct crossover_point_t { blt::ptrdiff_t p1_crossover_point; @@ -87,7 +77,7 @@ namespace blt::gp explicit crossover_t(const config_t& config): config(config) {} - blt::expected get_crossover_point(gp_program& program, const tree_t& c1, const tree_t& c2) const; + std::optional get_crossover_point(gp_program& program, const tree_t& c1, const tree_t& c2) const; /** * child1 and child2 are copies of the parents, the result of selecting a crossover point and performing standard subtree crossover. @@ -97,7 +87,7 @@ namespace blt::gp * @param p2 reference to the second parent * @return expected pair of child otherwise returns error enum */ - virtual blt::expected apply(gp_program& program, const tree_t& p1, const tree_t& p2); // NOLINT + virtual bool apply(gp_program& program, const tree_t& p1, const tree_t& p2, tree_t& c1, tree_t& c2); // NOLINT virtual ~crossover_t() = default; @@ -126,7 +116,7 @@ namespace blt::gp explicit mutation_t(const config_t& config): config(config) {} - virtual tree_t apply(gp_program& program, const tree_t& p); + virtual bool apply(gp_program& program, const tree_t& p, tree_t& c); // returns the point after the mutation blt::size_t mutate_point(gp_program& program, tree_t& c, blt::size_t node); @@ -155,7 +145,7 @@ namespace blt::gp explicit advanced_mutation_t(const config_t& config): mutation_t(config) {} - tree_t apply(gp_program& program, const tree_t& p) final; + bool apply(gp_program& program, const tree_t& p, tree_t& c) final; advanced_mutation_t& set_per_node_mutation_chance(double v) { diff --git a/include/blt/gp/tree.h b/include/blt/gp/tree.h index 7663957..51d833f 100644 --- a/include/blt/gp/tree.h +++ b/include/blt/gp/tree.h @@ -56,6 +56,37 @@ namespace blt::gp 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); + 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.internal_storage_size()); + values.reset(); + values.insert(copy.values); + + operations.clear(); + operations.reserve(copy.operations.size()); + operations.insert(operations.begin(), copy.operations.begin(), copy.operations.end()); + } + + tree_t(tree_t&& move) = default; + + tree_t& operator=(tree_t&& move) = default; + void clear(gp_program& program); struct child_t @@ -178,6 +209,14 @@ namespace blt::gp tree_t tree; fitness_t fitness; + void copy_fast(const tree_t& copy) + { + // fast copy of the tree + tree.copy_fast(copy); + // reset fitness + fitness = {}; + } + individual_t() = delete; explicit individual_t(tree_t&& tree): tree(std::move(tree)) @@ -284,6 +323,11 @@ namespace blt::gp return individuals; } + [[nodiscard]] const tracked_vector& get_individuals() const + { + return individuals; + } + population_tree_iterator for_each_tree() { return population_tree_iterator{individuals, 0}; diff --git a/src/selection.cpp b/src/selection.cpp index 0bdb7f5..a81983c 100644 --- a/src/selection.cpp +++ b/src/selection.cpp @@ -21,11 +21,11 @@ namespace blt::gp { - tree_t& select_best_t::select(gp_program&, population_t& pop) + const tree_t& select_best_t::select(gp_program&, const population_t& pop) { auto& first = pop.get_individuals()[0]; double best_fitness = first.fitness.adjusted_fitness; - tree_t* tree = &first.tree; + const tree_t* tree = &first.tree; for (auto& ind : pop.get_individuals()) { if (ind.fitness.adjusted_fitness > best_fitness) @@ -37,11 +37,11 @@ namespace blt::gp return *tree; } - tree_t& select_worst_t::select(gp_program&, population_t& pop) + const tree_t& select_worst_t::select(gp_program&, const population_t& pop) { auto& first = pop.get_individuals()[0]; double worst_fitness = first.fitness.adjusted_fitness; - tree_t* tree = &first.tree; + const tree_t* tree = &first.tree; for (auto& ind : pop.get_individuals()) { if (ind.fitness.adjusted_fitness < worst_fitness) @@ -53,12 +53,12 @@ namespace blt::gp return *tree; } - tree_t& select_random_t::select(gp_program& program, population_t& pop) + const tree_t& select_random_t::select(gp_program& program, const population_t& pop) { return pop.get_individuals()[program.get_random().get_size_t(0ul, pop.get_individuals().size())].tree; } - tree_t& select_tournament_t::select(gp_program& program, population_t& pop) + const tree_t& select_tournament_t::select(gp_program& program, const population_t& pop) { blt::u64 best = program.get_random().get_u64(0, pop.get_individuals().size()); auto& i_ref = pop.get_individuals(); @@ -71,7 +71,7 @@ namespace blt::gp return i_ref[best].tree; } - tree_t& select_fitness_proportionate_t::select(gp_program& program, population_t& pop) + const tree_t& select_fitness_proportionate_t::select(gp_program& program, const population_t& pop) { auto& stats = program.get_population_stats(); auto choice = program.get_random().get_double(); diff --git a/src/transformers.cpp b/src/transformers.cpp index 154e2cf..9e0d355 100644 --- a/src/transformers.cpp +++ b/src/transformers.cpp @@ -59,23 +59,21 @@ namespace blt::gp mutation_t::config_t::config_t(): generator(grow_generator) {} - blt::expected crossover_t::apply(gp_program& program, const tree_t& p1, const tree_t& p2) // NOLINT + bool crossover_t::apply(gp_program& program, const tree_t& p1, const tree_t& p2, tree_t& c1, tree_t& c2) // NOLINT { - result_t result{p1, p2}; - - auto& c1 = result.child1; - auto& c2 = result.child2; + c1.copy_fast(p1); + c2.copy_fast(p2); auto& c1_ops = c1.get_operations(); auto& c2_ops = c2.get_operations(); if (c1_ops.size() < 5 || c2_ops.size() < 5) - return blt::unexpected(error_t::TREE_TOO_SMALL); + return false; - auto point = get_crossover_point(program, c1, c2); + auto point = get_crossover_point(program, p1, p2); if (!point) - return blt::unexpected(point.error()); + return false; auto crossover_point_begin_itr = c1_ops.begin() + point->p1_crossover_point; auto crossover_point_end_itr = c1_ops.begin() + c1.find_endpoint(program, point->p1_crossover_point); @@ -104,8 +102,8 @@ namespace blt::gp blt::size_t c2_stack_for_bytes = accumulate_type_sizes(found_point_begin_itr, found_point_end_itr); auto c1_total = static_cast(c1_stack_after_bytes + c1_stack_for_bytes); auto c2_total = static_cast(c2_stack_after_bytes + c2_stack_for_bytes); - auto copy_ptr_c1 = get_thread_pointer_for_size(c1_total); - auto copy_ptr_c2 = get_thread_pointer_for_size(c2_total); + auto copy_ptr_c1 = get_thread_pointer_for_size(c1_total); + auto copy_ptr_c2 = get_thread_pointer_for_size(c2_total); c1_stack.copy_to(copy_ptr_c1, c1_total); c1_stack.pop_bytes(c1_total); @@ -131,8 +129,8 @@ namespace blt::gp c2_ops.insert(++insert_point_c2, c1_operators.begin(), c1_operators.end()); #if BLT_DEBUG_LEVEL >= 2 - blt::size_t c1_found_bytes = result.child1.get_values().size().total_used_bytes; - blt::size_t c2_found_bytes = result.child2.get_values().size().total_used_bytes; + blt::size_t c1_found_bytes = c1.get_values().size().total_used_bytes; + blt::size_t c2_found_bytes = c2.get_values().size().total_used_bytes; blt::size_t c1_expected_bytes = std::accumulate(result.child1.get_operations().begin(), result.child1.get_operations().end(), 0ul, [](const auto& v1, const auto& v2) { if (v2.is_value) @@ -153,10 +151,10 @@ namespace blt::gp } #endif - return result; + return true; } - blt::expected crossover_t::get_crossover_point(gp_program& program, const tree_t& c1, + std::optional crossover_t::get_crossover_point(gp_program& program, const tree_t& c1, const tree_t& c2) const { auto& c1_ops = c1.get_operations(); @@ -194,10 +192,10 @@ namespace blt::gp } } if (!found) - return blt::unexpected(error_t::NO_VALID_TYPE); + return {}; } // should we try again over the whole tree? probably not. - return blt::unexpected(error_t::NO_VALID_TYPE); + return {}; } else { attempted_point = program.get_random().get_size_t(1ul, c2_ops.size()); @@ -213,13 +211,13 @@ namespace blt::gp return crossover_point_t{static_cast(crossover_point), static_cast(attempted_point)}; } - tree_t mutation_t::apply(gp_program& program, const tree_t& p) + bool mutation_t::apply(gp_program& program, const tree_t& p, tree_t& c) { - auto c = p; + c.copy_fast(p); mutate_point(program, c, program.get_random().get_size_t(0ul, c.get_operations().size())); - return c; + return true; } blt::size_t mutation_t::mutate_point(gp_program& program, tree_t& c, blt::size_t node) @@ -304,10 +302,10 @@ namespace blt::gp return begin_point + new_ops_r.size(); } - tree_t advanced_mutation_t::apply(gp_program& program, const tree_t& p) + bool advanced_mutation_t::apply(gp_program& program, const tree_t& p, tree_t& c) { // child tree - tree_t c = p; + c.copy_fast(p); auto& ops = c.get_operations(); auto& vals = c.get_values(); @@ -732,6 +730,6 @@ namespace blt::gp } #endif - return c; + return true; } } \ No newline at end of file