diff --git a/CMakeLists.txt b/CMakeLists.txt index 81a0333..743655b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(blt-gp VERSION 0.1.42) +project(blt-gp VERSION 0.1.43) include(CTest) diff --git a/dhat.out.293761 b/dhat.out.293761 index 21dd56d..7579178 100644 --- a/dhat.out.293761 +++ b/dhat.out.293761 @@ -1240,9 +1240,9 @@ ,"0x122836: main (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" ,"0x12E6ED: void std::vector >::_M_realloc_insert(__gnu_cxx::__normal_iterator > >, blt::gp::operator_id const&) (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" ,"0x12284D: main (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" - ,"0x12EB76: void std::vector >::_M_realloc_insert(__gnu_cxx::__normal_iterator > >, blt::gp::operator_info const&) (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" + ,"0x12EB76: void std::vector >::_M_realloc_insert(__gnu_cxx::__normal_iterator > >, blt::gp::operator_info_t const&) (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" ,"0x12294C: main (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" - ,"0x12E897: void std::vector >::_M_realloc_insert(__gnu_cxx::__normal_iterator > >, blt::gp::operator_info const&) (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" + ,"0x12E897: void std::vector >::_M_realloc_insert(__gnu_cxx::__normal_iterator > >, blt::gp::operator_info_t const&) (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" ,"0x12F1FA: void std::vector, std::allocator > >::_M_realloc_insert >(__gnu_cxx::__normal_iterator*, std::vector, std::allocator > > >, std::function&&) (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" ,"0x12298F: main (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" ,"0x12F3EB: void std::vector > >, std::allocator > > > >::_M_realloc_insert > > >(__gnu_cxx::__normal_iterator > >*, std::vector > >, std::allocator > > > > >, std::optional > >&&) (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" @@ -1261,7 +1261,7 @@ ,"0x122F78: main (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" ,"0x123052: main (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" ,"0x12305D: main (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" - ,"0x12ED14: std::vector >::push_back(blt::gp::operator_info const&) (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" + ,"0x12ED14: std::vector >::push_back(blt::gp::operator_info_t const&) (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" ,"0x1231C6: main (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" ,"0x123321: main (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" ,"0x123390: main (in /home/brett/Documents/code/c++/blt-gp/cmake-build-release/blt-SR-playground-example)" diff --git a/examples/symbolic_regression.cpp b/examples/symbolic_regression.cpp index 66fc8d9..ab87400 100644 --- a/examples/symbolic_regression.cpp +++ b/examples/symbolic_regression.cpp @@ -22,6 +22,7 @@ #include #include #include "operations_common.h" +#include "blt/math/averages.h" //static constexpr long SEED = 41912; static const unsigned long SEED = std::random_device()(); @@ -36,13 +37,13 @@ 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(200) + .set_elite_count(0) .set_crossover_chance(0.9) .set_mutation_chance(0.1) .set_reproduction_chance(0) .set_max_generations(50) - .set_pop_size(20000) - .set_thread_count(0); + .set_pop_size(1) + .set_thread_count(1); blt::gp::gp_program program{SEED, config}; @@ -143,30 +144,40 @@ int main() BLT_TRACE("Total Allocations: %ld times with a total of %s, peak allocated bytes %s", blt::gp::tracker.getAllocations(), blt::byte_convert_t(blt::gp::tracker.getAllocatedBytes()).convert_to_nearest_type().to_pretty_string().c_str(), blt::byte_convert_t(blt::gp::tracker.getPeakAllocatedBytes()).convert_to_nearest_type().to_pretty_string().c_str()); + BLT_TRACE("------------------------------------------------------"); + auto evaluation_calls_v = blt::gp::evaluation_calls.get_calls(); + auto evaluation_allocations_v = blt::gp::evaluation_allocations.get_calls(); + BLT_TRACE("Total Evaluation Calls: %ld; Peak Bytes Allocated %s", evaluation_calls_v, + blt::string::bytes_to_pretty(blt::gp::evaluation_calls.get_value()).c_str()); + BLT_TRACE("Total Evaluation Allocations: %ld; Bytes %s; Average %s", evaluation_allocations_v, + blt::string::bytes_to_pretty(blt::gp::evaluation_allocations.get_value()).c_str(), + blt::string::bytes_to_pretty(blt::average(blt::gp::evaluation_allocations.get_value(), evaluation_allocations_v)).c_str()); + BLT_TRACE("Percent Evaluation calls allocate? %lf%%", blt::average(evaluation_allocations_v, evaluation_calls_v) * 100); + BLT_TRACE("------------------------------------------------------"); auto crossover_calls_v = blt::gp::crossover_calls.get_calls(); auto crossover_allocations_v = blt::gp::crossover_allocations.get_calls(); auto mutation_calls_v = blt::gp::mutation_calls.get_calls(); auto mutation_allocations_v = blt::gp::mutation_allocations.get_calls(); auto reproduction_calls_v = blt::gp::reproduction_calls.get_calls(); auto reproduction_allocations_v = blt::gp::reproduction_allocations.get_calls(); - BLT_TRACE("Total Crossover Calls: %ld Peak Bytes Allocated %s", crossover_calls_v, - blt::byte_convert_t(blt::gp::crossover_calls.get_value()).convert_to_nearest_type().to_pretty_string().c_str()); - BLT_TRACE("Total Mutation Calls: %ld Peak Bytes Allocated %s", mutation_calls_v, - blt::byte_convert_t(blt::gp::mutation_calls.get_value()).convert_to_nearest_type().to_pretty_string().c_str()); - BLT_TRACE("Total Reproduction Calls: %ld Peak Bytes Allocated %s", reproduction_calls_v, - blt::byte_convert_t(blt::gp::reproduction_calls.get_value()).convert_to_nearest_type().to_pretty_string().c_str()); - BLT_TRACE("Total Crossover Allocations: %ld Bytes %s", crossover_allocations_v, - blt::byte_convert_t(blt::gp::crossover_allocations.get_value()).convert_to_nearest_type().to_pretty_string().c_str()); - BLT_TRACE("Total Mutation Allocations: %ld Bytes %s", mutation_allocations_v, - blt::byte_convert_t(blt::gp::mutation_allocations.get_value()).convert_to_nearest_type().to_pretty_string().c_str()); - BLT_TRACE("Total Reproduction Allocations: %ld Bytes %s", reproduction_allocations_v, - blt::byte_convert_t(blt::gp::reproduction_allocations.get_value()).convert_to_nearest_type().to_pretty_string().c_str()); - BLT_TRACE("Percent Crossover calls allocate? %lf%%", - static_cast(crossover_allocations_v) / static_cast(crossover_calls_v == 0 ? 1 : crossover_calls_v) * 100); - BLT_TRACE("Percent Mutation calls allocate? %lf%%", - static_cast(mutation_allocations_v) / static_cast(mutation_calls_v == 0 ? 1 : mutation_calls_v) * 100); - BLT_TRACE("Percent Reproduction calls allocate? %lf%%", - static_cast(reproduction_allocations_v) / static_cast(reproduction_calls_v == 0 ? 1 : reproduction_calls_v) * 100); + BLT_TRACE("Total Crossover Calls: %ld; Peak Bytes Allocated %s", crossover_calls_v, + blt::string::bytes_to_pretty(blt::gp::crossover_calls.get_value()).c_str()); + BLT_TRACE("Total Mutation Calls: %ld; Peak Bytes Allocated %s", mutation_calls_v, + blt::string::bytes_to_pretty(blt::gp::mutation_calls.get_value()).c_str()); + BLT_TRACE("Total Reproduction Calls: %ld; Peak Bytes Allocated %s", reproduction_calls_v, + blt::string::bytes_to_pretty(blt::gp::reproduction_calls.get_value()).c_str()); + BLT_TRACE("Total Crossover Allocations: %ld; Bytes %s; Average %s", crossover_allocations_v, + blt::string::bytes_to_pretty(blt::gp::crossover_allocations.get_value()).c_str(), + blt::string::bytes_to_pretty(blt::average(blt::gp::crossover_allocations.get_value(), crossover_allocations_v)).c_str()); + BLT_TRACE("Total Mutation Allocations: %ld; Bytes %s; Average %s", mutation_allocations_v, + blt::string::bytes_to_pretty(blt::gp::mutation_allocations.get_value()).c_str(), + blt::string::bytes_to_pretty(blt::average(blt::gp::mutation_allocations.get_value(), mutation_allocations_v)).c_str()); + BLT_TRACE("Total Reproduction Allocations: %ld; Bytes %s; Average %s", reproduction_allocations_v, + blt::string::bytes_to_pretty(blt::gp::reproduction_allocations.get_value()).c_str(), + blt::string::bytes_to_pretty(blt::average(blt::gp::reproduction_allocations.get_value(), reproduction_allocations_v)).c_str()); + BLT_TRACE("Percent Crossover calls allocate? %lf%%", blt::average(crossover_allocations_v, crossover_calls_v) * 100); + BLT_TRACE("Percent Mutation calls allocate? %lf%%", blt::average(mutation_allocations_v, mutation_calls_v) * 100); + BLT_TRACE("Percent Reproduction calls allocate? %lf%%", blt::average(reproduction_allocations_v, reproduction_calls_v) * 100); #endif return 0; diff --git a/include/blt/gp/fwdecl.h b/include/blt/gp/fwdecl.h index 7655dd2..e0cea23 100644 --- a/include/blt/gp/fwdecl.h +++ b/include/blt/gp/fwdecl.h @@ -30,12 +30,18 @@ namespace blt::gp { #ifdef BLT_TRACK_ALLOCATIONS inline allocation_tracker_t tracker; + + // population gen specifics inline call_tracker_t crossover_calls; inline call_tracker_t mutation_calls; inline call_tracker_t reproduction_calls; inline call_tracker_t crossover_allocations; inline call_tracker_t mutation_allocations; inline call_tracker_t reproduction_allocations; + + // for evaluating fitness + inline call_tracker_t evaluation_calls; + inline call_tracker_t evaluation_allocations; #endif class gp_program; diff --git a/include/blt/gp/program.h b/include/blt/gp/program.h index ff8fb8e..b3b574b 100644 --- a/include/blt/gp/program.h +++ b/include/blt/gp/program.h @@ -67,7 +67,7 @@ namespace blt::gp } }; - struct operator_info + struct operator_info_t { // types of the arguments tracked_vector argument_types; @@ -79,6 +79,12 @@ namespace blt::gp detail::operator_func_t func; }; + struct operator_metadata_t + { + blt::size_t arg_size_bytes = 0; + blt::size_t return_size_bytes = 0; + }; + struct program_operator_storage_t { // indexed from return TYPE ID, returns index of operator @@ -87,7 +93,8 @@ namespace blt::gp blt::expanding_buffer>> operators_ordered_terminals; // indexed from OPERATOR ID (operator number) blt::hashset_t ephemeral_leaf_operators; - tracked_vector operators; + tracked_vector operators; + tracked_vector operator_metadata; tracked_vector print_funcs; tracked_vector destroy_funcs; tracked_vector> names; @@ -110,11 +117,9 @@ namespace blt::gp template program_operator_storage_t& build(Operators& ... operators) { - tracked_vector sizes; - (sizes.push_back(add_operator(operators)), ...); blt::size_t largest = 0; - for (auto v : sizes) - largest = std::max(v, largest); + operator_metadata_t meta; + ((meta = add_operator(operators), largest = std::max(std::max(meta.arg_size_bytes, meta.return_size_bytes), largest)), ...); storage.eval_func = [&operators..., largest](const tree_t& tree, void* context) -> evaluation_context& { const auto& ops = tree.get_operations(); @@ -214,14 +219,18 @@ namespace blt::gp (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))), ...); + operator_metadata_t meta; + if constexpr (sizeof...(Args) != 0) + { + meta.arg_size_bytes = (stack_allocator::aligned_size(sizeof(Args)) + ...); + } + meta.return_size_bytes = sizeof(Return); auto return_type_id = storage.system.get_type().id(); auto operator_id = blt::gp::operator_id(storage.operators.size()); op.id = operator_id; - operator_info info; + operator_info_t info; if constexpr (sizeof...(Args) > 0) { @@ -240,6 +249,7 @@ namespace blt::gp BLT_ASSERT(info.argc.argc_context - info.argc.argc <= 1 && "Cannot pass multiple context as arguments!"); storage.operators.push_back(info); + storage.operator_metadata.push_back(meta); storage.print_funcs.push_back([&op](std::ostream& out, stack_allocator& stack) { if constexpr (blt::meta::is_streamable_v) { @@ -267,11 +277,11 @@ namespace blt::gp storage.names.push_back(op.get_name()); if (op.is_ephemeral()) storage.ephemeral_leaf_operators.insert(operator_id); - return total_size_required * 2; + return meta; } template - void add_non_context_argument(decltype(operator_info::argument_types)& types) + void add_non_context_argument(decltype(operator_info_t::argument_types)& types) { if constexpr (!std::is_same_v>) { @@ -386,6 +396,12 @@ namespace blt::gp #ifdef BLT_TRACK_ALLOCATIONS blt::gp::tracker.stop_measurement(fitness_alloc); fitness_alloc.pretty_print("Fitness"); + evaluation_calls.call(); + evaluation_calls.set_value(std::max(evaluation_calls.get_value(), fitness_alloc.getAllocatedByteDifference())); + if (fitness_alloc.getAllocatedByteDifference() > 0) + { + evaluation_allocations.call(fitness_alloc.getAllocatedByteDifference()); + } #endif } @@ -475,9 +491,7 @@ namespace blt::gp mutation_selection.pre_process(*this, current_pop); reproduction_selection.pre_process(*this, current_pop); - perform_elitism(args, next_pop); - - blt::size_t start = config.elites; + blt::size_t start = perform_elitism(args, next_pop); while (start < config.population_size) { @@ -566,10 +580,8 @@ namespace blt::gp mutation_selection.pre_process(*this, current_pop); if (&crossover_selection != &reproduction_selection) reproduction_selection.pre_process(*this, current_pop); - - perform_elitism(args, next_pop); - - thread_helper.next_gen_left -= config.elites; + auto elite_amount = perform_elitism(args, next_pop); + thread_helper.next_gen_left -= elite_amount; } thread_helper.barrier.wait(); @@ -653,7 +665,7 @@ namespace blt::gp return storage.system; } - [[nodiscard]] inline operator_info& get_operator_info(operator_id id) + [[nodiscard]] inline operator_info_t& get_operator_info(operator_id id) { return storage.operators[id]; } @@ -723,8 +735,10 @@ namespace blt::gp return a.second > b.second; }); - for (blt::size_t i = 0; i < size; i++) + for (blt::size_t i = 0; i < std::min(size, config.population_size); i++) arr[i] = values[i].first; + for (blt::size_t i = std::min(size, config.population_size); i < size; i++) + arr[i] = 0; return arr; } @@ -791,7 +805,7 @@ namespace blt::gp struct concurrency_storage { - tracked_vector> threads; + std::vector> threads; std::mutex thread_function_control{}; std::condition_variable thread_function_condition{}; diff --git a/include/blt/gp/selection.h b/include/blt/gp/selection.h index 38c1f81..7c4c67b 100644 --- a/include/blt/gp/selection.h +++ b/include/blt/gp/selection.h @@ -41,7 +41,7 @@ namespace blt::gp 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) + if (config.elites > 0 && current_pop.get_individuals().size() >= config.elites) { static thread_local tracked_vector> values; values.clear(); @@ -70,7 +70,9 @@ namespace blt::gp for (blt::size_t i = 0; i < config.elites; i++) next_pop.get_individuals()[i].copy_fast(current_pop.get_individuals()[values[i].first].tree); + return config.elites; } + return 0ul; }; template diff --git a/include/blt/gp/stack.h b/include/blt/gp/stack.h index 6ee7e0a..2a73404 100644 --- a/include/blt/gp/stack.h +++ b/include/blt/gp/stack.h @@ -125,7 +125,7 @@ namespace blt::gp { if (bytes == 0) return; - if (size_ < bytes + bytes_stored) + if (bytes + bytes_stored >= size_) expand(bytes + size_); std::memcpy(data_ + bytes_stored, stack.data_ + (stack.bytes_stored - bytes), bytes); bytes_stored += bytes; @@ -135,7 +135,7 @@ namespace blt::gp { if (bytes == 0 || data == nullptr) return; - if (size_ < bytes + bytes_stored) + if (bytes + bytes_stored >= size_) expand(bytes + size_); std::memcpy(data_ + bytes_stored, data, bytes); bytes_stored += bytes; diff --git a/include/blt/gp/stats.h b/include/blt/gp/stats.h index d08800b..5d87e02 100644 --- a/include/blt/gp/stats.h +++ b/include/blt/gp/stats.h @@ -130,7 +130,7 @@ namespace blt::gp return tl.allocations.size(); } - void await_completion(blt::u64 required_threads) + void await_thread_loading_complete(blt::u64 required_threads) { std::unique_lock lock(tl.mutex); tl.var.wait(lock, [this, required_threads]() { diff --git a/lib/blt b/lib/blt index ab482f1..a7645d9 160000 --- a/lib/blt +++ b/lib/blt @@ -1 +1 @@ -Subproject commit ab482f1a1c5782bd3501428f26c02f0bb4729946 +Subproject commit a7645d9ddec57ecaad525b48a30f8001adcf75e8 diff --git a/src/program.cpp b/src/program.cpp index e8032a9..248d658 100644 --- a/src/program.cpp +++ b/src/program.cpp @@ -69,7 +69,7 @@ namespace blt::gp thread_helper.threads.emplace_back(new std::thread([i, this]() { #ifdef BLT_TRACK_ALLOCATIONS tracker.reserve(); - tracker.await_completion(config.threads); + tracker.await_thread_loading_complete(config.threads); #endif std::function* execution_function = nullptr; while (!should_thread_terminate()) @@ -91,7 +91,7 @@ namespace blt::gp })); } #ifdef BLT_TRACK_ALLOCATIONS - tracker.await_completion(config.threads); + tracker.await_thread_loading_complete(config.threads); #endif } } \ No newline at end of file diff --git a/src/transformers.cpp b/src/transformers.cpp index ab2b7f9..e2a0685 100644 --- a/src/transformers.cpp +++ b/src/transformers.cpp @@ -171,7 +171,7 @@ namespace blt::gp blt::size_t attempted_point = 0; const auto& crossover_point_type = program.get_operator_info(c1_ops[crossover_point].id); - operator_info* attempted_point_type = nullptr; + operator_info_t* attempted_point_type = nullptr; blt::size_t counter = 0; do